Merge "Rename ANAS to ANS"
diff --git a/Android.bp b/Android.bp
index f1e154c..13c6e85 100644
--- a/Android.bp
+++ b/Android.bp
@@ -152,9 +152,10 @@
         ":libcamera_client_framework_aidl",
         "core/java/android/hardware/IConsumerIrService.aidl",
         "core/java/android/hardware/ISerialManager.aidl",
+        "core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl",
+        "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
         "core/java/android/hardware/biometrics/IBiometricService.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl",
-        "core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
         "core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
         "core/java/android/hardware/display/IDisplayManager.aidl",
         "core/java/android/hardware/display/IDisplayManagerCallback.aidl",
@@ -702,6 +703,7 @@
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.wifi-V1.0-java-constants",
         "android.hardware.radio-V1.0-java",
+        "android.hardware.radio-V1.3-java",
         "android.hardware.usb.gadget-V1.0-java",
     ],
 
diff --git a/api/current.txt b/api/current.txt
index e6a31af..373bc18 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -23,6 +23,7 @@
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
     field public static final java.lang.String BIND_AUTOFILL_SERVICE = "android.permission.BIND_AUTOFILL_SERVICE";
+    field public static final java.lang.String BIND_CALL_REDIRECTION_SERVICE = "android.permission.BIND_CALL_REDIRECTION_SERVICE";
     field public static final deprecated java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
     field public static final java.lang.String BIND_CARRIER_SERVICES = "android.permission.BIND_CARRIER_SERVICES";
     field public static final java.lang.String BIND_CHOOSER_TARGET_SERVICE = "android.permission.BIND_CHOOSER_TARGET_SERVICE";
@@ -280,7 +281,7 @@
     field public static final int allowBackup = 16843392; // 0x1010280
     field public static final int allowClearUserData = 16842757; // 0x1010005
     field public static final int allowEmbedded = 16843765; // 0x10103f5
-    field public static final int allowForceDark = 16844171; // 0x101058b
+    field public static final int allowForceDark = 16844172; // 0x101058c
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -774,11 +775,11 @@
     field public static final int isFeatureSplit = 16844123; // 0x101055b
     field public static final int isGame = 16843764; // 0x10103f4
     field public static final int isIndicator = 16843079; // 0x1010147
-    field public static final int isLightTheme = 16844175; // 0x101058f
+    field public static final int isLightTheme = 16844176; // 0x1010590
     field public static final int isModifier = 16843334; // 0x1010246
     field public static final int isRepeatable = 16843336; // 0x1010248
     field public static final int isScrollContainer = 16843342; // 0x101024e
-    field public static final int isSplitRequired = 16844176; // 0x1010590
+    field public static final int isSplitRequired = 16844177; // 0x1010591
     field public static final int isStatic = 16844122; // 0x101055a
     field public static final int isSticky = 16843335; // 0x1010247
     field public static final int isolatedProcess = 16843689; // 0x10103a9
@@ -938,7 +939,7 @@
     field public static final int minSdkVersion = 16843276; // 0x101020c
     field public static final int minWidth = 16843071; // 0x101013f
     field public static final int minimumHorizontalAngle = 16843901; // 0x101047d
-    field public static final int minimumUiTimeout = 16844174; // 0x101058e
+    field public static final int minimumUiTimeout = 16844175; // 0x101058f
     field public static final int minimumVerticalAngle = 16843902; // 0x101047e
     field public static final int mipMap = 16843725; // 0x10103cd
     field public static final int mirrorForRtl = 16843726; // 0x10103ce
@@ -978,10 +979,10 @@
     field public static final int onClick = 16843375; // 0x101026f
     field public static final int oneshot = 16843159; // 0x1010197
     field public static final int opacity = 16843550; // 0x101031e
-    field public static final int opticalInsetBottom = 16844170; // 0x101058a
-    field public static final int opticalInsetLeft = 16844167; // 0x1010587
-    field public static final int opticalInsetRight = 16844169; // 0x1010589
-    field public static final int opticalInsetTop = 16844168; // 0x1010588
+    field public static final int opticalInsetBottom = 16844171; // 0x101058b
+    field public static final int opticalInsetLeft = 16844168; // 0x1010588
+    field public static final int opticalInsetRight = 16844170; // 0x101058a
+    field public static final int opticalInsetTop = 16844169; // 0x1010589
     field public static final int order = 16843242; // 0x10101ea
     field public static final int orderInCategory = 16843231; // 0x10101df
     field public static final int ordering = 16843490; // 0x10102e2
@@ -997,6 +998,7 @@
     field public static final int overlapAnchor = 16843874; // 0x1010462
     field public static final int overridesImplicitlyEnabledSubtype = 16843682; // 0x10103a2
     field public static final int packageNames = 16843649; // 0x1010381
+    field public static final int packageType = 16844167; // 0x1010587
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
     field public static final int paddingEnd = 16843700; // 0x10103b4
@@ -1306,7 +1308,7 @@
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
-    field public static final int supportsAmbientMode = 16844172; // 0x101058c
+    field public static final int supportsAmbientMode = 16844173; // 0x101058d
     field public static final int supportsAssist = 16844016; // 0x10104f0
     field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
     field public static final int supportsLocalInteraction = 16844047; // 0x101050f
@@ -6735,14 +6737,21 @@
     field public static final java.lang.String EXTRA_PROVISIONING_SKIP_ENCRYPTION = "android.app.extra.PROVISIONING_SKIP_ENCRYPTION";
     field public static final java.lang.String EXTRA_PROVISIONING_SKIP_USER_CONSENT = "android.app.extra.PROVISIONING_SKIP_USER_CONSENT";
     field public static final java.lang.String EXTRA_PROVISIONING_TIME_ZONE = "android.app.extra.PROVISIONING_TIME_ZONE";
+    field public static final java.lang.String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY = "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY";
+    field public static final java.lang.String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE";
+    field public static final java.lang.String EXTRA_PROVISIONING_WIFI_DOMAIN = "android.app.extra.PROVISIONING_WIFI_DOMAIN";
+    field public static final java.lang.String EXTRA_PROVISIONING_WIFI_EAP_METHOD = "android.app.extra.PROVISIONING_WIFI_EAP_METHOD";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_HIDDEN = "android.app.extra.PROVISIONING_WIFI_HIDDEN";
+    field public static final java.lang.String EXTRA_PROVISIONING_WIFI_IDENTITY = "android.app.extra.PROVISIONING_WIFI_IDENTITY";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PAC_URL = "android.app.extra.PROVISIONING_WIFI_PAC_URL";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PASSWORD = "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+    field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PHASE2_AUTH = "android.app.extra.PROVISIONING_WIFI_PHASE2_AUTH";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_BYPASS = "android.app.extra.PROVISIONING_WIFI_PROXY_BYPASS";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_HOST = "android.app.extra.PROVISIONING_WIFI_PROXY_HOST";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_PROXY_PORT = "android.app.extra.PROVISIONING_WIFI_PROXY_PORT";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SECURITY_TYPE = "android.app.extra.PROVISIONING_WIFI_SECURITY_TYPE";
     field public static final java.lang.String EXTRA_PROVISIONING_WIFI_SSID = "android.app.extra.PROVISIONING_WIFI_SSID";
+    field public static final java.lang.String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE = "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
     field public static final int FLAG_EVICT_CREDENTIAL_ENCRYPTION_KEY = 1; // 0x1
     field public static final int FLAG_MANAGED_CAN_ACCESS_PARENT = 2; // 0x2
     field public static final int FLAG_PARENT_CAN_ACCESS_MANAGED = 1; // 0x1
@@ -13397,6 +13406,8 @@
     method public void drawCircle(float, float, float, android.graphics.Paint);
     method public void drawColor(int);
     method public void drawColor(int, android.graphics.PorterDuff.Mode);
+    method public void drawDoubleRoundRect(android.graphics.RectF, float, float, android.graphics.RectF, float, float, android.graphics.Paint);
+    method public void drawDoubleRoundRect(android.graphics.RectF, float[], android.graphics.RectF, float[], android.graphics.Paint);
     method public void drawLine(float, float, float, float, android.graphics.Paint);
     method public void drawLines(float[], int, int, android.graphics.Paint);
     method public void drawLines(float[], android.graphics.Paint);
@@ -13748,6 +13759,7 @@
     method public static android.graphics.ImageDecoder.Source createSource(android.content.res.AssetManager, java.lang.String);
     method public static android.graphics.ImageDecoder.Source createSource(java.nio.ByteBuffer);
     method public static android.graphics.ImageDecoder.Source createSource(java.io.File);
+    method public static android.graphics.ImageDecoder.Source createSource(java.util.concurrent.Callable<android.content.res.AssetFileDescriptor>);
     method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
     method public static android.graphics.Bitmap decodeBitmap(android.graphics.ImageDecoder.Source) throws java.io.IOException;
     method public static android.graphics.drawable.Drawable decodeDrawable(android.graphics.ImageDecoder.Source, android.graphics.ImageDecoder.OnHeaderDecodedListener) throws java.io.IOException;
@@ -15963,7 +15975,11 @@
 package android.hardware.biometrics {
 
   public class BiometricManager {
-    method public boolean hasEnrolledBiometrics();
+    method public int canAuthenticate();
+    field public static final int ERROR_NONE = 0; // 0x0
+    field public static final int ERROR_NO_BIOMETRICS = 11; // 0xb
+    field public static final int ERROR_NO_HARDWARE = 12; // 0xc
+    field public static final int ERROR_UNAVAILABLE = 1; // 0x1
   }
 
   public class BiometricPrompt {
@@ -36955,6 +36971,7 @@
     field public static final java.lang.String ACTION_APPLICATION_DEVELOPMENT_SETTINGS = "android.settings.APPLICATION_DEVELOPMENT_SETTINGS";
     field public static final java.lang.String ACTION_APPLICATION_SETTINGS = "android.settings.APPLICATION_SETTINGS";
     field public static final java.lang.String ACTION_APP_NOTIFICATION_SETTINGS = "android.settings.APP_NOTIFICATION_SETTINGS";
+    field public static final java.lang.String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
     field public static final java.lang.String ACTION_BATTERY_SAVER_SETTINGS = "android.settings.BATTERY_SAVER_SETTINGS";
     field public static final java.lang.String ACTION_BLUETOOTH_SETTINGS = "android.settings.BLUETOOTH_SETTINGS";
     field public static final java.lang.String ACTION_CAPTIONING_SETTINGS = "android.settings.CAPTIONING_SETTINGS";
@@ -43343,6 +43360,36 @@
 
 }
 
+package android.telephony.emergency {
+
+  public final class EmergencyNumber implements android.os.Parcelable {
+    method public int describeContents();
+    method public java.lang.String getCountryIso();
+    method public int getEmergencyNumberSourceBitmask();
+    method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
+    method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
+    method public int getEmergencyServiceCategoryBitmask();
+    method public java.lang.String getNumber();
+    method public boolean isFromSources(int);
+    method public boolean isInEmergencyServiceCategories(int);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+    field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
+    field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
+    field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1
+    field public static final int EMERGENCY_NUMBER_SOURCE_SIM = 2; // 0x2
+    field public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = 64; // 0x40
+    field public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE = 2; // 0x2
+    field public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE = 4; // 0x4
+    field public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD = 8; // 0x8
+    field public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = 32; // 0x20
+    field public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE = 16; // 0x10
+    field public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = 1; // 0x1
+    field public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED = 0; // 0x0
+  }
+
+}
+
 package android.telephony.euicc {
 
   public final class DownloadableSubscription implements android.os.Parcelable {
@@ -46641,7 +46688,12 @@
   }
 
   public final class DisplayCutout {
-    ctor public DisplayCutout(android.graphics.Rect, java.util.List<android.graphics.Rect>);
+    ctor public DisplayCutout(android.graphics.Insets, android.graphics.Rect, android.graphics.Rect, android.graphics.Rect, android.graphics.Rect);
+    ctor public deprecated DisplayCutout(android.graphics.Rect, java.util.List<android.graphics.Rect>);
+    method public android.graphics.Rect getBoundingRectBottom();
+    method public android.graphics.Rect getBoundingRectLeft();
+    method public android.graphics.Rect getBoundingRectRight();
+    method public android.graphics.Rect getBoundingRectTop();
     method public java.util.List<android.graphics.Rect> getBoundingRects();
     method public int getSafeInsetBottom();
     method public int getSafeInsetLeft();
diff --git a/api/system-current.txt b/api/system-current.txt
index 041b497..e134554 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5330,9 +5330,12 @@
   }
 
   public class ServiceState implements android.os.Parcelable {
+    method public android.telephony.NetworkRegistrationState getNetworkRegistrationState(int, int);
     method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates();
-    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
-    method public android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+    method public deprecated java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStates(int);
+    method public deprecated android.telephony.NetworkRegistrationState getNetworkRegistrationStates(int, int);
+    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForDomain(int);
+    method public java.util.List<android.telephony.NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(int);
   }
 
   public final class SmsManager {
@@ -5849,7 +5852,7 @@
     field public static final java.lang.String EXTRA_CODEC = "Codec";
     field public static final java.lang.String EXTRA_DIALSTRING = "dialstring";
     field public static final java.lang.String EXTRA_DISPLAY_TEXT = "DisplayText";
-    field public static final java.lang.String EXTRA_E_CALL = "e_call";
+    field public static final java.lang.String EXTRA_EMERGENCY_CALL = "e_call";
     field public static final java.lang.String EXTRA_IS_CALL_PULL = "CallPull";
     field public static final java.lang.String EXTRA_OI = "oi";
     field public static final java.lang.String EXTRA_OIR = "oir";
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 988ffc4..9d83afd 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -31,6 +31,7 @@
 import "frameworks/base/core/proto/android/view/enums.proto";
 import "frameworks/base/proto/src/stats_enums.proto";
 import "frameworks/base/core/proto/android/service/procstats.proto";
+import "frameworks/base/core/proto/android/internal/powerprofile.proto";
 
 /**
  * The master atom class. This message defines all of the available
@@ -175,10 +176,12 @@
         DirectoryUsage directory_usage = 10026;
         AppSize app_size = 10027;
         CategorySize category_size = 10028;
+        ProcStats proc_stats = 10029;
         BatteryVoltage battery_voltage = 10030;
         NumFingerprints num_fingerprints = 10031;
-        ProcStats proc_stats = 10029;
         DiskIo disk_io = 10032;
+        PowerProfile power_profile = 10033;
+        ProcStats proc_stats_pkg_proc = 10034;
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -2665,3 +2668,11 @@
 message ProcStats {
     optional android.service.procstats.ProcessStatsSectionProto proc_stats_section = 1;
 }
+
+/**
+ * power_profile.xml and other constants for power model calculations.
+ * Pulled from PowerProfile.java
+ */
+message PowerProfile {
+    optional com.android.internal.os.PowerProfileProto power_profile_proto = 1;
+}
\ No newline at end of file
diff --git a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
index 6d7bba0..3eb05a9 100644
--- a/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
+++ b/cmds/statsd/src/external/StatsCompanionServicePuller.cpp
@@ -54,7 +54,7 @@
         vector<StatsLogEventWrapper> returned_value;
         Status status = statsCompanionServiceCopy->pullData(mTagId, &returned_value);
         if (!status.isOk()) {
-            ALOGW("StatsCompanionServicePuller::pull failed to pull for %d", mTagId);
+            ALOGW("StatsCompanionServicePuller::pull failed for %d", mTagId);
             return false;
         }
         data->clear();
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index fd86714..dab64ca 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -211,12 +211,18 @@
         // ProcStats.
         {android::util::PROC_STATS,
          {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}},
+        // ProcStatsPkgProc.
+        {android::util::PROC_STATS_PKG_PROC,
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS_PKG_PROC)}},
         // Disk I/O stats per uid.
         {android::util::DISK_IO,
          {{2,3,4,5,6,7,8,9,10,11},
           {},
           3 * NS_PER_SEC,
           new StatsCompanionServicePuller(android::util::DISK_IO)}},
+        // PowerProfile constants for power model calculations.
+        {android::util::POWER_PROFILE,
+         {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::POWER_PROFILE)}},
 };
 
 StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 4325f0f..8b1f5cb 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -489,6 +489,9 @@
                                                              {"AID_RESERVED_DISK", 1065},
                                                              {"AID_STATSD", 1066},
                                                              {"AID_INCIDENTD", 1067},
+                                                             {"AID_SECURE_ELEMENT", 1068},
+                                                             {"AID_LMKD", 1069},
+                                                             {"AID_LLKD", 1070},
                                                              {"AID_SHELL", 2000},
                                                              {"AID_CACHE", 2001},
                                                              {"AID_DIAG", 2002}};
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index d490701..ced65f2 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -327,7 +327,7 @@
     EXPECT_EQ(33, item5.mValue.int_value);
 
     const FieldValue& item6 = event1.getValues()[6];
-    EXPECT_EQ(0x2020382, item6.mField.getField());
+    EXPECT_EQ(0x2020383, item6.mField.getField());
     EXPECT_EQ(Type::LONG, item6.mValue.getType());
     EXPECT_EQ(678L, item6.mValue.long_value);
 
@@ -337,7 +337,7 @@
     EXPECT_EQ(44, item7.mValue.int_value);
 
     const FieldValue& item8 = event1.getValues()[8];
-    EXPECT_EQ(0x2020482, item8.mField.getField());
+    EXPECT_EQ(0x2020483, item8.mField.getField());
     EXPECT_EQ(Type::LONG, item8.mValue.getType());
     EXPECT_EQ(890L, item8.mValue.long_value);
 
@@ -347,7 +347,7 @@
     EXPECT_EQ(1, item9.mValue.int_value);
 
     const FieldValue& item10 = event1.getValues()[10];
-    EXPECT_EQ(0x2020583, item10.mField.getField());
+    EXPECT_EQ(0x2020584, item10.mField.getField());
     EXPECT_EQ(Type::STRING, item10.mValue.getType());
     EXPECT_EQ("test2", item10.mValue.str_value);
 
@@ -357,7 +357,7 @@
     EXPECT_EQ(2, item11.mValue.int_value);
 
     const FieldValue& item12 = event1.getValues()[12];
-    EXPECT_EQ(0x2020683, item12.mField.getField());
+    EXPECT_EQ(0x2020684, item12.mField.getField());
     EXPECT_EQ(Type::STRING, item12.mValue.getType());
     EXPECT_EQ("test1", item12.mValue.str_value);
 
@@ -367,7 +367,7 @@
     EXPECT_EQ(111, item13.mValue.int_value);
 
     const FieldValue& item14 = event1.getValues()[14];
-    EXPECT_EQ(0x2020784, item14.mField.getField());
+    EXPECT_EQ(0x2020785, item14.mField.getField());
     EXPECT_EQ(Type::FLOAT, item14.mValue.getType());
     EXPECT_EQ(2.2f, item14.mValue.float_value);
 
@@ -377,7 +377,7 @@
     EXPECT_EQ(222, item15.mValue.int_value);
 
     const FieldValue& item16 = event1.getValues()[16];
-    EXPECT_EQ(0x2028884, item16.mField.getField());
+    EXPECT_EQ(0x2028885, item16.mField.getField());
     EXPECT_EQ(Type::FLOAT, item16.mValue.getType());
     EXPECT_EQ(1.1f, item16.mValue.float_value);
 }
diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java
index a3b3a9f..25cd342 100644
--- a/core/java/android/accounts/AbstractAccountAuthenticator.java
+++ b/core/java/android/accounts/AbstractAccountAuthenticator.java
@@ -17,7 +17,6 @@
 package android.accounts;
 
 import android.Manifest;
-import android.annotation.SystemApi;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -32,8 +31,8 @@
 
 /**
  * Abstract base class for creating AccountAuthenticators.
- * In order to be an authenticator one must extend this class, provider implementations for the
- * abstract methods and write a service that returns the result of {@link #getIBinder()}
+ * In order to be an authenticator one must extend this class, provide implementations for the
+ * abstract methods, and write a service that returns the result of {@link #getIBinder()}
  * in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked
  * with an intent with action {@link AccountManager#ACTION_AUTHENTICATOR_INTENT}. This service
  * must specify the following intent filter and metadata tags in its AndroidManifest.xml file
@@ -974,7 +973,8 @@
      *
      * @param response to send the result back to the AccountManager, will never be null.
      * @param account the account to check, will never be null
-     * @param statusToken a String of token to check if update of credentials is suggested.
+     * @param statusToken a String of token which can be used to check the status of locally
+     *            stored credentials and if update of credentials is suggested
      * @return a Bundle result or null if the result is to be returned via the response. The result
      *         will contain either:
      *         <ul>
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 294a3ec..76b90f5 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -245,4 +245,6 @@
     public abstract ComponentName startServiceInPackage(int uid, Intent service,
             String resolvedType, boolean fgRequired, String callingPackage, int userId)
             throws TransactionTooLargeException;
+
+    public abstract void disconnectActivityFromServices(Object connectionHolder);
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 34c2282..1144e26 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1760,7 +1760,7 @@
     /**
      * Like {@link #execStartActivity(android.content.Context, android.os.IBinder,
      * android.os.IBinder, String, android.content.Intent, int, android.os.Bundle)},
-     * but for calls from a {#link Fragment}.
+     * but for calls from a {@link Fragment}.
      * 
      * @param who The Context from which the activity is being started.
      * @param contextThread The main thread of the Context from which the activity
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 07a8504..3f07024 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -48,6 +48,7 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
@@ -722,10 +723,15 @@
     public List<NotificationChannelGroup> getNotificationChannelGroups() {
         INotificationManager service = getService();
         try {
-            return service.getNotificationChannelGroups(mContext.getPackageName()).getList();
+            final ParceledListSlice<NotificationChannelGroup> parceledList =
+                    service.getNotificationChannelGroups(mContext.getPackageName());
+            if (parceledList != null) {
+                return parceledList.getList();
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
+        return new ArrayList<>();
     }
 
     /**
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 67acfe9..16f6bda 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -21,13 +21,13 @@
 import android.annotation.UnsupportedAppUsage;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ContextWrapper;
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Build;
-import android.os.RemoteException;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.util.Log;
 
 import java.io.FileDescriptor;
@@ -391,7 +391,7 @@
      * don't recreate until a future explicit call to
      * {@link Context#startService Context.startService(Intent)}.  The
      * service will not receive a {@link #onStartCommand(Intent, int, int)}
-     * call with a null Intent because it will not be re-started if there
+     * call with a null Intent because it will not be restarted if there
      * are no pending Intents to deliver.
      * 
      * <p>This mode makes sense for things that want to do some work as a
@@ -416,7 +416,7 @@
      * redelivery until the service calls {@link #stopSelf(int)} with the
      * start ID provided to {@link #onStartCommand}.  The
      * service will not receive a {@link #onStartCommand(Intent, int, int)}
-     * call with a null Intent because it will will only be re-started if
+     * call with a null Intent because it will only be restarted if
      * it is not finished processing all Intents sent to it (and any such
      * pending events will be delivered at the point of restart).
      */
diff --git a/core/java/android/app/SmsAppService.java b/core/java/android/app/SmsAppService.java
index 3f2b025..3829d71 100644
--- a/core/java/android/app/SmsAppService.java
+++ b/core/java/android/app/SmsAppService.java
@@ -24,21 +24,42 @@
  * it so that the process is always running, which allows the app to have a persistent connection
  * to the server.
  *
- * <p>The service must have {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
+ * <p>The service must have an {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE}
  * action in the intent handler, and be protected with
  * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to
  * be exported.
  *
- * <p>Apps can use
+ * <p>The service must be associated with a non-main process, meaning it must have an
+ * {@code android:process} tag in its manifest entry.
+ *
+ * <p>An app can use
  * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)}
- * to disable/enable the service. Apps should use it to disable the service when it no longer needs
- * to be running.
+ * to disable or enable the service. An app should use it to disable the service when it no longer
+ * needs to be running.
  *
  * <p>When the owner process crashes, the service will be re-bound automatically after a
  * back-off.
  *
  * <p>Note the process may still be killed if the system is under heavy memory pressure, in which
  * case the process will be re-started later.
+ *
+ * <p>Example: First, define a subclass in the application:
+ * <pre>
+ * public class MySmsAppService extends SmsAppService {
+ * }
+ * </pre>
+ * Then, declare it in its {@code AndroidManifest.xml}:
+ * <pre>
+ * &lt;service
+ *    android:name=".MySmsAppService"
+ *    android:exported="false"
+ *    android:process=":persistent"
+ *    android:permission="android.permission.BIND_SMS_APP_SERVICE"&gt;
+ *    &lt;intent-filter&gt;
+ *        &lt;action android:name="android.telephony.action.SMS_APP_SERVICE" /&gt;
+ *    &lt;/intent-filter&gt;
+ * &lt;/service&gt;
+ * </pre>
  */
 public class SmsAppService extends Service {
     private final ISmsAppService mImpl;
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 1839263..09ab671 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -664,8 +664,8 @@
 
     /**
      * A String extra indicating the security type of the wifi network in
-     * {@link #EXTRA_PROVISIONING_WIFI_SSID} and could be one of {@code NONE}, {@code WPA} or
-     * {@code WEP}.
+     * {@link #EXTRA_PROVISIONING_WIFI_SSID} and could be one of {@code NONE}, {@code WPA},
+     * {@code WEP} or {@code EAP}.
      *
      * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
      * provisioning via an NFC bump.
@@ -680,8 +680,89 @@
      * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
      * provisioning via an NFC bump.
      */
-    public static final String EXTRA_PROVISIONING_WIFI_PASSWORD
-        = "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+    public static final String EXTRA_PROVISIONING_WIFI_PASSWORD =
+            "android.app.extra.PROVISIONING_WIFI_PASSWORD";
+
+    /**
+     * The EAP method of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}
+     * and could be one of {@code PEAP}, {@code TLS}, {@code TTLS}, {@code PWD}, {@code SIM},
+     * {@code AKA} or {@code AKA_PRIME}. This is only used if the
+     * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump. It can also be used for QR code provisioning.
+     */
+    public static final String EXTRA_PROVISIONING_WIFI_EAP_METHOD =
+            "android.app.extra.PROVISIONING_WIFI_EAP_METHOD";
+
+    /**
+     * The phase 2 authentication of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}
+     * and could be one of {@code NONE}, {@code PAP}, {@code MSCHAP}, {@code MSCHAPV2}, {@code GTC},
+     * {@code SIM}, {@code AKA} or {@code AKA_PRIME}. This is only used if the
+     * {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump. It can also be used for QR code provisioning.
+     */
+    public static final String EXTRA_PROVISIONING_WIFI_PHASE2_AUTH =
+            "android.app.extra.PROVISIONING_WIFI_PHASE2_AUTH";
+
+    /**
+     * The CA certificate of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This should
+     * be an X.509 certificate Base64 encoded DER format, ie. PEM representation of a certificate
+     * without header, footer and line breaks. <a href=
+     * "https://tools.ietf.org/html/rfc7468"> More information</a> This is only
+     * used if the {@link
+     * #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump. It can also be used for QR code provisioning.
+     */
+    public static final String EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE =
+            "android.app.extra.PROVISIONING_WIFI_CA_CERTIFICATE";
+
+    /**
+     * The user certificate of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This
+     * should be an X.509 certificate and private key Base64 encoded DER format, ie. PEM
+     * representation of a certificate and key without header, footer and line breaks. <a href=
+     * "https://tools.ietf.org/html/rfc7468"> More information</a> This is only
+     * used if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump. It can also be used for QR code provisioning.
+     */
+    public static final String EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE =
+            "android.app.extra.PROVISIONING_WIFI_USER_CERTIFICATE";
+
+    /**
+     * The identity of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is only used
+     * if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump. It can also be used for QR code provisioning.
+     */
+    public static final String EXTRA_PROVISIONING_WIFI_IDENTITY =
+            "android.app.extra.PROVISIONING_WIFI_IDENTITY";
+
+    /**
+     * The anonymous identity of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is
+     * only used if the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump. It can also be used for QR code provisioning.
+     */
+
+    public static final String EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY =
+            "android.app.extra.PROVISIONING_WIFI_ANONYMOUS_IDENTITY";
+    /**
+     * The domain of the wifi network in {@link #EXTRA_PROVISIONING_WIFI_SSID}. This is only used if
+     * the {@link #EXTRA_PROVISIONING_WIFI_SECURITY_TYPE} is {@code EAP}.
+     *
+     * <p>Use in an NFC record with {@link #MIME_TYPE_PROVISIONING_NFC} that starts device owner
+     * provisioning via an NFC bump. It can also be used for QR code provisioning.
+     */
+    public static final String EXTRA_PROVISIONING_WIFI_DOMAIN =
+            "android.app.extra.PROVISIONING_WIFI_DOMAIN";
 
     /**
      * A String extra holding the proxy host for the wifi network in
@@ -1067,8 +1148,22 @@
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_PORT} (convert to String), optional</li>
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PROXY_BYPASS}, optional</li>
      * <li>{@link #EXTRA_PROVISIONING_WIFI_PAC_URL}, optional</li>
-     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional, supported from
-     * {@link android.os.Build.VERSION_CODES#M} </li></ul>
+     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#M} </li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_EAP_METHOD}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#Q}</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_PHASE2_AUTH}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#Q}</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_CA_CERTIFICATE}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#Q}</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_USER_CERTIFICATE}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#Q}</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_IDENTITY}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#Q}</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_ANONYMOUS_IDENTITY}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#Q}</li>
+     * <li>{@link #EXTRA_PROVISIONING_WIFI_DOMAIN}, optional, supported from {@link
+     * android.os.Build.VERSION_CODES#Q}</li></ul>
      *
      * <p>
      * As of {@link android.os.Build.VERSION_CODES#M}, the properties should contain
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 654bfaf..8e6a385 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2973,7 +2973,7 @@
      * socket will be encrypted.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
      * {@link BluetoothServerSocket}.
-     * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {#link
+     * <p>The system will assign a dynamic PSM value. This PSM value can be read from the {@link
      * BluetoothServerSocket#getPsm()} and this value will be released when this server socket is
      * closed, Bluetooth is turned off, or the application exits unexpectedly.
      * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
@@ -3031,7 +3031,7 @@
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming connections from a listening
      * {@link BluetoothServerSocket}.
      * <p>The system will assign a dynamic protocol/service multiplexer (PSM) value. This PSM value
-     * can be read from the {#link BluetoothServerSocket#getPsm()} and this value will be released
+     * can be read from the {@link BluetoothServerSocket#getPsm()} and this value will be released
      * when this server socket is closed, Bluetooth is turned off, or the application exits
      * unexpectedly.
      * <p>The mechanism of disclosing the assigned dynamic PSM value to the initiating peer is
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 73e98cd..30d5fbc 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1596,7 +1596,7 @@
      * For example, for Bluetooth 2.1 devices, if any of the devices does not
      * have an input and output capability or just has the ability to
      * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {#link createInsecureRfcommSocket}.
+     * In such a case, use {@link createInsecureRfcommSocket}.
      * For more details, refer to the Security Model section 5.2 (vol 3) of
      * Bluetooth Core Specification version 2.1 + EDR.
      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
@@ -1631,7 +1631,7 @@
      * For example, for Bluetooth 2.1 devices, if any of the devices does not
      * have an input and output capability or just has the ability to
      * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {#link createInsecureRfcommSocket}.
+     * In such a case, use {@link createInsecureRfcommSocket}.
      * For more details, refer to the Security Model section 5.2 (vol 3) of
      * Bluetooth Core Specification version 2.1 + EDR.
      * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
@@ -1688,7 +1688,7 @@
      * For example, for Bluetooth 2.1 devices, if any of the devices does not
      * have an input and output capability or just has the ability to
      * display a numeric key, a secure socket connection is not possible.
-     * In such a case, use {#link createInsecureRfcommSocketToServiceRecord}.
+     * In such a case, use {@link #createInsecureRfcommSocketToServiceRecord}.
      * For more details, refer to the Security Model section 5.2 (vol 3) of
      * Bluetooth Core Specification version 2.1 + EDR.
      * <p>Hint: If you are connecting to a Bluetooth serial board then try
@@ -1972,7 +1972,7 @@
      * encrypted.
      * <p> Use this socket if an authenticated socket link is possible. Authentication refers
      * to the authentication of the link key to prevent man-in-the-middle type of attacks. When a
-     * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
+     * secure socket connection is not possible, use {@link createInsecureLeL2capCocSocket(int,
      * int)}.
      *
      * @param psm dynamic PSM value from remote device
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index 5fc344a..758c68d 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -203,7 +203,7 @@
     /**
      * Returns the assigned dynamic protocol/service multiplexer (PSM) value for the listening L2CAP
      * Connection-oriented Channel (CoC) server socket. This server socket must be returned by the
-     * {#link BluetoothAdapter.listenUsingL2capChannel()} or {#link
+     * {@link BluetoothAdapter.listenUsingL2capChannel()} or {@link
      * BluetoothAdapter.listenUsingInsecureL2capChannel()}. The returned value is undefined if this
      * method is called on non-L2CAP server sockets.
      *
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index a64eead..a13a438 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2104,7 +2104,11 @@
         // a source of security issues.
         final String encodedPath = uri.getEncodedPath();
         if (encodedPath != null && encodedPath.indexOf("//") != -1) {
-            return uri.buildUpon().encodedPath(encodedPath.replaceAll("//+", "/")).build();
+            final Uri normalized = uri.buildUpon()
+                    .encodedPath(encodedPath.replaceAll("//+", "/")).build();
+            Log.w(TAG, "Normalized " + uri + " to " + normalized
+                    + " to avoid possible security issues");
+            return normalized;
         } else {
             return uri;
         }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index f5431ca..24675d3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -5785,52 +5785,32 @@
             int AUTH = 16;
         }
 
-        /**
-         * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
-         * contains two pieces of information:
-         *   1) the past signing certificates
-         *   2) the flags that APK wants to assign to each of the past signing certificates.
-         *
-         * These flags, which have a one-to-one relationship for the {@code pastSigningCertificates}
-         * collection, represent the second piece of information and are viewed as capabilities.
-         * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
-         * please enforce that." This is useful for situation where this app itself is using its
-         * signing certificate as an authorization mechanism, like whether or not to allow another
-         * app to have its SIGNATURE permission.  An app could specify whether to allow other apps
-         * signed by its old cert 'X' to still get a signature permission it defines, for example.
-         */
-        @Nullable
-        public final int[] pastSigningCertificatesFlags;
-
         /** A representation of unknown signing details. Use instead of null. */
         public static final SigningDetails UNKNOWN =
-                new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null, null);
+                new SigningDetails(null, SignatureSchemeVersion.UNKNOWN, null, null);
 
         @VisibleForTesting
         public SigningDetails(Signature[] signatures,
                 @SignatureSchemeVersion int signatureSchemeVersion,
-                ArraySet<PublicKey> keys, Signature[] pastSigningCertificates,
-                int[] pastSigningCertificatesFlags) {
+                ArraySet<PublicKey> keys, Signature[] pastSigningCertificates) {
             this.signatures = signatures;
             this.signatureSchemeVersion = signatureSchemeVersion;
             this.publicKeys = keys;
             this.pastSigningCertificates = pastSigningCertificates;
-            this.pastSigningCertificatesFlags = pastSigningCertificatesFlags;
         }
 
         public SigningDetails(Signature[] signatures,
                 @SignatureSchemeVersion int signatureSchemeVersion,
-                Signature[] pastSigningCertificates, int[] pastSigningCertificatesFlags)
+                Signature[] pastSigningCertificates)
                 throws CertificateException {
             this(signatures, signatureSchemeVersion, toSigningKeys(signatures),
-                    pastSigningCertificates, pastSigningCertificatesFlags);
+                    pastSigningCertificates);
         }
 
         public SigningDetails(Signature[] signatures,
                 @SignatureSchemeVersion int signatureSchemeVersion)
                 throws CertificateException {
-            this(signatures, signatureSchemeVersion,
-                    null, null);
+            this(signatures, signatureSchemeVersion, null);
         }
 
         public SigningDetails(SigningDetails orig) {
@@ -5844,17 +5824,14 @@
                 this.publicKeys = new ArraySet<>(orig.publicKeys);
                 if (orig.pastSigningCertificates != null) {
                     this.pastSigningCertificates = orig.pastSigningCertificates.clone();
-                    this.pastSigningCertificatesFlags = orig.pastSigningCertificatesFlags.clone();
                 } else {
                     this.pastSigningCertificates = null;
-                    this.pastSigningCertificatesFlags = null;
                 }
             } else {
                 this.signatures = null;
                 this.signatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
                 this.publicKeys = null;
                 this.pastSigningCertificates = null;
-                this.pastSigningCertificatesFlags = null;
             }
         }
 
@@ -5956,7 +5933,7 @@
                     if (Signature.areEffectiveMatch(
                             oldDetails.signatures[0],
                             pastSigningCertificates[i])
-                            && pastSigningCertificatesFlags[i] == flags) {
+                            && pastSigningCertificates[i].getFlags() == flags) {
                         return true;
                     }
                 }
@@ -6006,7 +5983,7 @@
                 for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
                     if (pastSigningCertificates[i].equals(signature)) {
                         if (flags == PAST_CERT_EXISTS
-                                || (flags & pastSigningCertificatesFlags[i]) == flags) {
+                                || (flags & pastSigningCertificates[i].getFlags()) == flags) {
                             return true;
                         }
                     }
@@ -6090,7 +6067,7 @@
                             pastSigningCertificates[i].toByteArray());
                     if (Arrays.equals(sha256Certificate, digest)) {
                         if (flags == PAST_CERT_EXISTS
-                                || (flags & pastSigningCertificatesFlags[i]) == flags) {
+                                || (flags & pastSigningCertificates[i].getFlags()) == flags) {
                             return true;
                         }
                     }
@@ -6127,7 +6104,6 @@
             dest.writeInt(this.signatureSchemeVersion);
             dest.writeArraySet(this.publicKeys);
             dest.writeTypedArray(this.pastSigningCertificates, flags);
-            dest.writeIntArray(this.pastSigningCertificatesFlags);
         }
 
         protected SigningDetails(Parcel in) {
@@ -6136,7 +6112,6 @@
             this.signatureSchemeVersion = in.readInt();
             this.publicKeys = (ArraySet<PublicKey>) in.readArraySet(boot);
             this.pastSigningCertificates = in.createTypedArray(Signature.CREATOR);
-            this.pastSigningCertificatesFlags = in.createIntArray();
         }
 
         public static final Creator<SigningDetails> CREATOR = new Creator<SigningDetails>() {
@@ -6175,9 +6150,6 @@
             if (!Arrays.equals(pastSigningCertificates, that.pastSigningCertificates)) {
                 return false;
             }
-            if (!Arrays.equals(pastSigningCertificatesFlags, that.pastSigningCertificatesFlags)) {
-                return false;
-            }
 
             return true;
         }
@@ -6188,7 +6160,6 @@
             result = 31 * result + signatureSchemeVersion;
             result = 31 * result + (publicKeys != null ? publicKeys.hashCode() : 0);
             result = 31 * result + Arrays.hashCode(pastSigningCertificates);
-            result = 31 * result + Arrays.hashCode(pastSigningCertificatesFlags);
             return result;
         }
 
@@ -6199,7 +6170,6 @@
             private Signature[] mSignatures;
             private int mSignatureSchemeVersion = SignatureSchemeVersion.UNKNOWN;
             private Signature[] mPastSigningCertificates;
-            private int[] mPastSigningCertificatesFlags;
 
             @UnsupportedAppUsage
             public Builder() {
@@ -6226,34 +6196,12 @@
                 return this;
             }
 
-            /** set the flags for the {@code pastSigningCertificates} */
-            @UnsupportedAppUsage
-            public Builder setPastSigningCertificatesFlags(int[] pastSigningCertificatesFlags) {
-                mPastSigningCertificatesFlags = pastSigningCertificatesFlags;
-                return this;
-            }
-
             private void checkInvariants() {
                 // must have signatures and scheme version set
                 if (mSignatures == null) {
                     throw new IllegalStateException("SigningDetails requires the current signing"
                             + " certificates.");
                 }
-
-                // pastSigningCerts and flags must match up
-                boolean pastMismatch = false;
-                if (mPastSigningCertificates != null && mPastSigningCertificatesFlags != null) {
-                    if (mPastSigningCertificates.length != mPastSigningCertificatesFlags.length) {
-                        pastMismatch = true;
-                    }
-                } else if (!(mPastSigningCertificates == null
-                        && mPastSigningCertificatesFlags == null)) {
-                    pastMismatch = true;
-                }
-                if (pastMismatch) {
-                    throw new IllegalStateException("SigningDetails must have a one to one mapping "
-                            + "between pastSigningCertificates and pastSigningCertificatesFlags");
-                }
             }
             /** build a {@code SigningDetails} object */
             @UnsupportedAppUsage
@@ -6261,7 +6209,7 @@
                     throws CertificateException {
                 checkInvariants();
                 return new SigningDetails(mSignatures, mSignatureSchemeVersion,
-                        mPastSigningCertificates, mPastSigningCertificatesFlags);
+                        mPastSigningCertificates);
             }
         }
     }
diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java
index e58ca60..349bb69 100644
--- a/core/java/android/content/pm/Signature.java
+++ b/core/java/android/content/pm/Signature.java
@@ -45,6 +45,20 @@
     private boolean mHaveHashCode;
     private SoftReference<String> mStringRef;
     private Certificate[] mCertificateChain;
+    /**
+     * APK Signature Scheme v3 includes support for adding a proof-of-rotation record that
+     * contains two pieces of information:
+     *   1) the past signing certificates
+     *   2) the flags that APK wants to assign to each of the past signing certificates.
+     *
+     * These flags represent the second piece of information and are viewed as capabilities.
+     * They are an APK's way of telling the platform: "this is how I want to trust my old certs,
+     * please enforce that." This is useful for situation where this app itself is using its
+     * signing certificate as an authorization mechanism, like whether or not to allow another
+     * app to have its SIGNATURE permission.  An app could specify whether to allow other apps
+     * signed by its old cert 'X' to still get a signature permission it defines, for example.
+     */
+    private int mFlags;
 
     /**
      * Create Signature from an existing raw byte array.
@@ -109,6 +123,22 @@
     }
 
     /**
+     * Sets the flags representing the capabilities of the past signing certificate.
+     * @hide
+     */
+    public void setFlags(int flags) {
+        this.mFlags = flags;
+    }
+
+    /**
+     * Returns the flags representing the capabilities of the past signing certificate.
+     * @hide
+     */
+    public int getFlags() {
+        return mFlags;
+    }
+
+    /**
      * Encode the Signature as ASCII text.
      */
     public char[] toChars() {
@@ -328,4 +358,4 @@
 
         return sPrime;
     }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index c604ff1..1734b41 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -201,6 +201,10 @@
         throw new UnsupportedOperationException("Stub!");
     }
 
+    default boolean hasEnrolledTemplates(int userId) {
+        throw new UnsupportedOperationException("Stub!");
+    }
+
     /**
      * This call warms up the hardware and starts scanning for valid biometrics. It terminates
      * when {@link AuthenticationCallback#onAuthenticationError(int,
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 6150be3..2a64c2e 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -34,6 +34,13 @@
     //
 
     /**
+     * This was not added here since it would update BiometricPrompt API. But, is used in
+     * BiometricManager.
+     * @hide
+     */
+    int BIOMETRIC_ERROR_NONE = 0;
+
+    /**
      * The hardware is unavailable. Try again later.
      */
     int BIOMETRIC_ERROR_HW_UNAVAILABLE = 1;
diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java
index eea5f9b..0faecb0 100644
--- a/core/java/android/hardware/biometrics/BiometricManager.java
+++ b/core/java/android/hardware/biometrics/BiometricManager.java
@@ -17,16 +17,44 @@
 package android.hardware.biometrics;
 
 import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 
+import android.annotation.IntDef;
 import android.annotation.RequiresPermission;
 import android.content.Context;
 import android.os.RemoteException;
+import android.util.Slog;
 
 /**
  * A class that contains biometric utilities. For authentication, see {@link BiometricPrompt}.
  */
 public class BiometricManager {
 
+    private static final String TAG = "BiometricManager";
+
+    /**
+     * No error detected.
+     */
+    public static final int ERROR_NONE = BiometricConstants.BIOMETRIC_ERROR_NONE;
+
+    /**
+     * The hardware is unavailable. Try again later.
+     */
+    public static final int ERROR_UNAVAILABLE = BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE;
+
+    /**
+     * The user does not have any biometrics enrolled.
+     */
+    public static final int ERROR_NO_BIOMETRICS = BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS;
+
+    /**
+     * There is no biometric hardware.
+     */
+    public static final int ERROR_NO_HARDWARE = BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT;
+
+    @IntDef({ERROR_NONE, ERROR_UNAVAILABLE, ERROR_NO_BIOMETRICS, ERROR_NO_HARDWARE})
+    @interface BiometricError {}
+
     private final Context mContext;
     private final IBiometricService mService;
 
@@ -41,16 +69,42 @@
     }
 
     /**
-     * Determine if there is at least one biometric enrolled.
+     * Determine if biometrics can be used. In other words, determine if {@link BiometricPrompt}
+     * can be expected to be shown (hardware available, templates enrolled, user-enabled).
      *
-     * @return true if at least one biometric is enrolled, false otherwise
+     * @return Returns {@link #ERROR_NO_BIOMETRICS} if the user does not have any enrolled, or
+     *     {@link #ERROR_UNAVAILABLE} if none are currently supported/enabled. Returns
+     *     {@link #ERROR_NONE} if a biometric can currently be used (enrolled and available).
      */
     @RequiresPermission(USE_BIOMETRIC)
-    public boolean hasEnrolledBiometrics() {
-        try {
-            return mService.hasEnrolledBiometrics(mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            return false;
+    public @BiometricError int canAuthenticate() {
+        if (mService != null) {
+            try {
+                return mService.canAuthenticate(mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Slog.w(TAG, "hasEnrolledBiometrics(): Service not connected");
+            return ERROR_UNAVAILABLE;
+        }
+    }
+
+    /**
+     * Listens for changes to biometric eligibility on keyguard from user settings.
+     * @param callback
+     * @hide
+     */
+    @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+    public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
+        if (mService != null) {
+            try {
+                mService.registerEnabledOnKeyguardCallback(callback);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        } else {
+            Slog.w(TAG, "registerEnabledOnKeyguardCallback(): Service not connected");
         }
     }
 }
diff --git a/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
new file mode 100644
index 0000000..d22e7e2
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricEnabledOnKeyguardCallback.aidl
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2018 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.hardware.biometrics;
+
+import android.hardware.biometrics.BiometricSourceType;
+
+/**
+ * @hide
+ */
+oneway interface IBiometricEnabledOnKeyguardCallback {
+    void onChanged(in BiometricSourceType type, boolean enabled);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
index 67c9346..27d25b8 100644
--- a/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl
@@ -15,9 +15,6 @@
  */
 package android.hardware.biometrics;
 
-import android.os.Bundle;
-import android.os.UserHandle;
-
 /**
  * Communication channel from the BiometricPrompt (SysUI) back to AuthenticationClient.
  * @hide
diff --git a/core/java/android/hardware/biometrics/IBiometricService.aidl b/core/java/android/hardware/biometrics/IBiometricService.aidl
index fd9d572..51e4ecb 100644
--- a/core/java/android/hardware/biometrics/IBiometricService.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricService.aidl
@@ -17,6 +17,7 @@
 package android.hardware.biometrics;
 
 import android.os.Bundle;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.biometrics.IBiometricServiceReceiver;
 
@@ -37,6 +38,9 @@
     // Cancel authentication for the given sessionId
     void cancelAuthentication(IBinder token, String opPackageName);
 
-    // Returns true if the user has at least one enrolled biometric.
-    boolean hasEnrolledBiometrics(String opPackageName);
+    // Checks if biometrics can be used.
+    int canAuthenticate(String opPackageName);
+
+    // Register callback for when keyguard biometric eligibility changes.
+    void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback);
 }
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
index 71abdd2..a6e3696 100644
--- a/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
+++ b/core/java/android/hardware/biometrics/IBiometricServiceReceiver.aidl
@@ -15,10 +15,6 @@
  */
 package android.hardware.biometrics;
 
-import android.hardware.biometrics.BiometricSourceType;
-import android.os.Bundle;
-import android.os.UserHandle;
-
 /**
  * Communication channel from the BiometricService back to BiometricPrompt.
  * @hide
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index e8fb287..09113e5 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -297,6 +297,15 @@
      */
     public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8;
 
+    /**
+     * Virtual display flag: Indicates that the display should support system decorations. Virtual
+     * displays without this flag shouldn't show home, IME or any other system decorations.
+     *
+     * @see #createVirtualDisplay
+     * @hide
+     */
+    public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 9;
+
     /** @hide */
     public DisplayManager(Context context) {
         mContext = context;
diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java
index 496f34c..df0d46b 100644
--- a/core/java/android/hardware/display/DisplayViewport.java
+++ b/core/java/android/hardware/display/DisplayViewport.java
@@ -16,9 +16,14 @@
 
 package android.hardware.display;
 
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.annotation.IntDef;
 import android.graphics.Rect;
 import android.text.TextUtils;
 
+import java.lang.annotation.Retention;
+
 /**
  * Describes how the pixels of physical display device reflects the content of
  * a logical display.
@@ -35,6 +40,10 @@
     public static final int VIEWPORT_INTERNAL = 1;
     public static final int VIEWPORT_EXTERNAL = 2;
     public static final int VIEWPORT_VIRTUAL = 3;
+    @IntDef(prefix = { "VIEWPORT_" }, value = {
+            VIEWPORT_INTERNAL, VIEWPORT_EXTERNAL, VIEWPORT_VIRTUAL})
+    @Retention(SOURCE)
+    public @interface ViewportType {};
 
     // True if this viewport is valid.
     public boolean valid;
@@ -62,6 +71,8 @@
     // The ID used to uniquely identify this display.
     public String uniqueId;
 
+    public @ViewportType int type;
+
     public void copyFrom(DisplayViewport viewport) {
         valid = viewport.valid;
         displayId = viewport.displayId;
@@ -71,6 +82,7 @@
         deviceWidth = viewport.deviceWidth;
         deviceHeight = viewport.deviceHeight;
         uniqueId = viewport.uniqueId;
+        type = viewport.type;
     }
 
     /**
@@ -100,7 +112,8 @@
               && physicalFrame.equals(other.physicalFrame)
               && deviceWidth == other.deviceWidth
               && deviceHeight == other.deviceHeight
-              && TextUtils.equals(uniqueId, other.uniqueId);
+              && TextUtils.equals(uniqueId, other.uniqueId)
+              && type == other.type;
     }
 
     @Override
@@ -115,13 +128,15 @@
         result += prime * result + deviceWidth;
         result += prime * result + deviceHeight;
         result += prime * result + uniqueId.hashCode();
+        result += prime * result + type;
         return result;
     }
 
     // For debugging purposes.
     @Override
     public String toString() {
-        return "DisplayViewport{valid=" + valid
+        return "DisplayViewport{type=" + typeToString(type)
+                + ", valid=" + valid
                 + ", displayId=" + displayId
                 + ", uniqueId='" + uniqueId + "'"
                 + ", orientation=" + orientation
@@ -131,4 +146,20 @@
                 + ", deviceHeight=" + deviceHeight
                 + "}";
     }
+
+    /**
+     * Human-readable viewport type.
+     */
+    public static String typeToString(@ViewportType int viewportType) {
+        switch (viewportType) {
+            case VIEWPORT_INTERNAL:
+                return "INTERNAL";
+            case VIEWPORT_EXTERNAL:
+                return "EXTERNAL";
+            case VIEWPORT_VIRTUAL:
+                return "VIRTUAL";
+            default:
+                return "UNKNOWN (" + viewportType + ")";
+        }
+    }
 }
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 873a24a..7aec43e 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -408,6 +408,7 @@
     @RequiresPermission(allOf = {
             USE_BIOMETRIC_INTERNAL,
             INTERACT_ACROSS_USERS})
+    @Override
     public boolean hasEnrolledTemplates(int userId) {
         if (mService != null) {
             try {
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 16fb690..b88574b 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -16,8 +16,6 @@
 package android.hardware.face;
 
 import android.hardware.face.Face;
-import android.os.Bundle;
-import android.os.UserHandle;
 
 /**
  * Communication channel from the FaceService back to FaceAuthenticationManager.
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index a4f3ce1..bf2280d 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -637,6 +637,14 @@
     }
 
     /**
+     * @hide
+     */
+    @Override
+    public boolean hasEnrolledTemplates(int userId) {
+        return hasEnrolledFingerprints(userId);
+    }
+
+    /**
      * Determine if there is at least one fingerprint enrolled.
      *
      * @return true if at least one fingerprint is enrolled, false otherwise
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index 370383f..cf1c94e 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -16,8 +16,6 @@
 package android.hardware.fingerprint;
 
 import android.hardware.fingerprint.Fingerprint;
-import android.os.Bundle;
-import android.os.UserHandle;
 
 /**
  * Communication channel from the FingerprintService back to FingerprintManager.
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index c4d7e40..d8da548 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -40,8 +40,7 @@
      * Called by the display manager to set information about the displays as needed
      * by the input system.  The input system must copy this information to retain it.
      */
-    public abstract void setDisplayViewports(DisplayViewport defaultViewport,
-            DisplayViewport externalTouchViewport, List<DisplayViewport> virtualTouchViewports);
+    public abstract void setDisplayViewports(List<DisplayViewport> viewports);
 
     /**
      * Called by the power manager to tell the input manager whether it should start
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f7f627e..097a3e3 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -2095,7 +2095,7 @@
      * Called when the application has reported a new location of its text
      * cursor.  This is only called if explicitly requested by the input method.
      * The default implementation does nothing.
-     * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
+     * @deprecated Use {@link #onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead.
      */
     @Deprecated
     public void onUpdateCursor(Rect newCursor) {
@@ -2162,7 +2162,7 @@
     }
 
     /**
-     * @return {#link ExtractEditText} if it is considered to be visible and active. Otherwise
+     * @return {@link ExtractEditText} if it is considered to be visible and active. Otherwise
      * {@code null} is returned.
      */
     private ExtractEditText getExtractEditTextIfVisible() {
diff --git a/core/java/android/inputmethodservice/SoftInputWindow.java b/core/java/android/inputmethodservice/SoftInputWindow.java
index b4b8887..0513fee 100644
--- a/core/java/android/inputmethodservice/SoftInputWindow.java
+++ b/core/java/android/inputmethodservice/SoftInputWindow.java
@@ -162,9 +162,9 @@
     /**
      * Set which boundary of the screen the DockWindow sticks to.
      * 
-     * @param gravity The boundary of the screen to stick. See {#link
-     *        android.view.Gravity.LEFT}, {#link android.view.Gravity.TOP},
-     *        {#link android.view.Gravity.BOTTOM}, {#link
+     * @param gravity The boundary of the screen to stick. See {@link
+     *        android.view.Gravity.LEFT}, {@link android.view.Gravity.TOP},
+     *        {@link android.view.Gravity.BOTTOM}, {@link
      *        android.view.Gravity.RIGHT}.
      */
     public void setGravity(int gravity) {
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 69f50a2..a2da6ea 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -46,7 +46,7 @@
 
     /**
      * Set if the user desires to use this network even if it is unvalidated. This field has meaning
-     * only if {#link explicitlySelected} is true. If it is, this field must also be set to the
+     * only if {@link explicitlySelected} is true. If it is, this field must also be set to the
      * appropriate value based on previous user choice.
      */
     public boolean acceptUnvalidated;
diff --git a/core/java/android/net/UrlQuerySanitizer.java b/core/java/android/net/UrlQuerySanitizer.java
index d2073b4..5b67406 100644
--- a/core/java/android/net/UrlQuerySanitizer.java
+++ b/core/java/android/net/UrlQuerySanitizer.java
@@ -287,7 +287,7 @@
         /**
          * Sanitize a value.
          * <ol>
-         * <li>If script URLs are not OK, the will be removed.
+         * <li>If script URLs are not OK, they will be removed.
          * <li>If neither spaces nor other white space is OK, then
          * white space will be trimmed from the beginning and end of
          * the URL. (Just the actual white space characters are trimmed, not
@@ -563,7 +563,7 @@
     }
 
     /**
-     * Constructs a UrlQuerySanitizer and parse a URL.
+     * Constructs a UrlQuerySanitizer and parses a URL.
      * This constructor is provided for convenience when the
      * default parsing behavior is acceptable.
      * <p>
@@ -644,7 +644,7 @@
     }
 
     /**
-     * An array list of all of the parameter value pairs in the sanitized
+     * An array list of all of the parameter-value pairs in the sanitized
      * query, in the order they appeared in the query. May contain duplicate
      * parameters.
      * <p class="note"><b>Note:</b> Do not modify this list. Treat it as a read-only list.</p>
@@ -656,7 +656,7 @@
     /**
      * Check if a parameter exists in the current sanitized query.
      * @param parameter the unencoded name of a parameter.
-     * @return true if the paramater exists in the current sanitized queary.
+     * @return true if the parameter exists in the current sanitized queary.
      */
     public boolean hasParameter(String parameter) {
         return mEntries.containsKey(parameter);
@@ -766,7 +766,7 @@
      * the value. If all goes well then addSanitizedValue is called with
      * the unescaped parameter and the sanitized unescaped value.
      * @param parameter an escaped parameter
-     * @param value an unsanitzied escaped value
+     * @param value an unsanitized escaped value
      */
     protected void parseEntry(String parameter, String value) {
         String unescapedParameter = unescape(parameter);
@@ -812,7 +812,7 @@
     /**
      * Get the effective value sanitizer for a parameter. Like getValueSanitizer,
      * except if there is no value sanitizer registered for a parameter, and
-     * unregistered paramaters are allowed, then the default value sanitizer is
+     * unregistered parameters are allowed, then the default value sanitizer is
      * returned.
      * @param parameter an unescaped parameter
      * @return the effective value sanitizer for a parameter.
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 3c43fd18..483b764 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -219,12 +219,11 @@
      * services to store files relating to the user. This directory will be
      * automatically deleted when the user is removed.
      *
-     * @deprecated This directory is valid and still exists, but callers should
-     *             <em>strongly</em> consider switching to
-     *             {@link #getDataSystemCeDirectory(int)} which is protected
-     *             with user credentials or
-     *             {@link #getDataSystemDeDirectory(int)} which supports fast
-     *             user wipe.
+     * @deprecated This directory is valid and still exists, but but callers
+     *             should <em>strongly</em> consider switching to using either
+     *             {@link #getDataSystemCeDirectory(int)} or
+     *             {@link #getDataSystemDeDirectory(int)}, both of which support
+     *             fast user wipe.
      * @hide
      */
     @Deprecated
@@ -292,12 +291,42 @@
         return buildPath(getDataDirectory(), "system_ce");
     }
 
-    /** {@hide} */
+    /**
+     * Return the "credential encrypted" system directory for a user. This is
+     * for use by system services to store files relating to the user. This
+     * directory supports fast user wipe, and will be automatically deleted when
+     * the user is removed.
+     * <p>
+     * Data stored under this path is "credential encrypted", which uses an
+     * encryption key that is entangled with user credentials, such as a PIN or
+     * password. The contents will only be available once the user has been
+     * unlocked, as reported by {@code SystemService.onUnlockUser()}.
+     * <p>
+     * New code should <em>strongly</em> prefer storing sensitive data in these
+     * credential encrypted areas.
+     *
+     * @hide
+     */
     public static File getDataSystemCeDirectory(int userId) {
         return buildPath(getDataDirectory(), "system_ce", String.valueOf(userId));
     }
 
-    /** {@hide} */
+    /**
+     * Return the "device encrypted" system directory for a user. This is for
+     * use by system services to store files relating to the user. This
+     * directory supports fast user wipe, and will be automatically deleted when
+     * the user is removed.
+     * <p>
+     * Data stored under this path is "device encrypted", which uses an
+     * encryption key that is tied to the physical device. The contents will
+     * only be available once the device has finished a {@code dm-verity}
+     * protected boot.
+     * <p>
+     * New code should <em>strongly</em> avoid storing sensitive data in these
+     * device encrypted areas.
+     *
+     * @hide
+     */
     public static File getDataSystemDeDirectory(int userId) {
         return buildPath(getDataDirectory(), "system_de", String.valueOf(userId));
     }
diff --git a/core/java/android/os/StatsLogEventWrapper.java b/core/java/android/os/StatsLogEventWrapper.java
index 051ab75..72e1ab9 100644
--- a/core/java/android/os/StatsLogEventWrapper.java
+++ b/core/java/android/os/StatsLogEventWrapper.java
@@ -70,11 +70,17 @@
                 }
             };
 
+    /**
+     * Write a int value.
+     */
     public void writeInt(int val) {
         mTypes.add(EVENT_TYPE_INT);
         mValues.add(val);
     }
 
+    /**
+     * Write a long value.
+     */
     public void writeLong(long val) {
         mTypes.add(EVENT_TYPE_LONG);
         mValues.add(val);
@@ -89,12 +95,23 @@
         mValues.add(val == null ? "" : val);
     }
 
+    /**
+     * Write a float value.
+     */
     public void writeFloat(float val) {
         mTypes.add(EVENT_TYPE_FLOAT);
         mValues.add(val);
     }
 
     /**
+     * Write a double value.
+     */
+    public void writeDouble(double val) {
+        mTypes.add(EVENT_TYPE_DOUBLE);
+        mValues.add(val);
+    }
+
+    /**
      * Write a storage value.
      */
     public void writeStorage(byte[] val) {
@@ -102,6 +119,9 @@
         mValues.add(val);
     }
 
+    /**
+     * Write a boolean value.
+     */
     public void writeBoolean(boolean val) {
         mTypes.add(EVENT_TYPE_INT);
         mValues.add(val ? 1 : 0);
@@ -134,6 +154,9 @@
                 case EVENT_TYPE_FLOAT:
                     out.writeFloat((float) mValues.get(i));
                     break;
+                case EVENT_TYPE_DOUBLE:
+                    out.writeDouble((double) mValues.get(i));
+                    break;
                 case EVENT_TYPE_STRING:
                     out.writeString((String) mValues.get(i));
                     break;
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index fb34a52..bdc776d 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -208,13 +208,18 @@
                 return;
             }
             ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
-            for (int i=0; i<callbacks.size(); i++) {
-                try {
-                    callbacks.get(i).run();
-                } catch (Throwable t) {
-                    Log.wtf(TAG, "Exception in SystemProperties change callback", t);
-                    // Ignore and try to go on.
+            final long token = Binder.clearCallingIdentity();
+            try {
+                for (int i = 0; i < callbacks.size(); i++) {
+                    try {
+                        callbacks.get(i).run();
+                    } catch (Throwable t) {
+                        Log.wtf(TAG, "Exception in SystemProperties change callback", t);
+                        // Ignore and try to go on.
+                    }
                 }
+            } finally {
+                Binder.restoreCallingIdentity(token);
             }
         }
     }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 80dbfe5..3f264b7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -497,6 +497,16 @@
             "android.settings.BLUETOOTH_SETTINGS";
 
     /**
+     * Activity action: Show Settings app search UI when this action is available for device.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_APP_SEARCH_SETTINGS = "android.settings.APP_SEARCH_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of Assist Gesture.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 7d1c6c4..50f63f8 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,13 +16,15 @@
 
 package android.util;
 
+import libcore.internal.StringPool;
+
 import java.io.Closeable;
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.Reader;
 import java.util.ArrayList;
 import java.util.List;
-import libcore.internal.StringPool;
+
 
 /**
  * Reads a JSON (<a href="http://www.ietf.org/rfc/rfc4627.txt">RFC 4627</a>)
@@ -295,7 +297,7 @@
 
     /**
      * Consumes the next token from the JSON stream and asserts that it is the
-     * end of the current array.
+     * end of the current object.
      */
     public void endObject() throws IOException {
         expect(JsonToken.END_OBJECT);
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index a299b11..ac4ea75 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -81,19 +81,17 @@
             Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
             Signature[] signerSigs = convertToSignatures(signerCerts);
             Signature[] pastSignerSigs = null;
-            int[] pastSignerSigsFlags = null;
             if (vSigner.por != null) {
                 // populate proof-of-rotation information
                 pastSignerSigs = new Signature[vSigner.por.certs.size()];
-                pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
                 for (int i = 0; i < pastSignerSigs.length; i++) {
                     pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
-                    pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+                    pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
                 }
             }
             return new PackageParser.SigningDetails(
                     signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
-                    pastSignerSigs, pastSignerSigsFlags);
+                    pastSignerSigs);
         } catch (SignatureNotFoundException e) {
             // not signed with v3, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
@@ -323,19 +321,17 @@
             Certificate[][] signerCerts = new Certificate[][] { vSigner.certs };
             Signature[] signerSigs = convertToSignatures(signerCerts);
             Signature[] pastSignerSigs = null;
-            int[] pastSignerSigsFlags = null;
             if (vSigner.por != null) {
                 // populate proof-of-rotation information
                 pastSignerSigs = new Signature[vSigner.por.certs.size()];
-                pastSignerSigsFlags = new int[vSigner.por.flagsList.size()];
                 for (int i = 0; i < pastSignerSigs.length; i++) {
                     pastSignerSigs[i] = new Signature(vSigner.por.certs.get(i).getEncoded());
-                    pastSignerSigsFlags[i] = vSigner.por.flagsList.get(i);
+                    pastSignerSigs[i].setFlags(vSigner.por.flagsList.get(i));
                 }
             }
             return new PackageParser.SigningDetails(
                     signerSigs, SignatureSchemeVersion.SIGNING_BLOCK_V3,
-                    pastSignerSigs, pastSignerSigsFlags);
+                    pastSignerSigs);
         } catch (SignatureNotFoundException e) {
             // not signed with v3, try older if allowed
             if (minSignatureSchemeVersion >= SignatureSchemeVersion.SIGNING_BLOCK_V3) {
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 4d96fc3..719a401 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -221,6 +221,18 @@
     public static final int FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5;
 
     /**
+     * Display flag: Indicates that the display should show system decorations.
+     * <p>
+     * This flag identifies secondary displays that should show system decorations, such as status
+     * bar, navigation bar, home activity or IME.
+     * </p>
+     *
+     * @see #supportsSystemDecorations
+     * @hide
+     */
+    public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 6;
+
+    /**
      * Display flag: Indicates that the contents of the display should not be scaled
      * to fit the physical screen dimensions.  Used for development only to emulate
      * devices with smaller physicals screens while preserving density.
@@ -874,6 +886,16 @@
     }
 
     /**
+     * Returns whether this display should support showing system decorations.
+     *
+     * @see #FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+     * @hide
+     */
+    public boolean supportsSystemDecorations() {
+        return (mDisplayInfo.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0;
+    }
+
+    /**
      * Returns the display's HDR capabilities.
      *
      * @see #isHdr()
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 5f80d31..f428c79 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -18,12 +18,19 @@
 
 import static android.util.DisplayMetrics.DENSITY_DEFAULT;
 import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
-import static android.view.DisplayCutoutProto.BOUNDS;
+import static android.view.DisplayCutoutProto.BOUND_BOTTOM;
+import static android.view.DisplayCutoutProto.BOUND_LEFT;
+import static android.view.DisplayCutoutProto.BOUND_RIGHT;
+import static android.view.DisplayCutoutProto.BOUND_TOP;
 import static android.view.DisplayCutoutProto.INSETS;
 
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.res.Resources;
+import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Path;
 import android.graphics.Rect;
@@ -42,7 +49,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -68,14 +78,14 @@
             "com.android.internal.display_cutout_emulation";
 
     private static final Rect ZERO_RECT = new Rect();
-    private static final Region EMPTY_REGION = new Region();
 
     /**
      * An instance where {@link #isEmpty()} returns {@code true}.
      *
      * @hide
      */
-    public static final DisplayCutout NO_CUTOUT = new DisplayCutout(ZERO_RECT, EMPTY_REGION,
+    public static final DisplayCutout NO_CUTOUT = new DisplayCutout(
+            ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
             false /* copyArguments */);
 
 
@@ -94,7 +104,152 @@
     private static Pair<Path, DisplayCutout> sCachedCutout = NULL_PAIR;
 
     private final Rect mSafeInsets;
-    private final Region mBounds;
+
+
+    /**
+     * The bound is at the left of the screen.
+     * @hide
+     */
+    public static final int BOUNDS_POSITION_LEFT = 0;
+
+    /**
+     * The bound is at the top of the screen.
+     * @hide
+     */
+    public static final int BOUNDS_POSITION_TOP = 1;
+
+    /**
+     * The bound is at the right of the screen.
+     * @hide
+     */
+    public static final int BOUNDS_POSITION_RIGHT = 2;
+
+    /**
+     * The bound is at the bottom of the screen.
+     * @hide
+     */
+    public static final int BOUNDS_POSITION_BOTTOM = 3;
+
+    /**
+     * The number of possible positions at which bounds can be located.
+     * @hide
+     */
+    public static final int BOUNDS_POSITION_LENGTH = 4;
+
+    /** @hide */
+    @IntDef(prefix = { "BOUNDS_POSITION_" }, value = {
+            BOUNDS_POSITION_LEFT,
+            BOUNDS_POSITION_TOP,
+            BOUNDS_POSITION_RIGHT,
+            BOUNDS_POSITION_BOTTOM
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BoundsPosition {}
+
+    private static class Bounds {
+        private final Rect[] mRects;
+
+        private Bounds(Rect left, Rect top, Rect right, Rect bottom, boolean copyArguments) {
+            mRects = new Rect[BOUNDS_POSITION_LENGTH];
+            mRects[BOUNDS_POSITION_LEFT] = getCopyOrRef(left, copyArguments);
+            mRects[BOUNDS_POSITION_TOP] = getCopyOrRef(top, copyArguments);
+            mRects[BOUNDS_POSITION_RIGHT] = getCopyOrRef(right, copyArguments);
+            mRects[BOUNDS_POSITION_BOTTOM] = getCopyOrRef(bottom, copyArguments);
+
+        }
+
+        private Bounds(Rect[] rects, boolean copyArguments) {
+            if (rects.length != BOUNDS_POSITION_LENGTH) {
+                throw new IllegalArgumentException(
+                        "rects must have exactly 4 elements: rects=" + Arrays.toString(rects));
+            }
+            if (copyArguments) {
+                mRects = new Rect[BOUNDS_POSITION_LENGTH];
+                for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+                    mRects[i] = new Rect(rects[i]);
+                }
+            } else {
+                for (Rect rect : rects) {
+                    if (rect == null) {
+                        throw new IllegalArgumentException(
+                                "rects must have non-null elements: rects="
+                                        + Arrays.toString(rects));
+                    }
+                }
+                mRects = rects;
+            }
+        }
+
+        private boolean isEmpty() {
+            for (Rect rect : mRects) {
+                if (!rect.isEmpty()) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        private Rect getRect(@BoundsPosition int pos) {
+            return new Rect(mRects[pos]);
+        }
+
+        private Rect[] getRects() {
+            Rect[] rects = new Rect[BOUNDS_POSITION_LENGTH];
+            for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+                rects[i] = new Rect(mRects[i]);
+            }
+            return rects;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 0;
+            for (Rect rect : mRects) {
+                result = result * 48271 + rect.hashCode();
+            }
+            return result;
+        }
+        @Override
+        public boolean equals(Object o) {
+            if (o == this) {
+                return true;
+            }
+            if (o instanceof Bounds) {
+                Bounds b = (Bounds) o;
+                return Arrays.deepEquals(mRects, b.mRects);
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            return "Bounds=" + Arrays.toString(mRects);
+        }
+
+    }
+
+    private final Bounds mBounds;
+
+    /**
+     * Creates a DisplayCutout instance.
+     *
+     * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+     *                   {@link #getSafeInsetTop()} etc.
+     * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+     *                  it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundTop the top bounding rect of the display cutout in pixels.  If null is passed,
+     *                  it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundRight the right bounding rect of the display cutout in pixels.  If null is
+     *                  passed, it's treated as an empty rectangle (0,0)-(0,0).
+     * @param boundBottom the bottom bounding rect of the display cutout in pixels.  If null is
+     *                   passed, it's treated as an empty rectangle (0,0)-(0,0).
+     */
+    // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
+    public DisplayCutout(
+            Insets safeInsets, @Nullable Rect boundLeft, @Nullable Rect boundTop,
+            @Nullable Rect boundRight, @Nullable Rect boundBottom) {
+        this(safeInsets.toRect(), boundLeft, boundTop, boundRight, boundBottom, true);
+    }
 
     /**
      * Creates a DisplayCutout instance.
@@ -103,25 +258,85 @@
      *                   {@link #getSafeInsetTop()} etc.
      * @param boundingRects the bounding rects of the display cutouts as returned by
      *               {@link #getBoundingRects()} ()}.
+     * @deprecated Use {@link DisplayCutout#DisplayCutout(Insets, Rect, Rect, Rect, Rect)} instead.
      */
     // TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
+    @Deprecated
     public DisplayCutout(Rect safeInsets, List<Rect> boundingRects) {
-        this(safeInsets != null ? new Rect(safeInsets) : ZERO_RECT,
-                boundingRectsToRegion(boundingRects),
+        this(safeInsets, extractBoundsFromList(safeInsets, boundingRects),
                 true /* copyArguments */);
     }
 
     /**
      * Creates a DisplayCutout instance.
      *
+     * @param safeInsets the insets from each edge which avoid the display cutout as returned by
+     *                   {@link #getSafeInsetTop()} etc.
      * @param copyArguments if true, create a copy of the arguments. If false, the passed arguments
      *                      are not copied and MUST remain unchanged forever.
      */
-    private DisplayCutout(Rect safeInsets, Region bounds, boolean copyArguments) {
-        mSafeInsets = safeInsets == null ? ZERO_RECT :
-                (copyArguments ? new Rect(safeInsets) : safeInsets);
-        mBounds = bounds == null ? Region.obtain() :
-                (copyArguments ? Region.obtain(bounds) : bounds);
+    private DisplayCutout(Rect safeInsets, Rect boundLeft, Rect boundTop, Rect boundRight,
+                         Rect boundBottom, boolean copyArguments) {
+        mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
+        mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
+    }
+
+    private DisplayCutout(Rect safeInsets, Rect[] bounds, boolean copyArguments) {
+        mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
+        mBounds = new Bounds(bounds, copyArguments);
+    }
+
+    private DisplayCutout(Rect safeInsets, Bounds bounds) {
+        mSafeInsets = safeInsets;
+        mBounds = bounds;
+
+    }
+
+    private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
+        if (r == null) {
+            return ZERO_RECT;
+        } else if (copyArguments) {
+            return new Rect(r);
+        } else {
+            return r;
+        }
+    }
+
+    /**
+     * Find the position of the bounding rect, and create an array of Rect whose index represents
+     * the position (= BoundsPosition).
+     *
+     * @hide
+     */
+    public static Rect[] extractBoundsFromList(Rect safeInsets, List<Rect> boundingRects) {
+        Rect[] sortedBounds = new Rect[BOUNDS_POSITION_LENGTH];
+        for (int i = 0; i < sortedBounds.length; ++i) {
+            sortedBounds[i] = ZERO_RECT;
+        }
+        for (Rect bound : boundingRects) {
+            // There will be at most one non-functional area per short edge of the device, and none
+            // on the long edges, so either safeInsets.right or safeInsets.bottom must be 0.
+            // TODO(b/117199965): Refine the logic to handle edge cases.
+            if (bound.left == 0) {
+                sortedBounds[BOUNDS_POSITION_LEFT] = bound;
+            } else if (bound.top == 0) {
+                sortedBounds[BOUNDS_POSITION_TOP] = bound;
+            } else if (safeInsets.right > 0) {
+                sortedBounds[BOUNDS_POSITION_RIGHT] = bound;
+            } else if (safeInsets.bottom > 0) {
+                sortedBounds[BOUNDS_POSITION_BOTTOM] = bound;
+            }
+        }
+        return sortedBounds;
+    }
+
+    /**
+     * Returns true if there is no cutout, i.e. the bounds are empty.
+     *
+     * @hide
+     */
+    public boolean isBoundsEmpty() {
+        return mBounds.isEmpty();
     }
 
     /**
@@ -134,15 +349,6 @@
         return mSafeInsets.equals(ZERO_RECT);
     }
 
-    /**
-     * Returns true if there is no cutout, i.e. the bounds are empty.
-     *
-     * @hide
-     */
-    public boolean isBoundsEmpty() {
-        return mBounds.isEmpty();
-    }
-
     /** Returns the inset from the top which avoids the display cutout in pixels. */
     public int getSafeInsetTop() {
         return mSafeInsets.top;
@@ -174,69 +380,89 @@
     }
 
     /**
-     * Returns the bounding region of the cutout.
-     *
-     * <p>
-     * <strong>Note:</strong> There may be more than one cutout, in which case the returned
-     * {@code Region} will be non-contiguous and its bounding rect will be meaningless without
-     * intersecting it first.
-     *
-     * Example:
-     * <pre>
-     *     // Getting the bounding rectangle of the top display cutout
-     *     Region bounds = displayCutout.getBounds();
-     *     bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(), Region.Op.INTERSECT);
-     *     Rect topDisplayCutout = bounds.getBoundingRect();
-     * </pre>
-     *
-     * @return the bounding region of the cutout. Coordinates are relative
-     *         to the top-left corner of the content view and in pixel units.
-     * @hide
-     */
-    public Region getBounds() {
-        return Region.obtain(mBounds);
-    }
-
-    /**
      * Returns a list of {@code Rect}s, each of which is the bounding rectangle for a non-functional
      * area on the display.
      *
      * There will be at most one non-functional area per short edge of the device, and none on
      * the long edges.
      *
-     * @return a list of bounding {@code Rect}s, one for each display cutout area.
+     * @return a list of bounding {@code Rect}s, one for each display cutout area. No empty Rect is
+     * returned.
      */
     public List<Rect> getBoundingRects() {
         List<Rect> result = new ArrayList<>();
-        Region bounds = Region.obtain();
-        // top
-        bounds.set(mBounds);
-        bounds.op(0, 0, Integer.MAX_VALUE, getSafeInsetTop(), Region.Op.INTERSECT);
-        if (!bounds.isEmpty()) {
-            result.add(bounds.getBounds());
+        for (Rect bound : getBoundingRectsAll()) {
+            if (!bound.isEmpty()) {
+                result.add(new Rect(bound));
+            }
         }
-        // left
-        bounds.set(mBounds);
-        bounds.op(0, 0, getSafeInsetLeft(), Integer.MAX_VALUE, Region.Op.INTERSECT);
-        if (!bounds.isEmpty()) {
-            result.add(bounds.getBounds());
-        }
-        // right & bottom
-        bounds.set(mBounds);
-        bounds.op(getSafeInsetLeft() + 1, getSafeInsetTop() + 1,
-                Integer.MAX_VALUE, Integer.MAX_VALUE, Region.Op.INTERSECT);
-        if (!bounds.isEmpty()) {
-            result.add(bounds.getBounds());
-        }
-        bounds.recycle();
         return result;
     }
 
+    /**
+     * Returns an array of {@code Rect}s, each of which is the bounding rectangle for a non-
+     * functional area on the display. Ordinal value of BoundPosition is used as an index of
+     * the array.
+     *
+     * There will be at most one non-functional area per short edge of the device, and none on
+     * the long edges.
+     *
+     * @return an array of bounding {@code Rect}s, one for each display cutout area. This might
+     * contain ZERO_RECT, which means there is no cutout area at the position.
+     *
+     * @hide
+     */
+    public Rect[] getBoundingRectsAll() {
+        return mBounds.getRects();
+    }
+
+    /**
+     * Returns a bounding rectangle for a non-functional area on the display which is located on
+     * the left of the screen.
+     *
+     * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+     * is returned.
+     */
+    public @NonNull Rect getBoundingRectLeft() {
+        return mBounds.getRect(BOUNDS_POSITION_LEFT);
+    }
+
+    /**
+     * Returns a bounding rectangle for a non-functional area on the display which is located on
+     * the top of the screen.
+     *
+     * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+     * is returned.
+     */
+    public @NonNull Rect getBoundingRectTop() {
+        return mBounds.getRect(BOUNDS_POSITION_TOP);
+    }
+
+    /**
+     * Returns a bounding rectangle for a non-functional area on the display which is located on
+     * the right of the screen.
+     *
+     * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+     * is returned.
+     */
+    public @NonNull Rect getBoundingRectRight() {
+        return mBounds.getRect(BOUNDS_POSITION_RIGHT);
+    }
+
+    /**
+     * Returns a bounding rectangle for a non-functional area on the display which is located on
+     * the bottom of the screen.
+     *
+     * @return bounding rectangle in pixels. In case of no bounding rectangle, an empty rectangle
+     * is returned.
+     */
+    public @NonNull Rect getBoundingRectBottom() {
+        return mBounds.getRect(BOUNDS_POSITION_BOTTOM);
+    }
+
     @Override
     public int hashCode() {
-        int result = mSafeInsets.hashCode();
-        result = result * 31 + mBounds.getBounds().hashCode();
-        return result;
+        return mSafeInsets.hashCode() * 48271 + mBounds.hashCode();
     }
 
     @Override
@@ -246,8 +472,7 @@
         }
         if (o instanceof DisplayCutout) {
             DisplayCutout c = (DisplayCutout) o;
-            return mSafeInsets.equals(c.mSafeInsets)
-                    && mBounds.equals(c.mBounds);
+            return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds);
         }
         return false;
     }
@@ -255,7 +480,7 @@
     @Override
     public String toString() {
         return "DisplayCutout{insets=" + mSafeInsets
-                + " boundingRect=" + mBounds.getBounds()
+                + " boundingRect={" + mBounds + "}"
                 + "}";
     }
 
@@ -265,7 +490,10 @@
     public void writeToProto(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         mSafeInsets.writeToProto(proto, INSETS);
-        mBounds.getBounds().writeToProto(proto, BOUNDS);
+        mBounds.getRect(BOUNDS_POSITION_LEFT).writeToProto(proto, BOUND_LEFT);
+        mBounds.getRect(BOUNDS_POSITION_TOP).writeToProto(proto, BOUND_TOP);
+        mBounds.getRect(BOUNDS_POSITION_RIGHT).writeToProto(proto, BOUND_RIGHT);
+        mBounds.getRect(BOUNDS_POSITION_BOTTOM).writeToProto(proto, BOUND_BOTTOM);
         proto.end(token);
     }
 
@@ -276,13 +504,12 @@
      * @hide
      */
     public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
-        if (mBounds.isEmpty()
+        if (isBoundsEmpty()
                 || insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
             return this;
         }
 
         Rect safeInsets = new Rect(mSafeInsets);
-        Region bounds = Region.obtain(mBounds);
 
         // Note: it's not really well defined what happens when the inset is negative, because we
         // don't know if the safe inset needs to expand in general.
@@ -299,7 +526,13 @@
             safeInsets.right = atLeastZero(safeInsets.right - insetRight);
         }
 
-        bounds.translate(-insetLeft, -insetTop);
+        Rect[] bounds = mBounds.getRects();
+        for (int i = 0; i < bounds.length; ++i) {
+            if (!bounds[i].equals(ZERO_RECT)) {
+                bounds[i].offset(-insetLeft, -insetTop);
+            }
+        }
+
         return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
     }
 
@@ -312,7 +545,7 @@
      * @hide
      */
     public DisplayCutout replaceSafeInsets(Rect safeInsets) {
-        return new DisplayCutout(new Rect(safeInsets), mBounds, false /* copyArguments */);
+        return new DisplayCutout(new Rect(safeInsets), mBounds);
     }
 
     private static int atLeastZero(int value) {
@@ -326,10 +559,13 @@
      * @hide
      */
     @VisibleForTesting
-    public static DisplayCutout fromBoundingRect(int left, int top, int right, int bottom) {
-        Region r = Region.obtain();
-        r.set(left, top, right, bottom);
-        return fromBounds(r);
+    public static DisplayCutout fromBoundingRect(
+            int left, int top, int right, int bottom, @BoundsPosition int pos) {
+        Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
+        for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+            bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect();
+        }
+        return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
     }
 
     /**
@@ -337,8 +573,8 @@
      *
      * @hide
      */
-    public static DisplayCutout fromBounds(Region region) {
-        return new DisplayCutout(ZERO_RECT, region, false /* copyArguments */);
+    public static DisplayCutout fromBounds(Rect[] bounds) {
+        return new DisplayCutout(ZERO_RECT, bounds, false /* copyArguments */);
     }
 
     /**
@@ -423,10 +659,11 @@
         m.postTranslate(offsetX, 0);
         p.transform(m);
 
-        final Rect tmpRect = new Rect();
-        toRectAndAddToRegion(p, r, tmpRect);
-        final int topInset = tmpRect.bottom;
+        Rect boundTop = new Rect();
+        toRectAndAddToRegion(p, r, boundTop);
+        final int topInset = boundTop.bottom;
 
+        Rect boundBottom = null;
         final int bottomInset;
         if (bottomSpec != null) {
             final Path bottomPath;
@@ -440,15 +677,17 @@
             m.postTranslate(0, displayHeight);
             bottomPath.transform(m);
             p.addPath(bottomPath);
-            toRectAndAddToRegion(bottomPath, r, tmpRect);
-            bottomInset = displayHeight - tmpRect.top;
+            boundBottom = new Rect();
+            toRectAndAddToRegion(bottomPath, r, boundBottom);
+            bottomInset = displayHeight - boundBottom.top;
         } else {
             bottomInset = 0;
         }
 
-        // Reuse tmpRect as the inset rect we store into the DisplayCutout instance.
-        tmpRect.set(0, topInset, 0, bottomInset);
-        final DisplayCutout cutout = new DisplayCutout(tmpRect, r, false /* copyArguments */);
+        Rect safeInset = new Rect(0, topInset, 0, bottomInset);
+        final DisplayCutout cutout = new DisplayCutout(
+                safeInset, null /* boundLeft */, boundTop, null /* boundRight */, boundBottom,
+                false /* copyArguments */);
 
         final Pair<Path, DisplayCutout> result = new Pair<>(p, cutout);
         synchronized (CACHE_LOCK) {
@@ -468,16 +707,6 @@
         inoutRegion.op(inoutRect, Op.UNION);
     }
 
-    private static Region boundingRectsToRegion(List<Rect> rects) {
-        Region result = Region.obtain();
-        if (rects != null) {
-            for (Rect r : rects) {
-                result.op(r, Region.Op.UNION);
-            }
-        }
-        return result;
-    }
-
     /**
      * Helper class for passing {@link DisplayCutout} through binder.
      *
@@ -520,7 +749,7 @@
             } else {
                 out.writeInt(1);
                 out.writeTypedObject(cutout.mSafeInsets, flags);
-                out.writeTypedObject(cutout.mBounds, flags);
+                out.writeTypedArray(cutout.mBounds.getRects(), flags);
             }
         }
 
@@ -561,7 +790,8 @@
             }
 
             Rect safeInsets = in.readTypedObject(Rect.CREATOR);
-            Region bounds = in.readTypedObject(Region.CREATOR);
+            Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
+            in.readTypedArray(bounds, Rect.CREATOR);
 
             return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
         }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 3bab87a..3e559d9 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -87,7 +87,6 @@
     void setEventDispatching(boolean enabled);
     void addWindowToken(IBinder token, int type, int displayId);
     void removeWindowToken(IBinder token, int displayId);
-    void setFocusedApp(IBinder token, boolean moveFocusNow);
     void prepareAppTransition(int transit, boolean alwaysKeepCurrent);
     int getPendingAppTransition();
     void overridePendingAppTransition(String packageName, int enterAnim, int exitAnim,
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index c38fcf3..0a3403b 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -896,8 +896,8 @@
      * @deprecated No longer used by the input system.
      * {@link #getAction} value: multiple duplicate key events have
      * occurred in a row, or a complex string is being delivered.  If the
-     * key code is not {#link {@link #KEYCODE_UNKNOWN} then the
-     * {#link {@link #getRepeatCount()} method returns the number of times
+     * key code is not {@link #KEYCODE_UNKNOWN} then the
+     * {@link #getRepeatCount()} method returns the number of times
      * the given key code should be executed.
      * Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then
      * this is a sequence of characters as returned by {@link #getCharacters}.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b2944d6..cddd83c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -14639,9 +14639,10 @@
      * Recomputes the matrix if necessary.
      *
      * @return True if the transform matrix is the identity matrix, false otherwise.
+     * @hide
      */
     @UnsupportedAppUsage
-    final boolean hasIdentityMatrix() {
+    public final boolean hasIdentityMatrix() {
         return mRenderNode.hasIdentityMatrix();
     }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 7abe19e79..d4c7069 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -803,10 +803,6 @@
                 return true;
             }
         }
-        if (sVerbose) {
-            Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id
-                    + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds);
-        }
         return false;
     }
 
@@ -845,6 +841,9 @@
         ensureServiceClientAddedIfNeededLocked();
 
         if (!mEnabled) {
+            if (sVerbose) {
+                Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
+            }
             if (mCallback != null) {
                 callback = mCallback;
             }
@@ -995,6 +994,9 @@
         ensureServiceClientAddedIfNeededLocked();
 
         if (!mEnabled) {
+            if (sVerbose) {
+                Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled");
+            }
             if (mCallback != null) {
                 callback = mCallback;
             }
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 9d74c98..8027dd7 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -101,6 +101,7 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -4800,6 +4801,24 @@
             return glyphHeight > magnifierContentHeight;
         }
 
+        private boolean viewIsMatrixTransformed() {
+            if (mMagnifierAnimator.mMagnifierIsShowing) {
+                // Do not check again when the magnifier is currently showing.
+                return false;
+            }
+            if (!mTextView.hasIdentityMatrix()) {
+                return true;
+            }
+            ViewParent viewParent = mTextView.getParent();
+            while (viewParent != null) {
+                if (viewParent instanceof View && !((View) viewParent).hasIdentityMatrix()) {
+                    return true;
+                }
+                viewParent = viewParent.getParent();
+            }
+            return false;
+        }
+
         /**
          * Computes the position where the magnifier should be shown, relative to
          * {@code mTextView}, and writes them to {@code showPosInView}. Also decides
@@ -4928,6 +4947,7 @@
 
             final PointF showPosInView = new PointF();
             final boolean shouldShow = !tooLargeTextForMagnifier()
+                    && !viewIsMatrixTransformed()
                     && obtainMagnifierShowCoordinates(event, showPosInView);
             if (shouldShow) {
                 // Make the cursor visible and stop blinking.
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 0a812c6..fd38917 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.app;
 
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.UiThread;
@@ -66,6 +68,7 @@
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.Toast;
+
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.content.PackageMonitor;
@@ -81,8 +84,6 @@
 import java.util.Objects;
 import java.util.Set;
 
-import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
 /**
  * This activity is displayed when the system attempts to start an Intent for
  * which there is more than one matching activity, allowing the user to decide
@@ -288,6 +289,7 @@
         mTitle = title;
         mDefaultTitleResId = defaultTitleRes;
 
+        mIconFactory = IconDrawableFactory.newInstance(this, true);
         if (configureContentView(mIntents, initialIntents, rList)) {
             return;
         }
@@ -335,7 +337,6 @@
                 : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
                 intent.getAction() + ":" + intent.getType() + ":"
                         + (categories != null ? Arrays.toString(categories.toArray()) : ""));
-        mIconFactory = IconDrawableFactory.newInstance(this, true);
     }
 
     @Override
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index c0c358d..856712f 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -436,6 +436,12 @@
     }
 
     public void setSamplingInterval(int samplingInterval) {
+        if (samplingInterval <= 0) {
+            Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): "
+                    + samplingInterval);
+            return;
+        }
+
         synchronized (mLock) {
             if (samplingInterval != mPeriodicSamplingInterval) {
                 mPeriodicSamplingInterval = samplingInterval;
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index 997b722..8338d78 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.XmlUtils;
@@ -501,4 +502,181 @@
     public double getBatteryCapacity() {
         return getAveragePower(POWER_BATTERY_CAPACITY);
     }
+
+    /**
+     * Dump power constants into PowerProfileProto
+     */
+    public void writeToProto(ProtoOutputStream proto) {
+        // cpu.suspend
+        writePowerConstantToProto(proto, POWER_CPU_SUSPEND, PowerProfileProto.CPU_SUSPEND);
+
+        // cpu.idle
+        writePowerConstantToProto(proto, POWER_CPU_IDLE, PowerProfileProto.CPU_IDLE);
+
+        // cpu.active
+        writePowerConstantToProto(proto, POWER_CPU_ACTIVE, PowerProfileProto.CPU_ACTIVE);
+
+        // cpu.clusters.cores
+        // cpu.cluster_power.cluster
+        // cpu.core_speeds.cluster
+        // cpu.core_power.cluster
+        for (int cluster = 0; cluster < mCpuClusters.length; cluster++) {
+            final long token = proto.start(PowerProfileProto.CPU_CLUSTER);
+            proto.write(PowerProfileProto.CpuCluster.ID, cluster);
+            proto.write(PowerProfileProto.CpuCluster.CLUSTER_POWER,
+                    sPowerItemMap.get(mCpuClusters[cluster].clusterPowerKey));
+            proto.write(PowerProfileProto.CpuCluster.CORES, mCpuClusters[cluster].numCpus);
+            for (Double speed : sPowerArrayMap.get(mCpuClusters[cluster].freqKey)) {
+                proto.write(PowerProfileProto.CpuCluster.SPEED, speed);
+            }
+            for (Double corePower : sPowerArrayMap.get(mCpuClusters[cluster].corePowerKey)) {
+                proto.write(PowerProfileProto.CpuCluster.CORE_POWER, corePower);
+            }
+            proto.end(token);
+        }
+
+        // wifi.scan
+        writePowerConstantToProto(proto, POWER_WIFI_SCAN, PowerProfileProto.WIFI_SCAN);
+
+        // wifi.on
+        writePowerConstantToProto(proto, POWER_WIFI_ON, PowerProfileProto.WIFI_ON);
+
+        // wifi.active
+        writePowerConstantToProto(proto, POWER_WIFI_ACTIVE, PowerProfileProto.WIFI_ACTIVE);
+
+        // wifi.controller.idle
+        writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_IDLE,
+                PowerProfileProto.WIFI_CONTROLLER_IDLE);
+
+        // wifi.controller.rx
+        writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_RX,
+                PowerProfileProto.WIFI_CONTROLLER_RX);
+
+        // wifi.controller.tx
+        writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_TX,
+                PowerProfileProto.WIFI_CONTROLLER_TX);
+
+        // wifi.controller.tx_levels
+        writePowerConstantArrayToProto(proto, POWER_WIFI_CONTROLLER_TX_LEVELS,
+                PowerProfileProto.WIFI_CONTROLLER_TX_LEVELS);
+
+        // wifi.controller.voltage
+        writePowerConstantToProto(proto, POWER_WIFI_CONTROLLER_OPERATING_VOLTAGE,
+                PowerProfileProto.WIFI_CONTROLLER_OPERATING_VOLTAGE);
+
+        // bluetooth.controller.idle
+        writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_IDLE,
+                PowerProfileProto.BLUETOOTH_CONTROLLER_IDLE);
+
+        // bluetooth.controller.rx
+        writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_RX,
+                PowerProfileProto.BLUETOOTH_CONTROLLER_RX);
+
+        // bluetooth.controller.tx
+        writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_TX,
+                PowerProfileProto.BLUETOOTH_CONTROLLER_TX);
+
+        // bluetooth.controller.voltage
+        writePowerConstantToProto(proto, POWER_BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE,
+                PowerProfileProto.BLUETOOTH_CONTROLLER_OPERATING_VOLTAGE);
+
+        // modem.controller.sleep
+        writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_SLEEP,
+                PowerProfileProto.MODEM_CONTROLLER_SLEEP);
+
+        // modem.controller.idle
+        writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_IDLE,
+                PowerProfileProto.MODEM_CONTROLLER_IDLE);
+
+        // modem.controller.rx
+        writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_RX,
+                PowerProfileProto.MODEM_CONTROLLER_RX);
+
+        // modem.controller.tx
+        writePowerConstantArrayToProto(proto, POWER_MODEM_CONTROLLER_TX,
+                PowerProfileProto.MODEM_CONTROLLER_TX);
+
+        // modem.controller.voltage
+        writePowerConstantToProto(proto, POWER_MODEM_CONTROLLER_OPERATING_VOLTAGE,
+                PowerProfileProto.MODEM_CONTROLLER_OPERATING_VOLTAGE);
+
+        // gps.on
+        writePowerConstantToProto(proto, POWER_GPS_ON, PowerProfileProto.GPS_ON);
+
+        // gps.signalqualitybased
+        writePowerConstantArrayToProto(proto, POWER_GPS_SIGNAL_QUALITY_BASED,
+                PowerProfileProto.GPS_SIGNAL_QUALITY_BASED);
+
+        // gps.voltage
+        writePowerConstantToProto(proto, POWER_GPS_OPERATING_VOLTAGE,
+                PowerProfileProto.GPS_OPERATING_VOLTAGE);
+
+        // bluetooth.on
+        writePowerConstantToProto(proto, POWER_BLUETOOTH_ON, PowerProfileProto.BLUETOOTH_ON);
+
+        // bluetooth.active
+        writePowerConstantToProto(proto, POWER_BLUETOOTH_ACTIVE,
+                PowerProfileProto.BLUETOOTH_ACTIVE);
+
+        // bluetooth.at
+        writePowerConstantToProto(proto, POWER_BLUETOOTH_AT_CMD,
+                PowerProfileProto.BLUETOOTH_AT_CMD);
+
+        // ambient.on
+        writePowerConstantToProto(proto, POWER_AMBIENT_DISPLAY, PowerProfileProto.AMBIENT_DISPLAY);
+
+        // screen.on
+        writePowerConstantToProto(proto, POWER_SCREEN_ON, PowerProfileProto.SCREEN_ON);
+
+        // radio.on
+        writePowerConstantToProto(proto, POWER_RADIO_ON, PowerProfileProto.RADIO_ON);
+
+        // radio.scanning
+        writePowerConstantToProto(proto, POWER_RADIO_SCANNING, PowerProfileProto.RADIO_SCANNING);
+
+        // radio.active
+        writePowerConstantToProto(proto, POWER_RADIO_ACTIVE, PowerProfileProto.RADIO_ACTIVE);
+
+        // screen.full
+        writePowerConstantToProto(proto, POWER_SCREEN_FULL, PowerProfileProto.SCREEN_FULL);
+
+        // audio
+        writePowerConstantToProto(proto, POWER_AUDIO, PowerProfileProto.AUDIO);
+
+        // video
+        writePowerConstantToProto(proto, POWER_VIDEO, PowerProfileProto.VIDEO);
+
+        // camera.flashlight
+        writePowerConstantToProto(proto, POWER_FLASHLIGHT, PowerProfileProto.FLASHLIGHT);
+
+        // memory.bandwidths
+        writePowerConstantToProto(proto, POWER_MEMORY, PowerProfileProto.MEMORY);
+
+        // camera.avg
+        writePowerConstantToProto(proto, POWER_CAMERA, PowerProfileProto.CAMERA);
+
+        // wifi.batchedscan
+        writePowerConstantToProto(proto, POWER_WIFI_BATCHED_SCAN,
+                PowerProfileProto.WIFI_BATCHED_SCAN);
+
+        // battery.capacity
+        writePowerConstantToProto(proto, POWER_BATTERY_CAPACITY,
+                PowerProfileProto.BATTERY_CAPACITY);
+    }
+
+    // Writes items in sPowerItemMap to proto if exists.
+    private void writePowerConstantToProto(ProtoOutputStream proto, String key, long fieldId) {
+        if (sPowerItemMap.containsKey(key)) {
+            proto.write(fieldId, sPowerItemMap.get(key));
+        }
+    }
+
+    // Writes items in sPowerArrayMap to proto if exists.
+    private void writePowerConstantArrayToProto(ProtoOutputStream proto, String key, long fieldId) {
+        if (sPowerArrayMap.containsKey(key)) {
+            for (Double d : sPowerArrayMap.get(key)) {
+                proto.write(fieldId, d);
+            }
+        }
+    }
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 762b430..8e24d10 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -209,6 +209,8 @@
         "com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
         "hwbinder/EphemeralStorage.cpp",
         "fd_utils.cpp",
+        "android_hardware_input_InputWindowHandle.cpp",
+        "android_hardware_input_InputApplicationHandle.cpp",
     ],
 
     include_dirs: [
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 897f6fa5..2635969 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -7,15 +7,9 @@
 #include "SkImageEncoder.h"
 #include "SkImageInfo.h"
 #include "SkColor.h"
-#include "SkColorPriv.h"
 #include "SkColorSpace.h"
-#include "SkColorSpaceXform.h"
-#include "SkHalf.h"
 #include "SkMatrix44.h"
-#include "SkPM4f.h"
-#include "SkPM4fPriv.h"
 #include "GraphicsJNI.h"
-#include "SkUnPreMultiply.h"
 #include "SkStream.h"
 
 #include <binder/Parcel.h>
@@ -559,13 +553,10 @@
         if (!p3.tryAllocPixels(info)) {
             return JNI_FALSE;
         }
-        auto xform = SkColorSpaceXform::New(skbitmap.colorSpace(), info.colorSpace());
-        if (!xform) {
-            return JNI_FALSE;
-        }
-        if (!xform->apply(SkColorSpaceXform::kRGBA_8888_ColorFormat, p3.getPixels(),
-                          SkColorSpaceXform::kRGBA_F16_ColorFormat, skbitmap.getPixels(),
-                          info.width() * info.height(), kUnpremul_SkAlphaType)) {
+
+        SkPixmap pm;
+        SkAssertResult(p3.peekPixels(&pm));  // should always work if tryAllocPixels() did.
+        if (!skbitmap.readPixels(pm)) {
             return JNI_FALSE;
         }
         skbitmap = p3;
diff --git a/core/jni/android_graphics_Canvas.cpp b/core/jni/android_graphics_Canvas.cpp
index 343aef2..dca2da3 100644
--- a/core/jni/android_graphics_Canvas.cpp
+++ b/core/jni/android_graphics_Canvas.cpp
@@ -273,6 +273,32 @@
     get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
 }
 
+static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+                    jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx,
+                    jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+                    jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+    get_canvas(canvasHandle)->drawDoubleRoundRectXY(
+                    outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy,
+                    innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint);
+}
+
+static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
+                     jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii,
+                     jfloat innerLeft, jfloat innerTop, jfloat innerRight,
+                     jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) {
+    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
+
+    float outerRadii[8];
+    float innerRadii[8];
+    env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii);
+    env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii);
+    get_canvas(canvasHandle)->drawDoubleRoundRectRadii(
+                    outerLeft, outerTop, outerRight, outerBottom, outerRadii,
+                    innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint);
+
+}
+
 static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
                        jlong paintHandle) {
     const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
@@ -651,6 +677,8 @@
     {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
     {"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
     {"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
+    {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY},
+    {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii},
     {"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
     {"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
     {"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index ab8e685..05f6556 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -40,6 +40,7 @@
     jfieldID deviceWidth;
     jfieldID deviceHeight;
     jfieldID uniqueId;
+    jfieldID type;
 } gDisplayViewportClassInfo;
 
 static struct {
@@ -64,6 +65,9 @@
         viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str();
     }
 
+    viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj,
+                gDisplayViewportClassInfo.type));
+
     jobject logicalFrameObj =
             env->GetObjectField(viewportObj, gDisplayViewportClassInfo.logicalFrame);
     viewport->logicalLeft = env->GetIntField(logicalFrameObj, gRectClassInfo.left);
@@ -108,6 +112,9 @@
     gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env,
             gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;");
 
+    gDisplayViewportClassInfo.type = GetFieldIDOrDie(env,
+            gDisplayViewportClassInfo.clazz, "type", "I");
+
     clazz = FindClassOrDie(env, "android/graphics/Rect");
     gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I");
     gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I");
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
similarity index 98%
rename from services/core/jni/com_android_server_input_InputApplicationHandle.cpp
rename to core/jni/android_hardware_input_InputApplicationHandle.cpp
index 514b6e1..8ace8da 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -21,7 +21,7 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/threads.h>
 
-#include "com_android_server_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
 
 namespace android {
 
diff --git a/services/core/jni/com_android_server_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
similarity index 95%
rename from services/core/jni/com_android_server_input_InputApplicationHandle.h
rename to core/jni/android_hardware_input_InputApplicationHandle.h
index c9af711..7115611 100644
--- a/services/core/jni/com_android_server_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -17,7 +17,9 @@
 #ifndef _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
 #define _ANDROID_SERVER_INPUT_APPLICATION_HANDLE_H
 
-#include <inputflinger/InputApplication.h>
+#include <string>
+
+#include <input/InputApplication.h>
 
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
similarity index 98%
rename from services/core/jni/com_android_server_input_InputWindowHandle.cpp
rename to core/jni/android_hardware_input_InputWindowHandle.cpp
index c13aa38..f4829ad 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -25,8 +25,8 @@
 #include <android/graphics/Region.h>
 #include <ui/Region.h>
 
-#include "com_android_server_input_InputWindowHandle.h"
-#include "com_android_server_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputWindowHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
 
 namespace android {
 
diff --git a/services/core/jni/com_android_server_input_InputWindowHandle.h b/core/jni/android_hardware_input_InputWindowHandle.h
similarity index 96%
rename from services/core/jni/com_android_server_input_InputWindowHandle.h
rename to core/jni/android_hardware_input_InputWindowHandle.h
index 44d4620..2be267e 100644
--- a/services/core/jni/com_android_server_input_InputWindowHandle.h
+++ b/core/jni/android_hardware_input_InputWindowHandle.h
@@ -17,7 +17,7 @@
 #ifndef _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
 #define _ANDROID_SERVER_INPUT_WINDOW_HANDLE_H
 
-#include <inputflinger/InputWindow.h>
+#include <input/InputWindow.h>
 
 #include <nativehelper/JNIHelp.h>
 #include "jni.h"
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index 0a90b97..2f17907 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -202,17 +202,9 @@
     if (parcel) {
         bool isInitialized = parcel->readInt32();
         if (isInitialized) {
-            String8 name = parcel->readString8();
-            int rawFd = parcel->readFileDescriptor();
-            int dupFd = dup(rawFd);
-            if (dupFd < 0) {
-                ALOGE("Error %d dup channel fd %d.", errno, rawFd);
-                jniThrowRuntimeException(env,
-                        "Could not read input channel file descriptors from parcel.");
-                return;
-            }
+            InputChannel* inputChannel = new InputChannel();
+            inputChannel->read(*parcel);
 
-            InputChannel* inputChannel = new InputChannel(name.string(), dupFd);
             NativeInputChannel* nativeInputChannel = new NativeInputChannel(inputChannel);
 
             android_view_InputChannel_setNativeInputChannel(env, obj, nativeInputChannel);
@@ -230,8 +222,7 @@
             sp<InputChannel> inputChannel = nativeInputChannel->getInputChannel();
 
             parcel->writeInt32(1);
-            parcel->writeString8(String8(inputChannel->getName().c_str()));
-            parcel->writeDupFileDescriptor(inputChannel->getFd());
+            inputChannel->write(*parcel);
         } else {
             parcel->writeInt32(0);
         }
diff --git a/core/proto/android/internal/powerprofile.proto b/core/proto/android/internal/powerprofile.proto
new file mode 100644
index 0000000..9dd5e7e
--- /dev/null
+++ b/core/proto/android/internal/powerprofile.proto
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+syntax = "proto2";
+package com.android.internal.os;
+
+option java_multiple_files = true;
+
+// next: 41
+message PowerProfileProto {
+    optional double cpu_suspend = 1;
+
+    optional double cpu_idle = 2;
+
+    optional double cpu_active = 3;
+
+    message CpuCluster {
+        optional int32 id = 1;
+        optional double cluster_power = 2;
+        optional int32 cores = 3;
+        repeated int64 speed = 4;
+        repeated double core_power = 5;
+    }
+
+    repeated CpuCluster cpu_cluster = 41;
+
+    optional double wifi_scan = 4;
+
+    optional double wifi_on = 5;
+
+    optional double wifi_active = 6;
+
+    optional double wifi_controller_idle = 7;
+
+    optional double wifi_controller_rx = 8;
+
+    optional double wifi_controller_tx = 9;
+
+    repeated double wifi_controller_tx_levels = 10;
+
+    optional double wifi_controller_operating_voltage = 11;
+
+    optional double bluetooth_controller_idle = 12;
+
+    optional double bluetooth_controller_rx = 13;
+
+    optional double bluetooth_controller_tx = 14;
+
+    optional double bluetooth_controller_operating_voltage = 15;
+
+    optional double modem_controller_sleep = 16;
+
+    optional double modem_controller_idle = 17;
+
+    optional double modem_controller_rx = 18;
+
+    repeated double modem_controller_tx = 19;
+
+    optional double modem_controller_operating_voltage = 20;
+
+    optional double gps_on = 21;
+
+    repeated double gps_signal_quality_based = 22;
+
+    optional double gps_operating_voltage = 23;
+
+    optional double bluetooth_on = 24;
+
+    optional double bluetooth_active = 25;
+
+    optional double bluetooth_at_cmd = 26;
+
+    optional double ambient_display = 27;
+
+    optional double screen_on = 29;
+
+    optional double radio_on = 30;
+
+    optional double radio_scanning = 31;
+
+    optional double radio_active = 32;
+
+    optional double screen_full = 33;
+
+    optional double audio = 34;
+
+    optional double video = 35;
+
+    optional double flashlight = 36;
+
+    optional double memory = 37;
+
+    optional double camera = 38;
+
+    optional double wifi_batched_scan = 39;
+
+    optional double battery_capacity = 40;
+}
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index d33ea0c..c1c86f0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -158,6 +158,7 @@
     optional ScreenRotationAnimationProto screen_rotation_animation = 12;
     optional DisplayFramesProto display_frames = 13;
     optional int32 surface_size = 14;
+    optional string focused_app = 15;
 }
 
 /* represents DisplayFrames */
diff --git a/core/proto/android/view/displaycutout.proto b/core/proto/android/view/displaycutout.proto
index f4744da..0a33101 100644
--- a/core/proto/android/view/displaycutout.proto
+++ b/core/proto/android/view/displaycutout.proto
@@ -26,5 +26,9 @@
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
     optional .android.graphics.RectProto insets = 1;
-    optional .android.graphics.RectProto bounds = 2;
+    reserved 2;
+    optional .android.graphics.RectProto bound_left = 3;
+    optional .android.graphics.RectProto bound_top = 4;
+    optional .android.graphics.RectProto bound_right = 5;
+    optional .android.graphics.RectProto bound_bottom = 6;
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f654ce2..f3f012d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1100,7 +1100,7 @@
 
     <!-- Allows a calling application which manages it own calls through the self-managed
          {@link android.telecom.ConnectionService} APIs.  See
-         {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED for more information on the
+         {@link android.telecom.PhoneAccount#CAPABILITY_SELF_MANAGED} for more information on the
          self-managed ConnectionService APIs.
          <p>Protection level: normal
     -->
@@ -1932,6 +1932,13 @@
     <permission android:name="android.permission.BIND_SCREENING_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Must be required by a {@link android.telecom.CallRedirectionService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature|privileged
+    -->
+    <permission android:name="android.permission.BIND_CALL_REDIRECTION_SERVICE"
+                android:protectionLevel="signature|privileged" />
+
     <!-- Must be required by a {@link android.telecom.ConnectionService},
          to ensure that only the system can bind to it.
          @deprecated {@link android.telecom.ConnectionService}s should require
diff --git a/core/res/res/values-night/values.xml b/core/res/res/values-night/values.xml
index 45cf0f0..9de8842 100644
--- a/core/res/res/values-night/values.xml
+++ b/core/res/res/values-night/values.xml
@@ -20,7 +20,7 @@
         <!-- Color palette -->
         <item name="colorPrimaryDark">@color/primary_dark_device_default_settings</item>
         <item name="colorSecondary">@color/secondary_device_default_settings</item>
-        <item name="colorAccent">@color/accent_device_default_dark</item>
+        <item name="colorAccent">@color/white</item>
         <item name="colorError">@color/error_color_device_default_dark</item>
         <item name="colorControlNormal">?attr/textColorPrimary</item>
         <item name="alertDialogTheme">@style/Theme.DeviceDefault.Dialog.Alert</item>
@@ -32,6 +32,8 @@
         <item name="panelColorBackground">@color/material_grey_800</item>
     </style>
 
+    <style name="Theme.DeviceDefault.QuickSettings.Dialog" parent="Theme.DeviceDefault.Dialog" />
+
     <style name="TextAppearance.Material.Notification">
         <item name="textColor">@color/notification_secondary_text_color_dark</item>
         <item name="textSize">@dimen/notification_text_size</item>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e0db946..cdb65ed 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4184,9 +4184,9 @@
         row is full. The rowCount attribute may be used similarly in the vertical case.
         The default is horizontal. -->
         <attr name="orientation" />
-        <!-- The maxmimum number of rows to create when automatically positioning children. -->
+        <!-- The maximum number of rows to create when automatically positioning children. -->
         <attr name="rowCount" format="integer" />
-        <!-- The maxmimum number of columns to create when automatically positioning children. -->
+        <!-- The maximum number of columns to create when automatically positioning children. -->
         <attr name="columnCount" format="integer" />
         <!-- When set to true, tells GridLayout to use default margins when none are specified
         in a view's layout parameters.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 8ff29ba..99af0de 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1442,8 +1442,11 @@
          {@link #AndroidManifestService service},
          {@link #AndroidManifestReceiver receiver},
          {@link #AndroidManifestActivity activity},
-         {@link #AndroidManifestActivityAlias activity-alias}, and
-         {@link #AndroidManifestUsesLibrary uses-library}.  The application tag
+         {@link #AndroidManifestActivityAlias activity-alias},
+         {@link #AndroidManifestUsesLibrary uses-library},
+         {@link #AndroidManifestUsesStaticLibrary uses-static-library}, and
+         {@link #AndroidManifestUsesPackage uses-package}.
+         The application tag
          appears as a child of the root {@link #AndroidManifest manifest} tag in
          an application's manifest file. -->
     <declare-styleable name="AndroidManifestApplication" parent="AndroidManifest">
@@ -1877,12 +1880,35 @@
          library is singed with more than one certificate.
 
          <p>This appears as a child tag of the
-         {@link #AndroidManifestUsesStaticLibrary uses-static-library} tag. -->
+         {@link #AndroidManifestUsesStaticLibrary uses-static-library} or
+         {@link #AndroidManifestUsesPackage uses-package} tag. -->
     <declare-styleable name="AndroidManifestAdditionalCertificate" parent="AndroidManifestUsesStaticLibrary">
         <!-- The SHA-256 digest of the library signing certificate. -->
         <attr name="certDigest" />
     </declare-styleable>
 
+    <!-- The <code>uses-package</code> specifies some kind of dependency on another
+         package.  It does not have any impact on the app's execution on the device,
+         but provides information about dependencies it has on other packages that need
+         to  be satisfied for it to run correctly.  That is, this is primarily for
+         installers to know what other apps need to be installed along with this one.
+
+         <p>This appears as a child tag of the
+         {@link #AndroidManifestApplication application} tag. -->
+    <declare-styleable name="AndroidManifestUsesPackage" parent="AndroidManifestApplication">
+        <!-- Required type of association with the package, for example "android.package.ad_service"
+             if it provides an advertising service. -->
+        <attr name="packageType" format="string" />
+        <!-- Required name of the package you use. -->
+        <attr name="name" />
+        <!-- Optional minimum version of the package that satisfies the dependency. -->
+        <attr name="version" />
+        <!-- Optional minimum major version of the package that satisfies the dependency. -->
+        <attr name="versionMajor" format="integer" />
+        <!-- Optional SHA-256 digest of the package signing certificate. -->
+        <attr name="certDigest" format="string" />
+    </declare-styleable>
+
     <!-- The <code>supports-screens</code> specifies the screen dimensions an
          application supports.  By default a modern application supports all
          screen sizes and must explicitly disable certain screen sizes here;
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0daf5a7..8b73b67 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1527,7 +1527,7 @@
     <bool name="config_checkWallpaperAtBoot">true</bool>
 
     <!-- Class name of WallpaperManagerService. -->
-    <string name="config_wallpaperManagerServiceName">com.android.server.wallpaper.WallpaperManagerService</string>
+    <string name="config_wallpaperManagerServiceName" translatable="false">com.android.server.wallpaper.WallpaperManagerService</string>
 
     <!-- Enables the TimeZoneRuleManager service. This is the master switch for the updateable time
          zone update mechanism. -->
@@ -3550,4 +3550,7 @@
 
     <!-- Pre-scale volume at volume step 3 for Absolute Volume -->
     <fraction name="config_prescaleAbsoluteVolume_index3">85%</fraction>
+
+    <!-- Whether or not the "SMS app service" feature is enabled -->
+    <bool name="config_useSmsAppService">true</bool>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fadefff..2e42e4a 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2903,6 +2903,7 @@
   <eat-comment />
 
     <public-group type="attr" first-id="0x01010587">
+        <public name="packageType" />
         <public name="opticalInsetLeft" />
         <public name="opticalInsetTop" />
         <public name="opticalInsetRight" />
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 72ae0d6..a7b6dde 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3475,4 +3475,6 @@
   <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index1" />
   <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index2" />
   <java-symbol type="fraction" name="config_prescaleAbsoluteVolume_index3" />
+
+  <java-symbol type="bool" name="config_useSmsAppService" />
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index d704957..f60d8d0ad 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -61,6 +61,7 @@
     <uses-permission android:name="android.permission.READ_LOGS"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_SMS"/>
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 0d250b8..da17b56 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -64,7 +64,7 @@
 
         // TODO: Some sort of functional test (maybe not in the unit test here?)
         // that confirms that things are really happening e.g. screen power, keyboard power.
-}
+    }
 
     /**
      * Confirm that we can't create dysfunctional wakelocks.
@@ -85,6 +85,25 @@
     }
 
     /**
+     * Ensure that we can have work sources with work chains when uid is not set directly on work
+     * source, and that this doesn't crash system server.
+     *
+     * @throws Exception
+     */
+    @SmallTest
+    public void testWakeLockWithWorkChains() throws Exception {
+        PowerManager.WakeLock wakeLock = mPm.newWakeLock(
+                PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP,
+                "TEST_LOCK");
+        WorkSource workSource = new WorkSource();
+        WorkSource.WorkChain workChain = workSource.createWorkChain();
+        workChain.addNode(1000, "test");
+        wakeLock.setWorkSource(workSource);
+
+        doTestWakeLock(wakeLock);
+    }
+
+    /**
      * Apply a few tests to a wakelock to make sure it's healthy.
      *
      * @param wl The wakelock to be tested.
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index fe45fe7..c8d994c 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.DisplayCutout.NO_CUTOUT;
+import static android.view.DisplayCutout.extractBoundsFromList;
 import static android.view.DisplayCutout.fromSpec;
 
 import static org.hamcrest.Matchers.equalTo;
@@ -28,6 +29,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
 
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.platform.test.annotations.Presubmit;
@@ -39,38 +41,83 @@
 import org.junit.runner.RunWith;
 
 import java.util.Arrays;
+import java.util.Collections;
+
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 @Presubmit
 public class DisplayCutoutTest {
 
+    private static final Rect ZERO_RECT = new Rect();
+
     /** This is not a consistent cutout. Useful for verifying insets in one go though. */
     final DisplayCutout mCutoutNumbers = new DisplayCutout(
-            new Rect(1, 2, 3, 4),
-            Arrays.asList(new Rect(5, 6, 7, 8)));
+            Insets.of(5, 6, 7, 8) /* safeInsets */,
+            null /* boundLeft */,
+            new Rect(9, 0, 10, 1) /* boundTop */,
+            null /* boundRight */,
+            null /* boundBottom */);
 
     final DisplayCutout mCutoutTop = createCutoutTop();
 
     @Test
+    public void testExtractBoundsFromList_left() {
+        Rect safeInsets = new Rect(10, 0, 0, 0);
+        Rect bound = new Rect(0, 80, 10, 120);
+        assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+                equalTo(new Rect[]{bound, ZERO_RECT, ZERO_RECT, ZERO_RECT}));
+    }
+
+    @Test
+    public void testExtractBoundsFromList_top() {
+        Rect safeInsets = new Rect(0, 10, 0, 0);
+        Rect bound = new Rect(80, 0, 120, 10);
+        assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+                equalTo(new Rect[]{ZERO_RECT, bound, ZERO_RECT, ZERO_RECT}));
+    }
+
+    @Test
+    public void testExtractBoundsFromList_right() {
+        Rect safeInsets = new Rect(0, 0, 10, 0);
+        Rect bound = new Rect(190, 80, 200, 120);
+        assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+                equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, bound, ZERO_RECT}));
+    }
+
+    @Test
+    public void testExtractBoundsFromList_bottom() {
+        Rect safeInsets = new Rect(0, 0, 0, 10);
+        Rect bound = new Rect(80, 190, 120, 200);
+        assertThat(extractBoundsFromList(safeInsets, Collections.singletonList(bound)),
+                equalTo(new Rect[]{ZERO_RECT, ZERO_RECT, ZERO_RECT, bound}));
+    }
+
+    @Test
+    public void testExtractBoundsFromList_top_and_bottom() {
+        Rect safeInsets = new Rect(0, 1, 0, 10);
+        Rect boundTop = new Rect(80, 0, 120, 10);
+        Rect boundBottom = new Rect(80, 190, 120, 200);
+        assertThat(extractBoundsFromList(safeInsets,
+                Arrays.asList(new Rect[]{boundTop, boundBottom})),
+                equalTo(new Rect[]{ZERO_RECT, boundTop, ZERO_RECT, boundBottom}));
+    }
+
+
+    @Test
     public void hasCutout() throws Exception {
         assertTrue(NO_CUTOUT.isEmpty());
         assertFalse(mCutoutTop.isEmpty());
     }
 
     @Test
-    public void getSafeInsets() throws Exception {
-        assertEquals(1, mCutoutNumbers.getSafeInsetLeft());
-        assertEquals(2, mCutoutNumbers.getSafeInsetTop());
-        assertEquals(3, mCutoutNumbers.getSafeInsetRight());
-        assertEquals(4, mCutoutNumbers.getSafeInsetBottom());
+    public void testGetSafeInsets() throws Exception {
+        assertEquals(5, mCutoutNumbers.getSafeInsetLeft());
+        assertEquals(6, mCutoutNumbers.getSafeInsetTop());
+        assertEquals(7, mCutoutNumbers.getSafeInsetRight());
+        assertEquals(8, mCutoutNumbers.getSafeInsetBottom());
 
-        assertEquals(new Rect(1, 2, 3, 4), mCutoutNumbers.getSafeInsets());
-    }
-
-    @Test
-    public void getBoundingRect() throws Exception {
-        assertEquals(new Rect(50, 0, 75, 100), mCutoutTop.getBounds().getBounds());
+        assertEquals(new Rect(5, 6, 7, 8), mCutoutNumbers.getSafeInsets());
     }
 
     @Test
@@ -102,30 +149,30 @@
     public void inset_insets_withLeftCutout() throws Exception {
         DisplayCutout cutout = createCutoutWithInsets(100, 0, 0, 0).inset(1, 2, 3, 4);
 
-        assertEquals(cutout.getSafeInsetLeft(), 99);
-        assertEquals(cutout.getSafeInsetTop(), 0);
-        assertEquals(cutout.getSafeInsetRight(), 0);
-        assertEquals(cutout.getSafeInsetBottom(), 0);
+        assertEquals(99, cutout.getSafeInsetLeft());
+        assertEquals(0, cutout.getSafeInsetTop());
+        assertEquals(0, cutout.getSafeInsetRight());
+        assertEquals(0, cutout.getSafeInsetBottom());
     }
 
     @Test
     public void inset_insets_withTopCutout() throws Exception {
         DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
 
-        assertEquals(cutout.getSafeInsetLeft(), 0);
-        assertEquals(cutout.getSafeInsetTop(), 98);
-        assertEquals(cutout.getSafeInsetRight(), 0);
-        assertEquals(cutout.getSafeInsetBottom(), 0);
+        assertEquals(0, cutout.getSafeInsetLeft());
+        assertEquals(98, cutout.getSafeInsetTop());
+        assertEquals(0, cutout.getSafeInsetRight());
+        assertEquals(0, cutout.getSafeInsetBottom());
     }
 
     @Test
     public void inset_insets_withRightCutout() throws Exception {
         DisplayCutout cutout = createCutoutWithInsets(0, 0, 100, 0).inset(1, 2, 3, 4);
 
-        assertEquals(cutout.getSafeInsetLeft(), 0);
-        assertEquals(cutout.getSafeInsetTop(), 0);
-        assertEquals(cutout.getSafeInsetRight(), 97);
-        assertEquals(cutout.getSafeInsetBottom(), 0);
+        assertEquals(0, cutout.getSafeInsetLeft());
+        assertEquals(0, cutout.getSafeInsetTop());
+        assertEquals(97, cutout.getSafeInsetRight());
+        assertEquals(0, cutout.getSafeInsetBottom());
     }
 
     @Test
@@ -153,16 +200,20 @@
     @Test
     public void inset_bounds() throws Exception {
         DisplayCutout cutout = mCutoutTop.inset(1, 2, 3, 4);
-
-        assertEquals(new Rect(49, -2, 74, 98), cutout.getBounds().getBounds());
+        assertThat(cutout.getBoundingRectsAll(), equalTo(
+                new Rect[]{ ZERO_RECT, new Rect(49, -2, 74, 98), ZERO_RECT, ZERO_RECT }));
     }
 
+
+    // TODO: Deprecate fromBoundingRect.
+    /*
     @Test
     public void fromBoundingPolygon() throws Exception {
         assertEquals(
                 new Rect(50, 0, 75, 100),
                 DisplayCutout.fromBoundingRect(50, 0, 75, 100).getBounds().getBounds());
     }
+    */
 
     @Test
     public void parcel_unparcel_regular() {
@@ -231,6 +282,10 @@
         DisplayCutout cutout = fromSpec("M -50,0 v 20 h 100 v -20 z"
                 + "@bottom M -50,0 v -10,0 h 100 v 20 z", 200, 400, 2f);
         assertThat(cutout.getSafeInsets(), equalTo(new Rect(0, 20, 0, 10)));
+        assertThat(cutout.getBoundingRectsAll(), equalTo(new Rect[]{
+                ZERO_RECT, new Rect(50, 0, 150, 20),
+                ZERO_RECT, new Rect(50, 390, 150, 410)
+        }));
     }
 
     @Test
@@ -281,8 +336,10 @@
     }
 
     private static DisplayCutout createCutoutWithInsets(int left, int top, int right, int bottom) {
+        Insets safeInset = Insets.of(left, top, right, bottom);
+        Rect boundTop = new Rect(50, 0, 75, 100);
         return new DisplayCutout(
-                new Rect(left, top, right, bottom),
-                Arrays.asList(new Rect(50, 0, 75, 100)));
+                safeInset, null /* boundLeft */, boundTop, null /* boundRight */,
+                null /* boundBottom */);
     }
 }
diff --git a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
index 9edbf3e..1b00e09 100644
--- a/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/InputMethodInfoTest.java
@@ -26,8 +26,8 @@
 import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.Parcel;
-import android.support.test.filters.LargeTest;
 import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
 import com.android.frameworks.coretests.R;
@@ -35,7 +35,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-@LargeTest
+@SmallTest
 @RunWith(AndroidJUnit4.class)
 public class InputMethodInfoTest {
 
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index cac4e88..218566e 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -27,6 +27,7 @@
 import static org.junit.Assert.assertThat;
 
 import android.content.Context;
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -44,18 +45,20 @@
 import org.junit.runner.RunWith;
 
 import java.lang.reflect.Field;
-import java.util.Arrays;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class ActionBarOverlayLayoutTest {
 
-    private static final Rect TOP_INSET_5 = new Rect(0, 5, 0, 0);
-    private static final Rect TOP_INSET_25 = new Rect(0, 25, 0, 0);
-    private static final Rect ZERO_INSET = new Rect(0, 0, 0, 0);
+    private static final Insets TOP_INSET_5 = Insets.of(0, 5, 0, 0);
+    private static final Insets TOP_INSET_25 = Insets.of(0, 25, 0, 0);
     private static final DisplayCutout CONSUMED_CUTOUT = null;
-    private static final DisplayCutout CUTOUT_5 = new DisplayCutout(TOP_INSET_5, Arrays.asList(
-            new Rect(100, 0, 200, 5)));
+    private static final DisplayCutout CUTOUT_5 = new DisplayCutout(
+            TOP_INSET_5,
+            null /* boundLeft */,
+            new Rect(100, 0, 200, 5),
+            null /* boundRight */,
+            null /* boundBottom*/);
     private static final int EXACTLY_1000 = makeMeasureSpec(1000, EXACTLY);
 
     private Context mContext;
@@ -112,7 +115,7 @@
 
         mLayout.measure(EXACTLY_1000, EXACTLY_1000);
 
-        assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, CONSUMED_CUTOUT)));
+        assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, CONSUMED_CUTOUT)));
     }
 
     @Test
@@ -136,7 +139,7 @@
 
         mLayout.measure(EXACTLY_1000, EXACTLY_1000);
 
-        assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
+        assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, NO_CUTOUT)));
     }
 
     @Test
@@ -160,11 +163,11 @@
 
         mLayout.measure(EXACTLY_1000, EXACTLY_1000);
 
-        assertThat(mContentInsetsListener.captured, is(insetsWith(ZERO_INSET, NO_CUTOUT)));
+        assertThat(mContentInsetsListener.captured, is(insetsWith(Insets.NONE, NO_CUTOUT)));
     }
 
-    private WindowInsets insetsWith(Rect content, DisplayCutout cutout) {
-        return new WindowInsets(content, null, null, false, false, cutout);
+    private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
+        return new WindowInsets(content.toRect(), null, null, false, false, cutout);
     }
 
     private ViewGroup createViewGroupWithId(int id) {
diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java
index fa37bed..0885a05 100644
--- a/graphics/java/android/graphics/BaseCanvas.java
+++ b/graphics/java/android/graphics/BaseCanvas.java
@@ -376,6 +376,53 @@
         drawRoundRect(rect.left, rect.top, rect.right, rect.bottom, rx, ry, paint);
     }
 
+    /**
+     * Make lint happy.
+     * See {@link Canvas#drawDoubleRoundRect(RectF, float, float, RectF, float, float, Paint)}
+     */
+    public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+            @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
+        float outerLeft = outer.left;
+        float outerTop = outer.top;
+        float outerRight = outer.right;
+        float outerBottom = outer.bottom;
+
+        float innerLeft = inner.left;
+        float innerTop = inner.top;
+        float innerRight = inner.right;
+        float innerBottom = inner.bottom;
+        nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight, outerBottom,
+                outerRx, outerRy, innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy,
+                paint.getNativeInstance());
+    }
+
+    /**
+     * Make lint happy.
+     * See {@link Canvas#drawDoubleRoundRect(RectF, float[], RectF, float[], Paint)}
+     */
+    public void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+            @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+        throwIfHasHwBitmapInSwMode(paint);
+        if (innerRadii == null || outerRadii == null
+                || innerRadii.length != 8 || outerRadii.length != 8) {
+            throw new IllegalArgumentException("Both inner and outer radii arrays must contain "
+                    + "exactly 8 values");
+        }
+        float outerLeft = outer.left;
+        float outerTop = outer.top;
+        float outerRight = outer.right;
+        float outerBottom = outer.bottom;
+
+        float innerLeft = inner.left;
+        float innerTop = inner.top;
+        float innerRight = inner.right;
+        float innerBottom = inner.bottom;
+        nDrawDoubleRoundRect(mNativeCanvasWrapper, outerLeft, outerTop, outerRight,
+                outerBottom, outerRadii, innerLeft, innerTop, innerRight, innerBottom, innerRadii,
+                paint.getNativeInstance());
+    }
+
     public void drawText(@NonNull char[] text, int index, int count, float x, float y,
             @NonNull Paint paint) {
         if ((index | count | (index + count) |
@@ -631,6 +678,16 @@
     private static native void nDrawRoundRect(long nativeCanvas, float left, float top, float right,
             float bottom, float rx, float ry, long nativePaint);
 
+    private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+            float outerTop, float outerRight, float outerBottom, float outerRx, float outerRy,
+            float innerLeft, float innerTop, float innerRight, float innerBottom, float innerRx,
+            float innerRy, long nativePaint);
+
+    private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+            float outerTop, float outerRight, float outerBottom, float[] outerRadii,
+            float innerLeft, float innerTop, float innerRight, float innerBottom,
+            float[] innerRadii, long nativePaint);
+
     private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
 
     private static native void nDrawRegion(long nativeCanvas, long nativeRegion, long nativePaint);
diff --git a/graphics/java/android/graphics/BaseRecordingCanvas.java b/graphics/java/android/graphics/BaseRecordingCanvas.java
index 6e93691..fb30ca2 100644
--- a/graphics/java/android/graphics/BaseRecordingCanvas.java
+++ b/graphics/java/android/graphics/BaseRecordingCanvas.java
@@ -377,6 +377,24 @@
     }
 
     @Override
+    public final void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+            @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+        nDrawDoubleRoundRect(mNativeCanvasWrapper,
+                outer.left, outer.top, outer.right, outer.bottom, outerRx, outerRy,
+                inner.left, inner.top, inner.right, inner.bottom, innerRx, innerRy,
+                paint.getNativeInstance());
+    }
+
+    @Override
+    public final void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+            @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+        nDrawDoubleRoundRect(mNativeCanvasWrapper,
+                outer.left, outer.top, outer.right, outer.bottom, outerRadii,
+                inner.left, inner.top, inner.right, inner.bottom, innerRadii,
+                paint.getNativeInstance());
+    }
+
+    @Override
     public final void drawText(@NonNull char[] text, int index, int count, float x, float y,
             @NonNull Paint paint) {
         if ((index | count | (index + count)
@@ -593,6 +611,18 @@
             float bottom, float rx, float ry, long nativePaint);
 
     @FastNative
+    private static native void nDrawDoubleRoundRect(long nativeCanvas,
+            float outerLeft, float outerTop, float outerRight, float outerBottom,
+            float outerRx, float outerRy, float innerLeft, float innerTop, float innerRight,
+            float innerBottom, float innerRx, float innerRy, long nativePaint);
+
+    @FastNative
+    private static native void nDrawDoubleRoundRect(long nativeCanvas, float outerLeft,
+            float outerTop, float outerRight, float outerBottom, float[] outerRadii,
+            float innerLeft, float innerTop, float innerRight, float innerBottom,
+            float[] innerRadii, long nativePaint);
+
+    @FastNative
     private static native void nDrawPath(long nativeCanvas, long nativePath, long nativePaint);
 
     @FastNative
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 36c1c21..e35a3be 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1877,6 +1877,51 @@
     }
 
     /**
+     * Draws a double rounded rectangle using the specified paint. The resultant round rect
+     * will be filled in the area defined between the outer and inner rectangular bounds if
+     * the {@link Paint} configured with {@link Paint.Style#FILL}.
+     * Otherwise if {@link Paint.Style#STROKE} is used, then 2 rounded rect strokes will
+     * be drawn at the outer and inner rounded rectangles
+     *
+     * @param outer The outer rectangular bounds of the roundRect to be drawn
+     * @param outerRx The x-radius of the oval used to round the corners on the outer rectangle
+     * @param outerRy The y-radius of the oval used to round the corners on the outer rectangle
+     * @param inner The inner rectangular bounds of the roundRect to be drawn
+     * @param innerRx The x-radius of the oval used to round the corners on the inner rectangle
+     * @param innerRy The y-radius of the oval used to round the corners on the outer rectangle
+     * @param paint The paint used to draw the double roundRect
+     */
+    @Override
+    public void drawDoubleRoundRect(@NonNull RectF outer, float outerRx, float outerRy,
+            @NonNull RectF inner, float innerRx, float innerRy, @NonNull Paint paint) {
+        super.drawDoubleRoundRect(outer, outerRx, outerRy, inner, innerRx, innerRy, paint);
+    }
+
+    /**
+     * Draws a double rounded rectangle using the specified paint. The resultant round rect
+     * will be filled in the area defined between the outer and inner rectangular bounds if
+     * the {@link Paint} configured with {@link Paint.Style#FILL}.
+     * Otherwise if {@link Paint.Style#STROKE} is used, then 2 rounded rect strokes will
+     * be drawn at the outer and inner rounded rectangles
+     *
+     * @param outer The outer rectangular bounds of the roundRect to be drawn
+     * @param outerRadii Array of 8 float representing the x, y corner radii for top left,
+     *                   top right, bottom right, bottom left corners respectively on the outer
+     *                   rounded rectangle
+     *
+     * @param inner The inner rectangular bounds of the roundRect to be drawn
+     * @param innerRadii Array of 8 float representing the x, y corner radii for top left,
+     *                   top right, bottom right, bottom left corners respectively on the
+     *                   outer rounded rectangle
+     * @param paint The paint used to draw the double roundRect
+     */
+    @Override
+    public void drawDoubleRoundRect(@NonNull RectF outer, float[] outerRadii,
+            @NonNull RectF inner, float[] innerRadii, @NonNull Paint paint) {
+        super.drawDoubleRoundRect(outer, outerRadii, inner, innerRadii, paint);
+    }
+
+    /**
      * Draw the text, with origin at (x,y), using the specified paint. The origin is interpreted
      * based on the Align setting in the paint.
      *
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 6ce66bd..009e042 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -59,6 +59,7 @@
 import java.io.InputStream;
 import java.lang.annotation.Retention;
 import java.nio.ByteBuffer;
+import java.util.concurrent.Callable;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -283,26 +284,7 @@
 
                 return createFromStream(is, true, this);
             }
-
-            final FileDescriptor fd = assetFd.getFileDescriptor();
-            final long offset = assetFd.getStartOffset();
-
-            ImageDecoder decoder = null;
-            try {
-                try {
-                    Os.lseek(fd, offset, SEEK_SET);
-                    decoder = nCreate(fd, this);
-                } catch (ErrnoException e) {
-                    decoder = createFromStream(new FileInputStream(fd), true, this);
-                }
-            } finally {
-                if (decoder == null) {
-                    IoUtils.closeQuietly(assetFd);
-                } else {
-                    decoder.mAssetFd = assetFd;
-                }
-            }
-            return decoder;
+            return createFromAssetFileDescriptor(assetFd, this);
         }
     }
 
@@ -354,6 +336,30 @@
         return decoder;
     }
 
+    @NonNull
+    private static ImageDecoder createFromAssetFileDescriptor(@NonNull AssetFileDescriptor assetFd,
+            Source source) throws IOException {
+        final FileDescriptor fd = assetFd.getFileDescriptor();
+        final long offset = assetFd.getStartOffset();
+
+        ImageDecoder decoder = null;
+        try {
+            try {
+                Os.lseek(fd, offset, SEEK_SET);
+                decoder = nCreate(fd, source);
+            } catch (ErrnoException e) {
+                decoder = createFromStream(new FileInputStream(fd), true, source);
+            }
+        } finally {
+            if (decoder == null) {
+                IoUtils.closeQuietly(assetFd);
+            } else {
+                decoder.mAssetFd = assetFd;
+            }
+        }
+        return decoder;
+    }
+
     /**
      * For backwards compatibility, this does *not* close the InputStream.
      *
@@ -528,6 +534,29 @@
         }
     }
 
+    private static class CallableSource extends Source {
+        CallableSource(@NonNull Callable<AssetFileDescriptor> callable) {
+            mCallable = callable;
+        }
+
+        private final Callable<AssetFileDescriptor> mCallable;
+
+        @Override
+        public ImageDecoder createImageDecoder() throws IOException {
+            AssetFileDescriptor assetFd = null;
+            try {
+                assetFd = mCallable.call();
+            } catch (Exception e) {
+                if (e instanceof IOException) {
+                    throw (IOException) e;
+                } else {
+                    throw new IOException(e);
+                }
+            }
+            return createFromAssetFileDescriptor(assetFd, this);
+        }
+    }
+
     /**
      *  Information about an encoded image.
      */
@@ -971,6 +1000,27 @@
     }
 
     /**
+     * Create a new {@link Source Source} from a {@link Callable} that returns a
+     * new {@link AssetFileDescriptor} for each request. This provides control
+     * over how the {@link AssetFileDescriptor} is created, such as passing
+     * options into {@link ContentResolver#openTypedAssetFileDescriptor}, or
+     * enabling use of a {@link android.os.CancellationSignal}.
+     * <p>
+     * It's important for the given {@link Callable} to return a new, unique
+     * {@link AssetFileDescriptor} for each invocation, to support reuse of the
+     * returned {@link Source Source}.
+     *
+     * @return a new Source object, which can be passed to
+     *         {@link #decodeDrawable decodeDrawable} or {@link #decodeBitmap
+     *         decodeBitmap}.
+     */
+    @AnyThread
+    @NonNull
+    public static Source createSource(@NonNull Callable<AssetFileDescriptor> callable) {
+        return new CallableSource(callable);
+    }
+
+    /**
      *  Return the width and height of a given sample size.
      *
      *  <p>This takes an input that functions like
diff --git a/graphics/java/android/graphics/Insets.java b/graphics/java/android/graphics/Insets.java
index c3449dd..de110c8 100644
--- a/graphics/java/android/graphics/Insets.java
+++ b/graphics/java/android/graphics/Insets.java
@@ -73,6 +73,15 @@
     }
 
     /**
+     * Returns a Rect intance with the appropriate values.
+     *
+     * @hide
+     */
+    public @NonNull Rect toRect() {
+        return new Rect(left, top, right, bottom);
+    }
+
+    /**
      * Two Insets instances are equal iff they belong to the same class and their fields are
      * pairwise equal.
      *
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index c30af84..f928de9 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -23,6 +23,7 @@
 #include "SkDrawShadowInfo.h"
 #include "SkImage.h"
 #include "SkImageFilter.h"
+#include "SkLatticeIter.h"
 #include "SkMath.h"
 #include "SkPicture.h"
 #include "SkRSXform.h"
@@ -280,7 +281,8 @@
 
 struct DrawImage final : Op {
     static const auto kType = Type::DrawImage;
-    DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette)
+    DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint,
+              BitmapPalette palette)
             : image(std::move(image)), x(x), y(y), palette(palette) {
         if (paint) {
             this->paint = *paint;
@@ -312,7 +314,8 @@
 struct DrawImageRect final : Op {
     static const auto kType = Type::DrawImageRect;
     DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst,
-                  const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, BitmapPalette palette)
+                  const SkPaint* paint, SkCanvas::SrcRectConstraint constraint,
+                  BitmapPalette palette)
             : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) {
         this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height());
         if (paint) {
@@ -331,8 +334,14 @@
 struct DrawImageLattice final : Op {
     static const auto kType = Type::DrawImageLattice;
     DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs, const SkIRect& src,
-                     const SkRect& dst, const SkPaint* paint)
-            : image(std::move(image)), xs(xs), ys(ys), fs(fs), src(src), dst(dst) {
+                     const SkRect& dst, const SkPaint* paint, BitmapPalette palette)
+            : image(std::move(image))
+            , xs(xs)
+            , ys(ys)
+            , fs(fs)
+            , src(src)
+            , dst(dst)
+            , palette(palette) {
         if (paint) {
             this->paint = *paint;
         }
@@ -342,6 +351,7 @@
     SkIRect src;
     SkRect dst;
     SkPaint paint;
+    BitmapPalette palette;
     void draw(SkCanvas* c, const SkMatrix&) const {
         auto xdivs = pod<int>(this, 0), ydivs = pod<int>(this, xs * sizeof(int));
         auto colors = (0 == fs) ? nullptr : pod<SkColor>(this, (xs + ys) * sizeof(int));
@@ -511,16 +521,13 @@
         tree->getPaintFor(&paint, tree->stagingProperties());
     }
 
-    void draw(SkCanvas* canvas, const SkMatrix&) const {
-        mRoot->draw(canvas, mBounds, paint);
-    }
+    void draw(SkCanvas* canvas, const SkMatrix&) const { mRoot->draw(canvas, mBounds, paint); }
 
     sp<VectorDrawableRoot> mRoot;
     SkRect mBounds;
     SkPaint paint;
     BitmapPalette palette;
 };
-
 }
 
 template <typename T, typename... Args>
@@ -647,14 +654,15 @@
     this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette);
 }
 void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice,
-                                       const SkRect& dst, const SkPaint* paint) {
+                                       const SkRect& dst, const SkPaint* paint,
+                                       BitmapPalette palette) {
     int xs = lattice.fXCount, ys = lattice.fYCount;
     int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
     size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) +
                    fs * sizeof(SkColor);
     SkASSERT(lattice.fBounds);
     void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
-                                             dst, paint);
+                                             dst, paint, palette);
     copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
            fs);
 }
@@ -779,22 +787,26 @@
 
 template <class T>
 constexpr color_transform_fn colorTransformForOp() {
-    if constexpr(has_paint<T> && has_palette<T>) {
-        // It's a bitmap
-        return [](const void* opRaw, ColorTransform transform) {
-            // TODO: We should be const. Or not. Or just use a different map
-            // Unclear, but this is the quick fix
-            const T* op = reinterpret_cast<const T*>(opRaw);
-            transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
-        };
-    } else if constexpr(has_paint<T>) {
-        return [](const void* opRaw, ColorTransform transform) {
-            // TODO: We should be const. Or not. Or just use a different map
-            // Unclear, but this is the quick fix
-            const T* op = reinterpret_cast<const T*>(opRaw);
-            transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
-        };
-    } else {
+    if
+        constexpr(has_paint<T> && has_palette<T>) {
+            // It's a bitmap
+            return [](const void* opRaw, ColorTransform transform) {
+                // TODO: We should be const. Or not. Or just use a different map
+                // Unclear, but this is the quick fix
+                const T* op = reinterpret_cast<const T*>(opRaw);
+                transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette);
+            };
+        }
+    else if
+        constexpr(has_paint<T>) {
+            return [](const void* opRaw, ColorTransform transform) {
+                // TODO: We should be const. Or not. Or just use a different map
+                // Unclear, but this is the quick fix
+                const T* op = reinterpret_cast<const T*>(opRaw);
+                transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+            };
+        }
+    else {
         return nullptr;
     }
 }
@@ -931,11 +943,12 @@
 }
 void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst,
                                        const SkPaint* paint, SrcRectConstraint constraint) {
-    fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, BitmapPalette::Unknown);
+    fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint,
+                       BitmapPalette::Unknown);
 }
 void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice,
                                           const SkRect& dst, const SkPaint* paint) {
-    fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint);
+    fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint, BitmapPalette::Unknown);
 }
 
 void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
@@ -943,11 +956,34 @@
     fDL->drawImage(image, x, y, paint, palette);
 }
 
-void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
-                   const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette) {
+void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src,
+                                    const SkRect& dst, const SkPaint* paint,
+                                    SrcRectConstraint constraint, BitmapPalette palette) {
     fDL->drawImageRect(image, &src, dst, paint, constraint, palette);
 }
 
+void RecordingCanvas::drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice,
+                                       const SkRect& dst, const SkPaint* paint,
+                                       BitmapPalette palette) {
+    if (!image || dst.isEmpty()) {
+        return;
+    }
+
+    SkIRect bounds;
+    Lattice latticePlusBounds = lattice;
+    if (!latticePlusBounds.fBounds) {
+        bounds = SkIRect::MakeWH(image->width(), image->height());
+        latticePlusBounds.fBounds = &bounds;
+    }
+
+    if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
+        fDL->drawImageLattice(image, latticePlusBounds, dst, paint, palette);
+    } else {
+        fDL->drawImageRect(image, nullptr, dst, paint, SrcRectConstraint::kFast_SrcRectConstraint,
+                           palette);
+    }
+}
+
 void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
                                   const SkPaint* paint) {
     fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
@@ -962,7 +998,7 @@
 }
 void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
                                          const SkRect& dst, const SkPaint* paint) {
-    fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint);
+    fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint, BitmapPalette::Unknown);
 }
 
 void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
@@ -975,8 +1011,8 @@
     fDL->drawPoints(mode, count, pts, paint);
 }
 void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices,
-                                          const SkVertices::Bone bones[], int boneCount,
-                                          SkBlendMode mode, const SkPaint& paint) {
+                                           const SkVertices::Bone bones[], int boneCount,
+                                           SkBlendMode mode, const SkPaint& paint) {
     fDL->drawVertices(vertices, bones, boneCount, mode, paint);
 }
 void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[],
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 80c80ca..099e0be 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -110,7 +110,7 @@
     void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
                        SkCanvas::SrcRectConstraint, BitmapPalette palette);
     void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
-                          const SkPaint*);
+                          const SkPaint*, BitmapPalette);
 
     void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
                    const SkPaint&);
@@ -185,11 +185,13 @@
     void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*,
                           SrcRectConstraint) override;
 
-    void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top,
-                   const SkPaint* paint, BitmapPalette pallete);
+    void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, const SkPaint* paint,
+                   BitmapPalette pallete);
 
     void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
                        const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette);
+    void drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, const SkRect& dst,
+                          const SkPaint* paint, BitmapPalette palette);
 
     void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
     void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index d9a7cc3..d2a8f02 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -282,25 +282,45 @@
     mStagingDisplayList = nullptr;
     if (mDisplayList) {
         mDisplayList->syncContents();
+        handleForceDark(info);
+    }
+}
 
-        if (CC_UNLIKELY(info && !info->disableForceDark)) {
-            auto usage = usageHint();
-            if (mDisplayList->hasText()) {
-                usage = UsageHint::Foreground;
-            }
-            if (usage == UsageHint::Unknown) {
-                if (mDisplayList->mChildNodes.size() > 1) {
-                    usage = UsageHint::Background;
-                } else if (mDisplayList->mChildNodes.size() == 1 &&
-                           mDisplayList->mChildNodes.front().getRenderNode()->usageHint() !=
-                                   UsageHint::Background) {
-                    usage = UsageHint::Background;
-                }
-            }
-            mDisplayList->mDisplayList.applyColorTransform(
-                    usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
+void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) {
+    if (CC_LIKELY(!info || info->disableForceDark)) {
+        return;
+    }
+    auto usage = usageHint();
+    const auto& children = mDisplayList->mChildNodes;
+    if (mDisplayList->hasText()) {
+        usage = UsageHint::Foreground;
+    }
+    if (usage == UsageHint::Unknown) {
+        if (children.size() > 1) {
+            usage = UsageHint::Background;
+        } else if (children.size() == 1 &&
+                children.front().getRenderNode()->usageHint() !=
+                        UsageHint::Background) {
+            usage = UsageHint::Background;
         }
     }
+    if (children.size() > 1) {
+        // Crude overlap check
+        SkRect drawn = SkRect::MakeEmpty();
+        for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
+            const auto& child = iter->getRenderNode();
+            // We use stagingProperties here because we haven't yet sync'd the children
+            SkRect bounds = SkRect::MakeXYWH(child->stagingProperties().getX(), child->stagingProperties().getY(),
+                    child->stagingProperties().getWidth(), child->stagingProperties().getHeight());
+            if (bounds.contains(drawn)) {
+                // This contains everything drawn after it, so make it a background
+                child->setUsageHint(UsageHint::Background);
+            }
+            drawn.join(bounds);
+        }
+    }
+    mDisplayList->mDisplayList.applyColorTransform(
+            usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light);
 }
 
 void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) {
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index 211dd2d..be0b46b 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -220,6 +220,7 @@
 
     void syncProperties();
     void syncDisplayList(TreeObserver& observer, TreeInfo* info);
+    void handleForceDark(TreeInfo* info);
 
     void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer);
     void pushStagingPropertiesChanges(TreeInfo& info);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 17f1a3b..2e5aef5 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -504,6 +504,11 @@
     mCanvas->drawRoundRect(rect, rx, ry, *filterPaint(paint));
 }
 
+void SkiaCanvas::drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+                                const SkPaint& paint) {
+    mCanvas->drawDRRect(outer, inner, *filterPaint(paint));
+}
+
 void SkiaCanvas::drawCircle(float x, float y, float radius, const SkPaint& paint) {
     if (CC_UNLIKELY(radius <= 0 || paint.nothingToDraw())) return;
     mCanvas->drawCircle(x, y, radius, *filterPaint(paint));
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 24b7ec6..3a877cf 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -107,6 +107,10 @@
     virtual void drawRegion(const SkRegion& region, const SkPaint& paint) override;
     virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
                                const SkPaint& paint) override;
+
+   virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+                               const SkPaint& paint) override;
+
     virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) override;
     virtual void drawOval(float left, float top, float right, float bottom,
                           const SkPaint& paint) override;
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index dbbe9f3..6cf04bf 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -470,10 +470,10 @@
 void Tree::getPaintFor(SkPaint* outPaint, const TreeProperties &prop) const {
     // HWUI always draws VD with bilinear filtering.
     outPaint->setFilterQuality(kLow_SkFilterQuality);
-    if (prop.getRootAlpha() < 1.0f || prop.getColorFilter() != nullptr) {
+    if (prop.getColorFilter() != nullptr) {
         outPaint->setColorFilter(sk_ref_sp(prop.getColorFilter()));
-        outPaint->setAlpha(prop.getRootAlpha() * 255);
     }
+    outPaint->setAlpha(prop.getRootAlpha() * 255);
 }
 
 Bitmap& Tree::getBitmapUpdateIfDirty() {
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index af7f013..e2ea2bc 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -178,6 +178,41 @@
     MinikinUtils::forFontRun(layout, &paint, f);
 }
 
+void Canvas::drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+                            float outerBottom, float outerRx, float outerRy, float innerLeft,
+                            float innerTop, float innerRight, float innerBottom, float innerRx,
+                            float innerRy, const SkPaint& paint) {
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
+    SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+    SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+    SkRRect outerRRect;
+    outerRRect.setRectXY(outer, outerRx, outerRy);
+
+    SkRRect innerRRect;
+    innerRRect.setRectXY(inner, innerRx, innerRy);
+    drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
+void Canvas::drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+                            float outerBottom, const float* outerRadii, float innerLeft,
+                            float innerTop, float innerRight, float innerBottom,
+                            const float* innerRadii, const SkPaint& paint) {
+    static_assert(sizeof(SkVector) == sizeof(float) * 2);
+    if (CC_UNLIKELY(paint.nothingToDraw())) return;
+    SkRect outer = SkRect::MakeLTRB(outerLeft, outerTop, outerRight, outerBottom);
+    SkRect inner = SkRect::MakeLTRB(innerLeft, innerTop, innerRight, innerBottom);
+
+    SkRRect outerRRect;
+    const SkVector* outerSkVector = reinterpret_cast<const SkVector*>(outerRadii);
+    outerRRect.setRectRadii(outer, outerSkVector);
+
+    SkRRect innerRRect;
+    const SkVector* innerSkVector = reinterpret_cast<const SkVector*>(innerRadii);
+    innerRRect.setRectRadii(inner, innerSkVector);
+    drawDoubleRoundRect(outerRRect, innerRRect, paint);
+}
+
 class DrawTextOnPathFunctor {
 public:
     DrawTextOnPathFunctor(const minikin::Layout& layout, Canvas* canvas, float hOffset,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index b9af7de2..e99742b 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -236,6 +236,8 @@
     virtual void drawRegion(const SkRegion& region, const SkPaint& paint) = 0;
     virtual void drawRoundRect(float left, float top, float right, float bottom, float rx, float ry,
                                const SkPaint& paint) = 0;
+    virtual void drawDoubleRoundRect(const SkRRect& outer, const SkRRect& inner,
+                                const SkPaint& paint) = 0;
     virtual void drawCircle(float x, float y, float radius, const SkPaint& paint) = 0;
     virtual void drawOval(float left, float top, float right, float bottom,
                           const SkPaint& paint) = 0;
@@ -284,6 +286,16 @@
                         const SkPath& path, float hOffset, float vOffset, const Paint& paint,
                         const Typeface* typeface);
 
+    void drawDoubleRoundRectXY(float outerLeft, float outerTop, float outerRight,
+                                float outerBottom, float outerRx, float outerRy, float innerLeft,
+                                float innerTop, float innerRight, float innerBottom, float innerRx,
+                                float innerRy, const SkPaint& paint);
+
+    void drawDoubleRoundRectRadii(float outerLeft, float outerTop, float outerRight,
+                                float outerBottom, const float* outerRadii, float innerLeft,
+                                float innerTop, float innerRight, float innerBottom,
+                                const float* innerRadii, const SkPaint& paint);
+
     static int GetApiLevel() { return sApiLevel; }
 
 protected:
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index fac07d7..3fa73a4 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -245,8 +245,9 @@
     }
     sk_sp<SkColorFilter> colorFilter;
     sk_sp<SkImage> image = bitmap.makeImage(&colorFilter);
-    mRecorder.drawImageLattice(image.get(), lattice, dst,
-                               filterBitmap(std::move(filteredPaint), std::move(colorFilter)));
+    mRecorder.drawImageLattice(image, lattice, dst,
+                               filterBitmap(std::move(filteredPaint), std::move(colorFilter)),
+                               bitmap.palette());
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
         mDisplayList->mMutableImages.push_back(image.get());
     }
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index a2d8119..2ca110f 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -126,6 +126,12 @@
         mVkSurface = mVkManager.createSurface(surface, colorMode);
     }
 
+    if (colorMode == ColorMode::SRGB) {
+        mSurfaceColorType = SkColorType::kN32_SkColorType;
+    } else if (colorMode == ColorMode::WideColorGamut) {
+        mSurfaceColorType = SkColorType::kRGBA_F16_SkColorType;
+    }
+
     return mVkSurface != nullptr;
 }
 
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 83e9db3..b0d4505 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -78,7 +78,7 @@
         0,                                  // applicationVersion
         "android framework",                // pEngineName
         0,                                  // engineVerison
-        VK_MAKE_VERSION(1, 0, 0),           // apiVersion
+        VK_MAKE_VERSION(1, 1, 0),           // apiVersion
     };
 
     std::vector<const char*> instanceExtensions;
@@ -133,6 +133,7 @@
 
     GET_INST_PROC(DestroyInstance);
     GET_INST_PROC(EnumeratePhysicalDevices);
+    GET_INST_PROC(GetPhysicalDeviceProperties);
     GET_INST_PROC(GetPhysicalDeviceQueueFamilyProperties);
     GET_INST_PROC(GetPhysicalDeviceFeatures2);
     GET_INST_PROC(CreateDevice);
@@ -164,6 +165,13 @@
         return false;
     }
 
+    VkPhysicalDeviceProperties physDeviceProperties;
+    mGetPhysicalDeviceProperties(mPhysicalDevice, &physDeviceProperties);
+    if (physDeviceProperties.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
+        this->destroy();
+        return false;
+    }
+
     // query to get the initial queue props size
     uint32_t queueCount;
     mGetPhysicalDeviceQueueFamilyProperties(mPhysicalDevice, &queueCount, nullptr);
diff --git a/libs/hwui/renderthread/VulkanManager.h b/libs/hwui/renderthread/VulkanManager.h
index 7c59b6d..6702649 100644
--- a/libs/hwui/renderthread/VulkanManager.h
+++ b/libs/hwui/renderthread/VulkanManager.h
@@ -176,6 +176,7 @@
 
     VkPtr<PFN_vkDestroyInstance> mDestroyInstance;
     VkPtr<PFN_vkEnumeratePhysicalDevices> mEnumeratePhysicalDevices;
+    VkPtr<PFN_vkGetPhysicalDeviceProperties> mGetPhysicalDeviceProperties;
     VkPtr<PFN_vkGetPhysicalDeviceQueueFamilyProperties> mGetPhysicalDeviceQueueFamilyProperties;
     VkPtr<PFN_vkGetPhysicalDeviceFeatures2> mGetPhysicalDeviceFeatures2;
     VkPtr<PFN_vkCreateDevice> mCreateDevice;
diff --git a/libs/services/include/android/os/StatsLogEventWrapper.h b/libs/services/include/android/os/StatsLogEventWrapper.h
index 52cb75e..f60c338 100644
--- a/libs/services/include/android/os/StatsLogEventWrapper.h
+++ b/libs/services/include/android/os/StatsLogEventWrapper.h
@@ -58,6 +58,11 @@
     type = FLOAT;
   }
 
+  StatsLogValue(double v) {
+    double_value = v;
+    type = DOUBLE;
+  }
+
   StatsLogValue(const std::string& v) {
     str_value = v;
     type = STRING;
diff --git a/libs/services/src/os/StatsLogEventWrapper.cpp b/libs/services/src/os/StatsLogEventWrapper.cpp
index 04c4629..a1a6d9f 100644
--- a/libs/services/src/os/StatsLogEventWrapper.cpp
+++ b/libs/services/src/os/StatsLogEventWrapper.cpp
@@ -85,6 +85,9 @@
       case StatsLogValue::FLOAT:
         mElements.push_back(StatsLogValue(in->readFloat()));
         break;
+      case StatsLogValue::DOUBLE:
+        mElements.push_back(StatsLogValue(in->readDouble()));
+        break;
       case StatsLogValue::STORAGE:
         mElements.push_back(StatsLogValue());
         mElements.back().setType(StatsLogValue::STORAGE);
diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java
index a6099be..74eb445 100644
--- a/location/java/android/location/Criteria.java
+++ b/location/java/android/location/Criteria.java
@@ -21,9 +21,9 @@
 
 /**
  * A class indicating the application criteria for selecting a
- * location provider.  Providers maybe ordered according to accuracy,
- * power usage, ability to report altitude, speed,
- * and bearing, and monetary cost.
+ * location provider. Providers may be ordered according to accuracy,
+ * power usage, ability to report altitude, speed, bearing, and monetary
+ * cost.
  */
 public class Criteria implements Parcelable {
     /**
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 85eac4b..c074cce 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -4607,7 +4607,7 @@
 
     /**
      * The message sent to apps when the contents of the device list changes if they provide
-     * a {#link Handler} object to addOnAudioDeviceConnectionListener().
+     * a {@link Handler} object to addOnAudioDeviceConnectionListener().
      */
     private final static int MSG_DEVICES_CALLBACK_REGISTERED = 0;
     private final static int MSG_DEVICES_DEVICES_ADDED = 1;
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index 452ba0f..2a575b6 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -473,7 +473,7 @@
      *                 .setSampleRate(32000)
      *                 .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
      *                 .build())
-     *         .setBufferSize(2*minBuffSize)
+     *         .setBufferSizeInBytes(2*minBuffSize)
      *         .build();
      * </pre>
      * <p>
diff --git a/media/java/android/media/Image.java b/media/java/android/media/Image.java
index dff5e9a..26b9b8c 100644
--- a/media/java/android/media/Image.java
+++ b/media/java/android/media/Image.java
@@ -75,7 +75,7 @@
     /**
      * Get the format for this image. This format determines the number of
      * ByteBuffers needed to represent the image, and the general layout of the
-     * pixel data in each in ByteBuffer.
+     * pixel data in each ByteBuffer.
      *
      * <p>
      * The format is one of the values from
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 340f279..18d36eb 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -227,7 +227,7 @@
  *         transfers the object to the <em>Prepared</em> state once the method call
  *         returns, or a call to {@link #prepareAsync()} (asynchronous) which
  *         first transfers the object to the <em>Preparing</em> state after the
- *         call returns (which occurs almost right way) while the internal
+ *         call returns (which occurs almost right away) while the internal
  *         player engine continues working on the rest of preparation work
  *         until the preparation work completes. When the preparation completes or when {@link #prepare()} call returns,
  *         the internal player engine then calls a user supplied callback method,
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 7492aa6..8665f28 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -30,6 +30,8 @@
 import android.view.Surface;
 import android.view.SurfaceHolder;
 
+import dalvik.system.CloseGuard;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
@@ -45,425 +47,202 @@
 
 /**
  * @hide
- * MediaPlayer2 class can be used to control playback
- * of audio/video files and streams. An example on how to use the methods in
- * this class can be found in {@link android.widget.VideoView}.
+ *
+ * MediaPlayer2 class can be used to control playback of audio/video files and streams.
  *
  * <p>Topics covered here are:
  * <ol>
- * <li><a href="#StateDiagram">State Diagram</a>
- * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a>
+ * <li><a href="#PlayerStates">Player states</a>
+ * <li><a href="#InvalidStates">Invalid method calls</a>
  * <li><a href="#Permissions">Permissions</a>
- * <li><a href="#Callbacks">Register informational and error callbacks</a>
+ * <li><a href="#Callbacks">Callbacks</a>
  * </ol>
  *
- * <div class="special reference">
- * <h3>Developer Guides</h3>
- * <p>For more information about how to use MediaPlayer2, read the
- * <a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a> developer guide.</p>
- * </div>
  *
- * <a name="StateDiagram"></a>
- * <h3>State Diagram</h3>
+ * <h3 id="PlayerStates">Player states</h3>
  *
- * <p>Playback control of audio/video files and streams is managed as a state
- * machine. The following diagram shows the life cycle and the states of a
- * MediaPlayer2 object driven by the supported playback control operations.
- * The ovals represent the states a MediaPlayer2 object may reside
- * in. The arcs represent the playback control operations that drive the object
- * state transition. There are two types of arcs. The arcs with a single arrow
- * head represent synchronous method calls, while those with
- * a double arrow head represent asynchronous method calls.</p>
+ * <p>The playback control of audio/video files is managed as a state machine.</p>
+ * <p><div style="text-align:center;"><img src="../../../images/mediaplayer2_state_diagram.png"
+ *         alt="MediaPlayer2 State diagram"
+ *         border="0" /></div></p>
+ * <p>The MediaPlayer2 object has five states:</p>
+ * <ol>
+ *     <li><p>{@link #PLAYER_STATE_IDLE}: MediaPlayer2 is in the <strong>Idle</strong>
+ *         state after you create it using
+ *         {@link #create()}, or after calling {@link #reset()}.</p>
  *
- * <p><img src="../../../images/mediaplayer_state_diagram.gif"
- *         alt="MediaPlayer State diagram"
- *         border="0" /></p>
+ *         <p>While in this state, you should call
+ *         {@link #setDataSource(DataSourceDesc2) setDataSource()}. It is a good
+ *         programming practice to register an {@link EventCallback#onCallCompleted onCallCompleted}
+ *         <a href="#Callbacks">callback</a> and watch for {@link #CALL_STATUS_BAD_VALUE} and
+ *         {@link #CALL_STATUS_ERROR_IO}, which might be caused by <code>setDataSource</code>.
+ *         </p>
  *
- * <p>From this state diagram, one can see that a MediaPlayer2 object has the
- *    following states:</p>
+ *         <p>Calling {@link #prepare()} transfers a MediaPlayer2 object to
+ *         the <strong>Prepared</strong> state. Note
+ *         that {@link #prepare()} is asynchronous. When the preparation completes,
+ *         if you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
+ *         the player executes the callback
+ *         with {@link #MEDIA_INFO_PREPARED} and transitions to the
+ *         <strong>Prepared</strong> state.</p>
+ *         </li>
+ *
+ *     <li>{@link #PLAYER_STATE_PREPARED}: A MediaPlayer object must be in the
+ *         <strong>Prepared</strong> state before playback can be started for the first time.
+ *         While in this state, you can set player properties
+ *         such as audio/sound volume and looping by invoking the corresponding set methods.
+ *         Calling {@link #play()} transfers a MediaPlayer2 object to
+ *         the <strong>Playing</strong> state.
+ *      </li>
+ *
+ *     <li>{@link #PLAYER_STATE_PLAYING}:
+ *         <p>The player plays the data source while in this state.
+ *         If you register an {@link EventCallback#onInfo onInfo} <a href="#Callbacks">callback</a>,
+ *         the player regularly executes the callback with
+ *         {@link #MEDIA_INFO_BUFFERING_UPDATE}.
+ *         This allows applications to keep track of the buffering status
+ *         while streaming audio/video.</p>
+ *
+ *         <p> When the playback reaches the end of stream, the behavior depends on whether or
+ *         not you've enabled looping by calling {@link #loopCurrent(boolean) loopCurrent}:</p>
+ *         <ul>
+ *         <li>If the looping mode was set to <code>false</code>, the player will transfer
+ *         to the <strong>Paused</strong> state. If you registered an {@link EventCallback#onInfo
+ *         onInfo} <a href="#Callbacks">callback</a>
+ *         the player calls the callback with {@link #MEDIA_INFO_DATA_SOURCE_END} and enters
+ *         the <strong>Paused</strong> state.
+ *         </li>
+ *         <li>If the looping mode was set to <code>true</code>,
+ *         the MediaPlayer2 object remains in the <strong>Playing</strong> state and replays its
+ *         data source from the beginning.</li>
+ *         </ul>
+ *         </li>
+ *
+ *     <li>{@link #PLAYER_STATE_PAUSED}: Audio/video playback pauses while in this state.
+ *         Call {@link #play()} to resume playback from the position where it paused.</li>
+ *
+ *     <li>{@link #PLAYER_STATE_ERROR}: <p>In general, playback might fail due to various
+ *          reasons such as unsupported audio/video format, poorly interleaved
+ *          audio/video, resolution too high, streaming timeout, and others.
+ *          In addition, due to programming errors, a playback
+ *          control operation might be performed from an <a href="#InvalidStates">invalid state</a>.
+ *          In these cases the player transitions to the <strong>Error</strong> state.</p>
+ *
+ *          <p>If you register an {@link EventCallback#onError onError}}
+ *          <a href="#Callbacks">callback</a>,
+ *          the callback will be performed when entering the state. When programming errors happen,
+ *          such as calling {@link #prepare() prepare} and
+ *          {@link #setDataSource(DataSourceDesc) setDataSource} methods
+ *          from an <a href="#InvalidStates">invalid state</a>, the callback is called with
+ *          {@link #CALL_STATUS_INVALID_OPERATION}. The MediaPlayer2 object enters the
+ *          <strong>Error</strong> state whether or not a callback exists. </p>
+ *
+ *          <p>To recover from an error and reuse a MediaPlayer2 object that is in the <strong>
+ *          Error</strong> state,
+ *          call {@link #reset() reset}. The object will return to the <strong>Idle</strong>
+ *          state and all state information will be lost.</p>
+ *          </li>
+ * </ol>
+ *
+ * <p>You should follow these best practices when coding an app that uses MediaPlayer2:</p>
+ *
  * <ul>
- *     <li>When a MediaPlayer2 object is just created using <code>create</code> or
- *         after {@link #reset()} is called, it is in the <em>Idle</em> state; and after
- *         {@link #close()} is called, it is in the <em>End</em> state. Between these
- *         two states is the life cycle of the MediaPlayer2 object.
- *         <ul>
- *         <li> It is a programming error to invoke methods such
- *         as {@link #getCurrentPosition()},
- *         {@link #getDuration()}, {@link #getVideoHeight()},
- *         {@link #getVideoWidth()}, {@link #setAudioAttributes(AudioAttributes)},
- *         {@link #setPlayerVolume(float)}, {@link #pause()}, {@link #play()},
- *         {@link #seekTo(long, int)} or
- *         {@link #prepare()} in the <em>Idle</em> state.
- *         <li>It is also recommended that once
- *         a MediaPlayer2 object is no longer being used, call {@link #close()} immediately
- *         so that resources used by the internal player engine associated with the
- *         MediaPlayer2 object can be released immediately. Resource may include
- *         singleton resources such as hardware acceleration components and
- *         failure to call {@link #close()} may cause subsequent instances of
- *         MediaPlayer2 objects to fallback to software implementations or fail
- *         altogether. Once the MediaPlayer2
- *         object is in the <em>End</em> state, it can no longer be used and
- *         there is no way to bring it back to any other state. </li>
- *         <li>Furthermore,
- *         the MediaPlayer2 objects created using <code>new</code> is in the
- *         <em>Idle</em> state.
- *         </li>
- *         </ul>
- *         </li>
- *     <li>In general, some playback control operation may fail due to various
- *         reasons, such as unsupported audio/video format, poorly interleaved
- *         audio/video, resolution too high, streaming timeout, and the like.
- *         Thus, error reporting and recovery is an important concern under
- *         these circumstances. Sometimes, due to programming errors, invoking a playback
- *         control operation in an invalid state may also occur. Under all these
- *         error conditions, the internal player engine invokes a user supplied
- *         EventCallback.onError() method if an EventCallback has been
- *         registered beforehand via
- *         {@link #setEventCallback(Executor, EventCallback)}.
- *         <ul>
- *         <li>It is important to note that once an error occurs, the
- *         MediaPlayer2 object enters the <em>Error</em> state (except as noted
- *         above), even if an error listener has not been registered by the application.</li>
- *         <li>In order to reuse a MediaPlayer2 object that is in the <em>
- *         Error</em> state and recover from the error,
- *         {@link #reset()} can be called to restore the object to its <em>Idle</em>
- *         state.</li>
- *         <li>It is good programming practice to have your application
- *         register a OnErrorListener to look out for error notifications from
- *         the internal player engine.</li>
- *         <li>IllegalStateException is
- *         thrown to prevent programming errors such as calling
- *         {@link #prepare()}, {@link #setDataSource(DataSourceDesc)}
- *         methods in an invalid state. </li>
- *         </ul>
- *         </li>
- *     <li>Calling
- *         {@link #setDataSource(DataSourceDesc)} transfers a
- *         MediaPlayer2 object in the <em>Idle</em> state to the
- *         <em>Initialized</em> state.
- *         <ul>
- *         <li>An IllegalStateException is thrown if
- *         setDataSource() is called in any other state.</li>
- *         <li>It is good programming
- *         practice to always look out for <code>IllegalArgumentException</code>
- *         and <code>IOException</code> that may be thrown from
- *         <code>setDataSource</code>.</li>
- *         </ul>
- *         </li>
- *     <li>A MediaPlayer2 object must first enter the <em>Prepared</em> state
- *         before playback can be started.
- *         <ul>
- *         <li>There are an asynchronous way that the <em>Prepared</em> state can be reached:
- *         a call to {@link #prepare()} (asynchronous) which
- *         first transfers the object to the <em>Preparing</em> state after the
- *         call returns (which occurs almost right way) while the internal
- *         player engine continues working on the rest of preparation work
- *         until the preparation work completes. When the preparation completes,
- *         the internal player engine then calls a user supplied callback method,
- *         onInfo() of the EventCallback interface with {@link #MEDIA_INFO_PREPARED},
- *         if an EventCallback is registered beforehand via
- *         {@link #setEventCallback(Executor, EventCallback)}.</li>
- *         <li>It is important to note that
- *         the <em>Preparing</em> state is a transient state, and the behavior
- *         of calling any method with side effect while a MediaPlayer2 object is
- *         in the <em>Preparing</em> state is undefined.</li>
- *         <li>An IllegalStateException is
- *         thrown if {@link #prepare()} is called in
- *         any other state.</li>
- *         <li>While in the <em>Prepared</em> state, properties
- *         such as audio/sound volume, screenOnWhilePlaying, looping can be
- *         adjusted by invoking the corresponding set methods.</li>
- *         </ul>
- *         </li>
- *     <li>To start the playback, {@link #play()} must be called. After
- *         {@link #play()} returns successfully, the MediaPlayer2 object is in the
- *         <em>Started</em> state. {@link #getPlayerState()} can be called to test
- *         whether the MediaPlayer2 object is in the <em>Started</em> state.
- *         <ul>
- *         <li>While in the <em>Started</em> state, the internal player engine calls
- *         a user supplied callback method EventCallback.onInfo() with
- *         {@link #MEDIA_INFO_BUFFERING_UPDATE} if an EventCallback has been
- *         registered beforehand via
- *         {@link #setEventCallback(Executor, EventCallback)}.
- *         This callback allows applications to keep track of the buffering status
- *         while streaming audio/video.</li>
- *         <li>Calling {@link #play()} has not effect
- *         on a MediaPlayer2 object that is already in the <em>Started</em> state.</li>
- *         </ul>
- *         </li>
- *     <li>Playback can be paused and stopped, and the current playback position
- *         can be adjusted. Playback can be paused via {@link #pause()}. When the call to
- *         {@link #pause()} returns, the MediaPlayer2 object enters the
- *         <em>Paused</em> state. Note that the transition from the <em>Started</em>
- *         state to the <em>Paused</em> state and vice versa happens
- *         asynchronously in the player engine. It may take some time before
- *         the state is updated in calls to {@link #getPlayerState()}, and it can be
- *         a number of seconds in the case of streamed content.
- *         <ul>
- *         <li>Calling {@link #play()} to resume playback for a paused
- *         MediaPlayer2 object, and the resumed playback
- *         position is the same as where it was paused. When the call to
- *         {@link #play()} returns, the paused MediaPlayer2 object goes back to
- *         the <em>Started</em> state.</li>
- *         <li>Calling {@link #pause()} has no effect on
- *         a MediaPlayer2 object that is already in the <em>Paused</em> state.</li>
- *         </ul>
- *         </li>
- *     <li>The playback position can be adjusted with a call to
- *         {@link #seekTo(long, int)}.
- *         <ul>
- *         <li>Although the asynchronuous {@link #seekTo(long, int)}
- *         call returns right away, the actual seek operation may take a while to
- *         finish, especially for audio/video being streamed. When the actual
- *         seek operation completes, the internal player engine calls a user
- *         supplied EventCallback.onCallCompleted() with
- *         {@link #CALL_COMPLETED_SEEK_TO}
- *         if an EventCallback has been registered beforehand via
- *         {@link #setEventCallback(Executor, EventCallback)}.</li>
- *         <li>Please
- *         note that {@link #seekTo(long, int)} can also be called in the other states,
- *         such as <em>Prepared</em>, <em>Paused</em> and <em>PlaybackCompleted
- *         </em> state. When {@link #seekTo(long, int)} is called in those states,
- *         one video frame will be displayed if the stream has video and the requested
- *         position is valid.
- *         </li>
- *         <li>Furthermore, the actual current playback position
- *         can be retrieved with a call to {@link #getCurrentPosition()}, which
- *         is helpful for applications such as a Music player that need to keep
- *         track of the playback progress.</li>
- *         </ul>
- *         </li>
- *     <li>When the playback reaches the end of stream, the playback completes.
- *         <ul>
- *         <li>If current source is set to loop by {@link #loopCurrent(boolean)},
- *         the MediaPlayer2 object shall remain in the <em>Started</em> state.</li>
- *         <li>If the looping mode was set to <var>false
- *         </var>, the player engine calls a user supplied callback method,
- *         EventCallback.onCompletion(), if an EventCallback is
- *         registered beforehand via
- *         {@link #setEventCallback(Executor, EventCallback)}.
- *         The invoke of the callback signals that the object is now in the <em>
- *         PlaybackCompleted</em> state.</li>
- *         <li>While in the <em>PlaybackCompleted</em>
- *         state, calling {@link #play()} can restart the playback from the
- *         beginning of the audio/video source.</li>
+ *
+ * <li>Use <a href="#Callbacks">callbacks</a> to respond to state changes and errors.</li>
+ *
+ * <li>When  a MediaPlayer2 object is no longer being used, call {@link #close() close} as soon as
+ * possible to release the resources used by the internal player engine associated with the
+ * MediaPlayer2. Failure to call {@link #close() close} may cause subsequent instances of
+ * MediaPlayer2 objects to fallback to software implementations or fail altogether.
+ * You cannot use MediaPlayer2
+ * after you call {@link #close() close}. There is no way to bring it back to any other state.</li>
+ *
+ * <li>The current playback position can be retrieved with a call to
+ * {@link #getCurrentPosition() getCurrentPosition},
+ * which is helpful for applications such as a Music player that need to keep track of the playback
+ * progress.</li>
+ *
+ * <li>The playback position can be adjusted with a call to {@link #seekTo seekTo}. Although the
+ * asynchronous {@link #seekTo seekTo} call returns right away, the actual seek operation may take a
+ * while to finish, especially for audio/video being streamed. If you register an
+ * {@link EventCallback#onCallCompleted onCallCompleted} <a href="#Callbacks">callback</a>,
+ * the callback is
+ * called When the seek operation completes with {@link #CALL_COMPLETED_SEEK_TO}.</li>
+ *
+ * <li>You can call {@link #seekTo seekTo} from the <strong>Paused</strong> state.
+ * In this case, if you are playing a video stream and
+ * the requested position is valid  one video frame is displayed.</li>
+ *
  * </ul>
  *
+ * <h3 id="InvalidStates">Invalid method calls</h3>
  *
- * <a name="Valid_and_Invalid_States"></a>
- * <h3>Valid and invalid states</h3>
+ * <p>The only methods you safely call from the <strong>Error</strong> state are
+ * {@link #close() close},
+ * {@link #reset() reset},
+ * {@link #notifyWhenCommandLabelReached notifyWhenCommandLabelReached},
+ * {@link #clearPendingCommands() clearPendingCommands},
+ * {@link #setEventCallback setEventCallback},
+ * {@link #clearEventCallback() clearEventCallback}
+ * and {@link #getState() getState}.
+ * Any other methods might throw an exception, return meaningless data, or invoke a
+ * {@link EventCallback#onCallCompleted onCallCompleted} with an error code.</p>
+ *
+ * <p>Most methods can be called from any non-Error state. They will either perform their work or
+ * silently have no effect. The following table lists the methods that will invoke a
+ * {@link EventCallback#onCallCompleted onCallCompleted} with an error code
+ * or throw an exception when they are called from the associated invalid states.</p>
  *
  * <table border="0" cellspacing="0" cellpadding="0">
- * <tr><td>Method Name </p></td>
- *     <td>Valid Sates </p></td>
- *     <td>Invalid States </p></td>
- *     <td>Comments </p></td></tr>
- * <tr><td>attachAuxEffect </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- *     <td>{Idle, Error} </p></td>
- *     <td>This method must be called after setDataSource.
- *     Calling it does not change the object state. </p></td></tr>
- * <tr><td>getAudioSessionId </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>getCurrentPosition </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted} </p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change the
- *         state. Calling this method in an invalid state transfers the object
- *         to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getDuration </p></td>
- *     <td>{Prepared, Started, Paused, Stopped, PlaybackCompleted} </p></td>
- *     <td>{Idle, Initialized, Error} </p></td>
- *     <td>Successful invoke of this method in a valid state does not change the
- *         state. Calling this method in an invalid state transfers the object
- *         to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getVideoHeight </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change the
- *         state. Calling this method in an invalid state transfers the object
- *         to the <em>Error</em> state.  </p></td></tr>
- * <tr><td>getVideoWidth </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an invalid state transfers the
- *         object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>getPlayerState </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an invalid state transfers the
- *         object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>pause </p></td>
- *     <td>{Started, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Paused</em> state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>prepare </p></td>
- *     <td>{Initialized, Stopped} </p></td>
- *     <td>{Idle, Prepared, Started, Paused, PlaybackCompleted, Error} </p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Preparing</em> state. Calling this method in an
- *         invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>release </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>After {@link #close()}, the object is no longer available. </p></td></tr>
- * <tr><td>reset </p></td>
- *     <td>{Idle, Initialized, Prepared, Started, Paused, Stopped,
- *         PlaybackCompleted, Error}</p></td>
- *     <td>{}</p></td>
- *     <td>After {@link #reset()}, the object is like being just created.</p></td></tr>
- * <tr><td>seekTo </p></td>
- *     <td>{Prepared, Started, Paused, PlaybackCompleted} </p></td>
- *     <td>{Idle, Initialized, Stopped, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an invalid state transfers the
- *         object to the <em>Error</em> state. </p></td></tr>
- * <tr><td>setAudioAttributes </p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method does not change the state. In order for the
- *         target audio attributes type to become effective, this method must be called before
- *         prepare().</p></td></tr>
- * <tr><td>setAudioSessionId </p></td>
- *     <td>{Idle} </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- *          Error} </p></td>
- *     <td>This method must be called in idle state as the audio session ID must be known before
- *         calling setDataSource. Calling it does not change the object
- *         state. </p></td></tr>
- * <tr><td>setAudioStreamType (deprecated)</p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method does not change the state. In order for the
- *         target audio stream type to become effective, this method must be called before
- *         prepare().</p></td></tr>
- * <tr><td>setAuxEffectSendLevel </p></td>
- *     <td>any</p></td>
- *     <td>{} </p></td>
- *     <td>Calling this method does not change the object state. </p></td></tr>
- * <tr><td>setDataSource </p></td>
- *     <td>{Idle} </p></td>
- *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted,
- *          Error} </p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Initialized</em> state. Calling this method in an
- *         invalid state throws an IllegalStateException.</p></td></tr>
- * <tr><td>setDisplay </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>setSurface </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>loopCurrent </p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *         PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method in a valid state does not change
- *         the state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>isLooping </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>setDrmEventCallback </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>setEventCallback </p></td>
- *     <td>any </p></td>
- *     <td>{} </p></td>
- *     <td>This method can be called in any state and calling it does not change
- *         the object state. </p></td></tr>
- * <tr><td>setPlaybackParams</p></td>
- *     <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
- *     <td>{Idle, Stopped} </p></td>
- *     <td>This method will change state in some cases, depending on when it's called.
- *         </p></td></tr>
- * <tr><td>setPlayerVolume </p></td>
- *     <td>{Idle, Initialized, Stopped, Prepared, Started, Paused,
- *          PlaybackCompleted}</p></td>
- *     <td>{Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.
- * <tr><td>play </p></td>
- *     <td>{Prepared, Started, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Stopped, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Started</em> state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>stop </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method in a valid state transfers the
- *         object to the <em>Stopped</em> state. Calling this method in an
- *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
- * <tr><td>getTrackInfo </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>selectTrack </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
- * <tr><td>deselectTrack </p></td>
- *     <td>{Prepared, Started, Stopped, Paused, PlaybackCompleted}</p></td>
- *     <td>{Idle, Initialized, Error}</p></td>
- *     <td>Successful invoke of this method does not change the state.</p></td></tr>
+ * <tr><th>Method Name</th>
+ * <th>Invalid States</th></tr>
  *
+ * <tr><td>setDataSource</td> <td>{Prepared, Paused, Playing}</td></tr>
+ * <tr><td>prepare</td> <td>{Prepared, Paused, Playing}</td></tr>
+ * <tr><td>play</td> <td>{Idle}</td></tr>
+ * <tr><td>pause</td> <td>{Idle}</td></tr>
+ * <tr><td>seekTo</td> <td>{Idle}</td></tr>
+ * <tr><td>getCurrentPosition</td> <td>{Idle}</td></tr>
+ * <tr><td>getDuration</td> <td>{Idle}</td></tr>
+ * <tr><td>getBufferedPosition</td> <td>{Idle}</td></tr>
+ * <tr><td>getTrackInfo</td> <td>{Idle}</td></tr>
+ * <tr><td>getSelectedTrack</td> <td>{Idle}</td></tr>
+ * <tr><td>selectTrack</td> <td>{Idle}</td></tr>
+ * <tr><td>deselectTrack</td> <td>{Idle}</td></tr>
  * </table>
  *
- * <a name="Permissions"></a>
- * <h3>Permissions</h3>
- * <p>One may need to declare a corresponding WAKE_LOCK permission {@link
- * android.R.styleable#AndroidManifestUsesPermission &lt;uses-permission&gt;}
- * element.
- *
+ * <h3 id="Permissions">Permissions</h3>
  * <p>This class requires the {@link android.Manifest.permission#INTERNET} permission
  * when used with network-based content.
  *
- * <a name="Callbacks"></a>
- * <h3>Callbacks</h3>
- * <p>Applications may want to register for informational and error
- * events in order to be informed of some internal state update and
- * possible runtime errors during playback or streaming. Registration for
- * these events is done by properly setting the appropriate listeners (via calls
- * to
- * {@link #setEventCallback(Executor, EventCallback)},
- * {@link #setDrmEventCallback(Executor, DrmEventCallback)}).
- * In order to receive the respective callback
- * associated with these listeners, applications are required to create
- * MediaPlayer2 objects on a thread with its own Looper running (main UI
- * thread by default has a Looper running).
+ * <h3 id="Callbacks">Callbacks</h3>
+ * <p>Many errors do not result in a transition to the  <strong>Error</strong> state.
+ * It is good programming practice to register callback listeners using
+ * {@link #setEventCallback(Executor, EventCallback) setEventCallback} and
+ * {@link #setDrmEventCallback(Executor, DrmEventCallback) setDrmEventCallback}).
+ * You can receive a callback at any time and from any state.</p>
  *
+ * <p>If it's important for your app to respond to state changes (for instance, to update the
+ * controls on a transport UI), you should register an
+ * {@link EventCallback#onCallCompleted onCallCompleted} and
+ * detect state change commands by testing the <code>what</code> parameter for a callback from one
+ * of the state transition methods: {@link #CALL_COMPLETED_PREPARE}, {@link #CALL_COMPLETED_PLAY},
+ * and {@link #CALL_COMPLETED_PAUSE}.
+ * Then check the <code>status</code> parameter. The value {@link #CALL_STATUS_NO_ERROR} indicates a
+ * successful transition. Any other value will be an error. Call {@link #getState()} to
+ * determine the current state. </p>
  */
 public abstract class MediaPlayer2 implements SubtitleController.Listener
                                             , AutoCloseable
                                             , AudioRouting {
+    private final CloseGuard mGuard = CloseGuard.get();
+
     /**
      * Create a MediaPlayer2 object.
      *
      * @return A MediaPlayer2 object created
      */
     public static final MediaPlayer2 create() {
-        // TODO: load MediaUpdate APK
         return new MediaPlayer2Impl();
     }
 
@@ -512,7 +291,9 @@
      * @hide
      */
     // add hidden empty constructor so it doesn't show in SDK
-    public MediaPlayer2() { }
+    public MediaPlayer2() {
+        mGuard.open("close");
+    }
 
     /**
      * Returns a {@link MediaPlayerBase} implementation which runs based on
@@ -545,7 +326,22 @@
      */
     // This is a synchronous call.
     @Override
-    public abstract void close();
+    public void close() {
+        synchronized (mGuard) {
+            mGuard.close();
+        }
+    }
+
+    // Have to declare protected for finalize() since it is protected
+    // in the base class Object.
+    @Override
+    protected void finalize() throws Throwable {
+        if (mGuard != null) {
+            mGuard.warnIfOpen();
+        }
+
+        close();
+    }
 
     /**
      * Starts or resumes playback. If playback had previously been paused,
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index dfcbabe..678cb9a 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -53,9 +53,6 @@
 
 import com.android.framework.protobuf.InvalidProtocolBufferException;
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import dalvik.system.CloseGuard;
 
 import libcore.io.IoBridge;
 
@@ -79,7 +76,6 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Scanner;
-import java.util.Set;
 import java.util.UUID;
 import java.util.Vector;
 import java.util.concurrent.Executor;
@@ -105,7 +101,6 @@
     private boolean mScreenOnWhilePlaying;
     private boolean mStayAwake;
     private int mStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
-    private final CloseGuard mGuard = CloseGuard.get();
 
     private final Object mSrcLock = new Object();
     //--- guarded by |mSrcLock| start
@@ -145,6 +140,9 @@
     @GuardedBy("mTaskLock")
     private Task mCurrentTask;
 
+    @GuardedBy("this")
+    private boolean mReleased;
+
     /**
      * Default constructor.
      * <p>When done with the MediaPlayer2Impl, you should call  {@link #close()},
@@ -159,7 +157,6 @@
 
         mTimeProvider = new TimeProvider(this);
         mOpenSubtitleSources = new Vector<InputStream>();
-        mGuard.open("close");
 
         /* Native setup requires a weak reference to our object.
          * It's easier to create it here than in C++.
@@ -197,9 +194,8 @@
      */
     @Override
     public void close() {
-        synchronized (mGuard) {
-            release();
-        }
+        super.close();
+        release();
     }
 
     /**
@@ -353,7 +349,7 @@
         addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
             void process() throws IOException {
-                Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 int state = getState();
                 if (state != PLAYER_STATE_ERROR && state != PLAYER_STATE_IDLE) {
                     throw new IllegalStateException("called in wrong state " + state);
@@ -379,7 +375,7 @@
         addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
-                Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
+                checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 synchronized (mSrcLock) {
                     mNextDSDs = new ArrayList<DataSourceDesc>(1);
                     mNextDSDs.add(dsd);
@@ -693,7 +689,7 @@
 
     private void handleDataSource(boolean isCurrent, @NonNull DataSourceDesc dsd, long srcId)
             throws IOException {
-        Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+        checkArgument(dsd != null, "the DataSourceDesc cannot be null");
 
         switch (dsd.getType()) {
             case DataSourceDesc.TYPE_CALLBACK:
@@ -1293,7 +1289,7 @@
         addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
             @Override
             void process() {
-                Preconditions.checkArgument(params != null, "the BufferingParams cannot be null");
+                checkArgument(params != null, "the BufferingParams cannot be null");
                 _setBufferingParams(params);
             }
         });
@@ -1357,7 +1353,7 @@
         addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
             @Override
             void process() {
-                Preconditions.checkArgument(params != null, "the PlaybackParams cannot be null");
+                checkArgument(params != null, "the PlaybackParams cannot be null");
                 _setPlaybackParams(params);
             }
         });
@@ -1390,7 +1386,7 @@
         addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
             @Override
             void process() {
-                Preconditions.checkArgument(params != null, "the SyncParams cannot be null");
+                checkArgument(params != null, "the SyncParams cannot be null");
                 _setSyncParams(params);
             }
         });
@@ -2447,15 +2443,14 @@
     // in the base class Object.
     @Override
     protected void finalize() throws Throwable {
-        if (mGuard != null) {
-            mGuard.warnIfOpen();
-        }
-
-        close();
+        super.finalize();
         native_finalize();
     }
 
-    private void release() {
+    private synchronized void release() {
+        if (mReleased) {
+            return;
+        }
         stayAwake(false);
         updateSurfaceScreenOn();
         synchronized (mEventCbLock) {
@@ -2478,6 +2473,7 @@
         resetDrmState();
 
         _release();
+        mReleased = true;
     }
 
     private native void _release();
@@ -3030,6 +3026,12 @@
         }
     }
 
+    public static void checkArgument(boolean expression, String errorMessage) {
+        if (!expression) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+    }
+
     private void sendEvent(final EventNotifier notifier) {
         synchronized (mEventCbLock) {
             try {
@@ -3673,7 +3675,7 @@
             supportedSchemes = new UUID[supportedDRMsCount];
             for (int i = 0; i < supportedDRMsCount; i++) {
                 byte[] uuid = new byte[16];
-                in.next().getBytesValue().copyTo(uuid, uuid.length);
+                in.next().getBytesValue().copyTo(uuid, 0);
 
                 supportedSchemes[i] = bytesToUUID(uuid);
 
@@ -3806,7 +3808,7 @@
 
     private native void _prepareDrm(@NonNull byte[] uuid, @NonNull byte[] drmSessionId);
 
-        // Modular DRM helpers
+    // Modular DRM helpers
 
     private void prepareDrm_createDrmStep(@NonNull UUID uuid)
             throws UnsupportedSchemeException {
@@ -4684,7 +4686,7 @@
 
         private void sendCompleteNotification(int status) {
             // In {@link #notifyWhenCommandLabelReached} case, a separate callback
-            // {#link #onCommandLabelReached} is already called in {@code process()}.
+            // {@link #onCommandLabelReached} is already called in {@code process()}.
             if (mMediaCallType == CALL_COMPLETED_NOTIFY_WHEN_COMMAND_LABEL_REACHED) {
                 return;
             }
diff --git a/media/java/android/media/ThumbnailUtils.java b/media/java/android/media/ThumbnailUtils.java
index 4cc86fd..fd14060 100644
--- a/media/java/android/media/ThumbnailUtils.java
+++ b/media/java/android/media/ThumbnailUtils.java
@@ -160,7 +160,15 @@
         MediaMetadataRetriever retriever = new MediaMetadataRetriever();
         try {
             retriever.setDataSource(filePath);
-            bitmap = retriever.getFrameAtTime(-1);
+            // First retrieve album art in metadata if set.
+            byte[] embeddedPicture = retriever.getEmbeddedPicture();
+            if (embeddedPicture != null && embeddedPicture.length > 0) {
+                bitmap = BitmapFactory.decodeByteArray(embeddedPicture, 0, embeddedPicture.length);
+            }
+            // Fall back to first frame of the video.
+            if (bitmap == null) {
+                bitmap = retriever.getFrameAtTime(-1);
+            }
         } catch (IllegalArgumentException ex) {
             // Assume this is a corrupt video file
         } catch (RuntimeException ex) {
diff --git a/media/jni/OWNERS b/media/jni/OWNERS
new file mode 100644
index 0000000..bb91d4b
--- /dev/null
+++ b/media/jni/OWNERS
@@ -0,0 +1,2 @@
+# extra for MTP related files
+per-file android_mtp_*.cpp=marcone@google.com,jsharkey@android.com,jameswei@google.com,rmojumder@google.com
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
index 6595baa..7919723 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/SurfaceTextureSource.java
@@ -38,7 +38,7 @@
  * <p>To use, connect up the sourceListener callback, and then when executing
  * the graph, use the SurfaceTexture object passed to the callback to feed
  * frames into the filter graph. For example, pass the SurfaceTexture into
- * {#link
+ * {@link
  * android.hardware.Camera.setPreviewTexture(android.graphics.SurfaceTexture)}.
  * This filter is intended for applications that need for flexibility than the
  * CameraSource and MediaSource provide. Note that the application needs to
diff --git a/media/tests/MtpTests/OWNERS b/media/tests/MtpTests/OWNERS
new file mode 100644
index 0000000..1928ba8
--- /dev/null
+++ b/media/tests/MtpTests/OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+marcone@google.com
+jsharkey@android.com
+jameswei@google.com
+rmojumder@google.com
+
diff --git a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
index 0bf8bc1..4408ef5 100644
--- a/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
+++ b/packages/LocalTransport/src/com/android/localtransport/LocalTransport.java
@@ -95,7 +95,6 @@
     private long mFullBackupSize;
 
     private FileInputStream mCurFullRestoreStream;
-    private FileOutputStream mFullRestoreSocketStream;
     private byte[] mFullRestoreBuffer;
     private final LocalTransportParameters mParameters;
 
@@ -195,6 +194,15 @@
 
     @Override
     public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
+        try {
+            return performBackupInternal(packageInfo, data, flags);
+        } finally {
+            IoUtils.closeQuietly(data);
+        }
+    }
+
+    private int performBackupInternal(
+            PackageInfo packageInfo, ParcelFileDescriptor data, int flags) {
         boolean isIncremental = (flags & FLAG_INCREMENTAL) != 0;
         boolean isNonIncremental = (flags & FLAG_NON_INCREMENTAL) != 0;
 
@@ -750,7 +758,6 @@
     private void resetFullRestoreState() {
         IoUtils.closeQuietly(mCurFullRestoreStream);
         mCurFullRestoreStream = null;
-        mFullRestoreSocketStream = null;
         mFullRestoreBuffer = null;
     }
 
@@ -795,10 +802,11 @@
                 Log.e(TAG, "Unable to read archive for " + name);
                 return TRANSPORT_PACKAGE_REJECTED;
             }
-            mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
             mFullRestoreBuffer = new byte[2*1024];
         }
 
+        FileOutputStream stream = new FileOutputStream(socket.getFileDescriptor());
+
         int nRead;
         try {
             nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
@@ -815,14 +823,12 @@
                 if (DEBUG) {
                     Log.i(TAG, "   delivering restore chunk: " + nRead);
                 }
-                mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
+                stream.write(mFullRestoreBuffer, 0, nRead);
             }
         } catch (IOException e) {
             return TRANSPORT_ERROR;  // Hard error accessing the file; shouldn't happen
         } finally {
-            // Most transports will need to explicitly close 'socket' here, but this transport
-            // is in the same process as the caller so it can leave it up to the backup manager
-            // to manage both socket fds.
+            IoUtils.closeQuietly(socket);
         }
 
         return nRead;
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
index 95efb4c..cc970b9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java
@@ -36,10 +36,12 @@
 
     private final List<NetworkCycleDataForUid> mData;
     private final int mUid;
+    private final boolean mRetrieveDetail;
 
     private NetworkCycleDataForUidLoader(Builder builder) {
         super(builder);
         mUid = builder.mUid;
+        mRetrieveDetail = builder.mRetrieveDetail;
         mData = new ArrayList<NetworkCycleDataForUid>();
     }
 
@@ -50,13 +52,15 @@
                 mNetworkType, mSubId, start, end, mUid);
             final long total = getTotalUsage(stats);
             if (total > 0L) {
-                final long foreground = getForegroundUsage(start, end);
                 final NetworkCycleDataForUid.Builder builder = new NetworkCycleDataForUid.Builder();
-                builder.setBackgroundUsage(total - foreground)
-                    .setForegroundUsage(foreground)
-                    .setStartTime(start)
+                builder.setStartTime(start)
                     .setEndTime(end)
                     .setTotalUsage(total);
+                if (mRetrieveDetail) {
+                    final long foreground = getForegroundUsage(start, end);
+                    builder.setBackgroundUsage(total - foreground)
+                        .setForegroundUsage(foreground);
+                }
                 mData.add(builder.build());
             }
         } catch (Exception e) {
@@ -88,6 +92,7 @@
             extends NetworkCycleDataLoader.Builder<T> {
 
         private int mUid;
+        private boolean mRetrieveDetail = true;
 
         public Builder(Context context) {
             super(context);
@@ -97,6 +102,11 @@
             mUid = uid;
             return this;
         }
+
+        public Builder<T> setRetrieveDetail(boolean retrieveDetail) {
+            mRetrieveDetail = retrieveDetail;
+            return this;
+        }
     }
 
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
index cc936d2..b1c2c3a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java
@@ -22,6 +22,7 @@
 import android.app.usage.NetworkStats;
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.NetworkPolicy;
@@ -34,6 +35,8 @@
 import android.text.format.DateUtils;
 import android.util.Pair;
 
+import com.android.settingslib.NetworkPolicyEditor;
+
 import java.time.ZonedDateTime;
 import java.util.Iterator;
 
@@ -55,7 +58,6 @@
 
     protected NetworkCycleDataLoader(Builder<?> builder) {
         super(builder.mContext);
-        mPolicy = builder.mPolicy;
         mSubId = builder.mSubId;
         mNetworkType = builder.mNetworkType;
         mNetworkTemplate = builder.mNetworkTemplate;
@@ -63,6 +65,10 @@
             builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE);
         mNetworkStatsService = INetworkStatsService.Stub.asInterface(
             ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
+        final NetworkPolicyEditor policyEditor =
+            new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext));
+        policyEditor.read();
+        mPolicy = policyEditor.getPolicy(mNetworkTemplate);
     }
 
     @Override
@@ -115,7 +121,8 @@
 
             long cycleEnd = historyEnd;
             while (cycleEnd > historyStart) {
-                final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4);
+                final long cycleStart = Math.max(
+                    historyStart, cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4));
                 recordUsage(cycleStart, cycleEnd);
                 cycleEnd = cycleStart;
             }
@@ -154,7 +161,6 @@
 
     public static abstract class Builder<T extends NetworkCycleDataLoader> {
         private final Context mContext;
-        private NetworkPolicy mPolicy;
         private String mSubId;
         private int mNetworkType;
         private NetworkTemplate mNetworkTemplate;
@@ -163,27 +169,38 @@
             mContext = context;
         }
 
-        public Builder<T> setNetworkPolicy(NetworkPolicy policy) {
-            mPolicy = policy;
-            return this;
-        }
-
         public Builder<T> setSubscriberId(String subId) {
             mSubId = subId;
             return this;
         }
 
-        public Builder<T> setNetworkType(int networkType) {
-            mNetworkType = networkType;
-            return this;
-        }
-
         public Builder<T> setNetworkTemplate(NetworkTemplate template) {
             mNetworkTemplate = template;
+            setNetworkType();
             return this;
         }
 
         public abstract T build();
+
+        private void setNetworkType() {
+            if (mNetworkTemplate != null) {
+                final int matchRule = mNetworkTemplate.getMatchRule();
+                switch (matchRule) {
+                    case NetworkTemplate.MATCH_MOBILE:
+                    case NetworkTemplate.MATCH_MOBILE_WILDCARD:
+                        mNetworkType = ConnectivityManager.TYPE_MOBILE;
+                        break;
+                    case NetworkTemplate.MATCH_WIFI:
+                        mNetworkType = ConnectivityManager.TYPE_WIFI;
+                        break;
+                    case NetworkTemplate.MATCH_ETHERNET:
+                        mNetworkType = ConnectivityManager.TYPE_ETHERNET;
+                        break;
+                    default:
+                        mNetworkType = ConnectivityManager.TYPE_MOBILE;
+                }
+            }
+        }
     }
 
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
index 3dc110d..cad88b1 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java
@@ -22,6 +22,8 @@
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.os.RemoteException;
 import android.text.format.DateUtils;
 
@@ -39,6 +41,8 @@
     @Mock
     private NetworkStatsManager mNetworkStatsManager;
     @Mock
+    private NetworkPolicyManager mNetworkPolicyManager;
+    @Mock
     private Context mContext;
 
     private NetworkCycleChartDataLoader mLoader;
@@ -48,6 +52,9 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
             .thenReturn(mNetworkStatsManager);
+        when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+            .thenReturn(mNetworkPolicyManager);
+        when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
     }
 
     @Test
@@ -57,7 +64,7 @@
         final int networkType = ConnectivityManager.TYPE_MOBILE;
         final String subId = "TestSubscriber";
         mLoader = NetworkCycleChartDataLoader.builder(mContext)
-            .setNetworkType(networkType).setSubscriberId(subId).build();
+            .setSubscriberId(subId).build();
 
         mLoader.recordUsage(start, end);
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
index 53fe451..2314f27 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java
@@ -16,12 +16,21 @@
 
 package com.android.settingslib.net;
 
+import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND;
+import static android.net.NetworkStats.TAG_NONE;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.usage.NetworkStatsManager;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.text.format.DateUtils;
 
 import com.android.settingslib.SettingsLibRobolectricTestRunner;
@@ -38,6 +47,8 @@
     @Mock
     private NetworkStatsManager mNetworkStatsManager;
     @Mock
+    private NetworkPolicyManager mNetworkPolicyManager;
+    @Mock
     private Context mContext;
 
     private NetworkCycleDataForUidLoader mLoader;
@@ -47,20 +58,42 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
             .thenReturn(mNetworkStatsManager);
+        when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+            .thenReturn(mNetworkPolicyManager);
+        when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
     }
 
     @Test
-    public void recordUsage_shouldQueryNetworkDetailsForUid() {
+    public void recordUsage_shouldQueryNetworkDetailsForUidAndForegroundState() {
         final long end = System.currentTimeMillis();
         final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
         final int networkType = ConnectivityManager.TYPE_MOBILE;
         final String subId = "TestSubscriber";
         final int uid = 1;
-        mLoader = NetworkCycleDataForUidLoader.builder(mContext)
-            .setUid(uid).setNetworkType(networkType).setSubscriberId(subId).build();
+        mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
+            .setUid(uid).setSubscriberId(subId).build());
+        doReturn(1024L).when(mLoader).getTotalUsage(any());
 
         mLoader.recordUsage(start, end);
 
         verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid);
+        verify(mNetworkStatsManager).queryDetailsForUidTagState(
+            networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
+    }
+
+    @Test
+    public void recordUsage_retrieveDetailIsFalse_shouldNotQueryNetworkForegroundState() {
+        final long end = System.currentTimeMillis();
+        final long start = end - (DateUtils.WEEK_IN_MILLIS * 4);
+        final int networkType = ConnectivityManager.TYPE_MOBILE;
+        final String subId = "TestSubscriber";
+        final int uid = 1;
+        mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext)
+            .setRetrieveDetail(false).setUid(uid).setSubscriberId(subId).build());
+        doReturn(1024L).when(mLoader).getTotalUsage(any());
+
+        mLoader.recordUsage(start, end);
+        verify(mNetworkStatsManager, never()).queryDetailsForUidTagState(
+            networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND);
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
index be7f1bb..9d60a97 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java
@@ -30,6 +30,7 @@
 import android.net.INetworkStatsService;
 import android.net.INetworkStatsSession;
 import android.net.NetworkPolicy;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkStatsHistory;
 import android.net.NetworkTemplate;
 import android.os.RemoteException;
@@ -55,6 +56,8 @@
     @Mock
     private NetworkStatsManager mNetworkStatsManager;
     @Mock
+    private NetworkPolicyManager mNetworkPolicyManager;
+    @Mock
     private Context mContext;
     @Mock
     private NetworkPolicy mPolicy;
@@ -62,9 +65,6 @@
     private Iterator<Range<ZonedDateTime>> mIterator;
     @Mock
     private INetworkStatsService mNetworkStatsService;
-    @Mock
-    private NetworkCycleDataLoader.Builder mBuilder;
-
 
     private NetworkCycleDataTestLoader mLoader;
 
@@ -73,7 +73,10 @@
         MockitoAnnotations.initMocks(this);
         when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE))
             .thenReturn(mNetworkStatsManager);
+        when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE))
+            .thenReturn(mNetworkPolicyManager);
         when(mPolicy.cycleIterator()).thenReturn(mIterator);
+        when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]);
     }
 
     @Test
diff --git a/packages/SystemUI/docs/plugins.md b/packages/SystemUI/docs/plugins.md
index ed91f3d..6892005 100644
--- a/packages/SystemUI/docs/plugins.md
+++ b/packages/SystemUI/docs/plugins.md
@@ -73,7 +73,7 @@
 1. They must be signed with the platform cert
 2. They must include SystemUIPluginLib in LOCAL_JAVA_LIBRARIES (NOT LOCAL_STATIC_JAVA_LIBRARIES)
 
-Basically just copy the [example file](/packages/SystemUI/plugin/ExamplePlugin/Android.mk).
+Basically just copy the [example blueprint file](/packages/SystemUI/plugin/ExamplePlugin/Android.bp).
 
 To declare a plugin, you add a service to your manifest.  Add an intent filter to match the action for the plugin, and set the name to point at the class that implements the plugin interface.
 
diff --git a/packages/SystemUI/res/drawable/ic_lock.xml b/packages/SystemUI/res/drawable/ic_lock.xml
new file mode 100644
index 0000000..3fb8c8d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2018 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="32dp"
+        android:height="32dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0">
+    <path
+        android:pathData="M20.25,6.89C20.25,4.62 18.36,2.75 16,2.75C13.64,2.75 11.75,4.62 11.75,6.89V11.25H20.25V6.89ZM21.75,11.25V6.89C21.75,3.76 19.16,1.25 16,1.25C12.84,1.25 10.25,3.76 10.25,6.89V11.25H10C8.48,11.25 7.25,12.48 7.25,14V26C7.25,27.52 8.48,28.75 10,28.75H22C23.52,28.75 24.75,27.52 24.75,26V14C24.75,12.48 23.52,11.25 22,11.25H21.75ZM10,12.75C9.31,12.75 8.75,13.31 8.75,14V26C8.75,26.69 9.31,27.25 10,27.25H22C22.69,27.25 23.25,26.69 23.25,26V14C23.25,13.31 22.69,12.75 22,12.75H10ZM18,20C18,21.1 17.1,22 16,22C14.9,22 14,21.1 14,20C14,18.9 14.9,18 16,18C17.1,18 18,18.9 18,20Z"
+        android:fillType="evenOdd"
+        android:fillColor="?attr/wallpaperTextColor"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_24dp.xml b/packages/SystemUI/res/drawable/ic_lock_24dp.xml
deleted file mode 100644
index bf0dc95..0000000
--- a/packages/SystemUI/res/drawable/ic_lock_24dp.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<!--
-Copyright (C) 2017 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="24.0dp"
-        android:height="24.0dp"
-        android:viewportWidth="24.0"
-        android:viewportHeight="24.0">
-
-    <path
-        android:fillColor="?attr/wallpaperTextColor"
-        android:pathData="M18.0,8.0l-1.0,0.0L17.0,6.0c0.0,-2.8 -2.2,-5.0 -5.0,-5.0C9.2,1.0 7.0,3.2 7.0,6.0l0.0,2.0L6.0,8.0c-1.1,0.0 -2.0,0.9 -2.0,2.0l0.0,10.0c0.0,1.1 0.9,2.0 2.0,2.0l12.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L20.0,10.0C20.0,8.9 19.1,8.0 18.0,8.0zM12.0,17.0c-1.1,0.0 -2.0,-0.9 -2.0,-2.0s0.9,-2.0 2.0,-2.0c1.1,0.0 2.0,0.9 2.0,2.0S13.1,17.0 12.0,17.0zM15.1,8.0L8.9,8.0L8.9,6.0c0.0,-1.7 1.4,-3.1 3.1,-3.1c1.7,0.0 3.1,1.4 3.1,3.1L15.1,8.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/ic_lock_open.xml b/packages/SystemUI/res/drawable/ic_lock_open.xml
new file mode 100644
index 0000000..12a811c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_lock_open.xml
@@ -0,0 +1,26 @@
+<!--
+  ~ Copyright (C) 2018 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="32dp"
+        android:height="32dp"
+        android:viewportWidth="32.0"
+        android:viewportHeight="32.0">
+    <path
+        android:pathData="M21.75,6.89C21.75,4.62 23.64,2.75 26,2.75C28.36,2.75 30.25,4.62 30.25,6.89V12H31.75V6.89C31.75,3.76 29.16,1.25 26,1.25C22.84,1.25 20.25,3.76 20.25,6.89V11.25H10C8.48,11.25 7.25,12.48 7.25,14V26C7.25,27.52 8.48,28.75 10,28.75H22C23.52,28.75 24.75,27.52 24.75,26V14C24.75,12.48 23.52,11.25 22,11.25H21.75V6.89ZM10,12.75C9.31,12.75 8.75,13.31 8.75,14V26C8.75,26.69 9.31,27.25 10,27.25H22C22.69,27.25 23.25,26.69 23.25,26V14C23.25,13.31 22.69,12.75 22,12.75H10ZM18,20C18,21.1 17.1,22 16,22C14.9,22 14,21.1 14,20C14,18.9 14.9,18 16,18C17.1,18 18,18.9 18,20Z"
+        android:fillType="evenOdd"
+        android:fillColor="?attr/wallpaperTextColor"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/keyguard_bottom_area.xml b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
index d0d379c..96a1bab 100644
--- a/packages/SystemUI/res/layout/keyguard_bottom_area.xml
+++ b/packages/SystemUI/res/layout/keyguard_bottom_area.xml
@@ -88,7 +88,7 @@
         android:layout_width="@dimen/keyguard_affordance_width"
         android:layout_height="@dimen/keyguard_affordance_height"
         android:layout_gravity="bottom|center_horizontal"
-        android:src="@drawable/ic_lock_24dp"
+        android:src="@drawable/ic_lock"
         android:contentDescription="@string/accessibility_unlock_button"
         android:scaleType="center" />
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 7c355c9..ab7dec9 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -645,10 +645,6 @@
     <dimen name="keyguard_affordance_height">56dp</dimen>
     <dimen name="keyguard_affordance_width">56dp</dimen>
 
-    <!-- The width/height of the phone/camera/unlock icon drawable on keyguard. -->
-    <dimen name="keyguard_affordance_icon_height">24dp</dimen>
-    <dimen name="keyguard_affordance_icon_width">24dp</dimen>
-
     <dimen name="keyguard_indication_margin_bottom">65dp</dimen>
 
     <!-- The text size for battery level -->
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 2b51aaa..e1c71fa 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1081,7 +1081,7 @@
     <string name="clear_all_notifications_text">Clear all</string>
 
     <!-- The text for the manage notifications link. [CHAR LIMIT=40] -->
-    <string name="manage_notifications_text">Manage notifications</string>
+    <string name="manage_notifications_text">Manage</string>
 
     <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] -->
     <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 8442dd1..6446367 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -279,7 +279,7 @@
         <item name="android:fontFamily">sans-serif</item>
     </style>
 
-    <style name="BaseBrightnessDialogContainer">
+    <style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
         <item name="android:layout_width">match_parent</item>
         <item name="android:layout_height">wrap_content</item>
     </style>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index d38cc0f..69aea2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -24,8 +24,6 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
-import sun.misc.Resource;
-
 public class NavigationBarCompat {
     /**
      * Touch slopes and thresholds for quick step operations. Drag slop is the point where the
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index f1b53fe..c7685f8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -41,7 +41,6 @@
 import android.app.trust.TrustManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -49,13 +48,14 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
+import android.hardware.biometrics.BiometricManager;
 import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.face.FaceManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback;
 import android.hardware.fingerprint.FingerprintManager.AuthenticationResult;
 import android.media.AudioManager;
-import android.net.Uri;
 import android.os.BatteryManager;
 import android.os.CancellationSignal;
 import android.os.Handler;
@@ -250,51 +250,6 @@
     private static final int HW_UNAVAILABLE_TIMEOUT = 3000; // ms
     private static final int HW_UNAVAILABLE_RETRY_MAX = 3;
 
-    private class SettingObserver extends ContentObserver {
-        private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
-                Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
-
-        private final ContentResolver mContentResolver;
-
-        /**
-         * Creates a content observer.
-         *
-         * @param handler The handler to run {@link #onChange} on, or null if none.
-         */
-        public SettingObserver(Handler handler) {
-            super(handler);
-            mContentResolver = mContext.getContentResolver();
-            updateContentObserver();
-        }
-
-        public void updateContentObserver() {
-            mContentResolver.unregisterContentObserver(this);
-            mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
-                    false /* notifyForDescendents */,
-                    this,
-                    UserHandle.USER_CURRENT);
-
-            // Update the value immediately
-            onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
-                    mFaceSettingEnabledForUser =
-                            Settings.Secure.getIntForUser(
-                                    mContentResolver,
-                                    Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
-                                    1 /* default */,
-                                    UserHandle.USER_CURRENT) != 0;
-                    updateBiometricListeningState();
-            }
-        }
-    }
-
-    private final SettingObserver mSettingObserver;
-    private boolean mFaceSettingEnabledForUser;
-
     private final Handler mHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
@@ -400,6 +355,18 @@
         }
     };
 
+    private boolean mFaceSettingEnabledForUser;
+    private BiometricManager mBiometricManager;
+    private IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback =
+            new IBiometricEnabledOnKeyguardCallback.Stub() {
+        @Override
+        public void onChanged(BiometricSourceType type, boolean enabled) throws RemoteException {
+            if (type == BiometricSourceType.FACE) {
+                mFaceSettingEnabledForUser = enabled;
+            }
+        }
+    };
+
     private OnSubscriptionsChangedListener mSubscriptionListener =
             new OnSubscriptionsChangedListener() {
         @Override
@@ -1165,7 +1132,7 @@
     private CancellationSignal mFingerprintCancelSignal;
     private CancellationSignal mFaceCancelSignal;
     private FingerprintManager mFpm;
-    private FaceManager mFaceAuthenticationManager;
+    private FaceManager mFaceManager;
 
     /**
      * When we receive a
@@ -1434,7 +1401,6 @@
         mSubscriptionManager = SubscriptionManager.from(context);
         mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
         mStrongAuthTracker = new StrongAuthTracker(context);
-        mSettingObserver = new SettingObserver(mHandler);
 
         // Since device can't be un-provisioned, we only need to register a content observer
         // to update mDeviceProvisioned when we are...
@@ -1504,17 +1470,21 @@
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
             mFpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE);
         }
-
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FACE)) {
-            mFaceAuthenticationManager =
-                    (FaceManager) context.getSystemService(Context.FACE_SERVICE);
+            mFaceManager = (FaceManager) context.getSystemService(Context.FACE_SERVICE);
         }
+
+        if (mFpm != null || mFaceManager != null) {
+            mBiometricManager = context.getSystemService(BiometricManager.class);
+            mBiometricManager.registerEnabledOnKeyguardCallback(mBiometricEnabledCallback);
+        }
+
         updateBiometricListeningState();
         if (mFpm != null) {
             mFpm.addLockoutResetCallback(mFingerprintLockoutResetCallback);
         }
-        if (mFaceAuthenticationManager != null) {
-            mFaceAuthenticationManager.addLockoutResetCallback(mFaceLockoutResetCallback);
+        if (mFaceManager != null) {
+            mFaceManager.addLockoutResetCallback(mFaceLockoutResetCallback);
         }
 
         ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
@@ -1629,7 +1599,7 @@
                 mFaceCancelSignal.cancel();
             }
             mFaceCancelSignal = new CancellationSignal();
-            mFaceAuthenticationManager.authenticate(null, mFaceCancelSignal, 0,
+            mFaceManager.authenticate(null, mFaceCancelSignal, 0,
                     mFaceAuthenticationCallback, null);
             setFaceRunningState(BIOMETRIC_STATE_RUNNING);
         }
@@ -1641,9 +1611,9 @@
     }
 
     public boolean isUnlockWithFacePossible(int userId) {
-        return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
+        return mFaceManager != null && mFaceManager.isHardwareDetected()
                 && !isFaceDisabled(userId)
-                && mFaceAuthenticationManager.hasEnrolledTemplates(userId);
+                && mFaceManager.hasEnrolledTemplates(userId);
     }
 
     private void stopListeningForFingerprint() {
@@ -1765,7 +1735,6 @@
      * Handle {@link #MSG_USER_SWITCH_COMPLETE}
      */
     private void handleUserSwitchComplete(int userId) {
-        mSettingObserver.updateContentObserver();
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -2437,7 +2406,7 @@
             pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
             pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
         }
-        if (mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()) {
+        if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
             final int userId = ActivityManager.getCurrentUser();
             final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
             pw.println("  Face authentication state (user=" + userId + ")");
@@ -2449,6 +2418,7 @@
             pw.println("    possible=" + isUnlockWithFacePossible(userId));
             pw.println("    strongAuthFlags=" + Integer.toHexString(strongAuthFlags));
             pw.println("    trustManaged=" + getUserTrustIsManaged(userId));
+            pw.println("    enabledByUser=" + mFaceSettingEnabledForUser);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
index 1af2156..d351c4f3 100644
--- a/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/OverviewProxyService.java
@@ -17,6 +17,10 @@
 package com.android.systemui;
 
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+import static android.view.MotionEvent.ACTION_CANCEL;
+
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_DISABLE_SWIPE_UP;
 import static com.android.systemui.shared.system.NavigationBarCompat.FLAG_SHOW_OVERVIEW_BUTTON;
 import static com.android.systemui.shared.system.NavigationBarCompat.InteractionType;
@@ -86,6 +90,7 @@
     private boolean mIsEnabled;
     private int mCurrentBoundedUserId = -1;
     private float mBackButtonAlpha;
+    private MotionEvent mStatusBarGestureDownEvent;
 
     private ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
 
@@ -108,6 +113,9 @@
         }
 
         public void onStatusBarMotionEvent(MotionEvent event) {
+            if (!verifyCaller("onStatusBarMotionEvent")) {
+                return;
+            }
             long token = Binder.clearCallingIdentity();
             try {
                 // TODO move this logic to message queue
@@ -115,6 +123,16 @@
                     StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
                     if (bar != null) {
                         bar.dispatchNotificationsPanelTouchEvent(event);
+
+                        int action = event.getActionMasked();
+                        if (action == ACTION_DOWN) {
+                            mStatusBarGestureDownEvent = MotionEvent.obtain(event);
+                        }
+                        if (action == ACTION_UP || action == ACTION_CANCEL) {
+                            mStatusBarGestureDownEvent.recycle();
+                            mStatusBarGestureDownEvent = null;
+                        }
+                        event.recycle();
                     }
                 });
             } finally {
@@ -298,7 +316,7 @@
 
     // This is the death handler for the binder from the launcher service
     private final IBinder.DeathRecipient mOverviewServiceDeathRcpt
-            = this::startConnectionToCurrentUser;
+            = this::cleanupAfterDeath;
 
     public OverviewProxyService(Context context) {
         mContext = context;
@@ -328,6 +346,22 @@
         return mBackButtonAlpha;
     }
 
+    public void cleanupAfterDeath() {
+        if (mStatusBarGestureDownEvent != null) {
+            mHandler.post(()-> {
+                StatusBar bar = SysUiServiceProvider.getComponent(mContext, StatusBar.class);
+                if (bar != null) {
+                    System.out.println("MERONG dispatchNotificationPanelTouchEvent");
+                    mStatusBarGestureDownEvent.setAction(MotionEvent.ACTION_CANCEL);
+                    bar.dispatchNotificationsPanelTouchEvent(mStatusBarGestureDownEvent);
+                    mStatusBarGestureDownEvent.recycle();
+                    mStatusBarGestureDownEvent = null;
+                }
+            });
+        }
+        startConnectionToCurrentUser();
+    }
+
     public void startConnectionToCurrentUser() {
         if (mHandler.getLooper() != Looper.myLooper()) {
             mHandler.post(mConnectionRunnable);
diff --git a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
index 646f69e..6dc2d67 100644
--- a/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/RegionInterceptingFrameLayout.java
@@ -76,7 +76,7 @@
                 continue;
             }
 
-            internalInsetsInfo.touchableRegion.op(riv.getInterceptRegion(), Op.UNION);
+            internalInsetsInfo.touchableRegion.op(unionRegion, Op.UNION);
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 48181bc..4d24d82 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -76,6 +76,9 @@
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.leak.RotationUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import androidx.annotation.VisibleForTesting;
 
 /**
@@ -108,6 +111,23 @@
     private boolean mPendingRotationChange;
     private Handler mHandler;
 
+    /**
+     * Converts a set of {@link Rect}s into a {@link Region}
+     *
+     * @hide
+     */
+    public static Region rectsToRegion(List<Rect> rects) {
+        Region result = Region.obtain();
+        if (rects != null) {
+            for (Rect r : rects) {
+                if (r != null && !r.isEmpty()) {
+                    result.op(r, Region.Op.UNION);
+                }
+            }
+        }
+        return result;
+    }
+
     @Override
     public void start() {
         mHandler = startHandlerThread();
@@ -539,7 +559,7 @@
 
         private final DisplayInfo mInfo = new DisplayInfo();
         private final Paint mPaint = new Paint();
-        private final Region mBounds = new Region();
+        private final List<Rect> mBounds = new ArrayList();
         private final Rect mBoundingRect = new Rect();
         private final Path mBoundingPath = new Path();
         private final int[] mLocation = new int[2];
@@ -629,12 +649,12 @@
             mStart = isStart();
             requestLayout();
             getDisplay().getDisplayInfo(mInfo);
-            mBounds.setEmpty();
+            mBounds.clear();
             mBoundingRect.setEmpty();
             mBoundingPath.reset();
             int newVisible;
             if (shouldDrawCutout(getContext()) && hasCutout()) {
-                mBounds.set(mInfo.displayCutout.getBounds());
+                mBounds.addAll(mInfo.displayCutout.getBoundingRects());
                 localBounds(mBoundingRect);
                 updateBoundingPath();
                 invalidate();
@@ -713,32 +733,17 @@
 
         public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
                 Rect out) {
-            Region bounds = boundsFromDirection(displayCutout, gravity);
-            out.set(bounds.getBounds());
-            bounds.recycle();
-        }
-
-        public static Region boundsFromDirection(DisplayCutout displayCutout, int gravity) {
-            Region bounds = displayCutout.getBounds();
             switch (gravity) {
                 case Gravity.TOP:
-                    bounds.op(0, 0, Integer.MAX_VALUE, displayCutout.getSafeInsetTop(),
-                            Region.Op.INTERSECT);
-                    break;
+                    out.set(displayCutout.getBoundingRectTop());
                 case Gravity.LEFT:
-                    bounds.op(0, 0, displayCutout.getSafeInsetLeft(), Integer.MAX_VALUE,
-                            Region.Op.INTERSECT);
-                    break;
+                    out.set(displayCutout.getBoundingRectLeft());
                 case Gravity.BOTTOM:
-                    bounds.op(0, displayCutout.getSafeInsetTop() + 1, Integer.MAX_VALUE,
-                            Integer.MAX_VALUE, Region.Op.INTERSECT);
-                    break;
+                    out.set(displayCutout.getBoundingRectBottom());
                 case Gravity.RIGHT:
-                    bounds.op(displayCutout.getSafeInsetLeft() + 1, 0, Integer.MAX_VALUE,
-                            Integer.MAX_VALUE, Region.Op.INTERSECT);
-                    break;
+                    out.set(displayCutout.getBoundingRectRight());
             }
-            return bounds;
+            out.setEmpty();
         }
 
         private void localBounds(Rect out) {
@@ -771,7 +776,8 @@
             }
 
             View rootView = getRootView();
-            Region cutoutBounds = mInfo.displayCutout.getBounds();
+            Region cutoutBounds = rectsToRegion(
+                    mInfo.displayCutout.getBoundingRects());
 
             // Transform to window's coordinate space
             rootView.getLocationOnScreen(mLocation);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index bde7f1b..512cd82 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -90,6 +90,7 @@
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.EmergencyDialerConstants;
 import com.android.systemui.volume.SystemUIInterpolators.LogAccelerateInterpolator;
 
@@ -102,7 +103,8 @@
  * is provisioned.
  */
 class GlobalActionsDialog implements DialogInterface.OnDismissListener,
-        DialogInterface.OnClickListener, DialogInterface.OnShowListener {
+        DialogInterface.OnClickListener, DialogInterface.OnShowListener,
+        ConfigurationController.ConfigurationListener {
 
     static public final String SYSTEM_DIALOG_REASON_KEY = "reason";
     static public final String SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS = "globalactions";
@@ -197,6 +199,8 @@
 
         mEmergencyAffordanceManager = new EmergencyAffordanceManager(context);
         mScreenshotHelper = new ScreenshotHelper(context);
+
+        Dependency.get(ConfigurationController.class).addCallback(this);
     }
 
     /**
@@ -417,6 +421,15 @@
                 || state == SOME_AUTH_REQUIRED_AFTER_USER_REQUEST);
     }
 
+    @Override
+    public void onUiModeChanged() {
+        mContext.getTheme().applyStyle(mContext.getThemeResId(), true);
+    }
+
+    public void destroy() {
+        Dependency.get(ConfigurationController.class).removeCallback(this);
+    }
+
     private final class PowerAction extends SinglePressAction implements LongPressAction {
         private PowerAction() {
             super(R.drawable.ic_lock_power_off,
@@ -1530,7 +1543,6 @@
                                 * ScrimController.GRADIENT_SCRIM_ALPHA * 255);
                         mGradientDrawable.setAlpha(alpha);
                     })
-                    .withEndAction(() -> getWindow().getDecorView().requestAccessibilityFocus())
                     .start();
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index 1489c21..0394998 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -61,6 +61,10 @@
     @Override
     public void destroy() {
         SysUiServiceProvider.getComponent(mContext, CommandQueue.class).removeCallbacks(this);
+        if (mGlobalActions != null) {
+            mGlobalActions.destroy();
+            mGlobalActions = null;
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index 0b9067e..568a039 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -301,9 +301,10 @@
             // mark if we've already shown a warning this cycle. This will prevent the notification
             // trigger from spamming users by only showing low/critical warnings once per cycle
             if (hybridEnabled) {
-                if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()
-                        || mBatteryLevel < mLowBatteryReminderLevels[1]) {
+                if (mTimeRemaining <= mEnhancedEstimates.getSevereWarningThreshold()
+                        || mBatteryLevel <= mLowBatteryReminderLevels[1]) {
                     mSevereWarningShownThisChargeCycle = true;
+                    mLowWarningShownThisChargeCycle = true;
                 } else {
                     mLowWarningShownThisChargeCycle = true;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
index 4b5ab2a..c398a4a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java
@@ -6,7 +6,9 @@
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.content.Context;
+import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
@@ -29,6 +31,7 @@
 public class PagedTileLayout extends ViewPager implements QSTileLayout {
 
     private static final boolean DEBUG = false;
+    private static final String CURRENT_PAGE = "current_page";
 
     private static final String TAG = "PagedTileLayout";
     private static final int REVEAL_SCROLL_DURATION_MILLIS = 750;
@@ -54,6 +57,9 @@
     private AnimatorSet mBounceAnimatorSet;
     private float mLastExpansion;
     private boolean mDistributeTiles = false;
+    private int mPageToRestore = -1;
+    private int mLayoutOrientation;
+    private int mLayoutDirection;
 
     public PagedTileLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -61,13 +67,37 @@
         setAdapter(mAdapter);
         setOnPageChangeListener(mOnPageChangeListener);
         setCurrentItem(0, false);
+        mLayoutOrientation = getResources().getConfiguration().orientation;
+        mLayoutDirection = getLayoutDirection();
+    }
+
+    public void saveInstanceState(Bundle outState) {
+        outState.putInt(CURRENT_PAGE, getCurrentItem());
+    }
+
+    public void restoreInstanceState(Bundle savedInstanceState) {
+        // There's only 1 page at this point. We want to restore the correct page once the
+        // pages have been inflated
+        mPageToRestore = savedInstanceState.getInt(CURRENT_PAGE, -1);
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mLayoutOrientation != newConfig.orientation) {
+            mLayoutOrientation = newConfig.orientation;
+            setCurrentItem(0, false);
+        }
     }
 
     @Override
     public void onRtlPropertiesChanged(int layoutDirection) {
         super.onRtlPropertiesChanged(layoutDirection);
-        setAdapter(mAdapter);
-        setCurrentItem(0, false);
+        if (mLayoutDirection != layoutDirection) {
+            mLayoutDirection = layoutDirection;
+            setAdapter(mAdapter);
+            setCurrentItem(0, false);
+        }
     }
 
     @Override
@@ -116,6 +146,7 @@
         super.onFinishInflate();
         mPages.add((TilePage) LayoutInflater.from(getContext())
                 .inflate(R.layout.qs_paged_page, this, false));
+        mAdapter.notifyDataSetChanged();
     }
 
     public void setPageIndicator(PageIndicator indicator) {
@@ -218,7 +249,10 @@
         mPageIndicator.setNumPages(mPages.size());
         setAdapter(mAdapter);
         mAdapter.notifyDataSetChanged();
-        setCurrentItem(0, false);
+        if (mPageToRestore != -1) {
+            setCurrentItem(mPageToRestore, false);
+            mPageToRestore = -1;
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 79e5086..42dfcee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -103,6 +103,9 @@
             setListening(savedInstanceState.getBoolean(EXTRA_LISTENING));
             setEditLocation(view);
             mQSCustomizer.restoreInstanceState(savedInstanceState);
+            if (mQsExpanded) {
+                mQSPanel.getTileLayout().restoreInstanceState(savedInstanceState);
+            }
         }
         SysUiServiceProvider.getComponent(getContext(), CommandQueue.class).addCallbacks(this);
     }
@@ -127,6 +130,9 @@
         outState.putBoolean(EXTRA_EXPANDED, mQsExpanded);
         outState.putBoolean(EXTRA_LISTENING, mListening);
         mQSCustomizer.saveInstanceState(outState);
+        if (mQsExpanded) {
+            mQSPanel.getTileLayout().saveInstanceState(outState);
+        }
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index e98ef4c..cf63e47 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -25,6 +25,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.metrics.LogMaker;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.service.quicksettings.Tile;
@@ -666,6 +667,11 @@
     }
 
     public interface QSTileLayout {
+
+        default void saveInstanceState(Bundle outState) {}
+
+        default void restoreInstanceState(Bundle savedInstanceState) {}
+
         void addTile(TileRecord tile);
 
         void removeTile(TileRecord tile);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 556786a..6f847c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -253,7 +253,8 @@
 
             final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd();
             final int leftoverWithespace = availableWidth - maxTiles * mCellWidth;
-            final int smallestHorizontalMarginNeeded = leftoverWithespace / (maxTiles - 1);
+            final int smallestHorizontalMarginNeeded;
+            smallestHorizontalMarginNeeded = leftoverWithespace / Math.max(1, maxTiles - 1);
 
             if (smallestHorizontalMarginNeeded > 0){
                 mCellMarginHorizontal = smallestHorizontalMarginNeeded;
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
index 6918a63..2ae53b5 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessDialog.java
@@ -46,11 +46,7 @@
         window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
         window.requestFeature(Window.FEATURE_NO_TITLE);
 
-        // Use a dialog theme as the activity theme, but inflate the content as
-        // the QS content.
-        ContextThemeWrapper themedContext = new ContextThemeWrapper(this,
-                com.android.internal.R.style.Theme_DeviceDefault_QuickSettings);
-        View v = LayoutInflater.from(themedContext).inflate(
+        View v = LayoutInflater.from(this).inflate(
                 R.layout.quick_settings_brightness_dialog, null);
         setContentView(v);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index e12b574..9d773ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -76,7 +76,7 @@
     private float mCircleStartRadius;
     private float mMaxCircleSize;
     private Animator mPreviewClipper;
-    private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
+    private float mRestingAlpha = 1f;
     private boolean mSupportHardware;
     private boolean mFinishing;
     private boolean mLaunchingAffordance;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index 8969aca..0577841 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -426,6 +426,10 @@
         return mDarkAmount == 1;
     }
 
+    public boolean isDarkAtAll() {
+        return mDarkAmount != 0;
+    }
+
     public void setDarkTopPadding(int darkTopPadding) {
         mDarkTopPadding = darkTopPadding;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9978ec3..9ddab7c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -25,7 +25,6 @@
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeAnimator;
 import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.Nullable;
 import android.app.WallpaperManager;
 import android.content.Context;
@@ -40,7 +39,6 @@
 import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.ServiceManager;
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
@@ -82,7 +80,6 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.SwipeHelper;
-import com.android.systemui.SwipeHelper.Callback;
 import com.android.systemui.classifier.FalsingManager;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -3734,12 +3731,14 @@
         return y < getHeight() - getEmptyBottomMargin();
     }
 
+    @VisibleForTesting
     @ShadeViewRefactor(RefactorComponent.INPUT)
-    private void setIsBeingDragged(boolean isDragged) {
+    void setIsBeingDragged(boolean isDragged) {
         mIsBeingDragged = isDragged;
         if (isDragged) {
             requestDisallowInterceptTouchEvent(true);
             cancelLongPress();
+            resetExposedMenuView(true /* animate */, true /* force */);
         }
     }
 
@@ -3869,6 +3868,7 @@
     public void onPanelTrackingStarted() {
         mPanelTracking = true;
         mAmbientState.setPanelTracking(true);
+        resetExposedMenuView(true /* animate */, true /* force */);
     }
 
     @ShadeViewRefactor(RefactorComponent.STATE_RESOLVER)
@@ -4271,8 +4271,10 @@
         mLinearDarkAmount = linearDarkAmount;
         mInterpolatedDarkAmount = interpolatedDarkAmount;
         boolean wasFullyDark = mAmbientState.isFullyDark();
+        boolean wasDarkAtAll = mAmbientState.isDarkAtAll();
         mAmbientState.setDarkAmount(interpolatedDarkAmount);
         boolean nowFullyDark = mAmbientState.isFullyDark();
+        boolean nowDarkAtAll = mAmbientState.isDarkAtAll();
         if (nowFullyDark != wasFullyDark) {
             updateContentHeight();
             DozeParameters dozeParameters = DozeParameters.getInstance(mContext);
@@ -4283,6 +4285,9 @@
                 mIconAreaController.setFullyDark(nowFullyDark);
             }
         }
+        if (!wasDarkAtAll && nowDarkAtAll) {
+            resetExposedMenuView(true /* animate */, true /* animate */);
+        }
         updateAlgorithmHeightAndPadding();
         updateBackgroundDimming();
         updatePanelTranslation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
index cfc3271..976327a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpManagerPhone.java
@@ -22,6 +22,8 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import androidx.collection.ArraySet;
+
+import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Region.Op;
 import android.util.Log;
@@ -324,11 +326,10 @@
 
         // Expand touchable region such that we also catch touches that just start below the notch
         // area.
-        Region bounds = ScreenDecorations.DisplayCutoutView.boundsFromDirection(
-                cutout, Gravity.TOP);
-        bounds.translate(0, mDisplayCutoutTouchableRegionSize);
+        Rect bounds = new Rect();
+        ScreenDecorations.DisplayCutoutView.boundsFromDirection(cutout, Gravity.TOP, bounds);
+        bounds.offset(0, mDisplayCutoutTouchableRegionSize);
         region.op(bounds, Op.UNION);
-        bounds.recycle();
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index e2f3319..49d421b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -36,7 +36,6 @@
  */
 public class KeyguardAffordanceHelper {
 
-    public static final float SWIPE_RESTING_ALPHA_AMOUNT = 0.5f;
     public static final long HINT_PHASE1_DURATION = 200;
     private static final long HINT_PHASE2_DURATION = 350;
     private static final float BACKGROUND_RADIUS_SCALE_FACTOR = 0.25f;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index f667726..538a260 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -29,7 +29,6 @@
 import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.admin.DevicePolicyManager;
-import android.hardware.biometrics.BiometricSourceType;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -41,6 +40,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
 import android.graphics.drawable.Drawable;
+import android.hardware.biometrics.BiometricSourceType;
 import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -54,7 +54,6 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.util.MathUtils;
 import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
@@ -68,16 +67,14 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.EventLogTags;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistManager;
+import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.IntentButtonProvider;
 import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
 import com.android.systemui.plugins.IntentButtonProvider.IntentButton.IconState;
-import com.android.systemui.plugins.PluginListener;
-import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.KeyguardIndicationController;
@@ -705,8 +702,6 @@
             startFinishDozeAnimationElement(mLeftAffordanceView, delay);
             delay += DOZE_ANIMATION_STAGGER_DELAY;
         }
-        startFinishDozeAnimationElement(mLockIcon, delay);
-        delay += DOZE_ANIMATION_STAGGER_DELAY;
         if (mRightAffordanceView.getVisibility() == View.VISIBLE) {
             startFinishDozeAnimationElement(mRightAffordanceView, delay);
         }
@@ -823,10 +818,8 @@
         updateLeftAffordanceIcon();
 
         if (dozing) {
-            mLockIcon.setVisibility(INVISIBLE);
             mOverlayContainer.setVisibility(INVISIBLE);
         } else {
-            mLockIcon.setVisibility(VISIBLE);
             mOverlayContainer.setVisibility(VISIBLE);
             if (animate) {
                 startFinishDozeAnimation();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 8928530..2edc294 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -51,7 +51,6 @@
     private boolean mScreenOn;
     private boolean mLastScreenOn;
     private Drawable mUserAvatarIcon;
-    private TrustDrawable mTrustDrawable;
     private final UnlockMethodCache mUnlockMethodCache;
     private AccessibilityController mAccessibilityController;
     private boolean mHasFingerPrintIcon;
@@ -62,28 +61,10 @@
 
     public LockIcon(Context context, AttributeSet attrs) {
         super(context, attrs);
-        mTrustDrawable = new TrustDrawable(context);
-        setBackground(mTrustDrawable);
         mUnlockMethodCache = UnlockMethodCache.getInstance(context);
     }
 
     @Override
-    protected void onVisibilityChanged(View changedView, int visibility) {
-        super.onVisibilityChanged(changedView, visibility);
-        if (isShown()) {
-            mTrustDrawable.start();
-        } else {
-            mTrustDrawable.stop();
-        }
-    }
-
-    @Override
-    protected void onDetachedFromWindow() {
-        super.onDetachedFromWindow();
-        mTrustDrawable.stop();
-    }
-
-    @Override
     public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
         mUserAvatarIcon = picture;
         update();
@@ -110,9 +91,6 @@
         final int density = newConfig.densityDpi;
         if (density != mDensity) {
             mDensity = density;
-            mTrustDrawable.stop();
-            mTrustDrawable = new TrustDrawable(getContext());
-            setBackground(mTrustDrawable);
             update();
         }
     }
@@ -122,18 +100,9 @@
     }
 
     public void update(boolean force) {
-        boolean visible = isShown()
-                && KeyguardUpdateMonitor.getInstance(mContext).isDeviceInteractive();
-        if (visible) {
-            mTrustDrawable.start();
-        } else {
-            mTrustDrawable.stop();
-        }
         int state = getState();
         boolean anyFingerprintIcon = state == STATE_FINGERPRINT || state == STATE_FINGERPRINT_ERROR;
         mHasFaceUnlockIcon = state == STATE_FACE_UNLOCK;
-        boolean useAdditionalPadding = anyFingerprintIcon;
-        boolean trustHidden = anyFingerprintIcon;
         if (state != mLastState || mDeviceInteractive != mLastDeviceInteractive
                 || mScreenOn != mLastScreenOn || force) {
             int iconAnimRes =
@@ -142,16 +111,10 @@
             boolean isAnim = iconAnimRes != -1;
             if (iconAnimRes == R.drawable.lockscreen_fingerprint_draw_off_animation) {
                 anyFingerprintIcon = true;
-                useAdditionalPadding = true;
-                trustHidden = true;
             } else if (iconAnimRes == R.drawable.trusted_state_to_error_animation) {
                 anyFingerprintIcon = true;
-                useAdditionalPadding = false;
-                trustHidden = true;
             } else if (iconAnimRes == R.drawable.error_to_trustedstate_animation) {
                 anyFingerprintIcon = true;
-                useAdditionalPadding = false;
-                trustHidden = false;
             }
 
             Drawable icon;
@@ -166,20 +129,6 @@
             final AnimatedVectorDrawable animation = icon instanceof AnimatedVectorDrawable
                     ? (AnimatedVectorDrawable) icon
                     : null;
-            int iconHeight = getResources().getDimensionPixelSize(
-                    R.dimen.keyguard_affordance_icon_height);
-            int iconWidth = getResources().getDimensionPixelSize(
-                    R.dimen.keyguard_affordance_icon_width);
-            if (!anyFingerprintIcon && (icon.getIntrinsicHeight() != iconHeight
-                    || icon.getIntrinsicWidth() != iconWidth)) {
-                icon = new IntrinsicSizeDrawable(icon, iconWidth, iconHeight);
-            }
-            setPaddingRelative(0, 0, 0, useAdditionalPadding
-                    ? getResources().getDimensionPixelSize(
-                    R.dimen.fingerprint_icon_additional_padding)
-                    : 0);
-            setRestingAlpha(
-                    anyFingerprintIcon ? 1f : KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT);
             setImageDrawable(icon, false);
             if (mHasFaceUnlockIcon) {
                 announceForAccessibility(getContext().getString(
@@ -204,9 +153,6 @@
             mLastScreenOn = mScreenOn;
         }
 
-        // Hide trust circle when fingerprint is running.
-        boolean trustManaged = mUnlockMethodCache.isTrustManaged() && !trustHidden;
-        mTrustDrawable.setTrustManaged(trustManaged);
         updateClickability();
     }
 
@@ -250,27 +196,21 @@
     private Drawable getIconForState(int state, boolean screenOn, boolean deviceInteractive) {
         int iconRes;
         switch (state) {
+            case STATE_FINGERPRINT:
             case STATE_LOCKED:
-                iconRes = R.drawable.ic_lock_24dp;
+                iconRes = R.drawable.ic_lock;
                 break;
             case STATE_LOCK_OPEN:
                 if (mUnlockMethodCache.isTrustManaged() && mUnlockMethodCache.isTrusted()
                     && mUserAvatarIcon != null) {
                     return mUserAvatarIcon;
                 } else {
-                    iconRes = R.drawable.ic_lock_open_24dp;
+                    iconRes = R.drawable.ic_lock_open;
                 }
                 break;
             case STATE_FACE_UNLOCK:
                 iconRes = R.drawable.ic_face_unlock;
                 break;
-            case STATE_FINGERPRINT:
-                // If screen is off and device asleep, use the draw on animation so the first frame
-                // gets drawn.
-                iconRes = screenOn && deviceInteractive
-                        ? R.drawable.ic_fingerprint
-                        : R.drawable.lockscreen_fingerprint_draw_on_animation;
-                break;
             case STATE_FINGERPRINT_ERROR:
                 iconRes = R.drawable.ic_fingerprint_error;
                 break;
@@ -319,29 +259,4 @@
             return STATE_LOCKED;
         }
     }
-
-    /**
-     * A wrapper around another Drawable that overrides the intrinsic size.
-     */
-    private static class IntrinsicSizeDrawable extends InsetDrawable {
-
-        private final int mIntrinsicWidth;
-        private final int mIntrinsicHeight;
-
-        public IntrinsicSizeDrawable(Drawable drawable, int intrinsicWidth, int intrinsicHeight) {
-            super(drawable, 0);
-            mIntrinsicWidth = intrinsicWidth;
-            mIntrinsicHeight = intrinsicHeight;
-        }
-
-        @Override
-        public int getIntrinsicWidth() {
-            return mIntrinsicWidth;
-        }
-
-        @Override
-        public int getIntrinsicHeight() {
-            return mIntrinsicHeight;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 3a4c218..6d53cd3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -124,8 +124,6 @@
     private static final int CAP_HEIGHT = 1456;
     private static final int FONT_HEIGHT = 2163;
 
-    private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
-
     static final String COUNTER_PANEL_OPEN = "panel_open";
     static final String COUNTER_PANEL_OPEN_QS = "panel_open_qs";
     private static final String COUNTER_PANEL_OPEN_PEEK = "panel_open_peek";
@@ -733,7 +731,11 @@
             mQsExpandImmediate = true;
             mNotificationStackScroller.setShouldShowShelfOnly(true);
         }
-        expand(true /* animate */);
+        if (isFullyCollapsed()){
+            expand(true /* animate */);
+        } else {
+            flingSettings(0 /* velocity */, FLING_EXPAND);
+        }
     }
 
     public void expandWithoutQs() {
@@ -1780,13 +1782,9 @@
             KeyguardAffordanceView lockIcon = mKeyguardBottomArea.getLockIcon();
             if (active && !mUnlockIconActive && mTracking) {
                 lockIcon.setImageAlpha(1.0f, true, 150, Interpolators.FAST_OUT_LINEAR_IN, null);
-                lockIcon.setImageScale(LOCK_ICON_ACTIVE_SCALE, true, 150,
-                        Interpolators.FAST_OUT_LINEAR_IN);
             } else if (!active && mUnlockIconActive && mTracking) {
                 lockIcon.setImageAlpha(lockIcon.getRestingAlpha(), true /* animate */,
                         150, Interpolators.FAST_OUT_LINEAR_IN, null);
-                lockIcon.setImageScale(1.0f, true, 150,
-                        Interpolators.FAST_OUT_LINEAR_IN);
             }
             mUnlockIconActive = active;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index c3b87af..90ed97f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2852,6 +2852,7 @@
                 }
             }
             else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
+                mStatusBarWindowController.setNotTouchable(false);
                 finishBarAnimations();
                 resetUserExpandedStates();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
deleted file mode 100644
index aa60ec5..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TrustDrawable.java
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.systemui.statusbar.phone;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ValueAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.Paint;
-import android.graphics.PixelFormat;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.view.animation.Interpolator;
-
-import com.android.settingslib.Utils;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-
-public class TrustDrawable extends Drawable {
-
-    private static final long ENTERING_FROM_UNSET_START_DELAY = 200;
-    private static final long VISIBLE_DURATION = 1000;
-    private static final long EXIT_DURATION = 500;
-    private static final long ENTER_DURATION = 500;
-
-    private static final int ALPHA_VISIBLE_MIN = 0x26;
-    private static final int ALPHA_VISIBLE_MAX = 0x4c;
-
-    private static final int STATE_UNSET = -1;
-    private static final int STATE_GONE = 0;
-    private static final int STATE_ENTERING = 1;
-    private static final int STATE_VISIBLE = 2;
-    private static final int STATE_EXITING = 3;
-
-    private int mAlpha;
-    private boolean mAnimating;
-
-    private int mCurAlpha;
-    private float mCurInnerRadius;
-    private Animator mCurAnimator;
-    private int mState = STATE_UNSET;
-    private Paint mPaint;
-    private boolean mTrustManaged;
-
-    private final float mInnerRadiusVisibleMin;
-    private final float mInnerRadiusVisibleMax;
-    private final float mInnerRadiusExit;
-    private final float mInnerRadiusEnter;
-    private final float mThickness;
-
-    private final Animator mVisibleAnimator;
-
-    public TrustDrawable(Context context) {
-        Resources r = context.getResources();
-        mInnerRadiusVisibleMin = r.getDimension(R.dimen.trust_circle_inner_radius_visible_min);
-        mInnerRadiusVisibleMax = r.getDimension(R.dimen.trust_circle_inner_radius_visible_max);
-        mInnerRadiusExit = r.getDimension(R.dimen.trust_circle_inner_radius_exit);
-        mInnerRadiusEnter = r.getDimension(R.dimen.trust_circle_inner_radius_enter);
-        mThickness = r.getDimension(R.dimen.trust_circle_thickness);
-
-        mCurInnerRadius = mInnerRadiusEnter;
-
-        mVisibleAnimator = makeVisibleAnimator();
-
-        mPaint = new Paint();
-        mPaint.setStyle(Paint.Style.STROKE);
-        mPaint.setColor(Utils.getColorAttrDefaultColor(context, R.attr.wallpaperTextColor));
-        mPaint.setAntiAlias(true);
-        mPaint.setStrokeWidth(mThickness);
-    }
-
-    @Override
-    public void draw(Canvas canvas) {
-        int newAlpha = (mCurAlpha * mAlpha) / 256;
-        if (newAlpha == 0) {
-            return;
-        }
-        final Rect r = getBounds();
-        mPaint.setAlpha(newAlpha);
-        canvas.drawCircle(r.exactCenterX(), r.exactCenterY(), mCurInnerRadius, mPaint);
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        mAlpha = alpha;
-    }
-
-    @Override
-    public int getAlpha() {
-        return mAlpha;
-    }
-
-    @Override
-    public void setColorFilter(ColorFilter colorFilter) {
-        throw new UnsupportedOperationException("not implemented");
-    }
-
-    @Override
-    public int getOpacity() {
-        return PixelFormat.TRANSLUCENT;
-    }
-
-    public void start() {
-        if (!mAnimating) {
-            mAnimating = true;
-            updateState(true);
-            invalidateSelf();
-        }
-    }
-
-    public void stop() {
-        if (mAnimating) {
-            mAnimating = false;
-            if (mCurAnimator != null) {
-                mCurAnimator.cancel();
-                mCurAnimator = null;
-            }
-            mState = STATE_UNSET;
-            mCurAlpha = 0;
-            mCurInnerRadius = mInnerRadiusEnter;
-            invalidateSelf();
-        }
-    }
-
-    public void setTrustManaged(boolean trustManaged) {
-        if (trustManaged == mTrustManaged && mState != STATE_UNSET) return;
-        mTrustManaged = trustManaged;
-        updateState(true);
-    }
-
-    private void updateState(boolean allowTransientState) {
-        if (!mAnimating) {
-            return;
-        }
-
-        int nextState = mState;
-        if (mState == STATE_UNSET) {
-            nextState = mTrustManaged ? STATE_ENTERING : STATE_GONE;
-        } else if (mState == STATE_GONE) {
-            if (mTrustManaged) nextState = STATE_ENTERING;
-        } else if (mState == STATE_ENTERING) {
-            if (!mTrustManaged) nextState = STATE_EXITING;
-        } else if (mState == STATE_VISIBLE) {
-            if (!mTrustManaged) nextState = STATE_EXITING;
-        } else if (mState == STATE_EXITING) {
-            if (mTrustManaged) nextState = STATE_ENTERING;
-        }
-        if (!allowTransientState) {
-            if (nextState == STATE_ENTERING) nextState = STATE_VISIBLE;
-            if (nextState == STATE_EXITING) nextState = STATE_GONE;
-        }
-
-        if (nextState != mState) {
-            if (mCurAnimator != null) {
-                mCurAnimator.cancel();
-                mCurAnimator = null;
-            }
-
-            if (nextState == STATE_GONE) {
-                mCurAlpha = 0;
-                mCurInnerRadius = mInnerRadiusEnter;
-            } else if (nextState == STATE_ENTERING) {
-                mCurAnimator = makeEnterAnimator(mCurInnerRadius, mCurAlpha);
-                if (mState == STATE_UNSET) {
-                    mCurAnimator.setStartDelay(ENTERING_FROM_UNSET_START_DELAY);
-                }
-            } else if (nextState == STATE_VISIBLE) {
-                mCurAlpha = ALPHA_VISIBLE_MAX;
-                mCurInnerRadius = mInnerRadiusVisibleMax;
-                mCurAnimator = mVisibleAnimator;
-            } else if (nextState == STATE_EXITING) {
-                mCurAnimator = makeExitAnimator(mCurInnerRadius, mCurAlpha);
-            }
-
-            mState = nextState;
-            if (mCurAnimator != null) {
-                mCurAnimator.start();
-            }
-            invalidateSelf();
-        }
-    }
-
-    private Animator makeVisibleAnimator() {
-        return makeAnimators(mInnerRadiusVisibleMax, mInnerRadiusVisibleMin,
-                ALPHA_VISIBLE_MAX, ALPHA_VISIBLE_MIN, VISIBLE_DURATION,
-                Interpolators.ACCELERATE_DECELERATE,
-                true /* repeating */, false /* stateUpdateListener */);
-    }
-
-    private Animator makeEnterAnimator(float radius, int alpha) {
-        return makeAnimators(radius, mInnerRadiusVisibleMax,
-                alpha, ALPHA_VISIBLE_MAX, ENTER_DURATION, Interpolators.LINEAR_OUT_SLOW_IN,
-                false /* repeating */, true /* stateUpdateListener */);
-    }
-
-    private Animator makeExitAnimator(float radius, int alpha) {
-        return makeAnimators(radius, mInnerRadiusExit,
-                alpha, 0, EXIT_DURATION, Interpolators.FAST_OUT_SLOW_IN,
-                false /* repeating */, true /* stateUpdateListener */);
-    }
-
-    private Animator makeAnimators(float startRadius, float endRadius,
-            int startAlpha, int endAlpha, long duration, Interpolator interpolator,
-            boolean repeating, boolean stateUpdateListener) {
-        ValueAnimator alphaAnimator = configureAnimator(
-                ValueAnimator.ofInt(startAlpha, endAlpha),
-                duration, mAlphaUpdateListener, interpolator, repeating);
-        ValueAnimator sizeAnimator = configureAnimator(
-                ValueAnimator.ofFloat(startRadius, endRadius),
-                duration, mRadiusUpdateListener, interpolator, repeating);
-
-        AnimatorSet set = new AnimatorSet();
-        set.playTogether(alphaAnimator, sizeAnimator);
-        if (stateUpdateListener) {
-            set.addListener(new StateUpdateAnimatorListener());
-        }
-        return set;
-    }
-
-    private ValueAnimator configureAnimator(ValueAnimator animator, long duration,
-            ValueAnimator.AnimatorUpdateListener updateListener, Interpolator interpolator,
-            boolean repeating) {
-        animator.setDuration(duration);
-        animator.addUpdateListener(updateListener);
-        animator.setInterpolator(interpolator);
-        if (repeating) {
-            animator.setRepeatCount(ValueAnimator.INFINITE);
-            animator.setRepeatMode(ValueAnimator.REVERSE);
-        }
-        return animator;
-    }
-
-    private final ValueAnimator.AnimatorUpdateListener mAlphaUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            mCurAlpha = (int) animation.getAnimatedValue();
-            invalidateSelf();
-        }
-    };
-
-    private final ValueAnimator.AnimatorUpdateListener mRadiusUpdateListener =
-            new ValueAnimator.AnimatorUpdateListener() {
-        @Override
-        public void onAnimationUpdate(ValueAnimator animation) {
-            mCurInnerRadius = (float) animation.getAnimatedValue();
-            invalidateSelf();
-        }
-    };
-
-    private class StateUpdateAnimatorListener extends AnimatorListenerAdapter {
-        boolean mCancelled;
-
-        @Override
-        public void onAnimationStart(Animator animation) {
-            mCancelled = false;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            mCancelled = true;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animation) {
-            if (!mCancelled) {
-                updateState(false);
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 12b6f9d..298a93e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.policy;
 
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
 
@@ -33,6 +34,7 @@
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.TypedValue;
+import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
@@ -307,6 +309,14 @@
                 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
                 flags | KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                 InputDevice.SOURCE_KEYBOARD);
+        //Make KeyEvent work on multi-display environment
+        if (getDisplay() != null) {
+            final int displayId = getDisplay().getDisplayId();
+
+            if (displayId != INVALID_DISPLAY) {
+                ev.setDisplayId(displayId);
+            }
+        }
         InputManager.getInstance().injectInputEvent(ev,
                 InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index ef51bf0..8d2552f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -55,6 +55,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
+    private final Object mCallbacksLock = new Object();
     private final Context mContext;
     private final GlobalSetting mModeSetting;
     private final GlobalSetting mConfigSetting;
@@ -114,12 +115,16 @@
 
     @Override
     public void addCallback(Callback callback) {
-        mCallbacks.add(callback);
+        synchronized (mCallbacksLock) {
+            mCallbacks.add(callback);
+        }
     }
 
     @Override
     public void removeCallback(Callback callback) {
-        mCallbacks.remove(callback);
+        synchronized (mCallbacksLock) {
+            mCallbacks.remove(callback);
+        }
     }
 
     @Override
@@ -183,28 +188,40 @@
     }
 
     private void fireNextAlarmChanged() {
-        Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged());
+        }
     }
 
     private void fireEffectsSuppressorChanged() {
-        Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged());
+        }
     }
 
     private void fireZenChanged(int zen) {
-        Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen));
+        }
     }
 
     private void fireZenAvailableChanged(boolean available) {
-        Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available));
+        }
     }
 
     private void fireManualRuleChanged(ZenRule rule) {
-        Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule));
+        }
     }
 
     @VisibleForTesting
     protected void fireConfigChanged(ZenModeConfig config) {
-        Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+        synchronized (mCallbacksLock) {
+            Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config));
+        }
     }
 
     @VisibleForTesting
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index cc96917..b84f85b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -16,6 +16,7 @@
 
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 
+import  static com.android.systemui.ScreenDecorations.rectsToRegion;
 import static com.android.systemui.tuner.TunablePadding.FLAG_END;
 import static com.android.systemui.tuner.TunablePadding.FLAG_START;
 
@@ -35,6 +36,7 @@
 
 import android.app.Fragment;
 import android.content.res.Configuration;
+import android.graphics.Rect;
 import android.os.Handler;
 import android.support.test.filters.SmallTest;
 import android.testing.AndroidTestingRunner;
@@ -58,6 +60,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Collections;
+
 @RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
@@ -240,4 +244,11 @@
         mScreenDecorations.onConfigurationChanged(null);
         assertEquals(mScreenDecorations.mRoundedDefault, 5);
     }
+
+    @Test
+    public void testBoundingRectsToRegion() throws Exception {
+        Rect rect = new Rect(1, 2, 3, 4);
+        assertThat(rectsToRegion(Collections.singletonList(rect)).getBounds(), is(rect));
+    }
+
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
index bb67d6e..45342d4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeConfigurationTest.java
@@ -47,10 +47,10 @@
             return;
         }
 
-        Settings.Secure.putString(mContext.getContentResolver(), Settings.Secure.DOZE_ALWAYS_ON,
-                null);
+        Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                Settings.Secure.DOZE_ALWAYS_ON, null, UserHandle.USER_CURRENT);
         boolean defaultValue = mContext.getResources()
                 .getBoolean(com.android.internal.R.bool.config_dozeAlwaysOnEnabled);
-        assertEquals(mDozeConfig.alwaysOnEnabled(UserHandle.USER_CURRENT), defaultValue);
+        assertEquals(defaultValue, mDozeConfig.alwaysOnEnabled(UserHandle.USER_CURRENT));
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
index a9d49f9..b44630a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java
@@ -65,10 +65,12 @@
     private static final long ONE_HOUR_MILLIS = Duration.ofHours(1).toMillis();
     public static final int BELOW_WARNING_BUCKET = -1;
     public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2);
+    public static final long BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(30);
     public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4);
     private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis();
     private static final int OLD_BATTERY_LEVEL_NINE = 9;
     private static final int OLD_BATTERY_LEVEL_10 = 10;
+    private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15);
     private HardwarePropertiesManager mHardProps;
     private WarningsUI mMockWarnings;
     private PowerUI mPowerUI;
@@ -467,6 +469,35 @@
     }
 
     @Test
+    public void testSevereWarning_countsAsLowAndSevere_WarningOnlyShownOnce() {
+        mPowerUI.start();
+        when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true);
+        when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS);
+        when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS);
+        when(mEnhancedEstimates.getEstimate())
+                .thenReturn(new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true));
+        mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD;
+
+        // reduce battery level to handle time based trigger -> level trigger interactions
+        mPowerUI.mBatteryLevel = 5;
+        boolean shouldShow =
+                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+                        ABOVE_WARNING_BUCKET, BELOW_SEVERE_HYBRID_THRESHOLD,
+                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+        assertTrue(shouldShow);
+
+        // actually run the end to end since it handles changing the internal state.
+        mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED,
+                ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET);
+
+        shouldShow =
+                mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET,
+                        ABOVE_WARNING_BUCKET, VERY_BELOW_SEVERE_HYBRID_THRESHOLD,
+                        POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD);
+        assertFalse(shouldShow);
+    }
+
+    @Test
     public void testMaybeShowBatteryWarning_onlyQueriesEstimateOnBatteryLevelChangeOrNull() {
         mPowerUI.start();
         Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index b545e61..f8b2436 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -15,6 +15,8 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,6 +38,7 @@
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
 
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.R;
@@ -314,6 +317,36 @@
         verify(mStackScroller).setEmptyShadeView(any());
     }
 
+    @Test
+    @UiThreadTest
+    public void testSetIsBeingDraggedResetsExposedMenu() {
+        NotificationSwipeHelper swipeActionHelper =
+                (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+        swipeActionHelper.setExposedMenuView(new View(mContext));
+        mStackScroller.setIsBeingDragged(true);
+        assertNull(swipeActionHelper.getExposedMenuView());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testPanelTrackingStartResetsExposedMenu() {
+        NotificationSwipeHelper swipeActionHelper =
+                (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+        swipeActionHelper.setExposedMenuView(new View(mContext));
+        mStackScroller.onPanelTrackingStarted();
+        assertNull(swipeActionHelper.getExposedMenuView());
+    }
+
+    @Test
+    @UiThreadTest
+    public void testDarkModeResetsExposedMenu() {
+        NotificationSwipeHelper swipeActionHelper =
+                (NotificationSwipeHelper) mStackScroller.getSwipeActionHelper();
+        swipeActionHelper.setExposedMenuView(new View(mContext));
+        mStackScroller.setDarkAmount(0.1f, 0.1f);
+        assertNull(swipeActionHelper.getExposedMenuView());
+    }
+
     private void setBarStateForTest(int state) {
         ArgumentCaptor<StatusBarStateController.StateListener> captor =
                 ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 9bee8db..44ef8b6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -379,25 +379,20 @@
 
             @Override
             public void onPackageUpdateFinished(String packageName, int uid) {
-                // Unbind all services from this package, and then update the user state to
-                // re-bind new versions of them.
+                // The package should already be removed from mBoundServices, and added into
+                // mBindingServices in binderDied() during updating. Remove services from  this
+                // package from mBindingServices, and then update the user state to re-bind new
+                // versions of them.
                 synchronized (mLock) {
                     final int userId = getChangingUserId();
                     if (userId != mCurrentUserId) {
                         return;
                     }
                     UserState userState = getUserStateLocked(userId);
-                    boolean unboundAService = false;
-                    for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
-                        AccessibilityServiceConnection boundService =
-                                userState.mBoundServices.get(i);
-                        String servicePkg = boundService.mComponentName.getPackageName();
-                        if (servicePkg.equals(packageName)) {
-                            boundService.unbindLocked();
-                            unboundAService = true;
-                        }
-                    }
-                    if (unboundAService) {
+                    boolean reboundAService = userState.mBindingServices.removeIf(
+                            component -> component != null
+                                    && component.getPackageName().equals(packageName));
+                    if (reboundAService) {
                         onUserStateChangedLocked(userState);
                     }
                 }
@@ -419,6 +414,7 @@
                         String compPkg = comp.getPackageName();
                         if (compPkg.equals(packageName)) {
                             it.remove();
+                            userState.mBindingServices.remove(comp);
                             // Update the enabled services setting.
                             persistComponentNamesToSettingLocked(
                                     Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -457,6 +453,7 @@
                                     return true;
                                 }
                                 it.remove();
+                                userState.mBindingServices.remove(comp);
                                 persistComponentNamesToSettingLocked(
                                         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                                         userState.mEnabledServices, userId);
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e0eb269..9d84f57 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -253,11 +253,11 @@
                 return;
             }
             mWasConnectedAndDied = true;
-            mSystemSupport.getKeyEventDispatcher().flush(this);
             UserState userState = mUserStateWeakReference.get();
             if (userState != null) {
                 userState.serviceDisconnectedLocked(this);
             }
+            resetLocked();
             mSystemSupport.getMagnificationController().resetIfNeeded(mId);
             mSystemSupport.onClientChange(false);
         }
diff --git a/services/art-profile b/services/art-profile
index 3c60eee..328f8f7 100644
--- a/services/art-profile
+++ b/services/art-profile
@@ -2254,8 +2254,8 @@
 HPLcom/android/server/wm/DisplayContent;->lambda$new$8(Lcom/android/server/wm/DisplayContent;Lcom/android/server/wm/WindowState;)V
 HPLcom/android/server/wm/DisplayContent;->prepareSurfaces()V
 HPLcom/android/server/wm/DisplayContent;->resetAnimationBackgroundAnimator()V
-HPLcom/android/server/wm/DisplayContent;->setTouchExcludeRegion(Lcom/android/server/wm/Task;)V
 HPLcom/android/server/wm/DisplayContent;->skipTraverseChild(Lcom/android/server/wm/WindowContainer;)Z
+HPLcom/android/server/wm/DisplayContent;->updateTouchExcludeRegion()V
 HPLcom/android/server/wm/DockedStackDividerController;->isResizing()Z
 HPLcom/android/server/wm/DragDropController;->dragDropActiveLocked()Z
 HPLcom/android/server/wm/InputMonitor$UpdateInputForAllWindowsConsumer;->accept(Lcom/android/server/wm/WindowState;)V
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index cf323fb..c1b620c 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -400,7 +400,9 @@
     @Nullable
     private AutofillValue findValueLocked(@NonNull AutofillId autofillId) {
         final AutofillValue value = findValueFromThisSessionOnlyLocked(autofillId);
-        if (value != null) return value;
+        if (value != null) {
+            return getSanitizedValue(createSanitizers(getSaveInfoLocked()), autofillId, value);
+        }
 
         // TODO(b/113281366): rather than explicitly look for previous session, it might be better
         // to merge the sessions when created (see note on mergePreviousSessionLocked())
@@ -415,7 +417,8 @@
                 final AutofillValue previousValue = previousSession
                         .findValueFromThisSessionOnlyLocked(autofillId);
                 if (previousValue != null) {
-                    return previousValue;
+                    return getSanitizedValue(createSanitizers(previousSession.getSaveInfoLocked()),
+                            autofillId, previousValue);
                 }
             }
         }
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
new file mode 100644
index 0000000..df36c94
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunk;
+
+import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.CHUNK_ORDERING_TYPE_UNSPECIFIED;
+import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.EXPLICIT_STARTS;
+import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.INLINE_LENGTHS;
+
+import android.annotation.IntDef;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/** IntDef corresponding to the ChunkOrderingType enum in the ChunksMetadataProto protobuf. */
+@IntDef({CHUNK_ORDERING_TYPE_UNSPECIFIED, EXPLICIT_STARTS, INLINE_LENGTHS})
+@Retention(RetentionPolicy.SOURCE)
+public @interface ChunkOrderingType {}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java b/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java
new file mode 100644
index 0000000..68d9d14
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import java.io.IOException;
+
+/** Writes backup data either as a diff script or as raw data, determined by the implementation. */
+public interface BackupWriter {
+    /** Writes the given bytes to the output. */
+    void writeBytes(byte[] bytes) throws IOException;
+
+    /**
+     * Writes an existing chunk from the previous backup to the output.
+     *
+     * <p>Note: not all implementations support this method.
+     */
+    void writeChunk(long start, int length) throws IOException;
+
+    /** Returns the number of bytes written, included bytes copied from the old file. */
+    long getBytesWritten();
+
+    /**
+     * Indicates that no more bytes or chunks will be written.
+     *
+     * <p>After calling this, you may not call {@link #writeBytes(byte[])} or {@link
+     * #writeChunk(long, int)}
+     */
+    void flush() throws IOException;
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java
new file mode 100644
index 0000000..1f936eb
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import com.android.internal.util.Preconditions;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * A chunk of a file encrypted using AES/GCM.
+ *
+ * <p>TODO(b/116575321): After all code is ported, remove the factory method and rename
+ * encryptedBytes(), key() and nonce().
+ */
+public class EncryptedChunk {
+    public static final int KEY_LENGTH_BYTES = ChunkHash.HASH_LENGTH_BYTES;
+    public static final int NONCE_LENGTH_BYTES = 12;
+
+    /**
+     * Constructs a new instance with the given key, nonce, and encrypted bytes.
+     *
+     * @param key SHA-256 Hmac of the chunk plaintext.
+     * @param nonce Nonce with which the bytes of the chunk were encrypted.
+     * @param encryptedBytes Encrypted bytes of the chunk.
+     */
+    public static EncryptedChunk create(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
+        Preconditions.checkArgument(
+                nonce.length == NONCE_LENGTH_BYTES, "Nonce does not have the correct length.");
+        return new EncryptedChunk(key, nonce, encryptedBytes);
+    }
+
+    private ChunkHash mKey;
+    private byte[] mNonce;
+    private byte[] mEncryptedBytes;
+
+    private EncryptedChunk(ChunkHash key, byte[] nonce, byte[] encryptedBytes) {
+        mKey = key;
+        mNonce = nonce;
+        mEncryptedBytes = encryptedBytes;
+    }
+
+    /** The SHA-256 Hmac of the plaintext bytes of the chunk. */
+    public ChunkHash key() {
+        return mKey;
+    }
+
+    /** The nonce with which the chunk was encrypted. */
+    public byte[] nonce() {
+        return mNonce;
+    }
+
+    /** The encrypted bytes of the chunk. */
+    public byte[] encryptedBytes() {
+        return mEncryptedBytes;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof EncryptedChunk)) {
+            return false;
+        }
+
+        EncryptedChunk encryptedChunkOrdering = (EncryptedChunk) o;
+        return Arrays.equals(mEncryptedBytes, encryptedChunkOrdering.mEncryptedBytes)
+                && Arrays.equals(mNonce, encryptedChunkOrdering.mNonce)
+                && mKey.equals(encryptedChunkOrdering.mKey);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mKey, Arrays.hashCode(mNonce), Arrays.hashCode(mEncryptedBytes));
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
new file mode 100644
index 0000000..eaf701c
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkOrderingType;
+import java.io.IOException;
+
+/** Encodes an {@link EncryptedChunk} as bytes to write to the encrypted backup file. */
+public interface EncryptedChunkEncoder {
+    /**
+     * Encodes the given chunk and asks the writer to write it.
+     *
+     * <p>The chunk will be encoded in the format [nonce]+[encrypted data].
+     *
+     * <p>TODO(b/116575321): Choose a more descriptive method name after the code move is done.
+     */
+    void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException;
+
+    /**
+     * Returns the length in bytes that this chunk would be if encoded with {@link
+     * #writeChunkToWriter}.
+     */
+    int getEncodedLengthOfChunk(EncryptedChunk chunk);
+
+    /**
+     * Returns the {@link ChunkOrderingType} that must be included in the backup file, when using
+     * this decoder, so that the file may be correctly decoded.
+     */
+    @ChunkOrderingType
+    int getChunkOrderingType();
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java b/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
new file mode 100644
index 0000000..5c902ca
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkOrderingType;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import java.io.IOException;
+
+/**
+ * Encodes an {@link EncryptedChunk} as bytes, prepending the length of the chunk.
+ *
+ * <p>This allows us to decode the backup file during restore without any extra information about
+ * the boundaries of the chunks. The backup file should contain a chunk ordering in mode {@link
+ * ChunksMetadataProto#INLINE_LENGTHS}.
+ *
+ * <p>We use this implementation during key value backup.
+ */
+public class InlineLengthsEncryptedChunkEncoder implements EncryptedChunkEncoder {
+    public static final int BYTES_LENGTH = Integer.SIZE / Byte.SIZE;
+
+    private final LengthlessEncryptedChunkEncoder mLengthlessEncryptedChunkEncoder =
+            new LengthlessEncryptedChunkEncoder();
+
+    @Override
+    public void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException {
+        int length = mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
+        writer.writeBytes(toByteArray(length));
+        mLengthlessEncryptedChunkEncoder.writeChunkToWriter(writer, chunk);
+    }
+
+    @Override
+    public int getEncodedLengthOfChunk(EncryptedChunk chunk) {
+        return BYTES_LENGTH + mLengthlessEncryptedChunkEncoder.getEncodedLengthOfChunk(chunk);
+    }
+
+    @Override
+    @ChunkOrderingType
+    public int getChunkOrderingType() {
+        return ChunksMetadataProto.INLINE_LENGTHS;
+    }
+
+    /**
+     * Returns a big-endian representation of {@code value} in a 4-element byte array; equivalent to
+     * {@code ByteBuffer.allocate(4).putInt(value).array()}. For example, the input value {@code
+     * 0x12131415} would yield the byte array {@code {0x12, 0x13, 0x14, 0x15}}.
+     *
+     * <p>Equivalent to guava's Ints.toByteArray.
+     */
+    static byte[] toByteArray(int value) {
+        return new byte[] {
+            (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value
+        };
+    }
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java b/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
new file mode 100644
index 0000000..4b84981
--- /dev/null
+++ b/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import com.android.server.backup.encryption.chunk.ChunkOrderingType;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import java.io.IOException;
+
+/**
+ * Encodes an {@link EncryptedChunk} as bytes without including any information about the length of
+ * the chunk.
+ *
+ * <p>In order for us to decode the backup file during restore it must include a chunk ordering in
+ * mode {@link ChunksMetadataProto#EXPLICIT_STARTS}, which contains the boundaries of the chunks in
+ * the encrypted file. This information allows us to decode the backup file and divide it into
+ * chunks without including the length of each chunk inline.
+ *
+ * <p>We use this implementation during full backup.
+ */
+public class LengthlessEncryptedChunkEncoder implements EncryptedChunkEncoder {
+    @Override
+    public void writeChunkToWriter(BackupWriter writer, EncryptedChunk chunk) throws IOException {
+        writer.writeBytes(chunk.nonce());
+        writer.writeBytes(chunk.encryptedBytes());
+    }
+
+    @Override
+    public int getEncodedLengthOfChunk(EncryptedChunk chunk) {
+        return chunk.nonce().length + chunk.encryptedBytes().length;
+    }
+
+    @Override
+    @ChunkOrderingType
+    public int getChunkOrderingType() {
+        return ChunksMetadataProto.EXPLICIT_STARTS;
+    }
+}
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index ad2f82c..af33cbc 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -142,6 +142,8 @@
     static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
     static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
+    static final int TICK_HISTORY_DEPTH = 10;
+
     // Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays
     static final int ACTIVE_INDEX = 0;
     static final int WORKING_INDEX = 1;
@@ -176,21 +178,25 @@
     private long mNextNonWakeUpSetAt;
     private long mLastWakeup;
     private long mLastTrigger;
+
     private long mLastTickSet;
-    private long mLastTickIssued; // elapsed
     private long mLastTickReceived;
     private long mLastTickAdded;
     private long mLastTickRemoved;
+    // ring buffer of recent TIME_TICK issuance, in the elapsed timebase
+    private final long[] mTickHistory = new long[TICK_HISTORY_DEPTH];
+    private int mNextTickHistory;
+
     private final Injector mInjector;
     int mBroadcastRefCount = 0;
     PowerManager.WakeLock mWakeLock;
-    boolean mLastWakeLockUnimportantForLogging;
     ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>();
     ArrayList<InFlight> mInFlight = new ArrayList<>();
     AlarmHandler mHandler;
     ClockReceiver mClockReceiver;
     final DeliveryTracker mDeliveryTracker = new DeliveryTracker();
-    PendingIntent mTimeTickSender;
+    Intent mTimeTickIntent;
+    IAlarmListener mTimeTickTrigger;
     PendingIntent mDateChangeSender;
     Random mRandom;
     boolean mInteractive = true;
@@ -509,7 +515,7 @@
             end = clampPositive(seed.maxWhenElapsed);
             flags = seed.flags;
             alarms.add(seed);
-            if (seed.operation == mTimeTickSender) {
+            if (seed.listener == mTimeTickTrigger) {
                 mLastTickAdded = mInjector.getCurrentTimeMillis();
             }
         }
@@ -534,7 +540,7 @@
                 index = 0 - index - 1;
             }
             alarms.add(index, alarm);
-            if (alarm.operation == mTimeTickSender) {
+            if (alarm.listener == mTimeTickTrigger) {
                 mLastTickAdded = mInjector.getCurrentTimeMillis();
             }
             if (DEBUG_BATCH) {
@@ -572,7 +578,7 @@
                     if (alarm.alarmClock != null) {
                         mNextAlarmClockMayChange = true;
                     }
-                    if (alarm.operation == mTimeTickSender) {
+                    if (alarm.listener == mTimeTickTrigger) {
                         mLastTickRemoved = mInjector.getCurrentTimeMillis();
                     }
                 } else {
@@ -690,8 +696,7 @@
             Alarm a = alarms.get(i);
 
             final int alarmPrio;
-            if (a.operation != null
-                    && Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) {
+            if (a.listener == mTimeTickTrigger) {
                 alarmPrio = PRIO_TICK;
             } else if (a.wakeup) {
                 alarmPrio = PRIO_WAKEUP;
@@ -823,7 +828,7 @@
         }
         final int batchSize = alarms.size();
         for (int j = 0; j < batchSize; j++) {
-            if (alarms.get(j).operation == mTimeTickSender) {
+            if (alarms.get(j).listener == mTimeTickTrigger) {
                 return true;
             }
         }
@@ -1111,10 +1116,7 @@
         updateNextAlarmClockLocked();
 
         // And send a TIME_TICK right now, since it is important to get the UI updated.
-        try {
-            mTimeTickSender.send();
-        } catch (PendingIntent.CanceledException e) {
-        }
+        mHandler.post(() ->  getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL));
     }
 
     static final class InFlight {
@@ -1312,12 +1314,36 @@
             }
             mWakeLock = mInjector.getAlarmWakeLock();
 
-            mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0,
-                    new Intent(Intent.ACTION_TIME_TICK).addFlags(
-                            Intent.FLAG_RECEIVER_REGISTERED_ONLY
-                                    | Intent.FLAG_RECEIVER_FOREGROUND
-                                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0,
-                    UserHandle.ALL);
+            mTimeTickIntent = new Intent(Intent.ACTION_TIME_TICK).addFlags(
+                    Intent.FLAG_RECEIVER_REGISTERED_ONLY
+                    | Intent.FLAG_RECEIVER_FOREGROUND
+                    | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+
+            mTimeTickTrigger = new IAlarmListener.Stub() {
+                @Override
+                public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException {
+                    if (DEBUG_BATCH) {
+                        Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
+                    }
+
+                    // Via handler because dispatch invokes this within its lock.  OnAlarmListener
+                    // takes care of this automatically, but we're using the direct internal
+                    // interface here rather than that client-side wrapper infrastructure.
+                    mHandler.post(() -> {
+                        getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL);
+
+                        try {
+                            callback.alarmComplete(this);
+                        } catch (RemoteException e) { /* local method call */ }
+                    });
+
+                    synchronized (mLock) {
+                        mLastTickReceived = mInjector.getCurrentTimeMillis();
+                    }
+                    mClockReceiver.scheduleTimeTickEvent();
+                }
+            };
+
             Intent intent = new Intent(Intent.ACTION_DATE_CHANGED);
             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                     | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
@@ -1438,12 +1464,9 @@
         }
     }
 
-    void removeImpl(PendingIntent operation) {
-        if (operation == null) {
-            return;
-        }
+    void removeImpl(PendingIntent operation, IAlarmListener listener) {
         synchronized (mLock) {
-            removeLocked(operation, null);
+            removeLocked(operation, listener);
         }
     }
 
@@ -1887,9 +1910,9 @@
             pw.println("  App Standby Parole: " + mAppStandbyParole);
             pw.println();
 
-            final long nowRTC = mInjector.getCurrentTimeMillis();
             final long nowELAPSED = mInjector.getElapsedRealtime();
             final long nowUPTIME = SystemClock.uptimeMillis();
+            final long nowRTC = mInjector.getCurrentTimeMillis();
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
 
             pw.print("  nowRTC="); pw.print(nowRTC);
@@ -1899,13 +1922,27 @@
             pw.print("  mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime);
             pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime)));
             pw.print("  mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime);
-            pw.print("  mLastTickIssued=");
-            pw.println(sdf.format(new Date(nowRTC - (nowELAPSED - mLastTickIssued))));
             pw.print("  mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived)));
             pw.print("  mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet)));
             pw.print("  mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded)));
             pw.print("  mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved)));
 
+            if (RECORD_ALARMS_IN_HISTORY) {
+                pw.println();
+                pw.println("  Recent TIME_TICK history:");
+                int i = mNextTickHistory;
+                do {
+                    i--;
+                    if (i < 0) i = TICK_HISTORY_DEPTH - 1;
+                    final long time = mTickHistory[i];
+                    pw.print("    ");
+                    pw.println((time > 0)
+                            ? sdf.format(new Date(nowRTC - (nowELAPSED - time)))
+                            : "-");
+                } while (i != mNextTickHistory);
+                pw.println();
+            }
+
             SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class);
             if (ssm != null) {
                 pw.println();
@@ -3640,8 +3677,8 @@
                         }
                         // StatsLog requires currentTimeMillis(), which == nowRTC to within usecs.
                         StatsLog.write(StatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC);
-                        removeImpl(mTimeTickSender);
-                        removeImpl(mDateChangeSender);
+                        removeImpl(null, mTimeTickTrigger);
+                        removeImpl(mDateChangeSender, null);
                         rebatchAllAlarms();
                         mClockReceiver.scheduleTimeTickEvent();
                         mClockReceiver.scheduleDateChangedEvent();
@@ -3764,14 +3801,8 @@
     void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, String tag,
             int knownUid, boolean first) {
         try {
-            final boolean unimportant = pi == mTimeTickSender;
-            mWakeLock.setUnimportantForLogging(unimportant);
-            if (first || mLastWakeLockUnimportantForLogging) {
-                mWakeLock.setHistoryTag(tag);
-            } else {
-                mWakeLock.setHistoryTag(null);
-            }
-            mLastWakeLockUnimportantForLogging = unimportant;
+            mWakeLock.setHistoryTag(first ? tag : null);
+
             if (ws != null) {
                 mWakeLock.setWorkSource(ws);
                 return;
@@ -3828,7 +3859,7 @@
                             if (alarm.repeatInterval > 0) {
                                 // This IntentSender is no longer valid, but this
                                 // is a repeating alarm, so toss the hoser.
-                                removeImpl(alarm.operation);
+                                removeImpl(alarm.operation, null);
                             }
                         }
                     }
@@ -3886,22 +3917,13 @@
     class ClockReceiver extends BroadcastReceiver {
         public ClockReceiver() {
             IntentFilter filter = new IntentFilter();
-            filter.addAction(Intent.ACTION_TIME_TICK);
             filter.addAction(Intent.ACTION_DATE_CHANGED);
             getContext().registerReceiver(this, filter);
         }
 
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) {
-                if (DEBUG_BATCH) {
-                    Slog.v(TAG, "Received TIME_TICK alarm; rescheduling");
-                }
-                synchronized (mLock) {
-                    mLastTickReceived = mInjector.getCurrentTimeMillis();
-                }
-                scheduleTimeTickEvent();
-            } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
+            if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) {
                 // Since the kernel does not keep track of DST, we need to
                 // reset the TZ information at the beginning of each day
                 // based off of the current Zone gmt offset + userspace tracked
@@ -3923,7 +3945,7 @@
 
             final WorkSource workSource = null; // Let system take blame for time tick events.
             setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0,
-                    0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource,
+                    0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource,
                     null, Process.myUid(), "android");
 
             // Finally, remember when we set the tick alarm
@@ -4333,10 +4355,6 @@
                 // PendingIntent alarm
                 mSendCount++;
 
-                if (alarm.priorityClass.priority == PRIO_TICK) {
-                    mLastTickIssued = nowELAPSED;
-                }
-
                 try {
                     alarm.operation.send(getContext(), 0,
                             mBackgroundIntent.putExtra(
@@ -4344,13 +4362,10 @@
                                     mDeliveryTracker, mHandler, null,
                                     allowWhileIdle ? mIdleOptions : null);
                 } catch (PendingIntent.CanceledException e) {
-                    if (alarm.operation == mTimeTickSender) {
-                        Slog.wtf(TAG, "mTimeTickSender canceled");
-                    }
                     if (alarm.repeatInterval > 0) {
                         // This IntentSender is no longer valid, but this
                         // is a repeating alarm, so toss it
-                        removeImpl(alarm.operation);
+                        removeImpl(alarm.operation, null);
                     }
                     // No actual delivery was possible, so the delivery tracker's
                     // 'finished' callback won't be invoked.  We also don't need
@@ -4362,6 +4377,16 @@
             } else {
                 // Direct listener callback alarm
                 mListenerCount++;
+
+                if (RECORD_ALARMS_IN_HISTORY) {
+                    if (alarm.listener == mTimeTickTrigger) {
+                        mTickHistory[mNextTickHistory++] = nowELAPSED;
+                        if (mNextTickHistory >= TICK_HISTORY_DEPTH) {
+                            mNextTickHistory = 0;
+                        }
+                    }
+                }
+
                 try {
                     if (DEBUG_LISTENER_CALLBACK) {
                         Slog.v(TAG, "Alarm to uid=" + alarm.uid
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 953c99f..5e8ffb7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1837,7 +1837,7 @@
     }
 
     void systemReady() {
-        loadGlobalProxy();
+        mProxyTracker.loadGlobalProxy();
         registerNetdEventCallback();
 
         synchronized (this) {
@@ -3455,31 +3455,6 @@
         mProxyTracker.setGlobalProxy(proxyProperties);
     }
 
-    private void loadGlobalProxy() {
-        ContentResolver res = mContext.getContentResolver();
-        String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
-        int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
-        String exclList = Settings.Global.getString(res,
-                Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
-        String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
-        if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
-            ProxyInfo proxyProperties;
-            if (!TextUtils.isEmpty(pacFileUrl)) {
-                proxyProperties = new ProxyInfo(pacFileUrl);
-            } else {
-                proxyProperties = new ProxyInfo(host, port, exclList);
-            }
-            if (!proxyProperties.isValid()) {
-                if (DBG) log("Invalid proxy properties, ignoring: " + proxyProperties.toString());
-                return;
-            }
-
-            synchronized (mProxyTracker.mProxyLock) {
-                mProxyTracker.mGlobalProxy = proxyProperties;
-            }
-        }
-    }
-
     @Override
     @Nullable
     public ProxyInfo getGlobalProxy() {
diff --git a/services/core/java/com/android/server/DeviceIdleController.java b/services/core/java/com/android/server/DeviceIdleController.java
index a34c2b9..376bc0d 100644
--- a/services/core/java/com/android/server/DeviceIdleController.java
+++ b/services/core/java/com/android/server/DeviceIdleController.java
@@ -77,6 +77,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.AtomicFile;
 import com.android.internal.os.BackgroundThread;
@@ -104,6 +105,126 @@
 
 /**
  * Keeps track of device idleness and drives low power mode based on that.
+ *
+ * Test: atest com.android.server.DeviceIdleControllerTest
+ *
+ * Current idling state machine (as of Android 9 Pie). This can be visualized using Graphviz:
+
+   digraph {
+     subgraph deep {
+       label="deep";
+
+       STATE_ACTIVE [label="STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"]
+       STATE_INACTIVE [label="STATE_INACTIVE\nScreen off AND Not charging"]
+       STATE_IDLE_PENDING [
+         label="STATE_IDLE_PENDING\nSignificant motion monitoring turned on"
+       ]
+       STATE_SENSING [label="STATE_SENSING\nMonitoring for ANY motion"]
+       STATE_LOCATING [
+         label="STATE_LOCATING\nRequesting location, motion monitoring still on"
+       ]
+       STATE_IDLE [
+         label="STATE_IDLE\nLocation and motion detection turned off\n"
+             + "Significant motion monitoring still on"
+       ]
+       STATE_IDLE_MAINTENANCE [label="STATE_IDLE_MAINTENANCE\n"]
+
+       STATE_ACTIVE -> STATE_INACTIVE [label="becomeInactiveIfAppropriateLocked()"]
+
+       STATE_INACTIVE -> STATE_ACTIVE [
+         label="handleMotionDetectedLocked(), becomeActiveLocked()"
+       ]
+       STATE_INACTIVE -> STATE_IDLE_PENDING [label="stepIdleStateLocked()"]
+
+       STATE_IDLE_PENDING -> STATE_ACTIVE [
+         label="handleMotionDetectedLocked(), becomeActiveLocked()"
+       ]
+       STATE_IDLE_PENDING -> STATE_SENSING [label="stepIdleStateLocked()"]
+
+       STATE_SENSING -> STATE_ACTIVE [
+         label="handleMotionDetectedLocked(), becomeActiveLocked()"
+       ]
+       STATE_SENSING -> STATE_LOCATING [label="stepIdleStateLocked()"]
+       STATE_SENSING -> STATE_IDLE [
+         label="stepIdleStateLocked()\n"
+             + "No Location Manager OR (no Network provider AND no GPS provider)"
+       ]
+
+       STATE_LOCATING -> STATE_ACTIVE [
+         label="handleMotionDetectedLocked(), becomeActiveLocked()"
+       ]
+       STATE_LOCATING -> STATE_IDLE [label="stepIdleStateLocked()"]
+
+       STATE_IDLE -> STATE_ACTIVE [label="handleMotionDetectedLocked(), becomeActiveLocked()"]
+       STATE_IDLE -> STATE_IDLE_MAINTENANCE [label="stepIdleStateLocked()"]
+
+       STATE_IDLE_MAINTENANCE -> STATE_ACTIVE [
+         label="handleMotionDetectedLocked(), becomeActiveLocked()"
+       ]
+       STATE_IDLE_MAINTENANCE -> STATE_IDLE [
+         label="stepIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+       ]
+     }
+
+     subgraph light {
+       label="light"
+
+       LIGHT_STATE_ACTIVE [
+         label="LIGHT_STATE_ACTIVE\nScreen on OR Charging OR Alarm going off soon"
+       ]
+       LIGHT_STATE_INACTIVE [label="LIGHT_STATE_INACTIVE\nScreen off AND Not charging"]
+       LIGHT_STATE_PRE_IDLE [
+         label="LIGHT_STATE_PRE_IDLE\n"
+             + "Delay going into LIGHT_STATE_IDLE due to some running jobs or alarms"
+       ]
+       LIGHT_STATE_IDLE [label="LIGHT_STATE_IDLE\n"]
+       LIGHT_STATE_WAITING_FOR_NETWORK [
+         label="LIGHT_STATE_WAITING_FOR_NETWORK\n"
+             + "Coming out of LIGHT_STATE_IDLE, waiting for network"
+       ]
+       LIGHT_STATE_IDLE_MAINTENANCE [label="LIGHT_STATE_IDLE_MAINTENANCE\n"]
+       LIGHT_STATE_OVERRIDE [
+         label="LIGHT_STATE_OVERRIDE\nDevice in deep doze, light no longer changing states"
+       ]
+
+       LIGHT_STATE_ACTIVE -> LIGHT_STATE_INACTIVE [
+         label="becomeInactiveIfAppropriateLocked()"
+       ]
+       LIGHT_STATE_ACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+       LIGHT_STATE_INACTIVE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+       LIGHT_STATE_INACTIVE -> LIGHT_STATE_PRE_IDLE [label="active jobs"]
+       LIGHT_STATE_INACTIVE -> LIGHT_STATE_IDLE [label="no active jobs"]
+       LIGHT_STATE_INACTIVE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+       LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+       LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_IDLE [
+         label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+       ]
+       LIGHT_STATE_PRE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+       LIGHT_STATE_IDLE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+       LIGHT_STATE_IDLE -> LIGHT_STATE_WAITING_FOR_NETWORK [label="no network"]
+       LIGHT_STATE_IDLE -> LIGHT_STATE_IDLE_MAINTENANCE
+       LIGHT_STATE_IDLE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+       LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+       LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_IDLE_MAINTENANCE
+       LIGHT_STATE_WAITING_FOR_NETWORK -> LIGHT_STATE_OVERRIDE [
+         label="deep goes to STATE_IDLE"
+       ]
+
+       LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_ACTIVE [label="becomeActiveLocked()"]
+       LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_IDLE [
+         label="stepLightIdleStateLocked(), exitMaintenanceEarlyIfNeededLocked()"
+       ]
+       LIGHT_STATE_IDLE_MAINTENANCE -> LIGHT_STATE_OVERRIDE [label="deep goes to STATE_IDLE"]
+
+       LIGHT_STATE_OVERRIDE -> LIGHT_STATE_ACTIVE [
+         label="handleMotionDetectedLocked(), becomeActiveLocked()"
+       ]
+     }
+   }
  */
 public class DeviceIdleController extends SystemService
         implements AnyMotionDetector.DeviceIdleCallback {
@@ -148,21 +269,29 @@
     private boolean mScreenLocked;
 
     /** Device is currently active. */
-    private static final int STATE_ACTIVE = 0;
+    @VisibleForTesting
+    static final int STATE_ACTIVE = 0;
     /** Device is inactive (screen off, no motion) and we are waiting to for idle. */
-    private static final int STATE_INACTIVE = 1;
+    @VisibleForTesting
+    static final int STATE_INACTIVE = 1;
     /** Device is past the initial inactive period, and waiting for the next idle period. */
-    private static final int STATE_IDLE_PENDING = 2;
+    @VisibleForTesting
+    static final int STATE_IDLE_PENDING = 2;
     /** Device is currently sensing motion. */
-    private static final int STATE_SENSING = 3;
+    @VisibleForTesting
+    static final int STATE_SENSING = 3;
     /** Device is currently finding location (and may still be sensing). */
-    private static final int STATE_LOCATING = 4;
+    @VisibleForTesting
+    static final int STATE_LOCATING = 4;
     /** Device is in the idle state, trying to stay asleep as much as possible. */
-    private static final int STATE_IDLE = 5;
+    @VisibleForTesting
+    static final int STATE_IDLE = 5;
     /** Device is in the idle state, but temporarily out of idle to do regular maintenance. */
-    private static final int STATE_IDLE_MAINTENANCE = 6;
+    @VisibleForTesting
+    static final int STATE_IDLE_MAINTENANCE = 6;
 
-    private static String stateToString(int state) {
+    @VisibleForTesting
+    static String stateToString(int state) {
         switch (state) {
             case STATE_ACTIVE: return "ACTIVE";
             case STATE_INACTIVE: return "INACTIVE";
@@ -176,21 +305,30 @@
     }
 
     /** Device is currently active. */
-    private static final int LIGHT_STATE_ACTIVE = 0;
+    @VisibleForTesting
+    static final int LIGHT_STATE_ACTIVE = 0;
     /** Device is inactive (screen off) and we are waiting to for the first light idle. */
-    private static final int LIGHT_STATE_INACTIVE = 1;
+    @VisibleForTesting
+    static final int LIGHT_STATE_INACTIVE = 1;
     /** Device is about to go idle for the first time, wait for current work to complete. */
-    private static final int LIGHT_STATE_PRE_IDLE = 3;
+    @VisibleForTesting
+    static final int LIGHT_STATE_PRE_IDLE = 3;
     /** Device is in the light idle state, trying to stay asleep as much as possible. */
-    private static final int LIGHT_STATE_IDLE = 4;
+    @VisibleForTesting
+    static final int LIGHT_STATE_IDLE = 4;
     /** Device is in the light idle state, we want to go in to idle maintenance but are
      * waiting for network connectivity before doing so. */
-    private static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
+    @VisibleForTesting
+    static final int LIGHT_STATE_WAITING_FOR_NETWORK = 5;
     /** Device is in the light idle state, but temporarily out of idle to do regular maintenance. */
-    private static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
+    @VisibleForTesting
+    static final int LIGHT_STATE_IDLE_MAINTENANCE = 6;
     /** Device light idle state is overriden, now applying deep doze state. */
-    private static final int LIGHT_STATE_OVERRIDE = 7;
-    private static String lightStateToString(int state) {
+    @VisibleForTesting
+    static final int LIGHT_STATE_OVERRIDE = 7;
+
+    @VisibleForTesting
+    static String lightStateToString(int state) {
         switch (state) {
             case LIGHT_STATE_ACTIVE: return "ACTIVE";
             case LIGHT_STATE_INACTIVE: return "INACTIVE";
@@ -382,6 +520,8 @@
         public void onAlarm() {
             if (mState == STATE_SENSING) {
                 synchronized (DeviceIdleController.this) {
+                    // Restart the device idle progression in case the device moved but the screen
+                    // didn't turn on.
                     becomeInactiveIfAppropriateLocked();
                 }
             }
@@ -422,11 +562,16 @@
         }
     };
 
-    private final class MotionListener extends TriggerEventListener
+    @VisibleForTesting
+    final class MotionListener extends TriggerEventListener
             implements SensorEventListener {
 
         boolean active = false;
 
+        public boolean isActive() {
+            return active;
+        }
+
         @Override
         public void onTrigger(TriggerEvent event) {
             synchronized (DeviceIdleController.this) {
@@ -472,7 +617,7 @@
             active = false;
         }
     }
-    private final MotionListener mMotionListener = new MotionListener();
+    @VisibleForTesting final MotionListener mMotionListener = new MotionListener();
 
     private final LocationListener mGenericLocationListener = new LocationListener() {
         @Override
@@ -594,7 +739,7 @@
         public float LIGHT_IDLE_FACTOR;
 
         /**
-         * This is the maximum time we will run in idle maintenence mode.
+         * This is the maximum time we will run in idle maintenance mode.
          * @see Settings.Global#DEVICE_IDLE_CONSTANTS
          * @see #KEY_LIGHT_MAX_IDLE_TIMEOUT
          */
@@ -1360,6 +1505,45 @@
         }
     }
 
+    static class Injector {
+        private final Context mContext;
+
+        Injector(Context ctx) {
+            mContext = ctx;
+        }
+
+        AlarmManager getAlarmManager() {
+            return mContext.getSystemService(AlarmManager.class);
+        }
+
+        AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm,
+                AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) {
+            return new AnyMotionDetector(getPowerManager(), handler, sm, callback, angleThreshold);
+        }
+
+        AppStateTracker getAppStateTracker(Context ctx, Looper looper) {
+            return new AppStateTracker(ctx, looper);
+        }
+
+        ConnectivityService getConnectivityService() {
+            return (ConnectivityService) ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
+        }
+
+        LocationManager getLocationManager() {
+            return mContext.getSystemService(LocationManager.class);
+        }
+
+        MyHandler getHandler(DeviceIdleController ctlr) {
+            return ctlr.new MyHandler(BackgroundThread.getHandler().getLooper());
+        }
+
+        PowerManager getPowerManager() {
+            return mContext.getSystemService(PowerManager.class);
+        }
+    }
+
+    private final Injector mInjector;
+
     private ActivityTaskManagerInternal.ScreenObserver mScreenObserver =
             new ActivityTaskManagerInternal.ScreenObserver() {
                 @Override
@@ -1373,14 +1557,19 @@
                 }
             };
 
-    public DeviceIdleController(Context context) {
+    @VisibleForTesting DeviceIdleController(Context context, Injector injector) {
         super(context);
+        mInjector = injector;
         mConfigFile = new AtomicFile(new File(getSystemDir(), "deviceidle.xml"));
-        mHandler = new MyHandler(BackgroundThread.getHandler().getLooper());
-        mAppStateTracker = new AppStateTracker(context, FgThread.get().getLooper());
+        mHandler = mInjector.getHandler(this);
+        mAppStateTracker = mInjector.getAppStateTracker(context, FgThread.get().getLooper());
         LocalServices.addService(AppStateTracker.class, mAppStateTracker);
     }
 
+    public DeviceIdleController(Context context) {
+        this(context, new Injector(context));
+    }
+
     boolean isAppOnWhitelistInternal(int appid) {
         synchronized (this) {
             return Arrays.binarySearch(mPowerSaveWhitelistAllAppIdArray, appid) >= 0;
@@ -1459,20 +1648,19 @@
     public void onBootPhase(int phase) {
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
             synchronized (this) {
-                mAlarmManager = (AlarmManager) getContext().getSystemService(Context.ALARM_SERVICE);
+                mAlarmManager = mInjector.getAlarmManager();
                 mBatteryStats = BatteryStatsService.getService();
                 mLocalActivityManager = getLocalService(ActivityManagerInternal.class);
                 mLocalActivityTaskManager = getLocalService(ActivityTaskManagerInternal.class);
                 mLocalPowerManager = getLocalService(PowerManagerInternal.class);
-                mPowerManager = getContext().getSystemService(PowerManager.class);
+                mPowerManager = mInjector.getPowerManager();
                 mActiveIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                         "deviceidle_maint");
                 mActiveIdleWakeLock.setReferenceCounted(false);
                 mGoingIdleWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
                         "deviceidle_going_idle");
                 mGoingIdleWakeLock.setReferenceCounted(true);
-                mConnectivityService = (ConnectivityService)ServiceManager.getService(
-                        Context.CONNECTIVITY_SERVICE);
+                mConnectivityService = mInjector.getConnectivityService();
                 mNetworkPolicyManager = INetworkPolicyManager.Stub.asInterface(
                         ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
                 mNetworkPolicyManagerInternal = getLocalService(NetworkPolicyManagerInternal.class);
@@ -1495,8 +1683,7 @@
 
                 if (getContext().getResources().getBoolean(
                         com.android.internal.R.bool.config_autoPowerModePrefetchLocation)) {
-                    mLocationManager = (LocationManager) getContext().getSystemService(
-                            Context.LOCATION_SERVICE);
+                    mLocationManager = mInjector.getLocationManager();
                     mLocationRequest = new LocationRequest()
                         .setQuality(LocationRequest.ACCURACY_FINE)
                         .setInterval(0)
@@ -1506,9 +1693,8 @@
 
                 float angleThreshold = getContext().getResources().getInteger(
                         com.android.internal.R.integer.config_autoPowerModeThresholdAngle) / 100f;
-                mAnyMotionDetector = new AnyMotionDetector(
-                        (PowerManager) getContext().getSystemService(Context.POWER_SERVICE),
-                        mHandler, mSensorManager, this, angleThreshold);
+                mAnyMotionDetector = mInjector.getAnyMotionDetector(mHandler, mSensorManager, this,
+                        angleThreshold);
 
                 mAppStateTracker.onSystemServicesReady();
 
@@ -2005,6 +2191,11 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isScreenOn() {
+        return mScreenOn;
+    }
+
     void updateInteractivityLocked() {
         // The interactivity state from the power manager tells us whether the display is
         // in a state that we need to keep things running so they will update at a normal
@@ -2024,6 +2215,11 @@
         }
     }
 
+    @VisibleForTesting
+    boolean isCharging() {
+        return mCharging;
+    }
+
     void updateChargingLocked(boolean charging) {
         if (DEBUG) Slog.i(TAG, "updateChargingLocked: charging=" + charging);
         if (!charging && mCharging) {
@@ -2071,6 +2267,18 @@
         }
     }
 
+    /** Must only be used in tests. */
+    @VisibleForTesting
+    void setDeepEnabledForTest(boolean enabled) {
+        mDeepEnabled = enabled;
+    }
+
+    /** Must only be used in tests. */
+    @VisibleForTesting
+    void setLightEnabledForTest(boolean enabled) {
+        mLightEnabled = enabled;
+    }
+
     void becomeInactiveIfAppropriateLocked() {
         if (DEBUG) Slog.d(TAG, "becomeInactiveIfAppropriateLocked()");
         if ((!mScreenOn && !mCharging) || mForceIdle) {
@@ -2093,7 +2301,7 @@
         }
     }
 
-    void resetIdleManagementLocked() {
+    private void resetIdleManagementLocked() {
         mNextIdlePendingDelay = 0;
         mNextIdleDelay = 0;
         mNextLightIdleDelay = 0;
@@ -2104,7 +2312,7 @@
         mAnyMotionDetector.stop();
     }
 
-    void resetLightIdleManagementLocked() {
+    private void resetLightIdleManagementLocked() {
         cancelLightAlarmLocked();
     }
 
@@ -2117,6 +2325,11 @@
         }
     }
 
+    @VisibleForTesting
+    int getLightState() {
+        return mLightState;
+    }
+
     void stepLightIdleStateLocked(String reason) {
         if (mLightState == LIGHT_STATE_OVERRIDE) {
             // If we are already in deep device idle mode, then
@@ -2200,6 +2413,18 @@
         }
     }
 
+    /** Must only be used in tests. */
+    @VisibleForTesting
+    void setLocationManagerForTest(LocationManager lm) {
+        mLocationManager = lm;
+    }
+
+    @VisibleForTesting
+    int getState() {
+        return mState;
+    }
+
+    @VisibleForTesting
     void stepIdleStateLocked(String reason) {
         if (DEBUG) Slog.d(TAG, "stepIdleStateLocked: mState=" + mState);
         EventLogTags.writeDeviceIdleStep();
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index b7b5bd9..8077e34 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -356,6 +356,12 @@
 
     public boolean interceptPowerKeyDown(KeyEvent event, boolean interactive,
             MutableBoolean outLaunched) {
+        if (event.isLongPress()) {
+            // Long presses are sent as a second key down. If the long press threshold is set lower
+            // than the double tap of sequence interval thresholds, this could cause false double
+            // taps or consecutive taps, so we want to ignore the long press event.
+            return false;
+        }
         boolean launched = false;
         boolean intercept = false;
         long powerTapInterval;
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index a69d416..8c25917 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -19,6 +19,8 @@
 import static android.Manifest.permission.DUMP;
 import static android.net.IpSecManager.INVALID_RESOURCE_ID;
 import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNSPEC;
 import static android.system.OsConstants.EINVAL;
 import static android.system.OsConstants.IPPROTO_UDP;
 import static android.system.OsConstants.SOCK_DGRAM;
@@ -63,6 +65,8 @@
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.UnknownHostException;
@@ -1426,6 +1430,17 @@
                         + "or Encryption algorithms");
     }
 
+    private int getFamily(String inetAddress) {
+        int family = AF_UNSPEC;
+        InetAddress checkAddress = NetworkUtils.numericToInetAddress(inetAddress);
+        if (checkAddress instanceof Inet4Address) {
+            family = AF_INET;
+        } else if (checkAddress instanceof Inet6Address) {
+            family = AF_INET6;
+        }
+        return family;
+    }
+
     /**
      * Checks an IpSecConfig parcel to ensure that the contents are sane and throws an
      * IllegalArgumentException if they are not.
@@ -1479,6 +1494,26 @@
         // Require a valid source address for all transforms.
         checkInetAddress(config.getSourceAddress());
 
+        // Check to ensure source and destination have the same address family.
+        String sourceAddress = config.getSourceAddress();
+        String destinationAddress = config.getDestinationAddress();
+        int sourceFamily = getFamily(sourceAddress);
+        int destinationFamily = getFamily(destinationAddress);
+        if (sourceFamily != destinationFamily) {
+            throw new IllegalArgumentException(
+                    "Source address ("
+                            + sourceAddress
+                            + ") and destination address ("
+                            + destinationAddress
+                            + ") have different address families.");
+        }
+
+        // Throw an error if UDP Encapsulation is not used in IPv4.
+        if (config.getEncapType() != IpSecTransform.ENCAP_NONE && sourceFamily != AF_INET) {
+            throw new IllegalArgumentException(
+                    "UDP Encapsulation is not supported for this address family");
+        }
+
         switch (config.getMode()) {
             case IpSecTransform.MODE_TRANSPORT:
                 break;
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 461d39d..8e64b50 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1466,9 +1466,9 @@
                     + ") when binding service " + service);
         }
 
-        ActivityRecord activity = null;
+        ActivityServiceConnectionsHolder<ConnectionRecord> activity = null;
         if (token != null) {
-            activity = ActivityRecord.isInStackLocked(token);
+            activity = mAm.mAtmInternal.getServiceConnectionsHolder(token);
             if (activity == null) {
                 Slog.w(TAG, "Binding with unknown activity: " + token);
                 return 0;
@@ -1644,10 +1644,7 @@
             clist.add(c);
             b.connections.add(c);
             if (activity != null) {
-                if (activity.connections == null) {
-                    activity.connections = new HashSet<ConnectionRecord>();
-                }
-                activity.connections.add(c);
+                activity.addConnection(c);
             }
             b.client.connections.add(c);
             c.startAssociationIfNeeded();
@@ -2861,8 +2858,8 @@
         smap.ensureNotStartingBackgroundLocked(r);
     }
 
-    void removeConnectionLocked(
-        ConnectionRecord c, ProcessRecord skipApp, ActivityRecord skipAct) {
+    void removeConnectionLocked(ConnectionRecord c, ProcessRecord skipApp,
+            ActivityServiceConnectionsHolder skipAct) {
         IBinder binder = c.conn.asBinder();
         AppBindRecord b = c.binding;
         ServiceRecord s = b.service;
@@ -2876,9 +2873,7 @@
         b.connections.remove(c);
         c.stopAssociation();
         if (c.activity != null && c.activity != skipAct) {
-            if (c.activity.connections != null) {
-                c.activity.connections.remove(c);
-            }
+            c.activity.removeConnection(c);
         }
         if (b.client != skipApp) {
             b.client.connections.remove(c);
diff --git a/services/core/java/com/android/server/am/ActivityDisplay.java b/services/core/java/com/android/server/am/ActivityDisplay.java
index fab967c..fcb717e 100644
--- a/services/core/java/com/android/server/am/ActivityDisplay.java
+++ b/services/core/java/com/android/server/am/ActivityDisplay.java
@@ -51,6 +51,7 @@
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
 import android.graphics.Point;
+import android.os.UserHandle;
 import android.util.IntArray;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -112,6 +113,13 @@
      */
     private boolean mRemoved;
 
+    /**
+     * A focusable stack that is purposely to be positioned at the top. Although the stack may not
+     * have the topmost index, it is used as a preferred candidate to prevent being unable to resume
+     * target stack properly when there are other focusable always-on-top stacks.
+     */
+    private ActivityStack mPreferredTopFocusableStack;
+
     // Cached reference to some special stacks we tend to get a lot so we don't need to loop
     // through the list to find them.
     private ActivityStack mHomeStack = null;
@@ -164,6 +172,9 @@
         if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
                 + " from displayId=" + mDisplayId);
         mStacks.remove(stack);
+        if (mPreferredTopFocusableStack == stack) {
+            mPreferredTopFocusableStack = null;
+        }
         removeStackReferenceIfNeeded(stack);
         releaseSelfIfNeeded();
         mSupervisor.mService.updateSleepIfNeededLocked();
@@ -185,9 +196,21 @@
     private void positionChildAt(ActivityStack stack, int position, boolean includingParents) {
         // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
         //       the position internally, also update the logic here
-        mStacks.remove(stack);
+        final boolean wasContained = mStacks.remove(stack);
         final int insertPosition = getTopInsertPosition(stack, position);
         mStacks.add(insertPosition, stack);
+
+        // The insert position may be adjusted to non-top when there is always-on-top stack. Since
+        // the original position is preferred to be top, the stack should have higher priority when
+        // we are looking for top focusable stack. The condition {@code wasContained} restricts the
+        // preferred stack is set only when moving an existing stack to top instead of adding a new
+        // stack that may be too early (e.g. in the middle of launching or reparenting).
+        if (wasContained && position >= mStacks.size() - 1 && stack.isFocusableAndVisible()) {
+            mPreferredTopFocusableStack = stack;
+        } else if (mPreferredTopFocusableStack == stack) {
+            mPreferredTopFocusableStack = null;
+        }
+
         // Since positionChildAt() is called during the creation process of pinned stacks,
         // ActivityStack#getWindowContainerController() can be null. In this special case,
         // since DisplayContest#positionStackAt() is called in TaskStack#onConfigurationChanged(),
@@ -356,10 +379,18 @@
                         this, stackId, mSupervisor, windowingMode, activityType, onTop);
     }
 
+    /**
+     * Get the preferred focusable stack in priority. If the preferred stack does not exist, find a
+     * focusable and visible stack from the top of stacks in this display.
+     */
     ActivityStack getFocusedStack() {
+        if (mPreferredTopFocusableStack != null) {
+            return mPreferredTopFocusableStack;
+        }
+
         for (int i = mStacks.size() - 1; i >= 0; --i) {
             final ActivityStack stack = mStacks.get(i);
-            if (stack.isFocusable() && stack.shouldBeVisible(null /* starting */)) {
+            if (stack.isFocusableAndVisible()) {
                 return stack;
             }
         }
@@ -381,7 +412,7 @@
             if (ignoreCurrent && stack == currentFocus) {
                 continue;
             }
-            if (!stack.isFocusable() || !stack.shouldBeVisible(null)) {
+            if (!stack.isFocusableAndVisible()) {
                 continue;
             }
 
@@ -911,6 +942,13 @@
         return mDisplayAccessUIDs;
     }
 
+    /**
+     * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
+     */
+    boolean supportsSystemDecorations() {
+        return mDisplay.supportsSystemDecorations();
+    }
+
     private boolean shouldDestroyContentOnRemove() {
         return mDisplay.getRemoveMode() == REMOVE_MODE_DESTROY_CONTENT;
     }
@@ -920,6 +958,10 @@
                 && (mSupervisor.mService.mRunningVoice == null);
     }
 
+    void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
+        mWindowContainerController.setFocusedApp(r.appToken, moveFocusNow);
+    }
+
     /**
      * @return the stack currently above the {@param stack}.  Can be null if the {@param stack} is
      *         already top-most.
@@ -981,6 +1023,57 @@
         positionChildAt(stack, Math.max(0, insertIndex));
     }
 
+    void moveHomeStackToFront(String reason) {
+        if (mHomeStack != null) {
+            mHomeStack.moveToFront(reason);
+        }
+    }
+
+    /** Returns true if the focus activity was adjusted to the home stack top activity. */
+    boolean moveHomeActivityToTop(String reason) {
+        final ActivityRecord top = getHomeActivity();
+        if (top == null) {
+            return false;
+        }
+        mSupervisor.moveFocusableActivityToTop(top, reason);
+        return true;
+    }
+
+    @Nullable
+    ActivityStack getHomeStack() {
+        return mHomeStack;
+    }
+
+    @Nullable
+    ActivityRecord getHomeActivity() {
+        return getHomeActivityForUser(mSupervisor.mCurrentUser);
+    }
+
+    @Nullable
+    ActivityRecord getHomeActivityForUser(int userId) {
+        if (mHomeStack == null) {
+            return null;
+        }
+
+        final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
+        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
+            final TaskRecord task = tasks.get(taskNdx);
+            if (!task.isActivityTypeHome()) {
+                continue;
+            }
+
+            final ArrayList<ActivityRecord> activities = task.mActivities;
+            for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
+                final ActivityRecord r = activities.get(activityNdx);
+                if (r.isActivityTypeHome()
+                        && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
+                    return r;
+                }
+            }
+        }
+        return null;
+    }
+
     boolean isSleeping() {
         return mSleeping;
     }
@@ -1042,6 +1135,9 @@
         if (mSplitScreenPrimaryStack != null) {
             pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
         }
+        if (mPreferredTopFocusableStack != null) {
+            pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
+        }
     }
 
     public void dumpStacks(PrintWriter pw) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index aa14da0..9c96968 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -90,6 +90,7 @@
 import static android.provider.Settings.Global.NETWORK_ACCESS_TIMEOUT_MS;
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.view.Display.DEFAULT_DISPLAY;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ANR;
@@ -3725,6 +3726,14 @@
     }
 
     boolean startHomeActivityLocked(int userId, String reason) {
+        return startHomeActivityLocked(userId, reason, DEFAULT_DISPLAY);
+    }
+
+    /**
+     * This starts home activity on displays that can have system decorations and only if the
+     * home activity can have multiple instances.
+     */
+    boolean startHomeActivityLocked(int userId, String reason, int displayId) {
         if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
                 && mTopAction == null) {
             // We are running in factory test mode, but unable to find
@@ -3748,7 +3757,8 @@
                 // For ANR debugging to verify if the user activity is the one that actually
                 // launched.
                 final String myReason = reason + ":" + userId + ":" + resolvedUserId;
-                mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo, myReason);
+                mActivityTaskManager.getActivityStartController().startHomeActivity(intent, aInfo,
+                        myReason, displayId);
             }
         } else {
             Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
@@ -8817,7 +8827,7 @@
                         ? new ActivityOptions(options)
                         : ActivityOptions.makeBasic();
                 activityOptions.setLaunchTaskId(
-                        mStackSupervisor.getHomeActivity().getTask().taskId);
+                        mStackSupervisor.getDefaultDisplayHomeActivity().getTask().taskId);
                 mContext.startActivityAsUser(intent, activityOptions.toBundle(),
                         UserHandle.CURRENT);
             } finally {
@@ -10596,9 +10606,13 @@
                         currApp.importanceReasonImportance =
                                 ActivityManager.RunningAppProcessInfo.procStateToImportance(
                                         app.adjSourceProcState);
-                    } else if (app.adjSource instanceof ActivityRecord) {
-                        ActivityRecord r = (ActivityRecord)app.adjSource;
-                        if (r.app != null) currApp.importanceReasonPid = r.app.getPid();
+                    } else if (app.adjSource instanceof ActivityServiceConnectionsHolder) {
+                        ActivityServiceConnectionsHolder r =
+                                (ActivityServiceConnectionsHolder) app.adjSource;
+                        final int pid = r.getActivityPid();
+                        if (pid != -1) {
+                            currApp.importanceReasonPid = pid;
+                        }
                     }
                     if (app.adjTarget instanceof ComponentName) {
                         currApp.importanceReasonComponent = (ComponentName)app.adjTarget;
@@ -17773,10 +17787,10 @@
                     if ((cr.flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) {
                         app.treatLikeActivity = true;
                     }
-                    final ActivityRecord a = cr.activity;
+                    final ActivityServiceConnectionsHolder a = cr.activity;
                     if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
-                        if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ && (a.visible
-                                || a.isState(ActivityState.RESUMED, ActivityState.PAUSING))) {
+                        if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ
+                                && a.isActivityVisible()) {
                             adj = ProcessList.FOREGROUND_APP_ADJ;
                             if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
                                 if ((cr.flags&Context.BIND_IMPORTANT) != 0) {
@@ -20891,6 +20905,16 @@
                 return res;
             }
         }
+
+        @Override
+        public void disconnectActivityFromServices(Object connectionHolder) {
+            synchronized(ActivityManagerService.this) {
+                final ActivityServiceConnectionsHolder c =
+                        (ActivityServiceConnectionsHolder) connectionHolder;
+                c.forEachConnection(cr -> mServices.removeConnectionLocked(
+                        (ConnectionRecord) cr, null, c));
+            }
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 5853ad9..fe10baf 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -283,7 +283,7 @@
     ActivityOptions pendingOptions; // most recently given options
     ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
     AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
-    HashSet<ConnectionRecord> connections; // All ConnectionRecord we hold
+    ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
     UriPermissionOwner uriPermissions; // current special URI access perms.
     WindowProcessController app;      // if non-null, hosting application
     private ActivityState mState;    // current state we are in
@@ -549,8 +549,8 @@
                     pw.print(" configChangeFlags=");
                     pw.println(Integer.toHexString(configChangeFlags));
         }
-        if (connections != null) {
-            pw.print(prefix); pw.print("connections="); pw.println(connections);
+        if (mServiceConnectionsHolder != null) {
+            pw.print(prefix); pw.print("connections="); pw.println(mServiceConnectionsHolder);
         }
         if (info != null) {
             pw.println(prefix + "resizeMode=" + ActivityInfo.resizeModeToString(info.resizeMode));
diff --git a/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
new file mode 100644
index 0000000..b1ced29
--- /dev/null
+++ b/services/core/java/com/android/server/am/ActivityServiceConnectionsHolder.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static com.android.server.am.ActivityStack.ActivityState.PAUSING;
+import static com.android.server.am.ActivityStack.ActivityState.RESUMED;
+
+import java.io.PrintWriter;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.function.Consumer;
+
+/**
+ * Class for tracking the connections to services on the AM side that activities on the
+ * WM side (in the future) bind with for things like oom score adjustment. Would normally be one
+ * instance of this per activity for tracking all services connected to that activity. AM will
+ * sometimes query this to bump the OOM score for the processes with services connected to visible
+ * activities.
+ */
+public class ActivityServiceConnectionsHolder<T> {
+
+    private final ActivityTaskManagerService mService;
+
+    /** The activity the owns this service connection object. */
+    private final ActivityRecord mActivity;
+
+    /**
+     * The service connection object bounded with the owning activity. They represent
+     * ConnectionRecord on the AM side, however we don't need to know their object representation
+     * on the WM side since we don't perform operations on the object. Mainly here for communication
+     * and booking with the AM side.
+     */
+    private HashSet<T> mConnections;
+
+    ActivityServiceConnectionsHolder(ActivityTaskManagerService service, ActivityRecord activity) {
+        mService = service;
+        mActivity = activity;
+    }
+
+    /** Adds a connection record that the activity has bound to a specific service. */
+    public void addConnection(T c) {
+        synchronized (mService.mGlobalLock) {
+            if (mConnections == null) {
+                mConnections = new HashSet<>();
+            }
+            mConnections.add(c);
+        }
+    }
+
+    /** Removed a connection record between the activity and a specific service. */
+    public void removeConnection(T c) {
+        synchronized (mService.mGlobalLock) {
+            if (mConnections == null) {
+                return;
+            }
+            mConnections.remove(c);
+        }
+    }
+
+    public boolean isActivityVisible() {
+        synchronized (mService.mGlobalLock) {
+            return mActivity.visible || mActivity.isState(RESUMED, PAUSING);
+        }
+    }
+
+    public int getActivityPid() {
+        synchronized (mService.mGlobalLock) {
+            return mActivity.hasProcess() ? mActivity.app.getPid() : -1;
+        }
+    }
+
+    public void forEachConnection(Consumer<T> consumer) {
+        synchronized (mService.mGlobalLock) {
+            if (mConnections == null || mConnections.isEmpty()) {
+                return;
+            }
+            final Iterator<T> it = mConnections.iterator();
+            while (it.hasNext()) {
+                T c = it.next();
+                consumer.accept(c);
+            }
+        }
+    }
+
+    /** Removes the connection between the activity and all services that were connected to it. */
+    void disconnectActivityFromServices() {
+        if (mConnections == null || mConnections.isEmpty()) {
+            return;
+        }
+        mService.mH.post(() -> mService.mAmInternal.disconnectActivityFromServices(this));
+    }
+
+    public void dump(PrintWriter pw, String prefix) {
+        synchronized (mService.mGlobalLock) {
+            pw.println(prefix + "activity=" + mActivity);
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 29b04cc..ea807ad 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1102,8 +1102,7 @@
         if (!isActivityTypeHome() && returnsToHomeStack()) {
             // Make sure the home stack is behind this stack since that is where we should return to
             // when this stack is no longer visible.
-            // TODO(b/111541062): Move home stack on the current display
-            mStackSupervisor.moveHomeStackToFront(reason + " returnToHome");
+            display.moveHomeStackToFront(reason + " returnToHome");
         }
 
         display.positionChildAtTop(this, true /* includingParents */);
@@ -1147,6 +1146,10 @@
         return mStackSupervisor.isFocusable(this, r != null && r.isFocusable());
     }
 
+    boolean isFocusableAndVisible() {
+        return isFocusable() && shouldBeVisible(null /* starting */);
+    }
+
     final boolean isAttached() {
         return getParent() != null;
     }
@@ -2854,9 +2857,7 @@
         if (DEBUG_STATES) Slog.d(TAG_STATES,
                 "resumeTopActivityInNextFocusableStack: " + reason + ", go home");
         if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
-        // Only resume home if on home display
-        return isOnHomeDisplay() &&
-                mStackSupervisor.resumeHomeStackTask(prev, reason);
+        return mStackSupervisor.resumeHomeActivity(prev, reason, mDisplayId);
     }
 
     /** Returns the position the input task should be placed in this stack. */
@@ -3450,8 +3451,8 @@
         final String myReason = reason + " adjustFocus";
 
         if (next == r) {
-            mStackSupervisor.moveFocusableActivityStackToFrontLocked(
-                    mStackSupervisor.topRunningActivityLocked(), myReason);
+            mStackSupervisor.moveFocusableActivityToTop(mStackSupervisor.topRunningActivityLocked(),
+                    myReason);
             return;
         }
 
@@ -3482,7 +3483,7 @@
         }
 
         // Whatever...go home.
-        mStackSupervisor.moveHomeStackTaskToTop(myReason);
+        getDisplay().moveHomeActivityToTop(myReason);
     }
 
     /**
@@ -3511,7 +3512,7 @@
         if (stack.isActivityTypeHome() && (top == null || !top.visible)) {
             // If we will be focusing on the home stack next and its current top activity isn't
             // visible, then use the move the home stack task to top to make the activity visible.
-            mStackSupervisor.moveHomeStackTaskToTop(reason);
+            stack.getDisplay().moveHomeActivityToTop(reason);
             return stack;
         }
 
@@ -4233,15 +4234,11 @@
      * Perform clean-up of service connections in an activity record.
      */
     private void cleanUpActivityServicesLocked(ActivityRecord r) {
-        // Throw away any services that have been bound by this activity.
-        if (r.connections != null) {
-            Iterator<ConnectionRecord> it = r.connections.iterator();
-            while (it.hasNext()) {
-                ConnectionRecord c = it.next();
-                mService.mAm.mServices.removeConnectionLocked(c, null, r);
-            }
-            r.connections = null;
+        if (r.mServiceConnectionsHolder == null) {
+            return;
         }
+        // Throw away any services that have been bound by this activity.
+        r.mServiceConnectionsHolder.disconnectActivityFromServices();
     }
 
     final void scheduleDestroyActivities(WindowProcessController owner, String reason) {
@@ -4619,22 +4616,6 @@
         mStackSupervisor.invalidateTaskLayers();
     }
 
-    void moveHomeStackTaskToTop() {
-        if (!isActivityTypeHome()) {
-            throw new IllegalStateException("Calling moveHomeStackTaskToTop() on non-home stack: "
-                    + this);
-        }
-        final int top = mTaskHistory.size() - 1;
-        if (top >= 0) {
-            final TaskRecord task = mTaskHistory.get(top);
-            if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG_STACK,
-                    "moveHomeStackTaskToTop: moving " + task);
-            mTaskHistory.remove(top);
-            mTaskHistory.add(top, task);
-            updateTaskMovement(task, true);
-        }
-    }
-
     final void moveTaskToFrontLocked(TaskRecord tr, boolean noAnimation, ActivityOptions options,
             AppTimeTracker timeTracker, String reason) {
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);
@@ -4682,7 +4663,7 @@
 
             // Set focus to the top running activity of this stack.
             final ActivityRecord r = topRunningActivityLocked();
-            mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, reason);
+            mStackSupervisor.moveFocusableActivityToTop(r, reason);
 
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
             if (noAnimation) {
@@ -5223,11 +5204,11 @@
             if (DEBUG_STACK) Slog.i(TAG_STACK, "removeTask: removing stack=" + this);
             // We only need to adjust focused stack if this stack is in focus and we are not in the
             // process of moving the task to the top of the stack that will be focused.
-            if (isOnHomeDisplay() && mode != REMOVE_TASK_MODE_MOVING_TO_TOP
+            if (mode != REMOVE_TASK_MODE_MOVING_TO_TOP
                     && mStackSupervisor.isTopDisplayFocusedStack(this)) {
                 String myReason = reason + " leftTaskHistoryEmpty";
                 if (!inMultiWindowMode() || adjustFocusToNextFocusableStack(myReason) == null) {
-                    mStackSupervisor.moveHomeStackToFront(myReason);
+                    getDisplay().moveHomeStackToFront(myReason);
                 }
             }
             if (isAttached()) {
@@ -5434,7 +5415,7 @@
 
         // Do not sleep activities in this stack if we're marked as focused and the keyguard
         // is in the process of going away.
-        if (mStackSupervisor.getTopDisplayFocusedStack() == this
+        if (isFocusedStackOnDisplay()
                 && mStackSupervisor.getKeyguardController().isKeyguardGoingAway()) {
             return false;
         }
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 9688d26..a968ae4 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -40,6 +40,8 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.app.WindowConfiguration.activityTypeToString;
 import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
+import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.PackageManager.PERMISSION_DENIED;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.graphics.Rect.copyOrNull;
@@ -50,7 +52,6 @@
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.TYPE_VIRTUAL;
 import static android.view.WindowManager.TRANSIT_DOCK_TASK_FROM_RECENTS;
-
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_IDLE;
@@ -97,7 +98,6 @@
 import static com.android.server.am.TaskRecord.REPARENT_KEEP_STACK_AT_FRONT;
 import static com.android.server.am.TaskRecord.REPARENT_LEAVE_STACK_IN_PLACE;
 import static com.android.server.am.TaskRecord.REPARENT_MOVE_STACK_TO_FRONT;
-
 import static java.lang.Integer.MAX_VALUE;
 
 import android.Manifest;
@@ -132,6 +132,7 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
@@ -156,6 +157,7 @@
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.IntArray;
 import android.util.MergedConfiguration;
@@ -335,10 +337,6 @@
     /** The current user */
     int mCurrentUser;
 
-    /** The stack containing the launcher app. Assumed to always be attached to
-     * Display.DEFAULT_DISPLAY. */
-    ActivityStack mHomeStack;
-
     /** If this is the same as mFocusedStack then the activity on the top of the focused stack has
      * been resumed. If stacks are changing position this will hold the old stack until the new
      * stack becomes resumed after which it will be set to mFocusedStack. */
@@ -445,7 +443,7 @@
 
     // The default minimal size that will be used if the activity doesn't specify its minimal size.
     // It will be calculated when the default display gets added.
-    int mDefaultMinSizeOfResizeableTask = -1;
+    int mDefaultMinSizeOfResizeableTaskDp = -1;
 
     // Whether tasks have moved and we need to rank the tasks before next OOM scoring
     private boolean mTaskLayersChanged = true;
@@ -689,11 +687,12 @@
                 mDefaultDisplay = activityDisplay;
             }
             addChild(activityDisplay, ActivityDisplay.POSITION_TOP);
-            calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
         }
+        calculateDefaultMinimalSizeOfResizeableTasks();
 
         final ActivityDisplay defaultDisplay = getDefaultDisplay();
-        mHomeStack = mLastFocusedStack = defaultDisplay.getOrCreateStack(
+
+        mLastFocusedStack = defaultDisplay.getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
         positionChildAt(defaultDisplay, ActivityDisplay.POSITION_TOP);
     }
@@ -734,10 +733,6 @@
     }
 
     ActivityRecord getTopResumedActivity() {
-        if (mWindowManager == null) {
-            return null;
-        }
-
         final ActivityStack focusedStack = getTopDisplayFocusedStack();
         if (focusedStack == null) {
             return null;
@@ -782,7 +777,7 @@
             if (focusCandidate == null) {
                 Slog.w(TAG,
                         "setFocusStackUnchecked: No focusable stack found, focus home as default");
-                focusCandidate = mHomeStack;
+                focusCandidate = getDefaultDisplay().getHomeStack();
             }
         }
 
@@ -803,10 +798,6 @@
         }
     }
 
-    void moveHomeStackToFront(String reason) {
-        mHomeStack.moveToFront(reason);
-    }
-
     void moveRecentsStackToFront(String reason) {
         final ActivityStack recentsStack = getDefaultDisplay().getStack(
                 WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_RECENTS);
@@ -815,34 +806,47 @@
         }
     }
 
-    /** Returns true if the focus activity was adjusted to the home stack top activity. */
-    boolean moveHomeStackTaskToTop(String reason) {
-        mHomeStack.moveHomeStackTaskToTop();
-
-        final ActivityRecord top = getHomeActivity();
-        if (top == null) {
-            return false;
-        }
-        moveFocusableActivityStackToFrontLocked(top, reason);
-        return true;
-    }
-
-    boolean resumeHomeStackTask(ActivityRecord prev, String reason) {
+    boolean resumeHomeActivity(ActivityRecord prev, String reason, int displayId) {
         if (!mService.isBooting() && !mService.isBooted()) {
             // Not ready yet!
             return false;
         }
 
-        mHomeStack.moveHomeStackTaskToTop();
-        ActivityRecord r = getHomeActivity();
-        final String myReason = reason + " resumeHomeStackTask";
+        if (displayId == INVALID_DISPLAY) {
+            displayId = DEFAULT_DISPLAY;
+        }
+
+        final ActivityRecord r = getActivityDisplay(displayId).getHomeActivity();
+        final String myReason = reason + " resumeHomeActivity";
 
         // Only resume home activity if isn't finishing.
         if (r != null && !r.finishing) {
-            moveFocusableActivityStackToFrontLocked(r, myReason);
-            return resumeFocusedStacksTopActivitiesLocked(mHomeStack, prev, null);
+            moveFocusableActivityToTop(r, myReason);
+            return resumeFocusedStacksTopActivitiesLocked(r.getStack(), prev, null);
         }
-        return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason);
+        return mService.mAm.startHomeActivityLocked(mCurrentUser, myReason, displayId);
+    }
+
+    boolean canStartHomeOnDisplay(ActivityInfo homeActivity, int displayId) {
+        if (displayId == DEFAULT_DISPLAY) {
+            // No restrictions to default display.
+            return true;
+        }
+
+        final ActivityDisplay display = getActivityDisplay(displayId);
+        if (display == null || display.isRemoved() || !display.supportsSystemDecorations()) {
+            // Can't launch home on display that doesn't support system decorations.
+            return false;
+        }
+
+        final boolean supportMultipleInstance = homeActivity.launchMode != LAUNCH_SINGLE_TASK
+                && homeActivity.launchMode != LAUNCH_SINGLE_INSTANCE;
+        if (!supportMultipleInstance) {
+            // Can't launch home on other displays if it requested to be single instance.
+            return false;
+        }
+
+        return true;
     }
 
     TaskRecord anyTaskForIdLocked(int id) {
@@ -2206,7 +2210,8 @@
      */
     void updateUserStackLocked(int userId, ActivityStack stack) {
         if (userId != mCurrentUser) {
-            mUserStackInFront.put(userId, stack != null ? stack.getStackId() : mHomeStack.mStackId);
+            mUserStackInFront.put(userId, stack != null ? stack.getStackId()
+                    : getDefaultDisplay().getHomeStack().mStackId);
         }
     }
 
@@ -2275,7 +2280,8 @@
             return false;
         }
 
-        if (targetStack != null && targetStack.isTopStackOnDisplay()) {
+        if (targetStack != null && (targetStack.isTopStackOnDisplay()
+                || getTopDisplayFocusedStack() == targetStack)) {
             return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
         }
 
@@ -2348,7 +2354,7 @@
      */
     void findTaskToMoveToFront(TaskRecord task, int flags, ActivityOptions options, String reason,
             boolean forceNonResizeable) {
-        final ActivityStack currentStack = task.getStack();
+        ActivityStack currentStack = task.getStack();
         if (currentStack == null) {
             Slog.e(TAG, "findTaskToMoveToFront: can't move task="
                     + task + " to front. Stack is null");
@@ -2359,13 +2365,15 @@
             mUserLeaving = true;
         }
 
+        // TODO(b/111363427): The moving-to-top task may not be on the top display, so it could be
+        // different from where the prev activity stays on.
         final ActivityRecord prev = topRunningActivityLocked();
 
         if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0
                 || (prev != null && prev.isActivityTypeRecents())) {
             // Caller wants the home activity moved with it or the previous task is recents in which
             // case we always return home from the task we are moving to the front.
-            moveHomeStackToFront("findTaskToMoveToFront");
+            currentStack.getDisplay().moveHomeStackToFront("findTaskToMoveToFront");
         }
 
         if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
@@ -2377,7 +2385,7 @@
             if (stack != currentStack) {
                 task.reparent(stack, ON_TOP, REPARENT_KEEP_STACK_AT_FRONT, !ANIMATE, DEFER_RESUME,
                         "findTaskToMoveToFront");
-                stack = currentStack;
+                currentStack = stack;
                 // moveTaskToStackUncheckedLocked() should already placed the task on top,
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
@@ -2644,6 +2652,12 @@
         if (preferredFocusableStack != null) {
             return preferredFocusableStack;
         }
+        if (preferredDisplay.supportsSystemDecorations()) {
+            // Stop looking for focusable stack on other displays because the preferred display
+            // supports system decorations. Home activity would be launched on the same display if
+            // no focusable stack found.
+            return null;
+        }
 
         // Now look through all displays
         for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
@@ -2687,25 +2701,12 @@
         return null;
     }
 
-    ActivityRecord getHomeActivity() {
-        return getHomeActivityForUser(mCurrentUser);
+    ActivityRecord getDefaultDisplayHomeActivity() {
+        return getDefaultDisplayHomeActivityForUser(mCurrentUser);
     }
 
-    ActivityRecord getHomeActivityForUser(int userId) {
-        final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks();
-        for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
-            final TaskRecord task = tasks.get(taskNdx);
-            if (task.isActivityTypeHome()) {
-                final ArrayList<ActivityRecord> activities = task.mActivities;
-                for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
-                    final ActivityRecord r = activities.get(activityNdx);
-                    if (r.isActivityTypeHome()
-                            && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) {
-                        return r;
-                    }
-                }
-            }
-        }
+    ActivityRecord getDefaultDisplayHomeActivityForUser(int userId) {
+        getActivityDisplay(DEFAULT_DISPLAY).getHomeActivityForUser(userId);
         return null;
     }
 
@@ -3419,7 +3420,8 @@
     }
 
     /** Move activity with its stack to front and make the stack focused. */
-    boolean moveFocusableActivityStackToFrontLocked(ActivityRecord r, String reason) {
+    // TODO(b/111363427): Move this method to ActivityRecord.
+    boolean moveFocusableActivityToTop(ActivityRecord r, String reason) {
         if (r == null || !r.isFocusable()) {
             if (DEBUG_FOCUS) Slog.d(TAG_FOCUS,
                     "moveActivityStackToFront: unfocusable r=" + r);
@@ -3588,7 +3590,7 @@
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
                     stack.awakeFromSleepingLocked();
-                    if (isTopDisplayFocusedStack(stack) && !getKeyguardController()
+                    if (stack.isFocusedStackOnDisplay() && !getKeyguardController()
                             .isKeyguardOrAodShowing(display.mDisplayId)) {
                         // If the keyguard is unlocked - resume immediately.
                         // It is possible that the display will not be awake at the time we
@@ -3828,7 +3830,8 @@
         removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
 
         mUserStackInFront.put(mCurrentUser, focusStackId);
-        final int restoreStackId = mUserStackInFront.get(userId, mHomeStack.mStackId);
+        final int restoreStackId =
+                mUserStackInFront.get(userId, getDefaultDisplay().getHomeStack().mStackId);
         mCurrentUser = userId;
 
         mStartingUsers.add(uss);
@@ -3846,14 +3849,14 @@
 
         ActivityStack stack = getStack(restoreStackId);
         if (stack == null) {
-            stack = mHomeStack;
+            stack = getDefaultDisplay().getHomeStack();
         }
         final boolean homeInFront = stack.isActivityTypeHome();
         if (stack.isOnHomeDisplay()) {
             stack.moveToFront("switchUserOnHomeDisplay");
         } else {
             // Stack was moved to another display while user was swapped out.
-            resumeHomeStackTask(null, "switchUserOnOtherDisplay");
+            resumeHomeActivity(null, "switchUserOnOtherDisplay", DEFAULT_DISPLAY);
         }
         return homeInFront;
     }
@@ -3976,8 +3979,12 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        pw.print(prefix); pw.print("mFocusedStack=" + getTopDisplayFocusedStack());
-                pw.print(" mLastFocusedStack="); pw.println(mLastFocusedStack);
+        pw.println();
+        pw.println("ActivityStackSupervisor state:");
+        pw.print(prefix);
+        pw.println("topDisplayFocusedStack=" + getTopDisplayFocusedStack());
+        pw.print(prefix);
+        pw.println("mLastFocusedStack=" + mLastFocusedStack);
         pw.print(prefix);
         pw.println("mCurTaskIdForUser=" + mCurTaskIdForUser);
         pw.print(prefix); pw.println("mUserStackInFront=" + mUserStackInFront);
@@ -4273,6 +4280,7 @@
     private void handleDisplayAdded(int displayId) {
         synchronized (mService.mGlobalLock) {
             getActivityDisplayOrCreateLocked(displayId);
+            mService.mAm.startHomeActivityLocked(mCurrentUser, "displayAdded", displayId);
         }
     }
 
@@ -4319,7 +4327,6 @@
         // The display hasn't been added to ActivityManager yet, create a new record now.
         activityDisplay = new ActivityDisplay(this, display);
         addChild(activityDisplay, ActivityDisplay.POSITION_BOTTOM);
-        calculateDefaultMinimalSizeOfResizeableTasks(activityDisplay);
         mWindowManager.onDisplayAdded(displayId);
         return activityDisplay;
     }
@@ -4337,10 +4344,13 @@
         mActivityDisplays.remove(activityDisplay);
     }
 
-    private void calculateDefaultMinimalSizeOfResizeableTasks(ActivityDisplay display) {
-        mDefaultMinSizeOfResizeableTask =
-                mService.mContext.getResources().getDimensionPixelSize(
-                        com.android.internal.R.dimen.default_minimal_size_resizable_task);
+    private void calculateDefaultMinimalSizeOfResizeableTasks() {
+        final Resources res = mService.mContext.getResources();
+        final float minimalSize = res.getDimension(
+                com.android.internal.R.dimen.default_minimal_size_resizable_task);
+        final DisplayMetrics dm = res.getDisplayMetrics();
+
+        mDefaultMinSizeOfResizeableTaskDp = (int) (minimalSize / dm.density);
     }
 
     private void handleDisplayRemoved(int displayId) {
@@ -4836,7 +4846,8 @@
                 // We always want to return to the home activity instead of the recents activity
                 // from whatever is started from the recents activity, so move the home stack
                 // forward.
-                moveHomeStackToFront("startActivityFromRecents");
+                // TODO (b/115289124): Multi-display supports for recents.
+                getDefaultDisplay().moveHomeStackToFront("startActivityFromRecents");
             }
 
             // If the user must confirm credentials (e.g. when first launching a work app and the
@@ -4879,12 +4890,13 @@
                 final ActivityStack topSecondaryStack =
                         display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
                 if (topSecondaryStack.isActivityTypeHome()) {
-                    // If the home activity if the top split-screen secondary stack, then the
+                    // If the home activity is the top split-screen secondary stack, then the
                     // primary split-screen stack is in the minimized mode which means it can't
                     // receive input keys, so we should move the focused app to the home app so that
                     // window manager can correctly calculate the focus window that can receive
                     // input keys.
-                    moveHomeStackToFront("startActivityFromRecents: homeVisibleInSplitScreen");
+                    display.moveHomeStackToFront(
+                            "startActivityFromRecents: homeVisibleInSplitScreen");
 
                     // Immediately update the minimized docked stack mode, the upcoming animation
                     // for the docked activity (WMS.overridePendingAppTransitionMultiThumbFuture)
diff --git a/services/core/java/com/android/server/am/ActivityStartController.java b/services/core/java/com/android/server/am/ActivityStartController.java
index 6e3a79c..5e73bc3 100644
--- a/services/core/java/com/android/server/am/ActivityStartController.java
+++ b/services/core/java/com/android/server/am/ActivityStartController.java
@@ -17,12 +17,14 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.START_SUCCESS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 
+import android.app.ActivityOptions;
 import android.app.IApplicationThread;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -75,7 +77,7 @@
     /** Temporary array to capture start activity results */
     private ActivityRecord[] tmpOutRecord = new ActivityRecord[1];
 
-    /**The result of the last home activity we attempted to start. */
+    /** The result of the last home activity we attempted to start. */
     private int mLastHomeActivityStartResult;
 
     /** A list of activities that are waiting to launch. */
@@ -161,13 +163,20 @@
         mLastStarter.postStartActivityProcessing(r, result, targetStack);
     }
 
-    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) {
-        mSupervisor.moveHomeStackTaskToTop(reason);
+    void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
+        if (!mSupervisor.canStartHomeOnDisplay(aInfo, displayId)) {
+            return;
+        }
 
+        final ActivityOptions options = ActivityOptions.makeBasic();
+        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
+        options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
+        options.setLaunchDisplayId(displayId);
         mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                 .setOutActivity(tmpOutRecord)
                 .setCallingUid(0)
                 .setActivityInfo(aInfo)
+                .setActivityOptions(options.toBundle())
                 .execute();
         mLastHomeActivityStartRecord = tmpOutRecord[0];
         if (mSupervisor.inResumeTopActivity) {
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 1fb8f87..4789ff3 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -277,7 +277,7 @@
             mActivityOptions = ActivityOptions.makeBasic();
         }
 
-        ActivityRecord homeActivityRecord = mSupervisor.getHomeActivity();
+        ActivityRecord homeActivityRecord = mSupervisor.getDefaultDisplayHomeActivity();
         if (homeActivityRecord != null && homeActivityRecord.getTask() != null) {
             // Showing credential confirmation activity in home task to avoid stopping multi-windowed
             // mode after showing the full-screen credential confirmation activity.
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 8236bd0..de3b9cf 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -975,7 +975,8 @@
                             clearedTask);
                     break;
                 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
-                    final ActivityStack homeStack = mSupervisor.mHomeStack;
+                    final ActivityStack homeStack =
+                            startedActivityStack.getDisplay().getHomeStack();
                     if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
                         mService.mWindowManager.showRecentApps();
                     }
@@ -1280,6 +1281,15 @@
         setInitialState(r, options, inTask, doResume, startFlags, sourceRecord, voiceSession,
                 voiceInteractor);
 
+        // Do not start home activity if it cannot be launched on preferred display. We are not
+        // doing this in ActivityStackSupervisor#canPlaceEntityOnDisplay because it might
+        // fallback to launch on other displays.
+        if (r.isActivityTypeHome()
+                && !mSupervisor.canStartHomeOnDisplay(r.info, mPreferredDisplayId)) {
+            Slog.w(TAG, "Cannot launch home on display " + mPreferredDisplayId);
+            return START_CANCELED;
+        }
+
         computeLaunchingTaskFlags();
 
         computeSourceStack();
@@ -1430,7 +1440,11 @@
                 && top.userId == mStartActivity.userId
                 && top.attachedToProcess()
                 && ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
-                || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
+                || isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK))
+                // This allows home activity to automatically launch on secondary display when
+                // display added, if home was the top activity on default display, instead of
+                // sending new intent to the home activity on default display.
+                && (!top.isActivityTypeHome() || top.getDisplayId() == mPreferredDisplayId);
         if (dontStart) {
             // For paranoia, make sure we have correctly resumed the top activity.
             topStack.mLastPausedActivity = null;
@@ -1858,6 +1872,13 @@
                 intentActivity = mSupervisor.findTaskLocked(mStartActivity, mPreferredDisplayId);
             }
         }
+
+        if (mStartActivity.isActivityTypeHome() && intentActivity != null
+                && intentActivity.getDisplayId() != mPreferredDisplayId) {
+            // Do not reuse home activity on other displays.
+            intentActivity = null;
+        }
+
         return intentActivity;
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityTaskManagerService.java b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
index 36261b5..c1eab82 100644
--- a/services/core/java/com/android/server/am/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityTaskManagerService.java
@@ -1692,8 +1692,7 @@
                     return;
                 }
                 final ActivityRecord r = stack.topRunningActivityLocked();
-                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
-                        r, "setFocusedStack")) {
+                if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedStack")) {
                     mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 }
             }
@@ -1714,7 +1713,7 @@
                     return;
                 }
                 final ActivityRecord r = task.topRunningActivityLocked();
-                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(r, "setFocusedTask")) {
+                if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedTask")) {
                     mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 }
             }
@@ -4846,8 +4845,7 @@
         updateResumedAppTrace(r);
         mLastResumedActivity = r;
 
-        // TODO(b/111361570): Support multiple focused apps in WM
-        mWindowManager.setFocusedApp(r.appToken, true);
+        r.getDisplay().setFocusedApp(r, true);
 
         applyUpdateLockStateLocked(r);
         applyUpdateVrModeLocked(r);
@@ -5263,7 +5261,8 @@
         @Override
         public ComponentName getHomeActivityForUser(int userId) {
             synchronized (mGlobalLock) {
-                ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId);
+                ActivityRecord homeActivity =
+                        mStackSupervisor.getDefaultDisplayHomeActivityForUser(userId);
                 return homeActivity == null ? null : homeActivity.realActivity;
             }
         }
@@ -5437,8 +5436,7 @@
                     throw new IllegalArgumentException(
                             "setFocusedActivity: No activity record matching token=" + token);
                 }
-                if (mStackSupervisor.moveFocusableActivityStackToFrontLocked(
-                        r, "setFocusedActivity")) {
+                if (mStackSupervisor.moveFocusableActivityToTop(r, "setFocusedActivity")) {
                     mStackSupervisor.resumeFocusedStacksTopActivitiesLocked();
                 }
             }
@@ -5781,5 +5779,21 @@
                         resultWho, requestCode, intents, resolvedTypes, flags, bOptions);
             }
         }
+
+        @Override
+        public ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token) {
+            synchronized (mGlobalLock) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return null;
+                }
+                if (r.mServiceConnectionsHolder == null) {
+                    r.mServiceConnectionsHolder = new ActivityServiceConnectionsHolder(
+                            ActivityTaskManagerService.this, r);
+                }
+
+                return r.mServiceConnectionsHolder;
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/ConnectionRecord.java b/services/core/java/com/android/server/am/ConnectionRecord.java
index fa8e6c4..1242ed6 100644
--- a/services/core/java/com/android/server/am/ConnectionRecord.java
+++ b/services/core/java/com/android/server/am/ConnectionRecord.java
@@ -35,7 +35,7 @@
  */
 final class ConnectionRecord {
     final AppBindRecord binding;    // The application/service binding.
-    final ActivityRecord activity;  // If non-null, the owning activity.
+    final ActivityServiceConnectionsHolder<ConnectionRecord> activity;  // If non-null, the owning activity.
     final IServiceConnection conn;  // The client connection.
     final int flags;                // Binding options.
     final int clientLabel;          // String resource labeling this client.
@@ -85,13 +85,14 @@
     void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "binding=" + binding);
         if (activity != null) {
-            pw.println(prefix + "activity=" + activity);
+            activity.dump(pw, prefix);
         }
         pw.println(prefix + "conn=" + conn.asBinder()
                 + " flags=0x" + Integer.toHexString(flags));
     }
 
-    ConnectionRecord(AppBindRecord _binding, ActivityRecord _activity,
+    ConnectionRecord(AppBindRecord _binding,
+            ActivityServiceConnectionsHolder<ConnectionRecord> _activity,
             IServiceConnection _conn, int _flags,
             int _clientLabel, PendingIntent _clientIntent,
             int _clientUid, String _clientProcessName) {
diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java
index 080fa2a..3490b1d 100644
--- a/services/core/java/com/android/server/am/PersistentConnection.java
+++ b/services/core/java/com/android/server/am/PersistentConnection.java
@@ -59,6 +59,8 @@
  * know what to do when the service component has gone missing, for example.  If the user of this
  * class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind}
  * explicitly.
+ *
+ * atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
  */
 public abstract class PersistentConnection<T> {
     private final Object mLock = new Object();
@@ -76,6 +78,7 @@
     private final long mRebindBackoffMs;
     private final double mRebindBackoffIncrease;
     private final long mRebindMaxBackoffMs;
+    private final long mResetBackoffDelay;
 
     private long mReconnectTime;
 
@@ -109,6 +112,9 @@
     @GuardedBy("mLock")
     private int mNumBindingDied;
 
+    @GuardedBy("mLock")
+    private long mLastConnectedTime;
+
     private final ServiceConnection mServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
@@ -127,7 +133,10 @@
                 mNumConnected++;
 
                 mIsConnected = true;
+                mLastConnectedTime = injectUptimeMillis();
                 mService = asInterface(service);
+
+                scheduleStableCheckLocked();
             }
         }
 
@@ -140,6 +149,9 @@
                 mNumDisconnected++;
 
                 cleanUpConnectionLocked();
+
+                // Note we won't increase the rebind timeout here, because we don't explicitly
+                // rebind in this case.
             }
         }
 
@@ -168,7 +180,8 @@
 
     public PersistentConnection(@NonNull String tag, @NonNull Context context,
             @NonNull Handler handler, int userId, @NonNull ComponentName componentName,
-            long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
+            long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds,
+            long resetBackoffDelay) {
         mTag = tag;
         mContext = context;
         mHandler = handler;
@@ -178,6 +191,7 @@
         mRebindBackoffMs = rebindBackoffSeconds * 1000;
         mRebindBackoffIncrease = rebindBackoffIncrease;
         mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000;
+        mResetBackoffDelay = resetBackoffDelay * 1000;
 
         mNextBackoffMs = mRebindBackoffMs;
     }
@@ -242,6 +256,42 @@
         }
     }
 
+    /** Return the next back-off time */
+    public long getNextBackoffMs() {
+        synchronized (mLock) {
+            return mNextBackoffMs;
+        }
+    }
+
+    /** Return the number of times the connected callback called. */
+    public int getNumConnected() {
+        synchronized (mLock) {
+            return mNumConnected;
+        }
+    }
+
+    /** Return the number of times the disconnected callback called. */
+    public int getNumDisconnected() {
+        synchronized (mLock) {
+            return mNumDisconnected;
+        }
+    }
+
+    /** Return the number of times the binding died callback called. */
+    public int getNumBindingDied() {
+        synchronized (mLock) {
+            return mNumBindingDied;
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void resetBackoffLocked() {
+        if (mNextBackoffMs != mRebindBackoffMs) {
+            mNextBackoffMs = mRebindBackoffMs;
+            Log.i(mTag, "Backoff reset to " + mNextBackoffMs);
+        }
+    }
+
     @GuardedBy("mLock")
     public final void bindInnerLocked(boolean resetBackoff) {
         unscheduleRebindLocked();
@@ -251,9 +301,10 @@
         }
         mBound = true;
 
+        unscheduleStableCheckLocked();
+
         if (resetBackoff) {
-            // Note this is the only place we reset the backoff time.
-            mNextBackoffMs = mRebindBackoffMs;
+            resetBackoffLocked();
         }
 
         final Intent service = new Intent().setComponent(mComponentName);
@@ -287,6 +338,8 @@
     private void cleanUpConnectionLocked() {
         mIsConnected = false;
         mService = null;
+
+        unscheduleStableCheckLocked();
     }
 
     /**
@@ -297,6 +350,7 @@
             mShouldBeBound = false;
 
             unbindLocked();
+            unscheduleStableCheckLocked();
         }
     }
 
@@ -338,6 +392,33 @@
         }
     }
 
+    private final Runnable mStableCheck = this::stableConnectionCheck;
+
+    private void stableConnectionCheck() {
+        synchronized (mLock) {
+            final long now = injectUptimeMillis();
+            final long timeRemaining = (mLastConnectedTime + mResetBackoffDelay) - now;
+            if (DEBUG) {
+                Log.d(mTag, "stableConnectionCheck: bound=" + mBound + " connected=" + mIsConnected
+                        + " remaining=" + timeRemaining);
+            }
+            if (mBound && mIsConnected && timeRemaining <= 0) {
+                resetBackoffLocked();
+            }
+        }
+    }
+
+    @GuardedBy("mLock")
+    private void unscheduleStableCheckLocked() {
+        injectRemoveCallbacks(mStableCheck);
+    }
+
+    @GuardedBy("mLock")
+    private void scheduleStableCheckLocked() {
+        unscheduleStableCheckLocked();
+        injectPostAtTime(mStableCheck, injectUptimeMillis() + mResetBackoffDelay);
+    }
+
     /** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */
     protected abstract T asInterface(IBinder binder);
 
@@ -367,6 +448,10 @@
             pw.print(mNumDisconnected);
             pw.print("  Died: ");
             pw.print(mNumBindingDied);
+            if (mIsConnected) {
+                pw.print("  Duration: ");
+                TimeUtils.formatDuration((injectUptimeMillis() - mLastConnectedTime), pw);
+            }
             pw.println();
         }
     }
@@ -407,6 +492,11 @@
     }
 
     @VisibleForTesting
+    Runnable getStableCheckRunnableForTest() {
+        return mStableCheck;
+    }
+
+    @VisibleForTesting
     boolean shouldBeBoundForTest() {
         return mShouldBeBound;
     }
diff --git a/services/core/java/com/android/server/am/PersisterQueue.java b/services/core/java/com/android/server/am/PersisterQueue.java
new file mode 100644
index 0000000..60ea0fa
--- /dev/null
+++ b/services/core/java/com/android/server/am/PersisterQueue.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import android.os.Process;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.function.Predicate;
+
+/**
+ * The common threading logic for persisters to use so that they can run in the same threads.
+ * Methods in this class are synchronized on its instance, so caller could also synchronize on
+ * its instance to perform modifications in items.
+ */
+class PersisterQueue {
+    private static final String TAG = "PersisterQueue";
+    private static final boolean DEBUG = false;
+
+    /** When not flushing don't write out files faster than this */
+    private static final long INTER_WRITE_DELAY_MS = 500;
+
+    /**
+     * When not flushing delay this long before writing the first file out. This gives the next task
+     * being launched a chance to load its resources without this occupying IO bandwidth.
+     */
+    private static final long PRE_TASK_DELAY_MS = 3000;
+
+    /** The maximum number of entries to keep in the queue before draining it automatically. */
+    private static final int MAX_WRITE_QUEUE_LENGTH = 6;
+
+    /** Special value for mWriteTime to mean don't wait, just write */
+    private static final long FLUSH_QUEUE = -1;
+
+    /** An {@link WriteQueueItem} that doesn't do anything. Used to trigger {@link
+     * Listener#onPreProcessItem}. */
+    static final WriteQueueItem EMPTY_ITEM = () -> { };
+
+    private final long mInterWriteDelayMs;
+    private final long mPreTaskDelayMs;
+    private final LazyTaskWriterThread mLazyTaskWriterThread;
+    private final ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<>();
+
+    private final ArrayList<Listener> mListeners = new ArrayList<>();
+
+    /**
+     * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
+     * until the image queue is drained and all tasks needing persisting are written to disk. There
+     * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
+     * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
+     * writes will be delayed by #INTER_WRITE_DELAY_MS.
+     */
+    private long mNextWriteTime = 0;
+
+    PersisterQueue() {
+        this(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+    }
+
+    /** Used for tests to reduce waiting time. */
+    @VisibleForTesting
+    PersisterQueue(long interWriteDelayMs, long preTaskDelayMs) {
+        if (interWriteDelayMs < 0 || preTaskDelayMs < 0) {
+            throw new IllegalArgumentException("Both inter-write delay and pre-task delay need to"
+                    + "be non-negative. inter-write delay: " + interWriteDelayMs
+                    + "ms pre-task delay: " + preTaskDelayMs);
+        }
+        mInterWriteDelayMs = interWriteDelayMs;
+        mPreTaskDelayMs = preTaskDelayMs;
+        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+    }
+
+    synchronized void startPersisting() {
+        if (!mLazyTaskWriterThread.isAlive()) {
+            mLazyTaskWriterThread.start();
+        }
+    }
+
+    /** Stops persisting thread. Should only be used in tests. */
+    @VisibleForTesting
+    void stopPersisting() throws InterruptedException {
+        if (!mLazyTaskWriterThread.isAlive()) {
+            return;
+        }
+
+        synchronized (this) {
+            mLazyTaskWriterThread.interrupt();
+        }
+        mLazyTaskWriterThread.join();
+    }
+
+    synchronized void addItem(WriteQueueItem item, boolean flush) {
+        mWriteQueue.add(item);
+
+        if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
+            mNextWriteTime = FLUSH_QUEUE;
+        } else if (mNextWriteTime == 0) {
+            mNextWriteTime = SystemClock.uptimeMillis() + mPreTaskDelayMs;
+        }
+        notify();
+    }
+
+    synchronized <T extends WriteQueueItem> T findLastItem(Predicate<T> predicate, Class<T> clazz) {
+        for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+            WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+            if (clazz.isInstance(writeQueueItem)) {
+                T item = clazz.cast(writeQueueItem);
+                if (predicate.test(item)) {
+                    return item;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    synchronized <T extends WriteQueueItem> void removeItems(Predicate<T> predicate,
+            Class<T> clazz) {
+        for (int i = mWriteQueue.size() - 1; i >= 0; --i) {
+            WriteQueueItem writeQueueItem = mWriteQueue.get(i);
+            if (clazz.isInstance(writeQueueItem)) {
+                T item = clazz.cast(writeQueueItem);
+                if (predicate.test(item)) {
+                    if (DEBUG) Slog.d(TAG, "Removing " + item + " from write queue.");
+                    mWriteQueue.remove(i);
+                }
+            }
+        }
+    }
+
+    synchronized void flush() {
+        mNextWriteTime = FLUSH_QUEUE;
+        notifyAll();
+        do {
+            try {
+                wait();
+            } catch (InterruptedException e) {
+            }
+        } while (mNextWriteTime == FLUSH_QUEUE);
+    }
+
+    void yieldIfQueueTooDeep() {
+        boolean stall = false;
+        synchronized (this) {
+            if (mNextWriteTime == FLUSH_QUEUE) {
+                stall = true;
+            }
+        }
+        if (stall) {
+            Thread.yield();
+        }
+    }
+
+    void addListener(Listener listener) {
+        mListeners.add(listener);
+    }
+
+    private void processNextItem() throws InterruptedException {
+        // This part is extracted into a method so that the GC can clearly see the end of the
+        // scope of the variable 'item'.  If this part was in the loop in LazyTaskWriterThread, the
+        // last item it processed would always "leak".
+        // See https://b.corp.google.com/issues/64438652#comment7
+
+        // If mNextWriteTime, then don't delay between each call to saveToXml().
+        final WriteQueueItem item;
+        synchronized (this) {
+            if (mNextWriteTime != FLUSH_QUEUE) {
+                // The next write we don't have to wait so long.
+                mNextWriteTime = SystemClock.uptimeMillis() + mInterWriteDelayMs;
+                if (DEBUG) {
+                    Slog.d(TAG, "Next write time may be in " + mInterWriteDelayMs
+                            + " msec. (" + mNextWriteTime + ")");
+                }
+            }
+
+            while (mWriteQueue.isEmpty()) {
+                if (mNextWriteTime != 0) {
+                    mNextWriteTime = 0; // idle.
+                    notify(); // May need to wake up flush().
+                }
+                // Make sure we exit this thread correctly when interrupted before going to
+                // indefinite wait.
+                if (Thread.currentThread().isInterrupted()) {
+                    throw new InterruptedException();
+                }
+                if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
+                wait();
+                // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
+                // from now.
+            }
+            item = mWriteQueue.remove(0);
+
+            long now = SystemClock.uptimeMillis();
+            if (DEBUG) {
+                Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" + mNextWriteTime
+                        + " mWriteQueue.size=" + mWriteQueue.size());
+            }
+            while (now < mNextWriteTime) {
+                if (DEBUG) {
+                    Slog.d(TAG, "LazyTaskWriter: waiting " + (mNextWriteTime - now));
+                }
+                wait(mNextWriteTime - now);
+                now = SystemClock.uptimeMillis();
+            }
+
+            // Got something to do.
+        }
+
+        item.process();
+    }
+
+    interface WriteQueueItem {
+        void process();
+    }
+
+    interface Listener {
+        /**
+         * Called before {@link PersisterQueue} tries to process next item.
+         *
+         * Note if the queue is empty, this callback will be called before the indefinite wait. This
+         * will be called once when {@link PersisterQueue} starts the internal thread before the
+         * indefinite wait.
+         *
+         * This callback is called w/o locking the instance of {@link PersisterQueue}.
+         *
+         * @param queueEmpty {@code true} if the queue is empty, which indicates {@link
+         * PersisterQueue} is likely to enter indefinite wait; or {@code false} if there is still
+         * item to process.
+         */
+        void onPreProcessItem(boolean queueEmpty);
+    }
+
+    private class LazyTaskWriterThread extends Thread {
+
+        private LazyTaskWriterThread(String name) {
+            super(name);
+        }
+
+        @Override
+        public void run() {
+            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+            try {
+                while (true) {
+                    final boolean probablyDone;
+                    synchronized (PersisterQueue.this) {
+                        probablyDone = mWriteQueue.isEmpty();
+                    }
+
+                    for (int i = mListeners.size() - 1; i >= 0; --i) {
+                        mListeners.get(i).onPreProcessItem(probablyDone);
+                    }
+
+                    processNextItem();
+                }
+            } catch (InterruptedException e) {
+                Slog.e(TAG, "Persister thread is exiting. Should never happen in prod, but"
+                        + "it's OK in tests.");
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index e11e003..4d0b1da 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -30,8 +30,9 @@
 import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
-
 import static android.os.Process.SYSTEM_UID;
+import static android.view.Display.DEFAULT_DISPLAY;
+
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS_TRIM_TASKS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_TASKS;
@@ -55,7 +56,6 @@
 import android.content.pm.UserInfo;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
-import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.IBinder;
@@ -431,7 +431,7 @@
     void onSystemReadyLocked() {
         loadRecentsComponent(mService.mContext.getResources());
         mTasks.clear();
-        mTaskPersister.startPersisting();
+        mTaskPersister.onSystemReady();
     }
 
     Bitmap getTaskDescriptionIcon(String path) {
@@ -1244,7 +1244,6 @@
      */
     protected boolean isTrimmable(TaskRecord task) {
         final ActivityStack stack = task.getStack();
-        final ActivityStack homeStack = mSupervisor.mHomeStack;
 
         // No stack for task, just trim it
         if (stack == null) {
@@ -1252,13 +1251,14 @@
         }
 
         // Ignore tasks from different displays
-        if (stack.getDisplay() != homeStack.getDisplay()) {
+        // TODO (b/115289124): No Recents on non-default displays.
+        if (stack.mDisplayId != DEFAULT_DISPLAY) {
             return false;
         }
 
         // Trim tasks that are in stacks that are behind the home stack
         final ActivityDisplay display = stack.getDisplay();
-        return display.getIndexOf(stack) < display.getIndexOf(homeStack);
+        return display.getIndexOf(stack) < display.getIndexOf(display.getHomeStack());
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index 481bb2b..fc5dfb4 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -16,13 +16,14 @@
 
 package com.android.server.am;
 
+import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
+
 import android.annotation.NonNull;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.os.Debug;
 import android.os.Environment;
 import android.os.FileUtils;
-import android.os.Process;
 import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.AtomicFile;
@@ -34,6 +35,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
+
 import libcore.io.IoUtils;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -54,32 +56,18 @@
 import java.util.Comparator;
 import java.util.List;
 
-import static com.android.server.am.ActivityStackSupervisor.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS;
-
-public class TaskPersister {
+/**
+ * Persister that saves recent tasks into disk.
+ */
+public class TaskPersister implements PersisterQueue.Listener {
     static final String TAG = "TaskPersister";
     static final boolean DEBUG = false;
-
-    /** When not flushing don't write out files faster than this */
-    private static final long INTER_WRITE_DELAY_MS = 500;
-
-    /**
-     * When not flushing delay this long before writing the first file out. This gives the next task
-     * being launched a chance to load its resources without this occupying IO bandwidth.
-     */
-    private static final long PRE_TASK_DELAY_MS = 3000;
-
-    /** The maximum number of entries to keep in the queue before draining it automatically. */
-    private static final int MAX_WRITE_QUEUE_LENGTH = 6;
-
-    /** Special value for mWriteTime to mean don't wait, just write */
-    private static final long FLUSH_QUEUE = -1;
+    static final String IMAGE_EXTENSION = ".png";
 
     private static final String TASKS_DIRNAME = "recent_tasks";
     private static final String TASK_FILENAME_SUFFIX = "_task.xml";
     private static final String IMAGES_DIRNAME = "recent_images";
     private static final String PERSISTED_TASK_IDS_FILENAME = "persisted_taskIds.txt";
-    static final String IMAGE_EXTENSION = ".png";
 
     private static final String TAG_TASK = "task";
 
@@ -90,39 +78,9 @@
     private final File mTaskIdsDir;
     // To lock file operations in TaskPersister
     private final Object mIoLock = new Object();
+    private final PersisterQueue mPersisterQueue;
 
-    /**
-     * Value determines write delay mode as follows: < 0 We are Flushing. No delays between writes
-     * until the image queue is drained and all tasks needing persisting are written to disk. There
-     * is no delay between writes. == 0 We are Idle. Next writes will be delayed by
-     * #PRE_TASK_DELAY_MS. > 0 We are Actively writing. Next write will be at this time. Subsequent
-     * writes will be delayed by #INTER_WRITE_DELAY_MS.
-     */
-    private long mNextWriteTime = 0;
-
-    private final LazyTaskWriterThread mLazyTaskWriterThread;
-
-    private static class WriteQueueItem {}
-
-    private static class TaskWriteQueueItem extends WriteQueueItem {
-        final TaskRecord mTask;
-
-        TaskWriteQueueItem(TaskRecord task) {
-            mTask = task;
-        }
-    }
-
-    private static class ImageWriteQueueItem extends WriteQueueItem {
-        final String mFilePath;
-        Bitmap mImage;
-
-        ImageWriteQueueItem(String filePath, Bitmap image) {
-            mFilePath = filePath;
-            mImage = image;
-        }
-    }
-
-    ArrayList<WriteQueueItem> mWriteQueue = new ArrayList<WriteQueueItem>();
+    private final ArraySet<Integer> mTmpTaskIds = new ArraySet<>();
 
     TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor,
             ActivityTaskManagerService service, RecentTasks recentTasks) {
@@ -145,7 +103,8 @@
         mStackSupervisor = stackSupervisor;
         mService = service;
         mRecentTasks = recentTasks;
-        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThread");
+        mPersisterQueue = new PersisterQueue();
+        mPersisterQueue.addListener(this);
     }
 
     @VisibleForTesting
@@ -154,42 +113,21 @@
         mStackSupervisor = null;
         mService = null;
         mRecentTasks = null;
-        mLazyTaskWriterThread = new LazyTaskWriterThread("LazyTaskWriterThreadTest");
+        mPersisterQueue = new PersisterQueue();
+        mPersisterQueue.addListener(this);
     }
 
-    void startPersisting() {
-        if (!mLazyTaskWriterThread.isAlive()) {
-            mLazyTaskWriterThread.start();
-        }
+    void onSystemReady() {
+        mPersisterQueue.startPersisting();
     }
 
     private void removeThumbnails(TaskRecord task) {
-        final String taskString = Integer.toString(task.taskId);
-        for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
-            final WriteQueueItem item = mWriteQueue.get(queueNdx);
-            if (item instanceof ImageWriteQueueItem) {
-                final File thumbnailFile = new File(((ImageWriteQueueItem) item).mFilePath);
-                if (thumbnailFile.getName().startsWith(taskString)) {
-                    if (DEBUG) {
-                        Slog.d(TAG, "Removing " + ((ImageWriteQueueItem) item).mFilePath +
-                                " from write queue");
-                    }
-                    mWriteQueue.remove(queueNdx);
-                }
-            }
-        }
-    }
-
-    private void yieldIfQueueTooDeep() {
-        boolean stall = false;
-        synchronized (this) {
-            if (mNextWriteTime == FLUSH_QUEUE) {
-                stall = true;
-            }
-        }
-        if (stall) {
-            Thread.yield();
-        }
+        mPersisterQueue.removeItems(
+                item -> {
+                    File file = new File(item.mFilePath);
+                    return file.getName().startsWith(Integer.toString(task.taskId));
+                },
+                ImageWriteQueueItem.class);
     }
 
     @NonNull
@@ -251,84 +189,51 @@
     }
 
     void wakeup(TaskRecord task, boolean flush) {
-        synchronized (this) {
+        synchronized (mPersisterQueue) {
             if (task != null) {
-                int queueNdx;
-                for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
-                    final WriteQueueItem item = mWriteQueue.get(queueNdx);
-                    if (item instanceof TaskWriteQueueItem &&
-                            ((TaskWriteQueueItem) item).mTask == task) {
-                        if (!task.inRecents) {
-                            // This task is being removed.
-                            removeThumbnails(task);
-                        }
-                        break;
-                    }
+                final TaskWriteQueueItem item = mPersisterQueue.findLastItem(
+                        queueItem -> task == queueItem.mTask, TaskWriteQueueItem.class);
+                if (item != null && !task.inRecents) {
+                    removeThumbnails(task);
                 }
-                if (queueNdx < 0 && task.isPersistable) {
-                    mWriteQueue.add(new TaskWriteQueueItem(task));
+
+                if (item == null && task.isPersistable) {
+                    mPersisterQueue.addItem(new TaskWriteQueueItem(task, mService), flush);
                 }
             } else {
                 // Dummy. Ensures removeObsoleteFiles is called when LazyTaskThreadWriter is
                 // notified.
-                mWriteQueue.add(new WriteQueueItem());
+                mPersisterQueue.addItem(PersisterQueue.EMPTY_ITEM, flush);
             }
-            if (flush || mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
-                mNextWriteTime = FLUSH_QUEUE;
-            } else if (mNextWriteTime == 0) {
-                mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+            if (DEBUG) {
+                Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " Callers="
+                        + Debug.getCallers(4));
             }
-            if (DEBUG) Slog.d(TAG, "wakeup: task=" + task + " flush=" + flush + " mNextWriteTime="
-                    + mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size()
-                    + " Callers=" + Debug.getCallers(4));
-            notifyAll();
         }
 
-        yieldIfQueueTooDeep();
+        mPersisterQueue.yieldIfQueueTooDeep();
     }
 
     void flush() {
-        synchronized (this) {
-            mNextWriteTime = FLUSH_QUEUE;
-            notifyAll();
-            do {
-                try {
-                    wait();
-                } catch (InterruptedException e) {
-                }
-            } while (mNextWriteTime == FLUSH_QUEUE);
-        }
+        mPersisterQueue.flush();
     }
 
     void saveImage(Bitmap image, String filePath) {
-        synchronized (this) {
-            int queueNdx;
-            for (queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
-                final WriteQueueItem item = mWriteQueue.get(queueNdx);
-                if (item instanceof ImageWriteQueueItem) {
-                    ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
-                    if (imageWriteQueueItem.mFilePath.equals(filePath)) {
-                        // replace the Bitmap with the new one.
-                        imageWriteQueueItem.mImage = image;
-                        break;
-                    }
-                }
-            }
-            if (queueNdx < 0) {
-                mWriteQueue.add(new ImageWriteQueueItem(filePath, image));
-            }
-            if (mWriteQueue.size() > MAX_WRITE_QUEUE_LENGTH) {
-                mNextWriteTime = FLUSH_QUEUE;
-            } else if (mNextWriteTime == 0) {
-                mNextWriteTime = SystemClock.uptimeMillis() + PRE_TASK_DELAY_MS;
+        synchronized (mPersisterQueue) {
+            final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+                    queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+            if (item != null) {
+                // replace the Bitmap with the new one.
+                item.mImage = image;
+            } else {
+                mPersisterQueue.addItem(new ImageWriteQueueItem(filePath, image),
+                        /* flush */ false);
             }
             if (DEBUG) Slog.d(TAG, "saveImage: filePath=" + filePath + " now=" +
-                    SystemClock.uptimeMillis() + " mNextWriteTime=" +
-                    mNextWriteTime + " Callers=" + Debug.getCallers(4));
-            notifyAll();
+                    SystemClock.uptimeMillis() + " Callers=" + Debug.getCallers(4));
         }
 
-        yieldIfQueueTooDeep();
+        mPersisterQueue.yieldIfQueueTooDeep();
     }
 
     Bitmap getTaskDescriptionIcon(String filePath) {
@@ -340,41 +245,10 @@
         return restoreImage(filePath);
     }
 
-    Bitmap getImageFromWriteQueue(String filePath) {
-        synchronized (this) {
-            for (int queueNdx = mWriteQueue.size() - 1; queueNdx >= 0; --queueNdx) {
-                final WriteQueueItem item = mWriteQueue.get(queueNdx);
-                if (item instanceof ImageWriteQueueItem) {
-                    ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
-                    if (imageWriteQueueItem.mFilePath.equals(filePath)) {
-                        return imageWriteQueueItem.mImage;
-                    }
-                }
-            }
-            return null;
-        }
-    }
-
-    private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
-        if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
-        final XmlSerializer xmlSerializer = new FastXmlSerializer();
-        StringWriter stringWriter = new StringWriter();
-        xmlSerializer.setOutput(stringWriter);
-
-        if (DEBUG) xmlSerializer.setFeature(
-                "http://xmlpull.org/v1/doc/features.html#indent-output", true);
-
-        // save task
-        xmlSerializer.startDocument(null, true);
-
-        xmlSerializer.startTag(null, TAG_TASK);
-        task.saveToXml(xmlSerializer);
-        xmlSerializer.endTag(null, TAG_TASK);
-
-        xmlSerializer.endDocument();
-        xmlSerializer.flush();
-
-        return stringWriter;
+    private Bitmap getImageFromWriteQueue(String filePath) {
+        final ImageWriteQueueItem item = mPersisterQueue.findLastItem(
+                queueItem -> queueItem.mFilePath.equals(filePath), ImageWriteQueueItem.class);
+        return item != null ? item.mImage : null;
     }
 
     private String fileToString(File file) {
@@ -534,6 +408,26 @@
         return tasks;
     }
 
+    @Override
+    public void onPreProcessItem(boolean queueEmpty) {
+        // We can't lock mService while locking the queue, but we don't want to
+        // call removeObsoleteFiles before every item, only the last time
+        // before going to sleep. The risk is that we call removeObsoleteFiles()
+        // successively.
+        if (queueEmpty) {
+            if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
+            mTmpTaskIds.clear();
+            synchronized (mService.mGlobalLock) {
+                if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
+                mRecentTasks.getPersistableTaskIds(mTmpTaskIds);
+                mService.mWindowManager.removeObsoleteTaskFiles(mTmpTaskIds,
+                        mRecentTasks.usersWithRecentsLoadedLocked());
+            }
+            removeObsoleteFiles(mTmpTaskIds);
+        }
+        writeTaskIdsFiles();
+    }
+
     private static void removeObsoleteFiles(ArraySet<Integer> persistentTaskIds, File[] files) {
         if (DEBUG) Slog.d(TAG, "removeObsoleteFiles: persistentTaskIds=" + persistentTaskIds +
                 " files=" + files);
@@ -631,143 +525,117 @@
         return parentDir.exists() || parentDir.mkdirs();
     }
 
-    private class LazyTaskWriterThread extends Thread {
+    private static class TaskWriteQueueItem implements PersisterQueue.WriteQueueItem {
+        private final ActivityTaskManagerService mService;
+        private final TaskRecord mTask;
 
-        LazyTaskWriterThread(String name) {
-            super(name);
+        TaskWriteQueueItem(TaskRecord task, ActivityTaskManagerService service) {
+            mTask = task;
+            mService = service;
+        }
+
+        private StringWriter saveToXml(TaskRecord task) throws IOException, XmlPullParserException {
+            if (DEBUG) Slog.d(TAG, "saveToXml: task=" + task);
+            final XmlSerializer xmlSerializer = new FastXmlSerializer();
+            StringWriter stringWriter = new StringWriter();
+            xmlSerializer.setOutput(stringWriter);
+
+            if (DEBUG) {
+                xmlSerializer.setFeature(
+                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+            }
+
+            // save task
+            xmlSerializer.startDocument(null, true);
+
+            xmlSerializer.startTag(null, TAG_TASK);
+            task.saveToXml(xmlSerializer);
+            xmlSerializer.endTag(null, TAG_TASK);
+
+            xmlSerializer.endDocument();
+            xmlSerializer.flush();
+
+            return stringWriter;
         }
 
         @Override
-        public void run() {
-            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-            ArraySet<Integer> persistentTaskIds = new ArraySet<>();
-            while (true) {
-                // We can't lock mService while holding TaskPersister.this, but we don't want to
-                // call removeObsoleteFiles every time through the loop, only the last time before
-                // going to sleep. The risk is that we call removeObsoleteFiles() successively.
-                final boolean probablyDone;
-                synchronized (TaskPersister.this) {
-                    probablyDone = mWriteQueue.isEmpty();
-                }
-                if (probablyDone) {
-                    if (DEBUG) Slog.d(TAG, "Looking for obsolete files.");
-                    persistentTaskIds.clear();
-                    synchronized (mService.mGlobalLock) {
-                        if (DEBUG) Slog.d(TAG, "mRecents=" + mRecentTasks);
-                        mRecentTasks.getPersistableTaskIds(persistentTaskIds);
-                        mService.mWindowManager.removeObsoleteTaskFiles(persistentTaskIds,
-                                mRecentTasks.usersWithRecentsLoadedLocked());
+        public void process() {
+            // Write out one task.
+            StringWriter stringWriter = null;
+            TaskRecord task = mTask;
+            if (DEBUG) Slog.d(TAG, "Writing task=" + task);
+            synchronized (mService.mGlobalLock) {
+                if (task.inRecents) {
+                    // Still there.
+                    try {
+                        if (DEBUG) Slog.d(TAG, "Saving task=" + task);
+                        stringWriter = saveToXml(task);
+                    } catch (IOException e) {
+                    } catch (XmlPullParserException e) {
                     }
-                    removeObsoleteFiles(persistentTaskIds);
                 }
-                writeTaskIdsFiles();
-
-                processNextItem();
+            }
+            if (stringWriter != null) {
+                // Write out xml file while not holding mService lock.
+                FileOutputStream file = null;
+                AtomicFile atomicFile = null;
+                try {
+                    atomicFile = new AtomicFile(new File(
+                            getUserTasksDir(task.userId),
+                            String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
+                    file = atomicFile.startWrite();
+                    file.write(stringWriter.toString().getBytes());
+                    file.write('\n');
+                    atomicFile.finishWrite(file);
+                } catch (IOException e) {
+                    if (file != null) {
+                        atomicFile.failWrite(file);
+                    }
+                    Slog.e(TAG,
+                            "Unable to open " + atomicFile + " for persisting. " + e);
+                }
             }
         }
 
-        private void processNextItem() {
-            // This part is extracted into a method so that the GC can clearly see the end of the
-            // scope of the variable 'item'.  If this part was in the loop above, the last item
-            // it processed would always "leak".
-            // See https://b.corp.google.com/issues/64438652#comment7
+        @Override
+        public String toString() {
+            return "TaskWriteQueueItem{task=" + mTask + "}";
+        }
+    }
 
-            // If mNextWriteTime, then don't delay between each call to saveToXml().
-            final WriteQueueItem item;
-            synchronized (TaskPersister.this) {
-                if (mNextWriteTime != FLUSH_QUEUE) {
-                    // The next write we don't have to wait so long.
-                    mNextWriteTime = SystemClock.uptimeMillis() + INTER_WRITE_DELAY_MS;
-                    if (DEBUG) Slog.d(TAG, "Next write time may be in " +
-                            INTER_WRITE_DELAY_MS + " msec. (" + mNextWriteTime + ")");
-                }
+    private static class ImageWriteQueueItem implements PersisterQueue.WriteQueueItem {
+        final String mFilePath;
+        Bitmap mImage;
 
-                while (mWriteQueue.isEmpty()) {
-                    if (mNextWriteTime != 0) {
-                        mNextWriteTime = 0; // idle.
-                        TaskPersister.this.notifyAll(); // wake up flush() if needed.
-                    }
-                    try {
-                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting indefinitely.");
-                        TaskPersister.this.wait();
-                    } catch (InterruptedException e) {
-                    }
-                    // Invariant: mNextWriteTime is either FLUSH_QUEUE or PRE_WRITE_DELAY_MS
-                    // from now.
-                }
-                item = mWriteQueue.remove(0);
+        ImageWriteQueueItem(String filePath, Bitmap image) {
+            mFilePath = filePath;
+            mImage = image;
+        }
 
-                long now = SystemClock.uptimeMillis();
-                if (DEBUG) Slog.d(TAG, "LazyTaskWriter: now=" + now + " mNextWriteTime=" +
-                        mNextWriteTime + " mWriteQueue.size=" + mWriteQueue.size());
-                while (now < mNextWriteTime) {
-                    try {
-                        if (DEBUG) Slog.d(TAG, "LazyTaskWriter: waiting " +
-                                (mNextWriteTime - now));
-                        TaskPersister.this.wait(mNextWriteTime - now);
-                    } catch (InterruptedException e) {
-                    }
-                    now = SystemClock.uptimeMillis();
-                }
-
-                // Got something to do.
+        @Override
+        public void process() {
+            final String filePath = mFilePath;
+            if (!createParentDirectory(filePath)) {
+                Slog.e(TAG, "Error while creating images directory for file: " + filePath);
+                return;
             }
-
-            if (item instanceof ImageWriteQueueItem) {
-                ImageWriteQueueItem imageWriteQueueItem = (ImageWriteQueueItem) item;
-                final String filePath = imageWriteQueueItem.mFilePath;
-                if (!createParentDirectory(filePath)) {
-                    Slog.e(TAG, "Error while creating images directory for file: " + filePath);
-                    return;
-                }
-                final Bitmap bitmap = imageWriteQueueItem.mImage;
-                if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
-                FileOutputStream imageFile = null;
-                try {
-                    imageFile = new FileOutputStream(new File(filePath));
-                    bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
-                } catch (Exception e) {
-                    Slog.e(TAG, "saveImage: unable to save " + filePath, e);
-                } finally {
-                    IoUtils.closeQuietly(imageFile);
-                }
-            } else if (item instanceof TaskWriteQueueItem) {
-                // Write out one task.
-                StringWriter stringWriter = null;
-                TaskRecord task = ((TaskWriteQueueItem) item).mTask;
-                if (DEBUG) Slog.d(TAG, "Writing task=" + task);
-                synchronized (mService.mGlobalLock) {
-                    if (task.inRecents) {
-                        // Still there.
-                        try {
-                            if (DEBUG) Slog.d(TAG, "Saving task=" + task);
-                            stringWriter = saveToXml(task);
-                        } catch (IOException e) {
-                        } catch (XmlPullParserException e) {
-                        }
-                    }
-                }
-                if (stringWriter != null) {
-                    // Write out xml file while not holding mService lock.
-                    FileOutputStream file = null;
-                    AtomicFile atomicFile = null;
-                    try {
-                        atomicFile = new AtomicFile(new File(
-                                getUserTasksDir(task.userId),
-                                String.valueOf(task.taskId) + TASK_FILENAME_SUFFIX));
-                        file = atomicFile.startWrite();
-                        file.write(stringWriter.toString().getBytes());
-                        file.write('\n');
-                        atomicFile.finishWrite(file);
-                    } catch (IOException e) {
-                        if (file != null) {
-                            atomicFile.failWrite(file);
-                        }
-                        Slog.e(TAG,
-                                "Unable to open " + atomicFile + " for persisting. " + e);
-                    }
-                }
+            final Bitmap bitmap = mImage;
+            if (DEBUG) Slog.d(TAG, "writing bitmap: filename=" + filePath);
+            FileOutputStream imageFile = null;
+            try {
+                imageFile = new FileOutputStream(new File(filePath));
+                bitmap.compress(Bitmap.CompressFormat.PNG, 100, imageFile);
+            } catch (Exception e) {
+                Slog.e(TAG, "saveImage: unable to save " + filePath, e);
+            } finally {
+                IoUtils.closeQuietly(imageFile);
             }
         }
+
+        @Override
+        public String toString() {
+            return "ImageWriteQueueItem{path=" + mFilePath
+                    + ", image=(" + mImage.getWidth() + "x" + mImage.getHeight() + ")}";
+        }
     }
 }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index ef8cb1c..05b0d59 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -45,7 +45,6 @@
 import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ADD_REMOVE;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_LOCKTASK;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_RECENTS;
@@ -75,7 +74,6 @@
 import static com.android.server.am.TaskRecordProto.REAL_ACTIVITY;
 import static com.android.server.am.TaskRecordProto.RESIZE_MODE;
 import static com.android.server.am.TaskRecordProto.STACK_ID;
-
 import static java.lang.Integer.MAX_VALUE;
 
 import android.annotation.IntDef;
@@ -1686,11 +1684,19 @@
         // so that the user can not render the task too small to manipulate. We don't need
         // to do this for the pinned stack as the bounds are controlled by the system.
         if (!inPinnedWindowingMode()) {
+            final int defaultMinSizeDp =
+                    mService.mStackSupervisor.mDefaultMinSizeOfResizeableTaskDp;
+            final ActivityDisplay display =
+                    mService.mStackSupervisor.getActivityDisplay(mStack.mDisplayId);
+            final float density =
+                    (float) display.getConfiguration().densityDpi / DisplayMetrics.DENSITY_DEFAULT;
+            final int defaultMinSize = (int) (defaultMinSizeDp * density);
+
             if (minWidth == INVALID_MIN_SIZE) {
-                minWidth = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
+                minWidth = defaultMinSize;
             }
             if (minHeight == INVALID_MIN_SIZE) {
-                minHeight = mService.mStackSupervisor.mDefaultMinSizeOfResizeableTask;
+                minHeight = defaultMinSize;
             }
         }
         final boolean adjustWidth = minWidth > bounds.width();
diff --git a/services/core/java/com/android/server/appbinding/AppBindingConstants.java b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
index c2655a2..7184769 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingConstants.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingConstants.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.appbinding;
 
+import android.content.Context;
 import android.util.KeyValueListParser;
 import android.util.Slog;
 
@@ -24,7 +25,7 @@
 /**
  * Constants that are configurable via the global settings for {@link AppBindingService}.
  */
-class AppBindingConstants {
+public class AppBindingConstants {
     private static final String TAG = AppBindingService.TAG;
 
     private static final String SERVICE_RECONNECT_BACKOFF_SEC_KEY =
@@ -36,6 +37,15 @@
     private static final String SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
             "service_reconnect_max_backoff_sec";
 
+    private static final String SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
+            "service_stable_connection_threshold_sec";
+
+    private static final String SMS_SERVICE_ENABLED_KEY =
+            "sms_service_enabled";
+
+    private static final String SMS_APP_BIND_FLAGS_KEY =
+            "sms_app_bind_flags";
+
     public final String sourceSettings;
 
     /**
@@ -54,6 +64,21 @@
      */
     public final long SERVICE_RECONNECT_MAX_BACKOFF_SEC;
 
+    /**
+     * If a connection lasts more than this duration, we reset the re-connect back-off time.
+     */
+    public final long SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
+
+    /**
+     * Whether to actually bind to the default SMS app service. (Feature flag)
+     */
+    public final boolean SMS_SERVICE_ENABLED;
+
+    /**
+     * Extra binding flags for SMS service.
+     */
+    public final int SMS_APP_BIND_FLAGS;
+
     private AppBindingConstants(String settings) {
         sourceSettings = settings;
 
@@ -67,13 +92,22 @@
         }
 
         long serviceReconnectBackoffSec = parser.getLong(
-                SERVICE_RECONNECT_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
+                SERVICE_RECONNECT_BACKOFF_SEC_KEY, 10);
 
         double serviceReconnectBackoffIncrease = parser.getFloat(
                 SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f);
 
         long serviceReconnectMaxBackoffSec = parser.getLong(
-                SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
+                SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1));
+
+        boolean smsServiceEnabled = parser.getBoolean(SMS_SERVICE_ENABLED_KEY, true);
+
+        int smsAppBindFlags = parser.getInt(
+                SMS_APP_BIND_FLAGS_KEY,
+                Context.BIND_NOT_VISIBLE | Context.BIND_FOREGROUND_SERVICE);
+
+        long serviceStableConnectionThresholdSec = parser.getLong(
+                SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, TimeUnit.MINUTES.toSeconds(2));
 
         // Set minimum: 5 seconds.
         serviceReconnectBackoffSec = Math.max(5, serviceReconnectBackoffSec);
@@ -89,6 +123,9 @@
         SERVICE_RECONNECT_BACKOFF_SEC = serviceReconnectBackoffSec;
         SERVICE_RECONNECT_BACKOFF_INCREASE = serviceReconnectBackoffIncrease;
         SERVICE_RECONNECT_MAX_BACKOFF_SEC = serviceReconnectMaxBackoffSec;
+        SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = serviceStableConnectionThresholdSec;
+        SMS_SERVICE_ENABLED = smsServiceEnabled;
+        SMS_APP_BIND_FLAGS = smsAppBindFlags;
     }
 
     /**
@@ -103,7 +140,8 @@
      */
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
-        pw.println("Constants:");
+        pw.print("Constants: ");
+        pw.println(sourceSettings);
 
         pw.print(prefix);
         pw.print("  SERVICE_RECONNECT_BACKOFF_SEC: ");
@@ -116,5 +154,17 @@
         pw.print(prefix);
         pw.print("  SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
         pw.println(SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+
+        pw.print(prefix);
+        pw.print("  SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: ");
+        pw.println(SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
+
+        pw.print(prefix);
+        pw.print("  SMS_SERVICE_ENABLED: ");
+        pw.println(SMS_SERVICE_ENABLED);
+
+        pw.print(prefix);
+        pw.print("  SMS_APP_BIND_FLAGS: 0x");
+        pw.println(Integer.toHexString(SMS_APP_BIND_FLAGS));
     }
 }
diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java
index c5cb2a4..3131255 100644
--- a/services/core/java/com/android/server/appbinding/AppBindingService.java
+++ b/services/core/java/com/android/server/appbinding/AppBindingService.java
@@ -21,6 +21,7 @@
 import android.app.AppGlobals;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -58,10 +59,11 @@
  *
  * <p>As of android Q, we only use it for the default SMS app.
  *
- * TODO Unit tests
- * TODO How do we handle force stop??
- * TODO Change OOM adjustment to 200 or so
- * TODO Only allow it when the service is associated with a secondary process.
+ * Relevant tests:
+ * atest CtsAppBindingHostTestCases
+ *
+ * TODO Maybe handle force-stop differently. Right now we just get "binder died" and re-bind
+ * after a timeout. b/116813347
  */
 public class AppBindingService extends Binder {
     public static final String TAG = "AppBindingService";
@@ -91,17 +93,25 @@
         public IPackageManager getIPackageManager() {
             return AppGlobals.getPackageManager();
         }
+
+        public String getGlobalSettingString(ContentResolver resolver, String key) {
+            return Settings.Global.getString(resolver, key);
+        }
     }
 
     /**
      * {@link SystemService} for this service.
      */
-    public static final class Lifecycle extends SystemService {
+    public static class Lifecycle extends SystemService {
         final AppBindingService mService;
 
         public Lifecycle(Context context) {
+            this(context, new Injector());
+        }
+
+        Lifecycle(Context context, Injector injector) {
             super(context);
-            mService = new AppBindingService(new Injector(), context);
+            mService = new AppBindingService(injector, context);
         }
 
         @Override
@@ -171,7 +181,6 @@
         packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
         packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        packageFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
         packageFilter.addDataScheme("package");
 
         packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
@@ -197,7 +206,7 @@
     };
 
     private void refreshConstants() {
-        final String newSetting = Settings.Global.getString(
+        final String newSetting = mInjector.getGlobalSettingString(
                 mContext.getContentResolver(), Global.APP_BINDING_CONSTANTS);
 
         synchronized (mLock) {
@@ -215,6 +224,9 @@
     final BroadcastReceiver mPackageUserMonitor = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
+            if (DEBUG) {
+                Slog.d(TAG, "Broadcast received: " + intent);
+            }
             final int userId  = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
             if (userId == UserHandle.USER_NULL) {
                 Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
@@ -410,7 +422,7 @@
                 unbindServicesLocked(userId, target, reasonForLog);
             }
 
-            final ServiceInfo service = app.findService(userId, mIPackageManager);
+            final ServiceInfo service = app.findService(userId, mIPackageManager, mConstants);
             if (service == null) {
                 continue;
             }
@@ -454,14 +466,15 @@
             super(TAG, context, handler, userId, componentName,
                     constants.SERVICE_RECONNECT_BACKOFF_SEC,
                     constants.SERVICE_RECONNECT_BACKOFF_INCREASE,
-                    constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+                    constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC,
+                    constants.SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
             mFinder = finder;
             mConstants = constants;
         }
 
         @Override
         protected int getBindFlags() {
-            return Context.BIND_FOREGROUND_SERVICE;
+            return mFinder.getBindFlags(mConstants);
         }
 
         @Override
@@ -535,9 +548,21 @@
                 pw.print(conn.isBound() ? "bound" : "not-bound");
                 pw.print(",");
                 pw.print(conn.isConnected() ? "connected" : "not-connected");
+                pw.print(",#con=");
+                pw.print(conn.getNumConnected());
+                pw.print(",#dis=");
+                pw.print(conn.getNumDisconnected());
+                pw.print(",#died=");
+                pw.print(conn.getNumBindingDied());
+                pw.print(",backoff=");
+                pw.print(conn.getNextBackoffMs());
                 pw.println();
             }
             forAllAppsLocked((app) -> app.dumpSimple(pw));
         }
     }
+
+    AppBindingConstants getConstantsForTest() {
+        return mConstants;
+    }
 }
diff --git a/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
index 68c5e496..a075c50 100644
--- a/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java
@@ -24,10 +24,12 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.appbinding.AppBindingConstants;
 import com.android.server.appbinding.AppBindingService;
 import com.android.server.appbinding.AppBindingUtils;
 
@@ -51,13 +53,13 @@
     private final Object mLock = new Object();
 
     @GuardedBy("mLock")
-    private final SparseArray<String> mTargetPackages = new SparseArray(1);
+    private final SparseArray<String> mTargetPackages = new SparseArray(4);
 
     @GuardedBy("mLock")
-    private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(1);
+    private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(4);
 
     @GuardedBy("mLock")
-    private final SparseArray<String> mLastMessages = new SparseArray(1);
+    private final SparseArray<String> mLastMessages = new SparseArray(4);
 
     public AppServiceFinder(Context context,
             BiConsumer<AppServiceFinder, Integer> listener,
@@ -67,6 +69,11 @@
         mHandler = callbackHandler;
     }
 
+    /** Whether this service should really be enabled. */
+    protected boolean isEnabled(AppBindingConstants constants) {
+        return true;
+    }
+
     /** Human readable description of the type of apps; e.g. [Default SMS app] */
     @NonNull
     public abstract String getAppDescription();
@@ -80,6 +87,7 @@
         synchronized (mLock) {
             mTargetPackages.delete(userId);
             mTargetServices.delete(userId);
+            mLastMessages.delete(userId);
         }
     }
 
@@ -87,10 +95,19 @@
      * Find the target service from the target app on a given user.
      */
     @Nullable
-    public final ServiceInfo findService(int userId, IPackageManager ipm) {
+    public final ServiceInfo findService(int userId, IPackageManager ipm,
+            AppBindingConstants constants) {
         synchronized (mLock) {
             mTargetPackages.put(userId, null);
             mTargetServices.put(userId, null);
+            mLastMessages.put(userId, null);
+
+            if (!isEnabled(constants)) {
+                final String message = "feature disabled";
+                mLastMessages.put(userId, message);
+                Slog.i(TAG, getAppDescription() + " " + message);
+                return null;
+            }
 
             final String targetPackage = getTargetPackage(userId);
             if (DEBUG) {
@@ -118,11 +135,18 @@
                 final String message = errorMessage.toString();
                 mLastMessages.put(userId, message);
                 if (DEBUG) {
+                    // This log is optional because findService() already did Log.e().
                     Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId
                             + " " + message);
                 }
                 return null;
             }
+            final String error = validateService(service);
+            if (error != null) {
+                mLastMessages.put(userId, error);
+                Log.e(TAG, error);
+                return null;
+            }
 
             final String message = "Valid service found";
             mLastMessages.put(userId, message);
@@ -156,6 +180,17 @@
     @NonNull
     protected abstract String getServicePermission();
 
+    /**
+     * Subclass can implement it to decide whether to accept a service (by returning null) or not
+     * (by returning an error message.)
+     */
+    protected String validateService(ServiceInfo service) {
+        return null;
+    }
+
+    /** Return the bind flags for this service. */
+    public abstract int getBindFlags(AppBindingConstants constants);
+
     /** Dumpsys support. */
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix);
@@ -165,24 +200,25 @@
 
         synchronized (mLock) {
             for (int i = 0; i < mTargetPackages.size(); i++) {
+                final int userId = mTargetPackages.keyAt(i);
                 pw.print(prefix);
                 pw.print("  User: ");
-                pw.print(mTargetPackages.keyAt(i));
+                pw.print(userId);
                 pw.println();
 
                 pw.print(prefix);
                 pw.print("    Package: ");
-                pw.print(mTargetPackages.valueAt(i));
+                pw.print(mTargetPackages.get(userId));
                 pw.println();
 
                 pw.print(prefix);
                 pw.print("    Service: ");
-                pw.print(mTargetServices.valueAt(i));
+                pw.print(mTargetServices.get(userId));
                 pw.println();
 
                 pw.print(prefix);
                 pw.print("    Message: ");
-                pw.print(mLastMessages.valueAt(i));
+                pw.print(mLastMessages.get(userId));
                 pw.println();
             }
         }
@@ -192,16 +228,17 @@
     public void dumpSimple(PrintWriter pw) {
         synchronized (mLock) {
             for (int i = 0; i < mTargetPackages.size(); i++) {
+                final int userId = mTargetPackages.keyAt(i);
                 pw.print("finder,");
                 pw.print(getAppDescription());
                 pw.print(",");
-                pw.print(mTargetPackages.keyAt(i)); // User-id
+                pw.print(userId);
                 pw.print(",");
-                pw.print(mTargetPackages.valueAt(i));
+                pw.print(mTargetPackages.get(userId));
                 pw.print(",");
-                pw.print(mTargetServices.valueAt(i));
+                pw.print(mTargetServices.get(userId));
                 pw.print(",");
-                pw.print(mLastMessages.valueAt(i));
+                pw.print(mLastMessages.get(userId));
                 pw.println();
             }
         }
diff --git a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
index c908bd9..fcc28f8 100644
--- a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
+++ b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java
@@ -26,12 +26,17 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ServiceInfo;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.UserHandle;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Slog;
 
+import com.android.internal.R;
 import com.android.internal.telephony.SmsApplication;
+import com.android.server.appbinding.AppBindingConstants;
 
 import java.util.function.BiConsumer;
 
@@ -46,6 +51,12 @@
     }
 
     @Override
+    protected boolean isEnabled(AppBindingConstants constants) {
+        return constants.SMS_SERVICE_ENABLED
+                && mContext.getResources().getBoolean(R.bool.config_useSmsAppService);
+    }
+
+    @Override
     public String getAppDescription() {
         return "[Default SMS app]";
     }
@@ -74,7 +85,13 @@
     public String getTargetPackage(int userId) {
         final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser(
                 mContext, /* updateIfNeeded= */ true, userId);
-        return cn == null ? null : cn.getPackageName();
+        String ret = cn == null ? null : cn.getPackageName();
+
+        if (DEBUG) {
+            Slog.d(TAG, "getTargetPackage()=" + ret);
+        }
+
+        return ret;
     }
 
     @Override
@@ -84,6 +101,22 @@
                 /* permission= */ null, mHandler);
     }
 
+    @Override
+    protected String validateService(ServiceInfo service) {
+        final String packageName = service.packageName;
+        final String process = service.processName;
+
+        if (process == null || TextUtils.equals(packageName, process)) {
+            return "Service must not run on the main process";
+        }
+        return null; // Null means accept this service.
+    }
+
+    @Override
+    public int getBindFlags(AppBindingConstants constants) {
+        return constants.SMS_APP_BIND_FLAGS;
+    }
+
     private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/biometrics/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
index aa4d34e..8a72a6d 100644
--- a/services/core/java/com/android/server/biometrics/AuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/AuthenticationClient.java
@@ -195,7 +195,7 @@
             // ERROR_CANCELED message.
             return true;
         }
-        if (mBundle != null) {
+        if (mBundle != null && error != BiometricConstants.BIOMETRIC_ERROR_USER_CANCELED) {
             try {
                 mStatusBarService.onBiometricError(getErrorString(error, vendorCode));
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 87cf9c4..3d2f567 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -17,13 +17,20 @@
 package com.android.server.biometrics;
 
 import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
 import static android.Manifest.permission.USE_FINGERPRINT;
 
+import android.app.ActivityManager;
 import android.app.AppOpsManager;
+import android.app.UserSwitchObserver;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.database.ContentObserver;
 import android.hardware.biometrics.BiometricAuthenticator;
 import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricSourceType;
+import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
 import android.hardware.biometrics.IBiometricPromptReceiver;
 import android.hardware.biometrics.IBiometricService;
 import android.hardware.biometrics.IBiometricServiceReceiver;
@@ -31,8 +38,10 @@
 import android.hardware.face.IFaceService;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.IFingerprintService;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -40,19 +49,21 @@
 import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.util.Pair;
 import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.server.SystemService;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * System service that arbitrates the modality for BiometricPrompt to use.
  */
 public class BiometricService extends SystemService {
 
-    private static final String TAG = "BiometricPromptService";
+    private static final String TAG = "BiometricService";
 
     /**
      * No biometric methods or nothing has been enrolled.
@@ -87,6 +98,8 @@
     private final boolean mHasFeatureFingerprint;
     private final boolean mHasFeatureIris;
     private final boolean mHasFeatureFace;
+    private final SettingObserver mSettingObserver;
+    private final List<EnabledOnKeyguardCallback> mEnabledOnKeyguardCallbacks;
 
     private IFingerprintService mFingerprintService;
     private IFaceService mFaceService;
@@ -120,6 +133,107 @@
         }
     }
 
+    private final class SettingObserver extends ContentObserver {
+        private final Uri FACE_UNLOCK_KEYGUARD_ENABLED =
+                Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED);
+        private final Uri FACE_UNLOCK_APP_ENABLED =
+                Settings.Secure.getUriFor(Settings.Secure.FACE_UNLOCK_APP_ENABLED);
+
+        private final ContentResolver mContentResolver;
+        private boolean mFaceEnabledOnKeyguard;
+        private boolean mFaceEnabledForApps;
+
+        /**
+         * Creates a content observer.
+         *
+         * @param handler The handler to run {@link #onChange} on, or null if none.
+         */
+        SettingObserver(Handler handler) {
+            super(handler);
+            mContentResolver = getContext().getContentResolver();
+            updateContentObserver();
+        }
+
+        void updateContentObserver() {
+            mContentResolver.unregisterContentObserver(this);
+            mContentResolver.registerContentObserver(FACE_UNLOCK_KEYGUARD_ENABLED,
+                    false /* notifyForDescendents */,
+                    this /* observer */,
+                    UserHandle.USER_CURRENT);
+            mContentResolver.registerContentObserver(FACE_UNLOCK_APP_ENABLED,
+                    false /* notifyForDescendents */,
+                    this /* observer */,
+                    UserHandle.USER_CURRENT);
+
+            // Update the value immediately
+            onChange(true /* selfChange */, FACE_UNLOCK_KEYGUARD_ENABLED);
+            onChange(true /* selfChange */, FACE_UNLOCK_APP_ENABLED);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (FACE_UNLOCK_KEYGUARD_ENABLED.equals(uri)) {
+                mFaceEnabledOnKeyguard =
+                        Settings.Secure.getIntForUser(
+                                mContentResolver,
+                                Settings.Secure.FACE_UNLOCK_KEYGUARD_ENABLED,
+                                1 /* default */,
+                                UserHandle.USER_CURRENT) != 0;
+
+                List<EnabledOnKeyguardCallback> callbacks = mEnabledOnKeyguardCallbacks;
+                for (int i = 0; i < callbacks.size(); i++) {
+                    callbacks.get(i).notify(BiometricSourceType.FACE, mFaceEnabledOnKeyguard);
+                }
+            } else if (FACE_UNLOCK_APP_ENABLED.equals(uri)) {
+                mFaceEnabledForApps =
+                        Settings.Secure.getIntForUser(
+                                mContentResolver,
+                                Settings.Secure.FACE_UNLOCK_APP_ENABLED,
+                                1 /* default */,
+                                UserHandle.USER_CURRENT) != 0;
+            }
+        }
+
+        boolean getFaceEnabledOnKeyguard() {
+            return mFaceEnabledOnKeyguard;
+        }
+
+        boolean getFaceEnabledForApps() {
+            return mFaceEnabledForApps;
+        }
+    }
+
+    private final class EnabledOnKeyguardCallback implements IBinder.DeathRecipient {
+
+        private final IBiometricEnabledOnKeyguardCallback mCallback;
+
+        EnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback) {
+            mCallback = callback;
+            try {
+                mCallback.asBinder().linkToDeath(EnabledOnKeyguardCallback.this, 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Unable to linkToDeath", e);
+            }
+        }
+
+        void notify(BiometricSourceType sourceType, boolean enabled) {
+            try {
+                mCallback.onChanged(sourceType, enabled);
+            } catch (DeadObjectException e) {
+                Slog.w(TAG, "Death while invoking notify", e);
+                mEnabledOnKeyguardCallbacks.remove(this);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Failed to invoke onChanged", e);
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            Slog.e(TAG, "Enabled callback binder died");
+            mEnabledOnKeyguardCallbacks.remove(this);
+        }
+    }
+
     /**
      * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
      * should not carry any state. The reality is we need to keep a tiny amount of state so that
@@ -131,7 +245,7 @@
         public void authenticate(IBinder token, long sessionId, int userId,
                 IBiometricServiceReceiver receiver, int flags, String opPackageName,
                 Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
-            // Check the USE_BIOMETRIC permission here. In the BiometricService, check do the
+            // Check the USE_BIOMETRIC permission here. In the BiometricServiceBase, check do the
             // AppOps and foreground check.
             checkPermission();
 
@@ -146,8 +260,38 @@
             final int callingUserId = UserHandle.getCallingUserId();
 
             mHandler.post(() -> {
-                mCurrentModality = checkAndGetBiometricModality(receiver);
+                final Pair<Integer, Integer> result = checkAndGetBiometricModality(callingUserId);
+                final int modality = result.first;
+                final int error = result.second;
 
+                // Check for errors, notify callback, and return
+                if (error != BiometricConstants.BIOMETRIC_ERROR_NONE) {
+                    try {
+                        final String hardwareUnavailable =
+                                getContext().getString(R.string.biometric_error_hw_unavailable);
+                        switch (error) {
+                            case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
+                                receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+                                break;
+                            case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
+                                receiver.onError(0 /* deviceId */, error, hardwareUnavailable);
+                                break;
+                            case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
+                                receiver.onError(0 /* deviceId */, error,
+                                        getErrorString(modality, error, 0 /* vendorCode */));
+                                break;
+                            default:
+                                Slog.e(TAG, "Unhandled error");
+                                break;
+                        }
+                    } catch (RemoteException e) {
+                        Slog.e(TAG, "Unable to send error", e);
+                    }
+                    return;
+                }
+
+                // Actually start authentication
+                mCurrentModality = modality;
                 try {
                     // No polymorphism :(
                     if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
@@ -157,18 +301,9 @@
                     } else if (mCurrentModality == BIOMETRIC_IRIS) {
                         Slog.w(TAG, "Unsupported modality");
                     } else if (mCurrentModality == BIOMETRIC_FACE) {
-                        // If the user disabled face for apps, return ERROR_HW_UNAVAILABLE
-                        if (isFaceEnabledForApps()) {
-                            receiver.onError(0 /* deviceId */,
-                                    BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                                    FaceManager.getErrorString(getContext(),
-                                            BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                                            0 /* vendorCode */));
-                        } else {
-                            mFaceService.authenticateFromService(true /* requireConfirmation */,
-                                    token, sessionId, userId, receiver, flags, opPackageName,
-                                    bundle, dialogReceiver, callingUid, callingPid, callingUserId);
-                        }
+                        mFaceService.authenticateFromService(true /* requireConfirmation */,
+                                token, sessionId, userId, receiver, flags, opPackageName,
+                                bundle, dialogReceiver, callingUid, callingPid, callingUserId);
                     } else {
                         Slog.w(TAG, "Unsupported modality");
                     }
@@ -178,15 +313,6 @@
             });
         }
 
-        private boolean isFaceEnabledForApps() {
-            // TODO: maybe cache this and eliminate duplicated code with KeyguardUpdateMonitor
-            return Settings.Secure.getIntForUser(
-                    getContext().getContentResolver(),
-                    Settings.Secure.FACE_UNLOCK_APP_ENABLED,
-                    1 /* default */,
-                    UserHandle.USER_CURRENT) == 0;
-        }
-
         @Override // Binder call
         public void cancelAuthentication(IBinder token, String opPackageName)
                 throws RemoteException {
@@ -221,31 +347,47 @@
         }
 
         @Override // Binder call
-        public boolean hasEnrolledBiometrics(String opPackageName) {
+        public int canAuthenticate(String opPackageName) {
             checkPermission();
+            checkAppOp(opPackageName, Binder.getCallingUid());
 
-            if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, Binder.getCallingUid(),
-                    opPackageName) != AppOpsManager.MODE_ALLOWED) {
-                Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
-                throw new SecurityException("Permission denied");
-            }
-
+            final int userId = UserHandle.getCallingUserId();
             final long ident = Binder.clearCallingIdentity();
-            boolean hasEnrolled = false;
+            int error;
             try {
-                // Note: On devices with multi-modal authentication, the selection logic will need
-                // to be updated.
-                for (int i = 0; i < mAuthenticators.size(); i++) {
-                    if (mAuthenticators.get(i).getAuthenticator().hasEnrolledTemplates()) {
-                        hasEnrolled = true;
-                        break;
-                    }
-                }
+                final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId);
+                error = result.second;
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
-            return hasEnrolled;
+            return error;
         }
+
+        @Override
+        public void registerEnabledOnKeyguardCallback(IBiometricEnabledOnKeyguardCallback callback)
+                throws RemoteException {
+            checkInternalPermission();
+            mEnabledOnKeyguardCallbacks.add(new EnabledOnKeyguardCallback(callback));
+            try {
+                callback.onChanged(BiometricSourceType.FACE,
+                        mSettingObserver.getFaceEnabledOnKeyguard());
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Remote exception", e);
+            }
+        }
+    }
+
+    private void checkAppOp(String opPackageName, int callingUid) {
+        if (mAppOps.noteOp(AppOpsManager.OP_USE_BIOMETRIC, callingUid,
+                opPackageName) != AppOpsManager.MODE_ALLOWED) {
+            Slog.w(TAG, "Rejecting " + opPackageName + "; permission denied");
+            throw new SecurityException("Permission denied");
+        }
+    }
+
+    private void checkInternalPermission() {
+        getContext().enforceCallingPermission(USE_BIOMETRIC_INTERNAL,
+                "Must have MANAGE_BIOMETRIC permission");
     }
 
     private void checkPermission() {
@@ -270,11 +412,26 @@
 
         mAppOps = context.getSystemService(AppOpsManager.class);
         mHandler = new Handler(Looper.getMainLooper());
+        mEnabledOnKeyguardCallbacks = new ArrayList<>();
+        mSettingObserver = new SettingObserver(mHandler);
 
         final PackageManager pm = context.getPackageManager();
         mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
         mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
         mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+
+        try {
+            ActivityManager.getService().registerUserSwitchObserver(
+                    new UserSwitchObserver() {
+                        @Override
+                        public void onUserSwitchComplete(int newUserId) {
+                            mSettingObserver.updateContentObserver();
+                        }
+                    }, BiometricService.class.getName()
+            );
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Failed to register user switch observer", e);
+        }
     }
 
     @Override
@@ -305,65 +462,94 @@
      * Checks if there are any available biometrics, and returns the modality. This method also
      * returns errors through the callback (no biometric feature, hardware not detected, no
      * templates enrolled, etc). This service must not start authentication if errors are sent.
+     *
+     * @Returns A pair [Modality, Error] with Modality being one of {@link #BIOMETRIC_NONE},
+     * {@link #BIOMETRIC_FINGERPRINT}, {@link #BIOMETRIC_IRIS}, {@link #BIOMETRIC_FACE}
+     * and the error containing one of the {@link BiometricConstants} errors.
      */
-    private int checkAndGetBiometricModality(IBiometricServiceReceiver receiver) {
+    private Pair<Integer, Integer> checkAndGetBiometricModality(int callingUid) {
         int modality = BIOMETRIC_NONE;
-        final String hardwareUnavailable =
-                getContext().getString(R.string.biometric_error_hw_unavailable);
 
         // No biometric features, send error
         if (mAuthenticators.isEmpty()) {
-            try {
-                receiver.onError(0 /* deviceId */,
-                        BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
-                        hardwareUnavailable);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to send error", e);
-            }
-            return BIOMETRIC_NONE;
+            return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
         }
 
-        // Find first authenticator that's both detected and enrolled
+        // Assuming that authenticators are listed in priority-order, the rest of this function
+        // will go through and find the first authenticator that's available, enrolled, and enabled.
+        // The tricky part is returning the correct error. Error strings that are modality-specific
+        // should also respect the priority-order.
+
+        // Find first authenticator that's detected, enrolled, and enabled.
         boolean isHardwareDetected = false;
         boolean hasTemplatesEnrolled = false;
+        boolean enabledForApps = false;
+
+        int firstHwAvailable = BIOMETRIC_NONE;
         for (int i = 0; i < mAuthenticators.size(); i++) {
-            int featureId = mAuthenticators.get(i).getType();
+            modality = mAuthenticators.get(i).getType();
             BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
             if (authenticator.isHardwareDetected()) {
                 isHardwareDetected = true;
-                if (authenticator.hasEnrolledTemplates()) {
+                if (firstHwAvailable == BIOMETRIC_NONE) {
+                    // Store the first one since we want to return the error in correct priority
+                    // order.
+                    firstHwAvailable = modality;
+                }
+                if (authenticator.hasEnrolledTemplates(callingUid)) {
                     hasTemplatesEnrolled = true;
-                    modality = featureId;
-                    break;
+                    if (isEnabledForApp(modality)) {
+                        // TODO(b/110907543): When face settings (and other settings) have both a
+                        // user toggle as well as a work profile settings page, this needs to be
+                        // updated to reflect the correct setting.
+                        enabledForApps = true;
+                        break;
+                    }
                 }
             }
         }
 
         // Check error conditions
         if (!isHardwareDetected) {
-            try {
-                receiver.onError(0 /* deviceId */,
-                        BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
-                        hardwareUnavailable);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to send error", e);
-            }
-            return BIOMETRIC_NONE;
-        }
-        if (!hasTemplatesEnrolled) {
-            try {
-                receiver.onError(0 /* deviceId */,
-                        BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
-                        FaceManager.getErrorString(getContext(),
-                                BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
-                                0 /* vendorCode */));
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Unable to send error", e);
-            }
-            return BIOMETRIC_NONE;
+            return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
+        } else if (!hasTemplatesEnrolled) {
+            // Return the modality here so the correct error string can be sent. This error is
+            // preferred over !enabledForApps
+            return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
+        } else if (!enabledForApps) {
+            return new Pair<>(BIOMETRIC_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
         }
 
-        return modality;
+        return new Pair<>(modality, BiometricConstants.BIOMETRIC_ERROR_NONE);
+    }
+
+    private boolean isEnabledForApp(int modality) {
+        switch(modality) {
+            case BIOMETRIC_FINGERPRINT:
+                return true;
+            case BIOMETRIC_IRIS:
+                return true;
+            case BIOMETRIC_FACE:
+                return mSettingObserver.getFaceEnabledForApps();
+            default:
+                Slog.w(TAG, "Unsupported modality: " + modality);
+                return false;
+        }
+    }
+
+    private String getErrorString(int type, int error, int vendorCode) {
+        switch (type) {
+            case BIOMETRIC_FINGERPRINT:
+                return FingerprintManager.getErrorString(getContext(), error, vendorCode);
+            case BIOMETRIC_IRIS:
+                Slog.w(TAG, "Modality not supported");
+                return null; // not supported
+            case BIOMETRIC_FACE:
+                return FaceManager.getErrorString(getContext(), error, vendorCode);
+            default:
+                Slog.w(TAG, "Unable to get error string for modality: " + type);
+                return null;
+        }
     }
 
     private BiometricAuthenticator getAuthenticator(int type) {
diff --git a/services/core/java/com/android/server/connectivity/ProxyTracker.java b/services/core/java/com/android/server/connectivity/ProxyTracker.java
index dc65e1e..b7bbd42 100644
--- a/services/core/java/com/android/server/connectivity/ProxyTracker.java
+++ b/services/core/java/com/android/server/connectivity/ProxyTracker.java
@@ -142,6 +142,35 @@
     }
 
     /**
+     * Read the global proxy settings and cache them in memory.
+     */
+    public void loadGlobalProxy() {
+        ContentResolver res = mContext.getContentResolver();
+        String host = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST);
+        int port = Settings.Global.getInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, 0);
+        String exclList = Settings.Global.getString(res,
+                Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST);
+        String pacFileUrl = Settings.Global.getString(res, Settings.Global.GLOBAL_HTTP_PROXY_PAC);
+        if (!TextUtils.isEmpty(host) || !TextUtils.isEmpty(pacFileUrl)) {
+            ProxyInfo proxyProperties;
+            if (!TextUtils.isEmpty(pacFileUrl)) {
+                proxyProperties = new ProxyInfo(pacFileUrl);
+            } else {
+                proxyProperties = new ProxyInfo(host, port, exclList);
+            }
+            if (!proxyProperties.isValid()) {
+                if (DBG) Slog.d(TAG, "Invalid proxy properties, ignoring: " + proxyProperties);
+                return;
+            }
+
+            synchronized (mProxyLock) {
+                mGlobalProxy = proxyProperties;
+            }
+        }
+        // TODO : shouldn't this function call mPacManager.setCurrentProxyScriptUrl ?
+    }
+
+    /**
      * Sends the system broadcast informing apps about a new proxy configuration.
      *
      * Confusingly this method also sets the PAC file URL. TODO : separate this, it has nothing
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 2405925..7bfe9ce 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -19,7 +19,6 @@
 import android.graphics.Rect;
 import android.hardware.display.DisplayViewport;
 import android.os.IBinder;
-import android.view.Display;
 import android.view.Surface;
 import android.view.SurfaceControl;
 
@@ -224,6 +223,8 @@
         DisplayDeviceInfo info = getDisplayDeviceInfoLocked();
         viewport.deviceWidth = isRotated ? info.height : info.width;
         viewport.deviceHeight = isRotated ? info.width : info.height;
+
+        viewport.uniqueId = info.uniqueId;
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 512e851..c51dc52 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -110,6 +110,13 @@
     public static final int FLAG_MASK_DISPLAY_CUTOUT = 1 << 11;
 
     /**
+     * Flag: This flag identifies secondary displays that should show system decorations, such as
+     * status bar, navigation bar, home activity or IME.
+     * @hide
+     */
+    public static final int FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 1 << 12;
+
+    /**
      * Touch attachment: Display does not receive touch.
      */
     public static final int TOUCH_NONE = 0;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 0eff7f5..e70460a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -17,15 +17,14 @@
 package com.android.server.display;
 
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
+import static android.hardware.display.DisplayManager
+        .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
-import static android.hardware.display.DisplayManager
-        .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.DumpUtils;
-import com.android.internal.util.IndentingPrintWriter;
+import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
+import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL;
 
 import android.Manifest;
 import android.annotation.NonNull;
@@ -45,8 +44,8 @@
 import android.hardware.display.Curve;
 import android.hardware.display.DisplayManagerGlobal;
 import android.hardware.display.DisplayManagerInternal;
-import android.hardware.display.DisplayViewport;
 import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener;
+import android.hardware.display.DisplayViewport;
 import android.hardware.display.IDisplayManager;
 import android.hardware.display.IDisplayManagerCallback;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -83,14 +82,17 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 
-import com.android.internal.util.Preconditions;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.DumpUtils;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.AnimationThread;
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.UiThread;
-import com.android.server.wm.WindowManagerInternal;
 import com.android.server.wm.SurfaceAnimationThread;
+import com.android.server.wm.WindowManagerInternal;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -256,9 +258,8 @@
 
     // Viewports of the default display and the display that should receive touch
     // input from an external source.  Used by the input system.
-    private final DisplayViewport mDefaultViewport = new DisplayViewport();
-    private final DisplayViewport mExternalTouchViewport = new DisplayViewport();
-    private final ArrayList<DisplayViewport> mVirtualTouchViewports = new ArrayList<>();
+    @GuardedBy("mSyncRoot")
+    private final ArrayList<DisplayViewport> mViewports = new ArrayList<>();
 
     // Persistent data store for all internal settings maintained by the display manager service.
     private final PersistentDataStore mPersistentDataStore = new PersistentDataStore();
@@ -272,9 +273,7 @@
 
     // Temporary viewports, used when sending new viewport information to the
     // input system.  May be used outside of the lock but only on the handler thread.
-    private final DisplayViewport mTempDefaultViewport = new DisplayViewport();
-    private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport();
-    private final ArrayList<DisplayViewport> mTempVirtualTouchViewports = new ArrayList<>();
+    private final ArrayList<DisplayViewport> mTempViewports = new ArrayList<>();
 
     // The default color mode for default displays. Overrides the usual
     // Display.Display.COLOR_MODE_DEFAULT for displays with the
@@ -1255,9 +1254,7 @@
     }
 
     private void clearViewportsLocked() {
-        mDefaultViewport.valid = false;
-        mExternalTouchViewport.valid = false;
-        mVirtualTouchViewports.clear();
+        mViewports.clear();
     }
 
     private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
@@ -1287,40 +1284,89 @@
         }
         display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
 
-        // Update the viewports if needed.
-        if (!mDefaultViewport.valid
-                && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
-            setViewportLocked(mDefaultViewport, display, device);
+        // Update the corresponding viewport.
+        DisplayViewport internalViewport = getInternalViewportLocked();
+        if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) {
+            populateViewportLocked(internalViewport, display, device);
         }
-        if (!mExternalTouchViewport.valid
-                && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
-            setViewportLocked(mExternalTouchViewport, display, device);
+        DisplayViewport externalViewport = getExternalViewportLocked();
+        if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) {
+            populateViewportLocked(externalViewport, display, device);
+        } else if (!externalViewport.valid) {
+            // TODO (b/116850516) move this logic into InputReader
+            externalViewport.copyFrom(internalViewport);
+            externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL;
         }
 
         if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) {
-            final DisplayViewport viewport = getVirtualTouchViewportLocked(info.uniqueId);
-            setViewportLocked(viewport, display, device);
+            final DisplayViewport viewport = getVirtualViewportLocked(info.uniqueId);
+            populateViewportLocked(viewport, display, device);
         }
     }
 
-    /** Gets the virtual device viewport or creates it if not yet created. */
-    private DisplayViewport getVirtualTouchViewportLocked(@NonNull String uniqueId) {
+    /** Get the virtual device viewport that has the specified uniqueId.
+     * If such viewport does not exist, create it. */
+    private DisplayViewport getVirtualViewportLocked(@NonNull String uniqueId) {
         DisplayViewport viewport;
-        final int count = mVirtualTouchViewports.size();
+        final int count = mViewports.size();
         for (int i = 0; i < count; i++) {
-            viewport = mVirtualTouchViewports.get(i);
+            viewport = mViewports.get(i);
             if (uniqueId.equals(viewport.uniqueId)) {
+                if (viewport.type != VIEWPORT_VIRTUAL) {
+                    Slog.wtf(TAG, "Found a viewport with uniqueId '"  + uniqueId
+                            + "' but it has type " + DisplayViewport.typeToString(viewport.type)
+                            + " (expected VIRTUAL)");
+                    continue;
+                }
                 return viewport;
             }
         }
 
         viewport = new DisplayViewport();
         viewport.uniqueId = uniqueId;
-        mVirtualTouchViewports.add(viewport);
+        viewport.type = VIEWPORT_VIRTUAL;
+        mViewports.add(viewport);
         return viewport;
     }
 
-    private static void setViewportLocked(DisplayViewport viewport,
+    private DisplayViewport getInternalViewportLocked() {
+        return getViewportByTypeLocked(VIEWPORT_INTERNAL);
+    }
+
+    private DisplayViewport getExternalViewportLocked() {
+        return getViewportByTypeLocked(VIEWPORT_EXTERNAL);
+    }
+
+    /**
+     * Get internal or external viewport. Create it if does not currently exist.
+     * @param viewportType - either INTERNAL or EXTERNAL
+     * @return the viewport with the requested type
+     */
+    private DisplayViewport getViewportByTypeLocked(int viewportType) {
+        // Only allow a single INTERNAL or EXTERNAL viewport, which makes this function possible.
+        // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function.
+        // Creates the viewport if none exists.
+        if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL) {
+            Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type "
+                    + DisplayViewport.typeToString(viewportType));
+            return null;
+        }
+        DisplayViewport viewport;
+        final int count = mViewports.size();
+        for (int i = 0; i < count; i++) {
+            viewport = mViewports.get(i);
+            if (viewport.type == viewportType) {
+                return viewport;
+            }
+        }
+
+        viewport = new DisplayViewport();
+        viewport.type = viewportType;
+        mViewports.add(viewport);
+        return viewport;
+    }
+
+    private static void populateViewportLocked(DisplayViewport viewport,
             LogicalDisplay display, DisplayDevice device) {
         viewport.valid = true;
         viewport.displayId = display.getDisplayIdLocked();
@@ -1400,9 +1446,7 @@
             pw.println("  mPendingTraversal=" + mPendingTraversal);
             pw.println("  mGlobalDisplayState=" + Display.stateToString(mGlobalDisplayState));
             pw.println("  mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId);
-            pw.println("  mDefaultViewport=" + mDefaultViewport);
-            pw.println("  mExternalTouchViewport=" + mExternalTouchViewport);
-            pw.println("  mVirtualTouchViewports=" + mVirtualTouchViewports);
+            pw.println("  mViewports=" + mViewports);
             pw.println("  mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode);
             pw.println("  mSingleDisplayDemoMode=" + mSingleDisplayDemoMode);
             pw.println("  mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount);
@@ -1522,18 +1566,19 @@
                     break;
 
                 case MSG_UPDATE_VIEWPORT: {
+                    final boolean changed;
                     synchronized (mSyncRoot) {
-                        mTempDefaultViewport.copyFrom(mDefaultViewport);
-                        mTempExternalTouchViewport.copyFrom(mExternalTouchViewport);
-                        if (!mTempVirtualTouchViewports.equals(mVirtualTouchViewports)) {
-                          mTempVirtualTouchViewports.clear();
-                          for (DisplayViewport d : mVirtualTouchViewports) {
-                              mTempVirtualTouchViewports.add(d.makeCopy());
-                          }
+                        changed = !mTempViewports.equals(mViewports);
+                        if (changed) {
+                            mTempViewports.clear();
+                            for (DisplayViewport d : mViewports) {
+                                mTempViewports.add(d.makeCopy());
+                            }
                         }
                     }
-                    mInputManagerInternal.setDisplayViewports(mTempDefaultViewport,
-                            mTempExternalTouchViewport, mTempVirtualTouchViewports);
+                    if (changed) {
+                        mInputManagerInternal.setDisplayViewports(mTempViewports);
+                    }
                     break;
                 }
 
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5b7c520..6f726e6 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -18,7 +18,6 @@
 
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerInternal;
-import android.os.SystemProperties;
 import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.Surface;
@@ -256,6 +255,9 @@
             if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
                 mBaseDisplayInfo.flags |= Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
             }
+            if ((deviceInfo.flags & DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+                mBaseDisplayInfo.flags |= Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+            }
             Rect maskingInsets = getMaskingInsets(deviceInfo);
             int maskedWidth = deviceInfo.width - maskingInsets.left - maskingInsets.right;
             int maskedHeight = deviceInfo.height - maskingInsets.top - maskingInsets.bottom;
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 6111c23..5aa585f 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -17,15 +17,14 @@
 package com.android.server.display;
 
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
-import static android.hardware.display.DisplayManager
-        .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
-import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
-import static android.hardware.display.DisplayManager
-        .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
+import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
 
 import android.content.Context;
 import android.hardware.display.IVirtualDisplayCallback;
@@ -33,10 +32,10 @@
 import android.media.projection.IMediaProjectionCallback;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.SystemProperties;
 import android.os.IBinder.DeathRecipient;
 import android.os.Message;
 import android.os.RemoteException;
+import android.os.SystemProperties;
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.view.Display;
@@ -60,7 +59,8 @@
     static final boolean DEBUG = false;
 
     // Unique id prefix for virtual displays
-    private static final String UNIQUE_ID_PREFIX = "virtual:";
+    @VisibleForTesting
+    static final String UNIQUE_ID_PREFIX = "virtual:";
 
     private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
             new ArrayMap<IBinder, VirtualDisplayDevice>();
@@ -366,7 +366,10 @@
                     mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
                 }
                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
-                  mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
+                }
+                if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
                 }
 
                 mInfo.type = Display.TYPE_VIRTUAL;
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 0f28439..4913e8b 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -16,9 +16,6 @@
 
 package com.android.server.input;
 
-import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL;
-import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL;
-
 import android.annotation.NonNull;
 import android.app.IInputForwarder;
 import android.app.Notification;
@@ -188,13 +185,8 @@
     private static native long nativeInit(InputManagerService service,
             Context context, MessageQueue messageQueue);
     private static native void nativeStart(long ptr);
-    private static native void nativeSetVirtualDisplayViewports(long ptr,
+    private static native void nativeSetDisplayViewports(long ptr,
             DisplayViewport[] viewports);
-    private static native void nativeSetDisplayViewport(long ptr, int viewportType,
-            int displayId, int rotation,
-            int logicalLeft, int logicalTop, int logicalRight, int logicalBottom,
-            int physicalLeft, int physicalTop, int physicalRight, int physicalBottom,
-            int deviceWidth, int deviceHeight, String uniqueId);
 
     private static native int nativeGetScanCodeState(long ptr,
             int deviceId, int sourceMask, int scanCode);
@@ -217,7 +209,8 @@
     private static native void nativeSetInputDispatchMode(long ptr, boolean enabled, boolean frozen);
     private static native void nativeSetSystemUiVisibility(long ptr, int visibility);
     private static native void nativeSetFocusedApplication(long ptr,
-            InputApplicationHandle application);
+            int displayId, InputApplicationHandle application);
+    private static native void nativeSetFocusedDisplay(long ptr, int displayId);
     private static native boolean nativeTransferTouchFocus(long ptr,
             InputChannel fromChannel, InputChannel toChannel);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
@@ -409,31 +402,8 @@
         nativeReloadDeviceAliases(mPtr);
     }
 
-    private void setDisplayViewportsInternal(DisplayViewport defaultViewport,
-            DisplayViewport externalTouchViewport,
-            List<DisplayViewport> virtualTouchViewports) {
-        if (defaultViewport.valid) {
-            setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport);
-        }
-
-        if (externalTouchViewport.valid) {
-            setDisplayViewport(VIEWPORT_EXTERNAL, externalTouchViewport);
-        } else if (defaultViewport.valid) {
-            setDisplayViewport(VIEWPORT_EXTERNAL, defaultViewport);
-        }
-
-        nativeSetVirtualDisplayViewports(mPtr,
-                virtualTouchViewports.toArray(new DisplayViewport[0]));
-    }
-
-    private void setDisplayViewport(int viewportType, DisplayViewport viewport) {
-        nativeSetDisplayViewport(mPtr, viewportType,
-                viewport.displayId, viewport.orientation,
-                viewport.logicalFrame.left, viewport.logicalFrame.top,
-                viewport.logicalFrame.right, viewport.logicalFrame.bottom,
-                viewport.physicalFrame.left, viewport.physicalFrame.top,
-                viewport.physicalFrame.right, viewport.physicalFrame.bottom,
-                viewport.deviceWidth, viewport.deviceHeight, viewport.uniqueId);
+    private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
+        nativeSetDisplayViewports(mPtr, viewports.toArray(new DisplayViewport[0]));
     }
 
     /**
@@ -1462,21 +1432,27 @@
         }
     }
 
-    public void setInputWindows(InputWindowHandle[] windowHandles,
-            InputWindowHandle focusedWindowHandle, int displayId) {
-        final IWindow newFocusedWindow =
-            focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
-        if (mFocusedWindow != newFocusedWindow) {
-            mFocusedWindow = newFocusedWindow;
-            if (mFocusedWindowHasCapture) {
-                setPointerCapture(false);
-            }
-        }
+    public void setInputWindows(InputWindowHandle[] windowHandles, int displayId) {
         nativeSetInputWindows(mPtr, windowHandles, displayId);
     }
 
-    public void setFocusedApplication(InputApplicationHandle application) {
-        nativeSetFocusedApplication(mPtr, application);
+    public void setFocusedApplication(int displayId, InputApplicationHandle application) {
+        nativeSetFocusedApplication(mPtr, displayId, application);
+    }
+
+    public void setFocusedWindow(InputWindowHandle focusedWindowHandle) {
+        final IWindow newFocusedWindow =
+            focusedWindowHandle != null ? focusedWindowHandle.clientWindow : null;
+        if (mFocusedWindow != newFocusedWindow) {
+            if (mFocusedWindowHasCapture) {
+                setPointerCapture(false);
+            }
+            mFocusedWindow = newFocusedWindow;
+        }
+    }
+
+    public void setFocusedDisplay(int displayId) {
+        nativeSetFocusedDisplay(mPtr, displayId);
     }
 
     @Override
@@ -1491,16 +1467,18 @@
             return;
         }
         setPointerCapture(enabled);
-        try {
-            mFocusedWindow.dispatchPointerCaptureChanged(enabled);
-        } catch (RemoteException ex) {
-            /* ignore */
-        }
     }
 
     private void setPointerCapture(boolean enabled) {
-        mFocusedWindowHasCapture = enabled;
-        nativeSetPointerCapture(mPtr, enabled);
+        if (mFocusedWindowHasCapture != enabled) {
+            mFocusedWindowHasCapture = enabled;
+            try {
+                mFocusedWindow.dispatchPointerCaptureChanged(enabled);
+            } catch (RemoteException ex) {
+                /* ignore */
+            }
+            nativeSetPointerCapture(mPtr, enabled);
+        }
     }
 
     public void setInputDispatchMode(boolean enabled, boolean frozen) {
@@ -2203,11 +2181,8 @@
 
     private final class LocalService extends InputManagerInternal {
         @Override
-        public void setDisplayViewports(DisplayViewport defaultViewport,
-                DisplayViewport externalTouchViewport,
-                List<DisplayViewport> virtualTouchViewports) {
-            setDisplayViewportsInternal(defaultViewport, externalTouchViewport,
-                    virtualTouchViewports);
+        public void setDisplayViewports(List<DisplayViewport> viewports) {
+            setDisplayViewportsInternal(viewports);
         }
 
         @Override
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 60e9eaa..3f03169 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -159,8 +159,10 @@
     static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
     static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
 
+    // Perform polling and persist all (FLAG_PERSIST_ALL).
     private static final int MSG_PERFORM_POLL = 1;
-    private static final int MSG_REGISTER_GLOBAL_ALERT = 2;
+    // Perform polling, persist network, and register the global alert again.
+    private static final int MSG_PERFORM_POLL_REGISTER_ALERT = 2;
 
     /** Flags to control detail level of poll event. */
     private static final int FLAG_PERSIST_NETWORK = 0x1;
@@ -168,6 +170,14 @@
     private static final int FLAG_PERSIST_ALL = FLAG_PERSIST_NETWORK | FLAG_PERSIST_UID;
     private static final int FLAG_PERSIST_FORCE = 0x100;
 
+    /**
+     * When global alert quota is high, wait for this delay before processing each polling,
+     * and do not schedule further polls once there is already one queued.
+     * This avoids firing the global alert too often on devices with high transfer speeds and
+     * high quota.
+     */
+    private static final int PERFORM_POLL_DELAY_MS = 1000;
+
     private static final String TAG_NETSTATS_ERROR = "netstats_error";
 
     private final Context mContext;
@@ -920,7 +930,7 @@
         }
 
         // Create baseline stats
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL, FLAG_PERSIST_ALL));
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_PERFORM_POLL));
 
         return normalizedRequest;
    }
@@ -1055,13 +1065,12 @@
             mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
 
             if (LIMIT_GLOBAL_ALERT.equals(limitName)) {
-                // kick off background poll to collect network stats; UID stats
-                // are handled during normal polling interval.
-                final int flags = FLAG_PERSIST_NETWORK;
-                mHandler.obtainMessage(MSG_PERFORM_POLL, flags, 0).sendToTarget();
-
-                // re-arm global alert for next update
-                mHandler.obtainMessage(MSG_REGISTER_GLOBAL_ALERT).sendToTarget();
+                // kick off background poll to collect network stats unless there is already
+                // such a call pending; UID stats are handled during normal polling interval.
+                if (!mHandler.hasMessages(MSG_PERFORM_POLL_REGISTER_ALERT)) {
+                    mHandler.sendEmptyMessageDelayed(MSG_PERFORM_POLL_REGISTER_ALERT,
+                            PERFORM_POLL_DELAY_MS);
+                }
             }
         }
     };
@@ -1673,11 +1682,11 @@
         public boolean handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_PERFORM_POLL: {
-                    final int flags = msg.arg1;
-                    mService.performPoll(flags);
+                    mService.performPoll(FLAG_PERSIST_ALL);
                     return true;
                 }
-                case MSG_REGISTER_GLOBAL_ALERT: {
+                case MSG_PERFORM_POLL_REGISTER_ALERT: {
+                    mService.performPoll(FLAG_PERSIST_NETWORK);
                     mService.registerGlobalAlert();
                     return true;
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5005ea7..f9d49d7 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -16,6 +16,7 @@
 
 package com.android.server.notification;
 
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.NotificationManager.ACTION_APP_BLOCK_STATE_CHANGED;
 import static android.app.NotificationManager.ACTION_NOTIFICATION_CHANNEL_BLOCK_STATE_CHANGED;
@@ -2001,7 +2002,8 @@
         return mInternalService;
     }
 
-    private final IBinder mService = new INotificationManager.Stub() {
+    @VisibleForTesting
+    final IBinder mService = new INotificationManager.Stub() {
         // Toasts
         // ============================================================================
 
@@ -2015,21 +2017,30 @@
             }
 
             if (pkg == null || callback == null) {
-                Slog.e(TAG, "Not doing toast. pkg=" + pkg + " callback=" + callback);
+                Slog.e(TAG, "Not enqueuing toast. pkg=" + pkg + " callback=" + callback);
                 return ;
             }
-            final boolean isSystemToast = isCallerSystemOrPhone() || ("android".equals(pkg));
-            final boolean isPackageSuspended =
-                    isPackageSuspendedForUser(pkg, Binder.getCallingUid());
 
-            if (ENABLE_BLOCKED_TOASTS && !isSystemToast &&
-                    (!areNotificationsEnabledForPackage(pkg, Binder.getCallingUid())
-                            || isPackageSuspended)) {
-                Slog.e(TAG, "Suppressing toast from package " + pkg
-                        + (isPackageSuspended
-                                ? " due to package suspended by administrator."
-                                : " by user request."));
-                return;
+            final int callingUid = Binder.getCallingUid();
+            final boolean isSystemToast = isCallerSystemOrPhone()
+                    || PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
+            final boolean isPackageSuspended = isPackageSuspendedForUser(pkg, callingUid);
+            final boolean notificationsDisabledForPackage = !areNotificationsEnabledForPackage(pkg,
+                    callingUid);
+
+            long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                final boolean appIsForeground = mActivityManager.getUidImportance(callingUid)
+                        == IMPORTANCE_FOREGROUND;
+                if (ENABLE_BLOCKED_TOASTS && !isSystemToast && ((notificationsDisabledForPackage
+                        && !appIsForeground) || isPackageSuspended)) {
+                    Slog.e(TAG, "Suppressing toast from package " + pkg
+                            + (isPackageSuspended ? " due to package suspended."
+                            : " by user request."));
+                    return;
+                }
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
             }
 
             synchronized (mToastQueue) {
diff --git a/services/core/java/com/android/server/pm/PackageSignatures.java b/services/core/java/com/android/server/pm/PackageSignatures.java
index 471729e..6bce788 100644
--- a/services/core/java/com/android/server/pm/PackageSignatures.java
+++ b/services/core/java/com/android/server/pm/PackageSignatures.java
@@ -16,18 +16,18 @@
 
 package com.android.server.pm;
 
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.annotation.NonNull;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
 import android.content.pm.Signature;
 import android.util.Log;
 
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.IOException;
 import java.security.cert.CertificateException;
 import java.util.ArrayList;
@@ -61,23 +61,22 @@
         serializer.attribute(null, "count", Integer.toString(mSigningDetails.signatures.length));
         serializer.attribute(null, "schemeVersion",
                 Integer.toString(mSigningDetails.signatureSchemeVersion));
-        writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, null);
+        writeCertsListXml(serializer, writtenSignatures, mSigningDetails.signatures, false);
 
         // if we have past signer certificate information, write it out
         if (mSigningDetails.pastSigningCertificates != null) {
             serializer.startTag(null, "pastSigs");
             serializer.attribute(null, "count",
                     Integer.toString(mSigningDetails.pastSigningCertificates.length));
-            writeCertsListXml(
-                    serializer, writtenSignatures, mSigningDetails.pastSigningCertificates,
-                    mSigningDetails.pastSigningCertificatesFlags);
+            writeCertsListXml(serializer, writtenSignatures,
+                    mSigningDetails.pastSigningCertificates, true);
             serializer.endTag(null, "pastSigs");
         }
         serializer.endTag(null, tagName);
     }
 
     private void writeCertsListXml(XmlSerializer serializer, ArrayList<Signature> writtenSignatures,
-            Signature[] signatures, int[] flags) throws IOException {
+            Signature[] signatures, boolean isPastSigs) throws IOException {
         for (int i=0; i<signatures.length; i++) {
             serializer.startTag(null, "cert");
             final Signature sig = signatures[i];
@@ -96,8 +95,10 @@
                 serializer.attribute(null, "index", Integer.toString(numWritten));
                 serializer.attribute(null, "key", sig.toCharsString());
             }
-            if (flags != null) {
-                serializer.attribute(null, "flags", Integer.toString(flags[i]));
+            // The flags attribute is only written for previous signatures to represent the
+            // capabilities the developer wants to grant to the previous signing certificates.
+            if (isPastSigs) {
+                serializer.attribute(null, "flags", Integer.toString(sig.getFlags()));
             }
             serializer.endTag(null, "cert");
         }
@@ -114,6 +115,7 @@
                     "Error in package manager settings: <sigs> has"
                        + " no count at " + parser.getPositionDescription());
             XmlUtils.skipCurrentTag(parser);
+            return;
         }
         final int count = Integer.parseInt(countStr);
 
@@ -128,16 +130,11 @@
             signatureSchemeVersion = Integer.parseInt(schemeVersionStr);
         }
         builder.setSignatureSchemeVersion(signatureSchemeVersion);
-        Signature[] signatures = new Signature[count];
-        int pos = readCertsListXml(parser, readSignatures, signatures, null, builder);
+        ArrayList<Signature> signatureList = new ArrayList<>();
+        int pos = readCertsListXml(parser, readSignatures, signatureList, count, false, builder);
+        Signature[] signatures = signatureList.toArray(new Signature[signatureList.size()]);
         builder.setSignatures(signatures);
         if (pos < count) {
-            // Should never happen -- there is an error in the written
-            // settings -- but if it does we don't want to generate
-            // a bad array.
-            Signature[] newSigs = new Signature[pos];
-            System.arraycopy(signatures, 0, newSigs, 0, pos);
-            builder = builder.setSignatures(newSigs);
             PackageManagerService.reportSettingsProblem(Log.WARN,
                     "Error in package manager settings: <sigs> count does not match number of "
                             + " <cert> entries" + parser.getPositionDescription());
@@ -154,9 +151,9 @@
     }
 
     private int readCertsListXml(XmlPullParser parser, ArrayList<Signature> readSignatures,
-            Signature[] signatures, int[] flags, PackageParser.SigningDetails.Builder builder)
+            ArrayList<Signature> signatures, int count, boolean isPastSigs,
+            PackageParser.SigningDetails.Builder builder)
             throws IOException, XmlPullParserException {
-        int count = signatures.length;
         int pos = 0;
 
         int outerDepth = parser.getDepth();
@@ -174,6 +171,7 @@
                 if (pos < count) {
                     String index = parser.getAttributeValue(null, "index");
                     if (index != null) {
+                        boolean signatureParsed = false;
                         try {
                             int idx = Integer.parseInt(index);
                             String key = parser.getAttributeValue(null, "key");
@@ -181,7 +179,8 @@
                                 if (idx >= 0 && idx < readSignatures.size()) {
                                     Signature sig = readSignatures.get(idx);
                                     if (sig != null) {
-                                        signatures[pos] = readSignatures.get(idx);
+                                        signatures.add(sig);
+                                        signatureParsed = true;
                                     } else {
                                         PackageManagerService.reportSettingsProblem(Log.WARN,
                                                 "Error in package manager settings: <cert> "
@@ -195,12 +194,15 @@
                                                     + parser.getPositionDescription());
                                 }
                             } else {
-                                while (readSignatures.size() <= idx) {
+                                // Create the signature first to prevent adding null entries to the
+                                // output List if the key value is invalid.
+                                Signature sig = new Signature(key);
+                                while (readSignatures.size() < idx) {
                                     readSignatures.add(null);
                                 }
-                                Signature sig = new Signature(key);
-                                readSignatures.set(idx, sig);
-                                signatures[pos] = sig;
+                                readSignatures.add(sig);
+                                signatures.add(sig);
+                                signatureParsed = true;
                             }
                         } catch (NumberFormatException e) {
                             PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -215,11 +217,22 @@
                                             + e.getMessage());
                         }
 
-                        if (flags != null) {
+                        if (isPastSigs) {
                             String flagsStr = parser.getAttributeValue(null, "flags");
                             if (flagsStr != null) {
                                 try {
-                                    flags[pos] = Integer.parseInt(flagsStr);
+                                    int flagsValue = Integer.parseInt(flagsStr);
+                                    // only modify the flags if the signature of the previous signer
+                                    // was successfully parsed above
+                                    if (signatureParsed) {
+                                        signatures.get(signatures.size() - 1).setFlags(flagsValue);
+                                    } else {
+                                        PackageManagerService.reportSettingsProblem(Log.WARN,
+                                                "Error in package manager settings: signature not "
+                                                        + "available at index "
+                                                        + pos + " to set flags at "
+                                                        + parser.getPositionDescription());
+                                    }
                                 } catch (NumberFormatException e) {
                                     PackageManagerService.reportSettingsProblem(Log.WARN,
                                             "Error in package manager settings: <cert> "
@@ -246,7 +259,7 @@
                 pos++;
                 XmlUtils.skipCurrentTag(parser);
             } else if (tagName.equals("pastSigs")) {
-                if (flags == null) {
+                if (!isPastSigs) {
                     // we haven't encountered pastSigs yet, go ahead
                     String countStr = parser.getAttributeValue(null, "count");
                     if (countStr == null) {
@@ -254,32 +267,23 @@
                                 "Error in package manager settings: <pastSigs> has"
                                         + " no count at " + parser.getPositionDescription());
                         XmlUtils.skipCurrentTag(parser);
+                        continue;
                     }
                     try {
                         final int pastSigsCount = Integer.parseInt(countStr);
-                        Signature[] pastSignatures = new Signature[pastSigsCount];
-                        int[] pastSignaturesFlags = new int[pastSigsCount];
-                        int pastSigsPos = readCertsListXml(parser, readSignatures, pastSignatures,
-                                pastSignaturesFlags, builder);
-                        builder = builder
-                                .setPastSigningCertificates(pastSignatures)
-                                .setPastSigningCertificatesFlags(pastSignaturesFlags);
+                        ArrayList<Signature> pastSignatureList = new ArrayList<>();
+                        int pastSigsPos = readCertsListXml(parser, readSignatures,
+                                pastSignatureList,
+                                pastSigsCount, true, builder);
+                        Signature[] pastSignatures = pastSignatureList.toArray(
+                                new Signature[pastSignatureList.size()]);
+                        builder = builder.setPastSigningCertificates(pastSignatures);
 
                         if (pastSigsPos < pastSigsCount) {
-                            // Should never happen -- there is an error in the written
-                            // settings -- but if it does we don't want to generate
-                            // a bad array.
-                            Signature[] newSigs = new Signature[pastSigsPos];
-                            System.arraycopy(pastSignatures, 0, newSigs, 0, pastSigsPos);
-                            int[] newFlags = new int[pastSigsPos];
-                            System.arraycopy(pastSignaturesFlags, 0, newFlags, 0, pastSigsPos);
-                            builder = builder
-                                    .setPastSigningCertificates(newSigs)
-                                    .setPastSigningCertificatesFlags(newFlags);
                             PackageManagerService.reportSettingsProblem(Log.WARN,
                                     "Error in package manager settings: <pastSigs> count does not "
-                                    + "match number of <cert> entries "
-                                    + parser.getPositionDescription());
+                                            + "match number of <cert> entries "
+                                            + parser.getPositionDescription());
                         }
                     } catch (NumberFormatException e) {
                         PackageManagerService.reportSettingsProblem(Log.WARN,
@@ -326,7 +330,8 @@
                 buf.append(Integer.toHexString(
                         mSigningDetails.pastSigningCertificates[i].hashCode()));
                 buf.append(" flags: ");
-                buf.append(Integer.toHexString(mSigningDetails.pastSigningCertificatesFlags[i]));
+                buf.append(
+                        Integer.toHexString(mSigningDetails.pastSigningCertificates[i].getFlags()));
             }
         }
         buf.append("]}");
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index 910ea73..1f05dc9 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.pm.dex;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -57,8 +59,6 @@
 import dalvik.system.VMRuntime;
 
 import libcore.io.IoUtils;
-import libcore.util.NonNull;
-import libcore.util.Nullable;
 
 import java.io.File;
 import java.io.FileNotFoundException;
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 2557f46..21bf488 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1655,11 +1655,9 @@
         }
         final boolean keyguardShowing = isKeyguardShowingAndNotOccluded();
         mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
-        if (keyguardShowing) {
-            // since it took two seconds of long press to bring this up,
-            // poke the wake lock so they have some time to see the dialog.
-            mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
-        }
+        // since it took two seconds of long press to bring this up,
+        // poke the wake lock so they have some time to see the dialog.
+        mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
     }
 
     boolean isDeviceProvisioned() {
@@ -4777,6 +4775,7 @@
             pf.bottom = df.bottom = of.bottom = displayFrames.mUnrestricted.bottom;
             // ...with content insets above the nav bar
             cf.bottom = vf.bottom = displayFrames.mStable.bottom;
+            // TODO (b/111364446): Support showing IME on non-default displays
             if (mStatusBar != null && mFocusedWindow == mStatusBar && canReceiveInput(mStatusBar)) {
                 // The status bar forces the navigation bar while it's visible. Make sure the IME
                 // avoids the navigation bar in that case.
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index b3f2a27..43a9c78 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1079,18 +1079,39 @@
         return false;
     }
 
+    private static WorkChain getFirstNonEmptyWorkChain(WorkSource workSource) {
+        if (workSource.getWorkChains() == null) {
+            return null;
+        }
+
+        for (WorkChain workChain: workSource.getWorkChains()) {
+            if (workChain.getSize() > 0) {
+                return workChain;
+            }
+        }
+
+        return null;
+    }
+
     private void applyWakeLockFlagsOnAcquireLocked(WakeLock wakeLock, int uid) {
         if ((wakeLock.mFlags & PowerManager.ACQUIRE_CAUSES_WAKEUP) != 0
                 && isScreenLock(wakeLock)) {
             String opPackageName;
             int opUid;
-            if (wakeLock.mWorkSource != null && wakeLock.mWorkSource.getName(0) != null) {
-                opPackageName = wakeLock.mWorkSource.getName(0);
-                opUid = wakeLock.mWorkSource.get(0);
+            if (wakeLock.mWorkSource != null && !wakeLock.mWorkSource.isEmpty()) {
+                WorkSource workSource = wakeLock.mWorkSource;
+                WorkChain workChain = getFirstNonEmptyWorkChain(workSource);
+                if (workChain != null) {
+                    opPackageName = workChain.getAttributionTag();
+                    opUid = workChain.getAttributionUid();
+                } else {
+                    opPackageName = workSource.getName(0) != null
+                            ? workSource.getName(0) : wakeLock.mPackageName;
+                    opUid = workSource.get(0);
+                }
             } else {
                 opPackageName = wakeLock.mPackageName;
-                opUid = wakeLock.mWorkSource != null ? wakeLock.mWorkSource.get(0)
-                        : wakeLock.mOwnerUid;
+                opUid = wakeLock.mOwnerUid;
             }
             wakeUpNoUpdateLocked(SystemClock.uptimeMillis(), wakeLock.mTag, opUid,
                     opPackageName, opUid);
diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java
index d0de940..1abaaf2 100644
--- a/services/core/java/com/android/server/stats/StatsCompanionService.java
+++ b/services/core/java/com/android/server/stats/StatsCompanionService.java
@@ -15,6 +15,8 @@
  */
 package com.android.server.stats;
 
+import static com.android.internal.util.Preconditions.checkNotNull;
+
 import android.annotation.Nullable;
 import android.app.ActivityManagerInternal;
 import android.app.AlarmManager;
@@ -67,6 +69,7 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.StatsLog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.procstats.IProcessStats;
@@ -1256,18 +1259,19 @@
         Binder.restoreCallingIdentity(token);
     }
 
-    long mLastProcStatsHighWaterMark = readProcStatsHighWaterMark();
-
-    private long readProcStatsHighWaterMark() {
+    // read high watermark for section
+    private long readProcStatsHighWaterMark(int section) {
         try {
-            File[] files = mBaseDir.listFiles();
+            File[] files = mBaseDir.listFiles((d, name) -> {
+                return name.toLowerCase().startsWith(String.valueOf(section) + '_');
+            });
             if (files == null || files.length == 0) {
                 return 0;
             }
             if (files.length > 1) {
                 Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length);
             }
-            return Long.valueOf(files[0].getName());
+            return Long.valueOf(files[0].getName().split("_")[1]);
         } catch (SecurityException e) {
             Log.e(TAG, "Failed to get procstats high watermark file.", e);
         } catch (NumberFormatException e) {
@@ -1279,13 +1283,13 @@
     private IProcessStats mProcessStats =
             IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME));
 
-    private void pullProcessStats(
-            int tagId, long elapsedNanos, long wallClockNanos,
+    private void pullProcessStats(int section, int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         try {
+            long lastHighWaterMark = readProcStatsHighWaterMark(section);
             List<ParcelFileDescriptor> statsFiles = new ArrayList<>();
             long highWaterMark = mProcessStats.getCommittedStats(
-                    mLastProcStatsHighWaterMark, ProcessStats.REPORT_ALL, true, statsFiles);
+                    lastHighWaterMark, section, true, statsFiles);
             if (statsFiles.size() != 1) {
                 return;
             }
@@ -1295,10 +1299,10 @@
             StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
             e.writeStorage(Arrays.copyOf(stats, len[0]));
             pulledData.add(e);
-            new File(mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).delete();
-            mLastProcStatsHighWaterMark = highWaterMark;
+            new File(mBaseDir.getAbsolutePath() + "/" + section + "_" + lastHighWaterMark).delete();
             new File(
-                    mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).createNewFile();
+                    mBaseDir.getAbsolutePath() + "/" + section + "_"
+                            + highWaterMark).createNewFile();
         } catch (IOException e) {
             Log.e(TAG, "Getting procstats failed: ", e);
         } catch (RemoteException e) {
@@ -1336,12 +1340,28 @@
         }
     }
 
+    private void pullPowerProfile(
+            int tagId, long elapsedNanos, long wallClockNanos,
+            List<StatsLogEventWrapper> pulledData) {
+        PowerProfile powerProfile = new PowerProfile(mContext);
+        checkNotNull(powerProfile);
+
+        StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                wallClockNanos);
+        ProtoOutputStream proto = new ProtoOutputStream();
+        powerProfile.writeToProto(proto);
+        proto.flush();
+        e.writeStorage(proto.getBytes());
+        pulledData.add(e);
+    }
+
     private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
         mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead,
                 fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite,
                 fgFsync, bgFsync) -> {
-            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
+                    wallClockNanos);
             e.writeInt(uid);
             e.writeLong(fgCharsRead);
             e.writeLong(fgCharsWrite);
@@ -1471,13 +1491,22 @@
                 break;
             }
             case StatsLog.PROC_STATS: {
-                pullProcessStats(tagId, elapsedNanos, wallClockNanos, ret);
+                pullProcessStats(ProcessStats.REPORT_ALL, tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
+            case StatsLog.PROC_STATS_PKG_PROC: {
+                pullProcessStats(ProcessStats.REPORT_PKG_PROC_STATS, tagId, elapsedNanos,
+                        wallClockNanos, ret);
                 break;
             }
             case StatsLog.DISK_IO: {
                 pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret);
                 break;
             }
+            case StatsLog.POWER_PROFILE: {
+                pullPowerProfile(tagId, elapsedNanos, wallClockNanos, ret);
+                break;
+            }
             default:
                 Slog.w(TAG, "No such tagId data as " + tagId);
                 return null;
@@ -1539,7 +1568,8 @@
         public void onStart() {
             mStatsCompanionService = new StatsCompanionService(getContext());
             try {
-                publishBinderService(Context.STATS_COMPANION_SERVICE, mStatsCompanionService);
+                publishBinderService(Context.STATS_COMPANION_SERVICE,
+                        mStatsCompanionService);
                 if (DEBUG) Slog.d(TAG, "Published " + Context.STATS_COMPANION_SERVICE);
             } catch (Exception e) {
                 Slog.e(TAG, "Failed to publishBinderService", e);
@@ -1564,18 +1594,22 @@
     }
 
     /**
-     * Tells statsd that statscompanion is ready. If the binder call returns, link to statsd.
+     * Tells statsd that statscompanion is ready. If the binder call returns, link to
+     * statsd.
      */
     private void sayHiToStatsd() {
         synchronized (sStatsdLock) {
             if (sStatsd != null) {
                 Slog.e(TAG, "Trying to fetch statsd, but it was already fetched",
-                        new IllegalStateException("sStatsd is not null when being fetched"));
+                        new IllegalStateException(
+                                "sStatsd is not null when being fetched"));
                 return;
             }
             sStatsd = fetchStatsdService();
             if (sStatsd == null) {
-                Slog.i(TAG, "Could not yet find statsd to tell it that StatsCompanion is alive.");
+                Slog.i(TAG,
+                        "Could not yet find statsd to tell it that StatsCompanion is "
+                                + "alive.");
                 return;
             }
             if (DEBUG) Slog.d(TAG, "Saying hi to statsd");
@@ -1593,10 +1627,12 @@
                 filter.addAction(Intent.ACTION_PACKAGE_ADDED);
                 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
                 filter.addDataScheme("package");
-                mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter, null,
+                mContext.registerReceiverAsUser(mAppUpdateReceiver, UserHandle.ALL, filter,
+                        null,
                         null);
 
-                // Setup receiver for user initialize (which happens once for a new user) and
+                // Setup receiver for user initialize (which happens once for a new user)
+                // and
                 // if a user is removed.
                 filter = new IntentFilter(Intent.ACTION_USER_INITIALIZE);
                 filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -1610,7 +1646,8 @@
                         mShutdownEventReceiver, UserHandle.ALL, filter, null, null);
                 final long token = Binder.clearCallingIdentity();
                 try {
-                    // Pull the latest state of UID->app name, version mapping when statsd starts.
+                    // Pull the latest state of UID->app name, version mapping when
+                    // statsd starts.
                     informAllUidsLocked(mContext);
                 } finally {
                     restoreCallingIdentity(token);
@@ -1672,7 +1709,8 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
 
         synchronized (sStatsdLock) {
-            writer.println("Number of configuration files deleted: " + mDeletedFiles.size());
+            writer.println(
+                    "Number of configuration files deleted: " + mDeletedFiles.size());
             if (mDeletedFiles.size() > 0) {
                 writer.println("  timestamp, deleted file name");
             }
@@ -1680,7 +1718,8 @@
                     SystemClock.currentThreadTimeMillis() - SystemClock.elapsedRealtime();
             for (Long elapsedMillis : mDeletedFiles.keySet()) {
                 long deletionMillis = lastBootMillis + elapsedMillis;
-                writer.println("  " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
+                writer.println(
+                        "  " + deletionMillis + ", " + mDeletedFiles.get(elapsedMillis));
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index e449111..74922f6 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1043,7 +1043,8 @@
                 // Do not send the windows if there is no current focus as
                 // the window manager is still looking for where to put it.
                 // We will do the work when we get a focus change callback.
-                if (mService.mCurrentFocus == null) {
+                // TODO(b/112273690): Support multiple displays
+                if (mService.getDefaultDisplayContentLocked().mCurrentFocus == null) {
                     return;
                 }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index bcf9212..db0bd4f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -33,6 +33,7 @@
 import android.util.SparseIntArray;
 
 import com.android.internal.app.IVoiceInteractor;
+import com.android.server.am.ActivityServiceConnectionsHolder;
 import com.android.server.am.PendingIntentRecord;
 import com.android.server.am.SafeActivityOptions;
 import com.android.server.am.TaskRecord;
@@ -332,4 +333,7 @@
             int callingUid, int userId, IBinder token, String resultWho,
             int requestCode, Intent[] intents, String[] resolvedTypes, int flags,
             Bundle bOptions);
+
+    /** @return the service connection holder for a given activity token. */
+    public abstract ActivityServiceConnectionsHolder getServiceConnectionsHolder(IBinder token);
 }
diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java
index d73606f..a9d0978 100644
--- a/services/core/java/com/android/server/wm/AppTransition.java
+++ b/services/core/java/com/android/server/wm/AppTransition.java
@@ -77,6 +77,7 @@
 import static com.android.server.wm.AppTransitionProto.LAST_USED_APP_TRANSITION;
 
 import android.annotation.DrawableRes;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.content.ComponentName;
@@ -93,6 +94,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Binder;
 import android.os.Debug;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.IRemoteCallback;
 import android.os.RemoteException;
@@ -120,8 +122,8 @@
 
 import com.android.internal.R;
 import com.android.internal.util.DumpUtils.Dump;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AttributeCache;
-import com.android.server.wm.WindowManagerService.H;
 import com.android.server.wm.animation.ClipRectLRAnimation;
 import com.android.server.wm.animation.ClipRectTBAnimation;
 import com.android.server.wm.animation.CurvedTranslateAnimation;
@@ -252,9 +254,13 @@
 
     private RemoteAnimationController mRemoteAnimationController;
 
+    final Handler mHandler;
+    final Runnable mHandleAppTransitionTimeoutRunnable = () -> handleAppTransitionTimeout();
+
     AppTransition(Context context, WindowManagerService service) {
         mContext = context;
         mService = service;
+        mHandler = new Handler(service.mH.getLooper());
         mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
                 com.android.internal.R.interpolator.linear_out_slow_in);
         mFastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
@@ -1349,7 +1355,8 @@
 
                 @Override
                 public void onAnimationEnd(Animation animation) {
-                    mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK, callback).sendToTarget();
+                    mHandler.sendMessage(PooledLambda.obtainMessage(
+                            AppTransition::doAnimationCallback, callback));
                 }
 
                 @Override
@@ -1756,7 +1763,7 @@
 
     void postAnimationCallback() {
         if (mNextAppTransitionCallback != null) {
-            mService.mH.sendMessage(mService.mH.obtainMessage(H.DO_ANIMATION_CALLBACK,
+            mHandler.sendMessage(PooledLambda.obtainMessage(AppTransition::doAnimationCallback,
                     mNextAppTransitionCallback));
             mNextAppTransitionCallback = null;
         }
@@ -1869,7 +1876,7 @@
             clear();
             mNextAppTransitionType = NEXT_TRANSIT_TYPE_REMOTE;
             mRemoteAnimationController = new RemoteAnimationController(mService,
-                    remoteAnimationAdapter, mService.mH);
+                    remoteAnimationAdapter, mHandler);
         }
     }
 
@@ -2162,8 +2169,8 @@
         }
         boolean prepared = prepare();
         if (isTransitionSet()) {
-            mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
-            mService.mH.sendEmptyMessageDelayed(H.APP_TRANSITION_TIMEOUT, APP_TRANSITION_TIMEOUT_MS);
+            removeAppTransitionTimeoutCallbacks();
+            mHandler.postDelayed(mHandleAppTransitionTimeoutRunnable, APP_TRANSITION_TIMEOUT_MS);
         }
         return prepared;
     }
@@ -2208,4 +2215,32 @@
         return mGridLayoutRecentsEnabled
                 || orientation == Configuration.ORIENTATION_PORTRAIT;
     }
+
+    private void handleAppTransitionTimeout() {
+        synchronized (mService.mWindowMap) {
+            if (isTransitionSet() || !mService.mOpeningApps.isEmpty()
+                    || !mService.mClosingApps.isEmpty()) {
+                if (DEBUG_APP_TRANSITIONS) {
+                    Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
+                            + " isTransitionSet()="
+                            + mService.mAppTransition.isTransitionSet()
+                            + " mOpeningApps.size()=" + mService.mOpeningApps.size()
+                            + " mClosingApps.size()=" + mService.mClosingApps.size());
+                }
+                setTimeout();
+                mService.mWindowPlacerLocked.performSurfacePlacement();
+            }
+        }
+    }
+
+    private static void doAnimationCallback(@NonNull IRemoteCallback callback) {
+        try {
+            ((IRemoteCallback) callback).sendResult(null);
+        } catch (RemoteException e) {
+        }
+    }
+
+    void removeAppTransitionTimeoutCallbacks() {
+        mHandler.removeCallbacks(mHandleAppTransitionTimeoutRunnable);
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 36280dd..330c54c 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -407,8 +407,7 @@
                 if (mService.mAppTransition.getAppTransition()
                         == WindowManager.TRANSIT_TASK_OPEN_BEHIND) {
                     // We're launchingBehind, add the launching activity to mOpeningApps.
-                    final WindowState win =
-                            mService.getDefaultDisplayContentLocked().findFocusedWindow();
+                    final WindowState win = mContainer.getDisplayContent().findFocusedWindow();
                     if (win != null) {
                         final AppWindowToken focusedToken = win.mAppToken;
                         if (focusedToken != null) {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index fc76102..e57fea3 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -679,11 +679,12 @@
         removed = true;
         stopFreezingScreen(true, true);
 
-        if (mService.mFocusedApp == this) {
-            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this);
-            mService.mFocusedApp = null;
+        final DisplayContent dc = getDisplayContent();
+        if (dc.mFocusedApp == this) {
+            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Removing focused app token:" + this
+                   + " displayId=" + dc.getDisplayId());
+            dc.setFocusedApp(null);
             mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
-            getDisplayContent().getInputMonitor().setFocusedAppLw(null);
         }
 
         if (!delayed) {
@@ -1064,6 +1065,18 @@
         }
     }
 
+    @Override
+    void onDisplayChanged(DisplayContent dc) {
+        DisplayContent prevDc = mDisplayContent;
+        super.onDisplayChanged(dc);
+        if (prevDc != null && prevDc.mFocusedApp == this) {
+            prevDc.setFocusedApp(null);
+            if (dc.getTopStack().getTopChild().getTopChild() == this) {
+                dc.setFocusedApp(this);
+            }
+        }
+    }
+
     /**
      * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds
      * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index a762fe9..6f728fc 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -70,6 +70,7 @@
 import static com.android.server.wm.DisplayContentProto.DISPLAY_INFO;
 import static com.android.server.wm.DisplayContentProto.DOCKED_STACK_DIVIDER_CONTROLLER;
 import static com.android.server.wm.DisplayContentProto.DPI;
+import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
 import static com.android.server.wm.DisplayContentProto.ID;
 import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER;
@@ -98,12 +99,17 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.CUSTOM_SCREEN_ROTATION;
+import static com.android.server.wm.WindowManagerService.H.REPORT_FOCUS_CHANGE;
+import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.H.UPDATE_DOCKED_STACK_DIVIDER;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_HIDE_TIMEOUT;
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
 import static com.android.server.wm.WindowManagerService.MAX_ANIMATION_DURATION;
 import static com.android.server.wm.WindowManagerService.SEAMLESS_ROTATION_TIMEOUT_DURATION;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_REMOVING_FOCUS;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_ASSIGN_LAYERS;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_ACTIVE;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
@@ -114,7 +120,6 @@
 import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
 import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
 import static com.android.server.wm.WindowSurfacePlacer.SET_WALLPAPER_MAY_CHANGE;
-import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
 
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
@@ -152,6 +157,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ToBooleanFunction;
 import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.wm.utils.DisplayRotationUtil;
 import com.android.server.wm.utils.RotationCache;
 import com.android.server.wm.utils.WmDisplayCutout;
 
@@ -334,6 +340,7 @@
     private final Matrix mTmpMatrix = new Matrix();
     private final Region mTmpRegion = new Region();
 
+
     /** Used for handing back size of display */
     private final Rect mTmpBounds = new Rect();
 
@@ -371,6 +378,36 @@
     private final SurfaceSession mSession = new SurfaceSession();
 
     /**
+     * Window that is currently interacting with the user. This window is responsible for receiving
+     * key events and pointer events from the user.
+     */
+    WindowState mCurrentFocus = null;
+
+    /**
+     * The last focused window that we've notified the client that the focus is changed.
+     */
+    WindowState mLastFocus = null;
+
+    /**
+     * Windows that have lost input focus and are waiting for the new focus window to be displayed
+     * before they are told about this.
+     */
+    ArrayList<WindowState> mLosingFocus = new ArrayList<>();
+
+    /**
+     * The foreground app of this display. Windows below this app cannot be the focused window. If
+     * the user taps on the area outside of the task of the focused app, we will notify AM about the
+     * new task the user wants to interact with.
+     */
+    AppWindowToken mFocusedApp = null;
+
+    /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
+    final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
+
+    /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
+    final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
+
+    /**
      * We organize all top-level Surfaces in to the following layers.
      * mOverlayLayer contains a few Surfaces which are always on top of others
      * and omitted from Screen-Magnification, for example the strict mode flash or
@@ -412,6 +449,8 @@
     /** Caches the value whether told display manager that we have content. */
     private boolean mLastHasContent;
 
+    private DisplayRotationUtil mRotationUtil = new DisplayRotationUtil();
+
     /**
      * The input method window for this display.
      */
@@ -466,7 +505,7 @@
     };
 
     private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
-        final AppWindowToken focusedApp = mService.mFocusedApp;
+        final AppWindowToken focusedApp = mFocusedApp;
         if (DEBUG_FOCUS) Slog.v(TAG_WM, "Looking for focus: " + w
                 + ", flags=" + w.mAttrs.flags + ", canReceive=" + w.canReceiveKeys());
 
@@ -625,8 +664,6 @@
         final boolean obscuredChanged = w.mObscured !=
                 mTmpApplySurfaceChangesTransactionState.obscured;
         final RootWindowContainer root = mService.mRoot;
-        // Only used if default window
-        final boolean someoneLosingFocus = !mService.mLosingFocus.isEmpty();
 
         // Update effect.
         w.mObscured = mTmpApplySurfaceChangesTransactionState.obscured;
@@ -717,9 +754,8 @@
             }
         }
 
-        if (isDefaultDisplay && someoneLosingFocus && w == mService.mCurrentFocus
-                && w.isDisplayedLw()) {
-            mTmpApplySurfaceChangesTransactionState.focusDisplayed = true;
+        if (!mLosingFocus.isEmpty() && w.isFocused() && w.isDisplayedLw()) {
+            mService.mH.obtainMessage(REPORT_LOSING_FOCUS, this).sendToTarget();
         }
 
         w.updateResizingWindowIfNeeded();
@@ -1343,21 +1379,12 @@
                     cutout, mInitialDisplayWidth, mInitialDisplayHeight);
         }
         final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
-        final List<Rect> bounds = WmDisplayCutout.computeSafeInsets(
+        final Rect[] newBounds = mRotationUtil.getRotatedBounds(
+                WmDisplayCutout.computeSafeInsets(
                         cutout, mInitialDisplayWidth, mInitialDisplayHeight)
-                .getDisplayCutout().getBoundingRects();
-        transformPhysicalToLogicalCoordinates(rotation, mInitialDisplayWidth, mInitialDisplayHeight,
-                mTmpMatrix);
-        final Region region = Region.obtain();
-        for (int i = 0; i < bounds.size(); i++) {
-            final Rect rect = bounds.get(i);
-            final RectF rectF = new RectF(bounds.get(i));
-            mTmpMatrix.mapRect(rectF);
-            rectF.round(rect);
-            region.op(rect, Op.UNION);
-        }
-
-        return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(region),
+                        .getDisplayCutout().getBoundingRectsAll(),
+                rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+        return WmDisplayCutout.computeSafeInsets(DisplayCutout.fromBounds(newBounds),
                 rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
                 rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
     }
@@ -2070,22 +2097,22 @@
         return null;
     }
 
-    void setTouchExcludeRegion(Task focusedTask) {
-        // The provided task is the task on this display with focus, so if WindowManagerService's
-        // focused app is not on this display, focusedTask will be null.
+    void updateTouchExcludeRegion() {
+        final Task focusedTask = (mFocusedApp != null ? mFocusedApp.getTask() : null);
         if (focusedTask == null) {
             mTouchExcludeRegion.setEmpty();
         } else {
             mTouchExcludeRegion.set(mBaseDisplayRect);
             final int delta = dipToPixel(RESIZE_HANDLE_WIDTH_IN_DP, mDisplayMetrics);
             mTmpRect2.setEmpty();
-            for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0;
+                    --stackNdx) {
                 final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx);
                 stack.setTouchExcludeRegion(focusedTask, delta, mTouchExcludeRegion,
                         mDisplayFrames.mContent, mTmpRect2);
             }
             // If we removed the focused task above, add it back and only leave its
-            // outside touch area in the exclusion. TapDectector is not interested in
+            // outside touch area in the exclusion. TapDetector is not interested in
             // any touch inside the focused task itself.
             if (!mTmpRect2.isEmpty()) {
                 mTouchExcludeRegion.op(mTmpRect2, Region.Op.UNION);
@@ -2096,12 +2123,7 @@
             // events to be intercepted and used to change focus. This would likely cause a
             // disappearance of the input method.
             mInputMethodWindow.getTouchableRegion(mTmpRegion);
-            if (mInputMethodWindow.getDisplayId() == mDisplayId) {
-                mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
-            } else {
-                // IME is on a different display, so we need to update its tap detector.
-                setTouchExcludeRegion(null /* focusedTask */);
-            }
+            mTouchExcludeRegion.op(mTmpRegion, Op.UNION);
         }
         for (int i = mTapExcludedWindows.size() - 1; i >= 0; i--) {
             final WindowState win = mTapExcludedWindows.get(i);
@@ -2372,6 +2394,9 @@
         }
         mDisplayFrames.writeToProto(proto, DISPLAY_FRAMES);
         proto.write(SURFACE_SIZE, mSurfaceSize);
+        if (mFocusedApp != null) {
+            mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+        }
         proto.end(token);
     }
 
@@ -2412,6 +2437,27 @@
         pw.print(prefix);
         pw.print("mDeferredRotationPauseCount="); pw.println(mDeferredRotationPauseCount);
 
+        pw.print("  mCurrentFocus="); pw.println(mCurrentFocus);
+        if (mLastFocus != mCurrentFocus) {
+            pw.print("  mLastFocus="); pw.println(mLastFocus);
+        }
+        if (mLosingFocus.size() > 0) {
+            pw.println();
+            pw.println("  Windows losing focus:");
+            for (int i = mLosingFocus.size() - 1; i >= 0; i--) {
+                final WindowState w = mLosingFocus.get(i);
+                pw.print("  Losing #"); pw.print(i); pw.print(' ');
+                pw.print(w);
+                if (dumpAll) {
+                    pw.println(":");
+                    w.dump(pw, "    ", true);
+                } else {
+                    pw.println();
+                }
+            }
+        }
+        pw.print("  mFocusedApp="); pw.println(mFocusedApp);
+
         pw.println();
         pw.println(prefix + "Application tokens in top down Z order:");
         for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
@@ -2543,6 +2589,132 @@
         return mTmpWindow;
     }
 
+
+    /**
+     * Update the focused window and make some adjustments if the focus has changed.
+     *
+     * @param mode Indicates the situation we are in. Possible modes are:
+     *             {@link WindowManagerService#UPDATE_FOCUS_NORMAL},
+     *             {@link WindowManagerService#UPDATE_FOCUS_PLACING_SURFACES},
+     *             {@link WindowManagerService#UPDATE_FOCUS_WILL_PLACE_SURFACES},
+     *             {@link WindowManagerService#UPDATE_FOCUS_REMOVING_FOCUS}
+     * @param updateInputWindows Whether to sync the window information to the input module.
+     * @return {@code true} if the focused window has changed.
+     */
+    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, boolean focusFound) {
+        final WindowState newFocus = findFocusedWindow();
+        if (mCurrentFocus == newFocus) {
+            return false;
+        }
+        mService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
+        boolean imWindowChanged = false;
+        // TODO (b/111080190): Multi-Session IME
+        if (!focusFound) {
+            final WindowState imWindow = mInputMethodWindow;
+            if (imWindow != null) {
+                final WindowState prevTarget = mService.mInputMethodTarget;
+
+                final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
+                imWindowChanged = prevTarget != newTarget;
+
+                if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
+                        && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+                    assignWindowLayers(false /* setLayoutNeeded */);
+                }
+            }
+        }
+
+        if (imWindowChanged) {
+            mService.mWindowsChanged = true;
+            setLayoutNeeded();
+        }
+
+        if (DEBUG_FOCUS_LIGHT || mService.localLOGV) Slog.v(TAG_WM, "Changing focus from "
+                + mCurrentFocus + " to " + newFocus + " displayId=" + getDisplayId()
+                + " Callers=" + Debug.getCallers(4));
+        final WindowState oldFocus = mCurrentFocus;
+        mCurrentFocus = newFocus;
+        mLosingFocus.remove(newFocus);
+
+        if (newFocus != null) {
+            mWinAddedSinceNullFocus.clear();
+            mWinRemovedSinceNullFocus.clear();
+
+            if (newFocus.canReceiveKeys()) {
+                // Displaying a window implicitly causes dispatching to be unpaused.
+                // This is to protect against bugs if someone pauses dispatching but
+                // forgets to resume.
+                newFocus.mToken.paused = false;
+            }
+        }
+
+        // System UI is only shown on the default display.
+        int focusChanged = isDefaultDisplay
+                ? mService.mPolicy.focusChangedLw(oldFocus, newFocus) : 0;
+
+        if (imWindowChanged && oldFocus != mInputMethodWindow) {
+            // Focus of the input method window changed. Perform layout if needed.
+            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+                performLayout(true /*initial*/,  updateInputWindows);
+                focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
+            } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
+                // Client will do the layout, but we need to assign layers
+                // for handleNewWindowLocked() below.
+                assignWindowLayers(false /* setLayoutNeeded */);
+            }
+        }
+
+        if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
+            // The change in focus caused us to need to do a layout.  Okay.
+            setLayoutNeeded();
+            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+                performLayout(true /*initial*/, updateInputWindows);
+            } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
+                mService.mRoot.performSurfacePlacement(false);
+            }
+        }
+
+        if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
+            // If we defer assigning layers, then the caller is responsible for doing this part.
+            getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
+        }
+
+        adjustForImeIfNeeded();
+
+        // We may need to schedule some toast windows to be removed. The toasts for an app that
+        // does not have input focus are removed within a timeout to prevent apps to redress
+        // other apps' UI.
+        scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
+
+        if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
+            pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
+        }
+        return true;
+    }
+
+    /**
+     * Set the new focused app to this display.
+     *
+     * @param newFocus the new focused AppWindowToken.
+     * @return true if the focused app is changed.
+     */
+    boolean setFocusedApp(AppWindowToken newFocus) {
+        if (newFocus != null) {
+            final DisplayContent appDisplay = newFocus.getDisplayContent();
+            if (appDisplay != this) {
+                throw new IllegalStateException(newFocus + " is not on " + getName()
+                        + " but " + ((appDisplay != null) ? appDisplay.getName() : "none"));
+            }
+        }
+        if (mFocusedApp == newFocus) {
+            return false;
+        }
+        mFocusedApp = newFocus;
+        getInputMonitor().setFocusedAppLw(newFocus);
+        updateTouchExcludeRegion();
+        return true;
+    }
+
     /** Updates the layer assignment of windows on this display. */
     void assignWindowLayers(boolean setLayoutNeeded) {
         Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "assignWindowLayers");
@@ -2906,8 +3078,8 @@
 
         if (DEBUG_INPUT_METHOD) {
             Slog.i(TAG_WM, "Desired input method target: " + imFocus);
-            Slog.i(TAG_WM, "Current focus: " + mService.mCurrentFocus);
-            Slog.i(TAG_WM, "Last focus: " + mService.mLastFocus);
+            Slog.i(TAG_WM, "Current focus: " + mCurrentFocus + " displayId=" + mDisplayId);
+            Slog.i(TAG_WM, "Last focus: " + mLastFocus + " displayId=" + mDisplayId);
         }
 
         if (DEBUG_INPUT_METHOD) {
@@ -2975,7 +3147,7 @@
     }
 
     // TODO: Super crazy long method that should be broken down...
-    boolean applySurfaceChangesTransaction(boolean recoveringMemory) {
+    void applySurfaceChangesTransaction(boolean recoveringMemory) {
 
         final int dw = mDisplayInfo.logicalWidth;
         final int dh = mDisplayInfo.logicalHeight;
@@ -3059,8 +3231,6 @@
             // can now be shown.
             atoken.updateAllDrawn();
         }
-
-        return mTmpApplySurfaceChangesTransactionState.focusDisplayed;
     }
 
     private void updateBounds() {
@@ -3314,7 +3484,6 @@
         boolean displayHasContent;
         boolean obscured;
         boolean syswin;
-        boolean focusDisplayed;
         float preferredRefreshRate;
         int preferredModeId;
 
@@ -3322,7 +3491,6 @@
             displayHasContent = false;
             obscured = false;
             syswin = false;
-            focusDisplayed = false;
             preferredRefreshRate = 0;
             preferredModeId = 0;
         }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowController.java b/services/core/java/com/android/server/wm/DisplayWindowController.java
index 76b6dbe..ab87759 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowController.java
@@ -17,11 +17,14 @@
 package com.android.server.wm;
 
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
 import android.content.res.Configuration;
 import android.os.Binder;
+import android.os.IBinder;
 import android.util.Slog;
 import android.view.Display;
 
@@ -124,6 +127,42 @@
         }
     }
 
+    /**
+     * Sets a focused app on this display.
+     *
+     * @param token Specifies which app should be focused.
+     * @param moveFocusNow Specifies if we should update the focused window immediately.
+     */
+    public void setFocusedApp(IBinder token, boolean moveFocusNow) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "setFocusedApp: could not find displayId="
+                        + mDisplayId);
+                return;
+            }
+            final AppWindowToken newFocus;
+            if (token == null) {
+                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, displayId="
+                        + mDisplayId);
+                newFocus = null;
+            } else {
+                newFocus = mRoot.getAppWindowToken(token);
+                if (newFocus == null) {
+                    Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
+                            + ", displayId=" + mDisplayId);
+                }
+                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
+                        + " moveFocusNow=" + moveFocusNow + " displayId=" + mDisplayId);
+            }
+
+            final boolean changed = mContainer.setFocusedApp(newFocus);
+            if (moveFocusNow && changed) {
+                mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+                        true /*updateInputWindows*/);
+            }
+        }
+    }
+
     @Override
     public String toString() {
         return "{DisplayWindowController displayId=" + mDisplayId + "}";
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index ef3a770..15f6938 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -64,7 +64,6 @@
     // Array of window handles to provide to the input dispatcher.
     private InputWindowHandle[] mInputWindowHandles;
     private int mInputWindowHandleCount;
-    private InputWindowHandle mFocusedInputWindowHandle;
 
     private boolean mDisableWallpaperTouchEvents;
     private final Rect mTmpRect = new Rect();
@@ -229,16 +228,12 @@
                     + child + ", " + inputWindowHandle);
         }
         addInputWindowHandle(inputWindowHandle);
-        if (hasFocus) {
-            mFocusedInputWindowHandle = inputWindowHandle;
-        }
     }
 
     private void clearInputWindowHandlesLw() {
         while (mInputWindowHandleCount != 0) {
             mInputWindowHandles[--mInputWindowHandleCount] = null;
         }
-        mFocusedInputWindowHandle = null;
     }
 
     void setUpdateInputWindowsNeededLw() {
@@ -325,13 +320,13 @@
     public void setFocusedAppLw(AppWindowToken newApp) {
         // Focused app has changed.
         if (newApp == null) {
-            mService.mInputManager.setFocusedApplication(null);
+            mService.mInputManager.setFocusedApplication(mDisplayId, null);
         } else {
             final InputApplicationHandle handle = newApp.mInputApplicationHandle;
             handle.name = newApp.toString();
             handle.dispatchingTimeoutNanos = newApp.mInputDispatchingTimeoutNanos;
 
-            mService.mInputManager.setFocusedApplication(handle);
+            mService.mInputManager.setFocusedApplication(mDisplayId, handle);
         }
     }
 
@@ -370,8 +365,7 @@
     void onRemoved() {
         // If DisplayContent removed, we need find a way to remove window handles of this display
         // from InputDispatcher, so pass an empty InputWindowHandles to remove them.
-        mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
-                mDisplayId);
+        mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
     }
 
     private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {
@@ -414,8 +408,7 @@
             }
 
             // Send windows to native code.
-            mService.mInputManager.setInputWindows(mInputWindowHandles, mFocusedInputWindowHandle,
-                    mDisplayId);
+            mService.mInputManager.setInputWindows(mInputWindowHandles, mDisplayId);
 
             clearInputWindowHandlesLw();
 
@@ -435,7 +428,6 @@
             final int flags = w.mAttrs.flags;
             final int privateFlags = w.mAttrs.privateFlags;
             final int type = w.mAttrs.type;
-            // TODO(b/111361570): multi-display focus, one focus window per display.
             final boolean hasFocus = w.isFocused();
             final boolean isVisible = w.isVisibleLw();
 
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index a9571be..3fef87d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -17,19 +17,20 @@
 package com.android.server.wm;
 
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE;
 import static android.view.WindowManager.LayoutParams.TYPE_DREAM;
 import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG;
 
-import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
 import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
 import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
 import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
@@ -41,9 +42,9 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.REPORT_LOSING_FOCUS;
 import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
 import static com.android.server.wm.WindowManagerService.H.WINDOW_FREEZE_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_PLACING_SURFACES;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
 import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_NONE;
@@ -124,6 +125,10 @@
 
     private String mCloseSystemDialogsReason;
 
+    // The ID of the display which is responsible for receiving display-unspecified key and pointer
+    // events.
+    private int mTopFocusedDisplayId = INVALID_DISPLAY;
+
     // Only a seperate transaction until we seperate the apply surface changes
     // transaction from the global transaction.
     private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
@@ -153,23 +158,40 @@
         mWallpaperController = new WallpaperController(mService);
     }
 
-    WindowState computeFocusedWindow() {
-        // While the keyguard is showing, we must focus anything besides the main display.
-        // Otherwise we risk input not going to the keyguard when the user expects it to.
-        final boolean forceDefaultDisplay = mService.isKeyguardShowingAndNotOccluded();
-
+    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
+        boolean changed = false;
+        int topFocusedDisplayId = INVALID_DISPLAY;
         for (int i = mChildren.size() - 1; i >= 0; i--) {
             final DisplayContent dc = mChildren.get(i);
-            final WindowState win = dc.findFocusedWindow();
-            if (win != null) {
-                if (forceDefaultDisplay && !dc.isDefaultDisplay) {
-                    EventLog.writeEvent(0x534e4554, "71786287", win.mOwnerUid, "");
-                    continue;
-                }
-                return win;
+            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows,
+                    topFocusedDisplayId != INVALID_DISPLAY /* focusFound */);
+            if (topFocusedDisplayId == INVALID_DISPLAY && dc.mCurrentFocus != null) {
+                topFocusedDisplayId = dc.getDisplayId();
             }
         }
-        return null;
+        if (topFocusedDisplayId == INVALID_DISPLAY) {
+            topFocusedDisplayId = DEFAULT_DISPLAY;
+        }
+        if (mTopFocusedDisplayId != topFocusedDisplayId) {
+            mTopFocusedDisplayId = topFocusedDisplayId;
+            mService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
+            if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "New topFocusedDisplayId="
+                    + topFocusedDisplayId);
+        }
+        final WindowState topFocusedWindow = getTopFocusedDisplayContent().mCurrentFocus;
+        mService.mInputManager.setFocusedWindow(
+                topFocusedWindow != null ? topFocusedWindow.mInputWindowHandle : null);
+        return changed;
+    }
+
+    DisplayContent getTopFocusedDisplayContent() {
+        return getDisplayContent(mTopFocusedDisplayId == INVALID_DISPLAY
+                ? DEFAULT_DISPLAY : mTopFocusedDisplayId);
+    }
+
+    @Override
+    void onChildPositionChanged() {
+        mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateInputWindows */);
     }
 
     DisplayContent getDisplayContent(int displayId) {
@@ -636,7 +658,6 @@
             if (mService.updateFocusedWindowLocked(UPDATE_FOCUS_PLACING_SURFACES,
                     false /*updateInputWindows*/)) {
                 updateInputWindowsNeeded = true;
-                defaultDisplay.pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
             }
         }
 
@@ -646,7 +667,7 @@
                     defaultDisplay.pendingLayoutChanges);
         }
 
-        final ArraySet<DisplayContent> touchExcludeRegionUpdateDisplays = handleResizingWindows();
+        handleResizingWindows();
 
         if (DEBUG_ORIENTATION && mService.mDisplayFrozen) Slog.v(TAG,
                 "With display frozen, orientationChangeComplete=" + mOrientationChangeComplete);
@@ -765,17 +786,7 @@
                 dc.getInputMonitor().updateInputWindowsLw(false /*force*/);
             });
         }
-        mService.setFocusTaskRegionLocked(null);
-        if (touchExcludeRegionUpdateDisplays != null) {
-            final DisplayContent focusedDc = mService.mFocusedApp != null
-                    ? mService.mFocusedApp.getDisplayContent() : null;
-            for (DisplayContent dc : touchExcludeRegionUpdateDisplays) {
-                // The focused DisplayContent was recalcuated in setFocusTaskRegionLocked
-                if (focusedDc != dc) {
-                    dc.setTouchExcludeRegion(null /* focusedTask */);
-                }
-            }
-        }
+        forAllDisplays(DisplayContent::updateTouchExcludeRegion);
 
         // Check to see if we are now in a state where the screen should
         // be enabled, because the window obscured flags have changed.
@@ -808,16 +819,10 @@
                     mService.getDefaultDisplayRotation());
         }
 
-        boolean focusDisplayed = false;
-
         final int count = mChildren.size();
         for (int j = 0; j < count; ++j) {
             final DisplayContent dc = mChildren.get(j);
-            focusDisplayed |= dc.applySurfaceChangesTransaction(recoveringMemory);
-        }
-
-        if (focusDisplayed) {
-            mService.mH.sendEmptyMessage(REPORT_LOSING_FOCUS);
+            dc.applySurfaceChangesTransaction(recoveringMemory);
         }
 
         // Give the display manager a chance to adjust properties like display rotation if it needs
@@ -828,12 +833,8 @@
 
     /**
      * Handles resizing windows during surface placement.
-     *
-     * @return A set of any DisplayContent whose touch exclude region needs to be recalculated due
-     *         to a tap-exclude window resizing, or null if no such DisplayContents were found.
      */
-    private ArraySet<DisplayContent> handleResizingWindows() {
-        ArraySet<DisplayContent> touchExcludeRegionUpdateSet = null;
+    private void handleResizingWindows() {
         for (int i = mService.mResizingWindows.size() - 1; i >= 0; i--) {
             WindowState win = mService.mResizingWindows.get(i);
             if (win.mAppFreezing) {
@@ -842,15 +843,7 @@
             }
             win.reportResized();
             mService.mResizingWindows.remove(i);
-            if (WindowManagerService.excludeWindowTypeFromTapOutTask(win.mAttrs.type)) {
-                final DisplayContent dc = win.getDisplayContent();
-                if (touchExcludeRegionUpdateSet == null) {
-                    touchExcludeRegionUpdateSet = new ArraySet<>();
-                }
-                touchExcludeRegionUpdateSet.add(dc);
-            }
         }
-        return touchExcludeRegionUpdateSet;
     }
 
     /**
@@ -1004,6 +997,10 @@
         }
     }
 
+    void dumpTopFocusedDisplayId(PrintWriter pw) {
+        pw.print("  mTopFocusedDisplayId="); pw.println(mTopFocusedDisplayId);
+    }
+
     void dumpLayoutNeededDisplayIds(PrintWriter pw) {
         if (!isLayoutNeeded()) {
             return;
diff --git a/services/core/java/com/android/server/wm/TaskPositioningController.java b/services/core/java/com/android/server/wm/TaskPositioningController.java
index 33416f6..b7e37b2 100644
--- a/services/core/java/com/android/server/wm/TaskPositioningController.java
+++ b/services/core/java/com/android/server/wm/TaskPositioningController.java
@@ -131,9 +131,9 @@
         // of the app, it may not have focus since there might be other windows
         // on top (eg. a dialog window).
         WindowState transferFocusFromWin = win;
-        if (mService.mCurrentFocus != null && mService.mCurrentFocus != win
-                && mService.mCurrentFocus.mAppToken == win.mAppToken) {
-            transferFocusFromWin = mService.mCurrentFocus;
+        if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win
+                && displayContent.mCurrentFocus.mAppToken == win.mAppToken) {
+            transferFocusFromWin = displayContent.mCurrentFocus;
         }
         if (!mInputManager.transferTouchFocus(
                 transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
diff --git a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
index f1e1592..52f8510 100644
--- a/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
+++ b/services/core/java/com/android/server/wm/TaskTapPointerEventListener.java
@@ -19,12 +19,12 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.input.InputManager;
+import android.os.Handler;
 import android.view.MotionEvent;
 import android.view.WindowManagerPolicyConstants.PointerEventListener;
 
 import com.android.server.wm.WindowManagerService.H;
 
-import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.PointerIcon.TYPE_NOT_SPECIFIED;
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
@@ -36,6 +36,8 @@
     final private Region mTouchExcludeRegion = new Region();
     private final WindowManagerService mService;
     private final DisplayContent mDisplayContent;
+    private final Handler mHandler;
+    private final Runnable mMoveDisplayToTop;
     private final Rect mTmpRect = new Rect();
     private int mPointerIconType = TYPE_NOT_SPECIFIED;
 
@@ -43,6 +45,13 @@
             DisplayContent displayContent) {
         mService = service;
         mDisplayContent = displayContent;
+        mHandler = new Handler(mService.mH.getLooper());
+        mMoveDisplayToTop = () -> {
+            synchronized (mService.mWindowMap) {
+                mDisplayContent.getParent().positionChildAt(WindowContainer.POSITION_TOP,
+                        mDisplayContent, true /* includingParents */);
+            }
+        };
     }
 
     @Override
@@ -61,6 +70,7 @@
                         mService.mTaskPositioningController.handleTapOutsideTask(
                                 mDisplayContent, x, y);
                     }
+                    mHandler.post(mMoveDisplayToTop);
                 }
             }
             break;
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 4883f97..46999a2 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -273,6 +273,7 @@
             parent.mTreeWeight += child.mTreeWeight;
             parent = parent.getParent();
         }
+        onChildPositionChanged();
     }
 
     /**
@@ -298,6 +299,7 @@
             parent.mTreeWeight -= child.mTreeWeight;
             parent = parent.getParent();
         }
+        onChildPositionChanged();
     }
 
     /**
@@ -455,9 +457,15 @@
                 mChildren.remove(child);
                 mChildren.add(position, child);
         }
+        onChildPositionChanged();
     }
 
     /**
+     * Notify that a child's position has changed. Possible changes are adding or removing a child.
+     */
+    void onChildPositionChanged() { }
+
+    /**
      * Update override configuration and recalculate full config.
      * @see #mOverrideConfiguration
      * @see #mFullConfiguration
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 942e47b..10ba63e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -499,12 +499,6 @@
     final ArrayList<WindowState> mDestroyPreservedSurface = new ArrayList<>();
 
     /**
-     * Windows that have lost input focus and are waiting for the new
-     * focus window to be displayed before they are told about this.
-     */
-    ArrayList<WindowState> mLosingFocus = new ArrayList<>();
-
-    /**
      * This is set when we have run out of memory, and will either be an empty
      * list or contain windows that need to be force removed.
      */
@@ -639,14 +633,6 @@
      */
     final Handler mAnimationHandler = new Handler(AnimationThread.getHandler().getLooper());
 
-    WindowState mCurrentFocus = null;
-    WindowState mLastFocus = null;
-
-    /** Windows added since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
-    private final ArrayList<WindowState> mWinAddedSinceNullFocus = new ArrayList<>();
-    /** Windows removed since {@link #mCurrentFocus} was set to null. Used for ANR blaming. */
-    private final ArrayList<WindowState> mWinRemovedSinceNullFocus = new ArrayList<>();
-
     /** This just indicates the window the input method is on top of, not
      * necessarily the window its input is going to. */
     WindowState mInputMethodTarget = null;
@@ -721,9 +707,6 @@
         }
     }
 
-    // TODO: Move to RootWindowContainer
-    AppWindowToken mFocusedApp = null;
-
     PowerManager mPowerManager;
     PowerManagerInternal mPowerManagerInternal;
 
@@ -1371,8 +1354,8 @@
                 // the screen after the activity goes away.
                 if (addToastWindowRequiresToken
                         || (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
-                        || mCurrentFocus == null
-                        || mCurrentFocus.mOwnerUid != callingUid) {
+                        || displayContent.mCurrentFocus == null
+                        || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
                     mH.sendMessageDelayed(
                             mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                             win.mAttrs.hideTimeoutMilliseconds);
@@ -1382,8 +1365,8 @@
             // From now on, no exceptions or errors allowed!
 
             res = WindowManagerGlobal.ADD_OKAY;
-            if (mCurrentFocus == null) {
-                mWinAddedSinceNullFocus.add(win);
+            if (displayContent.mCurrentFocus == null) {
+                displayContent.mWinAddedSinceNullFocus.add(win);
             }
 
             if (excludeWindowTypeFromTapOutTask(type)) {
@@ -1504,7 +1487,7 @@
             win.getParent().assignChildLayers();
 
             if (focusChanged) {
-                displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus,
+                displayContent.getInputMonitor().setInputFocusLw(displayContent.mCurrentFocus,
                         false /*updateInputWindows*/);
             }
             displayContent.getInputMonitor().updateInputWindowsLw(false /*force*/);
@@ -1679,8 +1662,9 @@
 
         win.resetAppOpsState();
 
-        if (mCurrentFocus == null) {
-            mWinRemovedSinceNullFocus.add(win);
+        final DisplayContent dc = win.getDisplayContent();
+        if (dc.mCurrentFocus == null) {
+            dc.mWinRemovedSinceNullFocus.add(win);
         }
         mPendingRemove.remove(win);
         mResizingWindows.remove(win);
@@ -1716,7 +1700,6 @@
             atoken.postWindowRemoveStartingWindowCleanup(win);
         }
 
-        final DisplayContent dc = win.getDisplayContent();
         if (win.mAttrs.type == TYPE_WALLPAPER) {
             dc.mWallpaperController.clearLastWallpaperTimeoutTime();
             dc.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
@@ -1978,9 +1961,9 @@
             boolean imMayMove = (flagChanges & (FLAG_ALT_FOCUSABLE_IM | FLAG_NOT_FOCUSABLE)) != 0
                     || becameVisible;
             final boolean isDefaultDisplay = win.isDefaultDisplay();
-            boolean focusMayChange = isDefaultDisplay && (win.mViewVisibility != viewVisibility
+            boolean focusMayChange = win.mViewVisibility != viewVisibility
                     || ((flagChanges & FLAG_NOT_FOCUSABLE) != 0)
-                    || (!win.mRelayoutCalled));
+                    || (!win.mRelayoutCalled);
 
             boolean wallpaperMayMove = win.mViewVisibility != viewVisibility
                     && (win.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
@@ -2025,8 +2008,7 @@
                 }
                 result |= RELAYOUT_RES_SURFACE_CHANGED;
                 if (!win.mWillReplaceWindow) {
-                    focusMayChange = tryStartExitingAnimation(win, winAnimator, isDefaultDisplay,
-                            focusMayChange);
+                    focusMayChange = tryStartExitingAnimation(win, winAnimator, focusMayChange);
                 }
             }
 
@@ -2051,7 +2033,7 @@
                     return 0;
                 }
                 if ((result & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
-                    focusMayChange = isDefaultDisplay;
+                    focusMayChange = true;
                 }
                 final DisplayContent displayContent = win.getDisplayContent();
                 if (win.mAttrs.type == TYPE_INPUT_METHOD
@@ -2199,7 +2181,7 @@
     }
 
     private boolean tryStartExitingAnimation(WindowState win, WindowStateAnimator winAnimator,
-            boolean isDefaultDisplay, boolean focusMayChange) {
+            boolean focusMayChange) {
         // Try starting an animation; if there isn't one, we
         // can destroy the surface right away.
         int transit = WindowManagerPolicy.TRANSIT_EXIT;
@@ -2207,7 +2189,7 @@
             transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
         }
         if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) {
-            focusMayChange = isDefaultDisplay;
+            focusMayChange = true;
             win.mAnimatingExit = true;
         } else if (win.isAnimating()) {
             // Currently in a hide animation... turn this into
@@ -2504,57 +2486,6 @@
         }
     }
 
-    void setFocusTaskRegionLocked(AppWindowToken previousFocus) {
-        final Task focusedTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
-        final Task previousTask = previousFocus != null ? previousFocus.getTask() : null;
-        final DisplayContent focusedDisplayContent =
-                focusedTask != null ? focusedTask.getDisplayContent() : null;
-        final DisplayContent previousDisplayContent =
-                previousTask != null ? previousTask.getDisplayContent() : null;
-        if (previousDisplayContent != null && previousDisplayContent != focusedDisplayContent) {
-            previousDisplayContent.setTouchExcludeRegion(null);
-        }
-        if (focusedDisplayContent != null) {
-            focusedDisplayContent.setTouchExcludeRegion(focusedTask);
-        }
-    }
-
-    @Override
-    public void setFocusedApp(IBinder token, boolean moveFocusNow) {
-        if (!checkCallingPermission(MANAGE_APP_TOKENS, "setFocusedApp()")) {
-            throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
-        }
-
-        synchronized(mWindowMap) {
-            final AppWindowToken newFocus;
-            if (token == null) {
-                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Clearing focused app, was " + mFocusedApp);
-                newFocus = null;
-            } else {
-                newFocus = mRoot.getAppWindowToken(token);
-                if (newFocus == null) {
-                    Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token);
-                }
-                if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "Set focused app to: " + newFocus
-                        + " old focus=" + mFocusedApp + " moveFocusNow=" + moveFocusNow);
-            }
-
-            final boolean changed = mFocusedApp != newFocus;
-            if (changed) {
-                AppWindowToken prev = mFocusedApp;
-                mFocusedApp = newFocus;
-                mFocusedApp.getDisplayContent().getInputMonitor().setFocusedAppLw(newFocus);
-                setFocusTaskRegionLocked(prev);
-            }
-
-            if (moveFocusNow && changed) {
-                final long origId = Binder.clearCallingIdentity();
-                updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
-                Binder.restoreCallingIdentity(origId);
-            }
-        }
-    }
-
     @Override
     public void prepareAppTransition(@TransitionType int transit, boolean alwaysKeepCurrent) {
         prepareAppTransition(transit, alwaysKeepCurrent, 0 /* flags */, false /* forceOverride */);
@@ -4395,7 +4326,8 @@
     }
 
     private WindowState getFocusedWindowLocked() {
-        return mCurrentFocus;
+        // Return the focused window in the focused display.
+        return mRoot.getTopFocusedDisplayContent().mCurrentFocus;
     }
 
     TaskStack getImeFocusStackLocked() {
@@ -4403,8 +4335,11 @@
         // Also don't use mInputMethodTarget's stack, because some window with FLAG_NOT_FOCUSABLE
         // and FLAG_ALT_FOCUSABLE_IM flags both set might be set to IME target so they're moved
         // to make room for IME, but the window is not the focused window that's taking input.
-        return (mFocusedApp != null && mFocusedApp.getTask() != null) ?
-                mFocusedApp.getTask().mStack : null;
+        // TODO (b/111080190): Consider the case of multiple IMEs on multi-display.
+        final DisplayContent topFocusedDisplay = mRoot.getTopFocusedDisplayContent();
+        final AppWindowToken focusedApp = topFocusedDisplay.mFocusedApp;
+        return (focusedApp != null && focusedApp.getTask() != null)
+                ? focusedApp.getTask().mStack : null;
     }
 
     public boolean detectSafeMode() {
@@ -4542,7 +4477,6 @@
         public static final int REPORT_LOSING_FOCUS = 3;
         public static final int WINDOW_FREEZE_TIMEOUT = 11;
 
-        public static final int APP_TRANSITION_TIMEOUT = 13;
         public static final int PERSIST_ANIMATION_SCALE = 14;
         public static final int FORCE_GC = 15;
         public static final int ENABLE_SCREEN = 16;
@@ -4554,7 +4488,6 @@
         public static final int BOOT_TIMEOUT = 23;
         public static final int WAITING_FOR_DRAWN_TIMEOUT = 24;
         public static final int SHOW_STRICT_MODE_VIOLATION = 25;
-        public static final int DO_ANIMATION_CALLBACK = 26;
 
         public static final int CLIENT_FREEZE_TIMEOUT = 30;
         public static final int NOTIFY_ACTIVITY_DRAWN = 32;
@@ -4601,6 +4534,7 @@
             }
             switch (msg.what) {
                 case REPORT_FOCUS_CHANGE: {
+                    final DisplayContent displayContent = (DisplayContent) msg.obj;
                     WindowState lastFocus;
                     WindowState newFocus;
 
@@ -4608,24 +4542,22 @@
 
                     synchronized(mWindowMap) {
                         // TODO(multidisplay): Accessibility supported only of default desiplay.
-                        if (mAccessibilityController != null && getDefaultDisplayContentLocked()
-                                .getDisplayId() == DEFAULT_DISPLAY) {
+                        if (mAccessibilityController != null && displayContent.isDefaultDisplay) {
                             accessibilityController = mAccessibilityController;
                         }
 
-                        lastFocus = mLastFocus;
-                        newFocus = mCurrentFocus;
+                        lastFocus = displayContent.mLastFocus;
+                        newFocus = displayContent.mCurrentFocus;
                         if (lastFocus == newFocus) {
                             // Focus is not changing, so nothing to do.
                             return;
                         }
-                        mLastFocus = newFocus;
+                        displayContent.mLastFocus = newFocus;
                         if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Focus moving from " + lastFocus +
-                                " to " + newFocus);
-                        if (newFocus != null && lastFocus != null
-                                && !newFocus.isDisplayedLw()) {
-                            //Slog.i(TAG_WM, "Delaying loss of focus...");
-                            mLosingFocus.add(lastFocus);
+                                " to " + newFocus + " displayId=" + displayContent.getDisplayId());
+                        if (newFocus != null && lastFocus != null && !newFocus.isDisplayedLw()) {
+                            if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Delaying loss of focus...");
+                            displayContent.mLosingFocus.add(lastFocus);
                             lastFocus = null;
                         }
                     }
@@ -4636,8 +4568,6 @@
                         accessibilityController.onWindowFocusChangedNotLocked();
                     }
 
-                    //System.out.println("Changing focus from " + lastFocus
-                    //                   + " to " + newFocus);
                     if (newFocus != null) {
                         if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
                         newFocus.reportFocusChangedSerialized(true, mInTouchMode);
@@ -4651,15 +4581,16 @@
                 } break;
 
                 case REPORT_LOSING_FOCUS: {
+                    final DisplayContent displayContent = (DisplayContent) msg.obj;
                     ArrayList<WindowState> losers;
 
                     synchronized(mWindowMap) {
-                        losers = mLosingFocus;
-                        mLosingFocus = new ArrayList<WindowState>();
+                        losers = displayContent.mLosingFocus;
+                        displayContent.mLosingFocus = new ArrayList<>();
                     }
 
                     final int N = losers.size();
-                    for (int i=0; i<N; i++) {
+                    for (int i = 0; i < N; i++) {
                         if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing delayed focus: " +
                                 losers.get(i));
                         losers.get(i).reportFocusChangedSerialized(false, mInTouchMode);
@@ -4674,21 +4605,6 @@
                     break;
                 }
 
-                case APP_TRANSITION_TIMEOUT: {
-                    synchronized (mWindowMap) {
-                        if (mAppTransition.isTransitionSet() || !mOpeningApps.isEmpty()
-                                    || !mClosingApps.isEmpty()) {
-                            if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "*** APP TRANSITION TIMEOUT."
-                                    + " isTransitionSet()=" + mAppTransition.isTransitionSet()
-                                    + " mOpeningApps.size()=" + mOpeningApps.size()
-                                    + " mClosingApps.size()=" + mClosingApps.size());
-                            mAppTransition.setTimeout();
-                            mWindowPlacerLocked.performSurfacePlacement();
-                        }
-                    }
-                    break;
-                }
-
                 case PERSIST_ANIMATION_SCALE: {
                     Settings.Global.putFloat(mContext.getContentResolver(),
                             Settings.Global.WINDOW_ANIMATION_SCALE, mWindowAnimationScaleSetting);
@@ -4841,14 +4757,6 @@
                     break;
                 }
 
-                case DO_ANIMATION_CALLBACK: {
-                    try {
-                        ((IRemoteCallback)msg.obj).sendResult(null);
-                    } catch (RemoteException e) {
-                    }
-                    break;
-                }
-
                 case NOTIFY_ACTIVITY_DRAWN:
                     try {
                         mActivityTaskManager.notifyActivityDrawn((IBinder) msg.obj);
@@ -5553,89 +5461,11 @@
         }
     }
 
-    // TODO: Move to DisplayContent
     boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
-        WindowState newFocus = mRoot.computeFocusedWindow();
-        if (mCurrentFocus != newFocus) {
-            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
-            // This check makes sure that we don't already have the focus
-            // change message pending.
-            mH.removeMessages(H.REPORT_FOCUS_CHANGE);
-            mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
-            final DisplayContent displayContent = (newFocus != null) ? newFocus.getDisplayContent()
-                    : getDefaultDisplayContentLocked();
-            boolean imWindowChanged = false;
-            if (displayContent.mInputMethodWindow != null) {
-                final WindowState prevTarget = mInputMethodTarget;
-
-                final WindowState newTarget =
-                        displayContent.computeImeTarget(true /* updateImeTarget*/);
-                imWindowChanged = prevTarget != newTarget;
-
-                if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
-                        && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
-                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
-                }
-            }
-
-            if (imWindowChanged) {
-                mWindowsChanged = true;
-                displayContent.setLayoutNeeded();
-                newFocus = mRoot.computeFocusedWindow();
-            }
-
-            if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +
-                    mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
-            final WindowState oldFocus = mCurrentFocus;
-            mCurrentFocus = newFocus;
-            mLosingFocus.remove(newFocus);
-
-            if (mCurrentFocus != null) {
-                mWinAddedSinceNullFocus.clear();
-                mWinRemovedSinceNullFocus.clear();
-            }
-
-            int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);
-
-            if (imWindowChanged && oldFocus != displayContent.mInputMethodWindow) {
-                // Focus of the input method window changed. Perform layout if needed.
-                if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
-                    displayContent.performLayout(true /*initial*/,  updateInputWindows);
-                    focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
-                } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
-                    // Client will do the layout, but we need to assign layers
-                    // for handleNewWindowLocked() below.
-                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
-                }
-            }
-
-            if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
-                // The change in focus caused us to need to do a layout.  Okay.
-                displayContent.setLayoutNeeded();
-                if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
-                    displayContent.performLayout(true /*initial*/, updateInputWindows);
-                } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
-                    mRoot.performSurfacePlacement(false);
-                }
-            }
-
-            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
-                // If we defer assigning layers, then the caller is responsible for
-                // doing this part.
-                displayContent.getInputMonitor().setInputFocusLw(mCurrentFocus, updateInputWindows);
-            }
-
-            displayContent.adjustForImeIfNeeded();
-
-            // We may need to schedule some toast windows to be removed. The toasts for an app that
-            // does not have input focus are removed within a timeout to prevent apps to redress
-            // other apps' UI.
-            displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
-
-            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
-            return true;
-        }
-        return false;
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
+        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
+        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+        return changed;
     }
 
     void startFreezingDisplayLocked(int exitAnim, int enterAnim) {
@@ -6197,11 +6027,12 @@
     void writeToProtoLocked(ProtoOutputStream proto, boolean trim) {
         mPolicy.writeToProto(proto, POLICY);
         mRoot.writeToProto(proto, ROOT_WINDOW_CONTAINER, trim);
-        if (mCurrentFocus != null) {
-            mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
+        final DisplayContent topFocusedDisplayContent = mRoot.getTopFocusedDisplayContent();
+        if (topFocusedDisplayContent.mCurrentFocus != null) {
+            topFocusedDisplayContent.mCurrentFocus.writeIdentifierToProto(proto, FOCUSED_WINDOW);
         }
-        if (mFocusedApp != null) {
-            mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
+        if (topFocusedDisplayContent.mFocusedApp != null) {
+            topFocusedDisplayContent.mFocusedApp.writeNameToProto(proto, FOCUSED_APP);
         }
         final WindowState imeWindow = mRoot.getCurrentInputMethodWindow();
         if (imeWindow != null) {
@@ -6299,23 +6130,6 @@
                 }
             }
         }
-        if (mLosingFocus.size() > 0) {
-            pw.println();
-            pw.println("  Windows losing focus:");
-            for (int i=mLosingFocus.size()-1; i>=0; i--) {
-                WindowState w = mLosingFocus.get(i);
-                if (windows == null || windows.contains(w)) {
-                    pw.print("  Losing #"); pw.print(i); pw.print(' ');
-                            pw.print(w);
-                    if (dumpAll) {
-                        pw.println(":");
-                        w.dump(pw, "    ", true);
-                    } else {
-                        pw.println();
-                    }
-                }
-            }
-        }
         if (mResizingWindows.size() > 0) {
             pw.println();
             pw.println("  Windows waiting to resize:");
@@ -6344,11 +6158,7 @@
         pw.println();
         pw.print("  mGlobalConfiguration="); pw.println(mRoot.getConfiguration());
         pw.print("  mHasPermanentDpad="); pw.println(mHasPermanentDpad);
-        pw.print("  mCurrentFocus="); pw.println(mCurrentFocus);
-        if (mLastFocus != mCurrentFocus) {
-            pw.print("  mLastFocus="); pw.println(mLastFocus);
-        }
-        pw.print("  mFocusedApp="); pw.println(mFocusedApp);
+        mRoot.dumpTopFocusedDisplayId(pw);
         if (mInputMethodTarget != null) {
             pw.print("  mInputMethodTarget="); pw.println(mInputMethodTarget);
         }
@@ -6479,11 +6289,17 @@
         if (reason != null) {
             pw.println("  Reason: " + reason);
         }
-        if (!mWinAddedSinceNullFocus.isEmpty()) {
-            pw.println("  Windows added since null focus: " + mWinAddedSinceNullFocus);
-        }
-        if (!mWinRemovedSinceNullFocus.isEmpty()) {
-            pw.println("  Windows removed since null focus: " + mWinRemovedSinceNullFocus);
+        for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
+            final DisplayContent dc = mRoot.getChildAt(i);
+            final int displayId = dc.getDisplayId();
+            if (!dc.mWinAddedSinceNullFocus.isEmpty()) {
+                pw.println("  Windows added in display #" + displayId + " since null focus: "
+                        + dc.mWinAddedSinceNullFocus);
+            }
+            if (!dc.mWinRemovedSinceNullFocus.isEmpty()) {
+                pw.println("  Windows removed in display #" + displayId + " since null focus: "
+                        + dc.mWinRemovedSinceNullFocus);
+            }
         }
         pw.println();
         dumpWindowsNoHeaderLocked(pw, true, null);
@@ -6818,9 +6634,9 @@
     @Override
     public void setDockedStackDividerTouchRegion(Rect touchRegion) {
         synchronized (mWindowMap) {
-            getDefaultDisplayContentLocked().getDockedDividerController()
-                    .setTouchRegion(touchRegion);
-            setFocusTaskRegionLocked(null);
+            final DisplayContent dc = getDefaultDisplayContentLocked();
+            dc.getDockedDividerController().setTouchRegion(touchRegion);
+            dc.updateTouchExcludeRegion();
         }
     }
 
@@ -7373,7 +7189,14 @@
         @Override
         public boolean isUidFocused(int uid) {
             synchronized (mWindowMap) {
-                return mCurrentFocus != null ? uid == mCurrentFocus.getOwningUid() : false;
+                for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
+                    final DisplayContent displayContent = mRoot.getChildAt(i);
+                    if (displayContent.mCurrentFocus != null
+                            && uid == displayContent.mCurrentFocus.getOwningUid()) {
+                        return true;
+                    }
+                }
+                return false;
             }
         }
 
@@ -7396,8 +7219,9 @@
                 // press home.  Sometimes the IME won't go down.)
                 // Would be nice to fix this more correctly, but it's
                 // way at the end of a release, and this should be good enough.
-                if (mCurrentFocus != null && mCurrentFocus.mSession.mUid == uid
-                        && mCurrentFocus.mSession.mPid == pid) {
+                final WindowState currentFocus = mRoot.getTopFocusedDisplayContent().mCurrentFocus;
+                if (currentFocus != null && currentFocus.mSession.mUid == uid
+                        && currentFocus.mSession.mPid == pid) {
                     return true;
                 }
             }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a4bac31..8276952 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1833,7 +1833,7 @@
         if (startingWindow && DEBUG_STARTING_WINDOW) Slog.d(TAG_WM,
                 "Starting window removed " + this);
 
-        if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && this == mService.mCurrentFocus)
+        if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && isFocused())
             Slog.v(TAG_WM, "Remove " + this + " client="
                         + Integer.toHexString(System.identityHashCode(mClient.asBinder()))
                         + ", surfaceController=" + mWinAnimator.mSurfaceController + " Callers="
@@ -1945,7 +1945,7 @@
             if (wasVisible && mService.updateOrientationFromAppTokensLocked(displayId)) {
                 mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
             }
-            mService.updateFocusedWindowLocked(mService.mCurrentFocus == this
+            mService.updateFocusedWindowLocked(isFocused()
                             ? UPDATE_FOCUS_REMOVING_FOCUS
                             : UPDATE_FOCUS_NORMAL,
                     true /*updateInputWindows*/);
@@ -2180,7 +2180,7 @@
             mPolicyVisibility = mPolicyVisibilityAfterAnim;
             if (!mPolicyVisibility) {
                 mWinAnimator.hide("checkPolicyVisibilityChange");
-                if (mService.mCurrentFocus == this) {
+                if (isFocused()) {
                     if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                             "setAnimationLocked: setting mFocusMayChange true");
                     mService.mFocusMayChange = true;
@@ -2482,6 +2482,7 @@
             }
         }
         mPolicyVisibilityAfterAnim = false;
+        final boolean isFocused = isFocused();
         if (!doAnimation) {
             if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility false: " + this);
             mPolicyVisibility = false;
@@ -2489,7 +2490,7 @@
             // for it to be displayed before enabling the display, that
             // we allow the display to be enabled now.
             mService.enableScreenIfNeededLocked();
-            if (mService.mCurrentFocus == this) {
+            if (isFocused) {
                 if (DEBUG_FOCUS_LIGHT) Slog.i(TAG,
                         "WindowState.hideLw: setting mFocusMayChange true");
                 mService.mFocusMayChange = true;
@@ -2498,7 +2499,7 @@
         if (requestAnim) {
             mService.scheduleAnimationLocked();
         }
-        if (mService.mCurrentFocus == this) {
+        if (isFocused) {
             mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false /* updateImWindows */);
         }
         return true;
@@ -2994,10 +2995,8 @@
         }
     }
 
-    public boolean isFocused() {
-        synchronized(mService.mWindowMap) {
-            return mService.mCurrentFocus == this;
-        }
+    boolean isFocused() {
+        return getDisplayContent().mCurrentFocus == this;
     }
 
     @Override
@@ -4435,7 +4434,12 @@
         @Override
         public boolean isFocused() {
             final WindowState outer = mOuter.get();
-            return outer != null && outer.isFocused();
+            if (outer != null) {
+                synchronized (outer.mService.mWindowMap) {
+                    return outer.isFocused();
+                }
+            }
+            return false;
         }
     }
 
@@ -4663,10 +4667,7 @@
 
         mTapExcludeRegionHolder.updateRegion(regionId, left, top, width, height);
         // Trigger touch exclude region update on current display.
-        final boolean isAppFocusedOnDisplay = mService.mFocusedApp != null
-                && mService.mFocusedApp.getDisplayContent() == currentDisplay;
-        currentDisplay.setTouchExcludeRegion(isAppFocusedOnDisplay ? mService.mFocusedApp.getTask()
-                : null);
+        currentDisplay.updateTouchExcludeRegion();
     }
 
     /** Union the region with current tap exclude region that this window provides. */
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 080a3a2..e13a70a 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -69,7 +69,6 @@
 import android.view.animation.Animation;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.wm.WindowManagerService.H;
 
 import java.io.PrintWriter;
 import java.util.function.Predicate;
@@ -252,7 +251,7 @@
         mService.mSkipAppTransitionAnimation = false;
         mService.mNoAnimationNotifyOnTransitionFinished.clear();
 
-        mService.mH.removeMessages(H.APP_TRANSITION_TIMEOUT);
+        mService.mAppTransition.removeAppTransitionTimeoutCallbacks();
 
         final DisplayContent displayContent = mService.getDefaultDisplayContentLocked();
 
@@ -763,6 +762,7 @@
 
     private void processApplicationsAnimatingInPlace(int transit) {
         if (transit == TRANSIT_TASK_IN_PLACE) {
+            // TODO (b/111362605): non-default-display transition.
             // Find the focused window
             final WindowState win = mService.getDefaultDisplayContentLocked().findFocusedWindow();
             if (win != null) {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index fefd305..0cf79b6 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -255,6 +255,7 @@
         super.removeImmediately();
     }
 
+    @Override
     void onDisplayChanged(DisplayContent dc) {
         dc.reParentWindowToken(this);
         mDisplayContent = dc;
diff --git a/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
new file mode 100644
index 0000000..9f307bb
--- /dev/null
+++ b/services/core/java/com/android/server/wm/utils/DisplayRotationUtil.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2018 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.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_LENGTH;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+
+import static com.android.server.wm.utils.CoordinateTransforms.transformPhysicalToLogicalCoordinates;
+
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Utility to compute bounds after rotating the screen.
+ */
+public class DisplayRotationUtil {
+    private final Matrix mTmpMatrix = new Matrix();
+
+    private static int getRotationToBoundsOffset(int rotation) {
+        switch (rotation) {
+            case ROTATION_0:
+                return 0;
+            case ROTATION_90:
+                return -1;
+            case ROTATION_180:
+                return 2;
+            case ROTATION_270:
+                return 1;
+            default:
+                // should not happen
+                return 0;
+        }
+    }
+
+    @VisibleForTesting
+    static int getBoundIndexFromRotation(int i, int rotation) {
+        return Math.floorMod(i + getRotationToBoundsOffset(rotation),
+                BOUNDS_POSITION_LENGTH);
+    }
+
+    /**
+     * Compute bounds after rotating teh screen.
+     *
+     * @param bounds Bounds before the rotation. The array must contain exactly 4 non-null elements.
+     * @param rotation rotation constant defined in android.view.Surface.
+     * @param initialDisplayWidth width of the display before the rotation.
+     * @param initialDisplayHeight height of the display before the rotation.
+     * @return Bounds after the rotation.
+     *
+     * @hide
+     */
+    public Rect[] getRotatedBounds(
+            Rect[] bounds, int rotation, int initialDisplayWidth, int initialDisplayHeight) {
+        if (bounds.length != BOUNDS_POSITION_LENGTH) {
+            throw new IllegalArgumentException(
+                    "bounds must have exactly 4 elements: bounds=" + bounds);
+        }
+        if (rotation == ROTATION_0) {
+            return bounds;
+        }
+        transformPhysicalToLogicalCoordinates(rotation, initialDisplayWidth, initialDisplayHeight,
+                mTmpMatrix);
+        Rect[] newBounds = new Rect[BOUNDS_POSITION_LENGTH];
+        for (int i = 0; i < bounds.length; i++) {
+
+            final Rect rect = bounds[i];
+            if (!rect.isEmpty()) {
+                final RectF rectF = new RectF(rect);
+                mTmpMatrix.mapRect(rectF);
+                rectF.round(rect);
+            }
+            newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
+        }
+        return newBounds;
+    }
+}
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 157b634..045f4eb 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -8,6 +8,7 @@
         "-Wall",
         "-Werror",
         "-Wno-unused-parameter",
+        "-Wthread-safety",
 
         "-DEGL_EGLEXT_PROTOTYPES",
         "-DGL_GLEXT_PROTOTYPES",
@@ -29,9 +30,7 @@
         "com_android_server_devicepolicy_CryptoTestHelper.cpp",
         "com_android_server_HardwarePropertiesManagerService.cpp",
         "com_android_server_hdmi_HdmiCecController.cpp",
-        "com_android_server_input_InputApplicationHandle.cpp",
         "com_android_server_input_InputManagerService.cpp",
-        "com_android_server_input_InputWindowHandle.cpp",
         "com_android_server_lights_LightsService.cpp",
         "com_android_server_location_GnssLocationProvider.cpp",
         "com_android_server_locksettings_SyntheticPasswordManager.cpp",
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 3901ceb..dc0d53b 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -17,6 +17,7 @@
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 
+#include <binder/IServiceManager.h>
 #include <hidl/HidlTransportSupport.h>
 
 #include <schedulerservice/SchedulingPolicyService.h>
@@ -34,7 +35,8 @@
     char propBuf[PROPERTY_VALUE_MAX];
     property_get("system_init.startsensorservice", propBuf, "1");
     if (strcmp(propBuf, "1") == 0) {
-        SensorService::instantiate();
+        SensorService::publish(false /* allowIsolated */,
+                               IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL);
     }
 
 }
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 42ade38..c66d03c 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -60,10 +60,12 @@
 #include <nativehelper/ScopedUtfChars.h>
 
 #include "com_android_server_power_PowerManagerService.h"
-#include "com_android_server_input_InputApplicationHandle.h"
-#include "com_android_server_input_InputWindowHandle.h"
+#include "android_hardware_input_InputApplicationHandle.h"
+#include "android_hardware_input_InputWindowHandle.h"
 #include "android_hardware_display_DisplayViewport.h"
 
+#include <vector>
+
 #define INDENT "  "
 
 using android::base::StringPrintf;
@@ -144,8 +146,8 @@
 
 static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
         const sp<InputApplicationHandle>& inputApplicationHandle) {
-    if (inputApplicationHandle == NULL) {
-        return NULL;
+    if (inputApplicationHandle == nullptr) {
+        return nullptr;
     }
     return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())->
             getInputApplicationHandleObjLocalRef(env);
@@ -153,8 +155,8 @@
 
 static jobject getInputWindowHandleObjLocalRef(JNIEnv* env,
         const sp<InputWindowHandle>& inputWindowHandle) {
-    if (inputWindowHandle == NULL) {
-        return NULL;
+    if (inputWindowHandle == nullptr) {
+        return nullptr;
     }
     return static_cast<NativeInputWindowHandle*>(inputWindowHandle.get())->
             getInputWindowHandleObjLocalRef(env);
@@ -182,6 +184,15 @@
     loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon);
 }
 
+static void updatePointerControllerFromViewport(
+        sp<PointerController> controller, const DisplayViewport* const viewport) {
+    if (controller != nullptr && viewport != nullptr) {
+        const int32_t width = viewport->logicalRight - viewport->logicalLeft;
+        const int32_t height = viewport->logicalBottom - viewport->logicalTop;
+        controller->setDisplayViewport(width, height, viewport->orientation);
+    }
+}
+
 enum {
     WM_ACTION_PASS_TO_USER = 1,
 };
@@ -203,15 +214,15 @@
 
     void dump(std::string& dump);
 
-    void setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
-    void setDisplayViewport(int32_t viewportType, const DisplayViewport& viewport);
+    void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
 
     status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
             const sp<InputWindowHandle>& inputWindowHandle, bool monitor);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
 
     void setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray, int32_t displayId);
-    void setFocusedApplication(JNIEnv* env, jobject applicationHandleObj);
+    void setFocusedApplication(JNIEnv* env, int32_t displayId, jobject applicationHandleObj);
+    void setFocusedDisplay(JNIEnv* env, int32_t displayId);
     void setInputDispatchMode(bool enabled, bool frozen);
     void setSystemUiVisibility(int32_t visibility);
     void setPointerSpeed(int32_t speed);
@@ -277,9 +288,7 @@
     Mutex mLock;
     struct Locked {
         // Display size information.
-        DisplayViewport internalViewport;
-        DisplayViewport externalViewport;
-        Vector<DisplayViewport> virtualViewports;
+        std::vector<DisplayViewport> viewports;
 
         // System UI visibility.
         int32_t systemUiVisibility;
@@ -304,7 +313,7 @@
 
         // Input devices to be disabled
         SortedVector<int32_t> disabledInputDevices;
-    } mLocked;
+    } mLocked GUARDED_BY(mLock);
 
     std::atomic<bool> mInteractive;
 
@@ -384,8 +393,17 @@
     return false;
 }
 
-void NativeInputManager::setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
-    Vector<DisplayViewport> viewports;
+static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) {
+    for (const DisplayViewport& v : viewports) {
+        if (v.type == ViewportType::VIEWPORT_INTERNAL) {
+            return &v;
+        }
+    }
+    return nullptr;
+}
+
+void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) {
+    std::vector<DisplayViewport> viewports;
 
     if (viewportObjArray) {
         jsize length = env->GetArrayLength(viewportObjArray);
@@ -397,57 +415,32 @@
 
             DisplayViewport viewport;
             android_hardware_display_DisplayViewport_toNative(env, viewportObj, &viewport);
-            ALOGI("Viewport [%d] to add: %s", (int) length, viewport.uniqueId.c_str());
-            viewports.push(viewport);
+            ALOGI("Viewport [%d] to add: %s", (int) i, viewport.uniqueId.c_str());
+            viewports.push_back(viewport);
 
             env->DeleteLocalRef(viewportObj);
         }
     }
 
+    const DisplayViewport* newInternalViewport = findInternalViewport(viewports);
     {
         AutoMutex _l(mLock);
-        mLocked.virtualViewports = viewports;
+        const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports);
+        // Internal viewport has changed if there wasn't one earlier, and there is one now, or,
+        // if they are different.
+        const bool internalViewportChanged = (newInternalViewport != nullptr) &&
+                (oldInternalViewport == nullptr || (*oldInternalViewport != *newInternalViewport));
+        if (internalViewportChanged) {
+            sp<PointerController> controller = mLocked.pointerController.promote();
+            updatePointerControllerFromViewport(controller, newInternalViewport);
+        }
+        mLocked.viewports = viewports;
     }
 
     mInputManager->getReader()->requestRefreshConfiguration(
             InputReaderConfiguration::CHANGE_DISPLAY_INFO);
 }
 
-void NativeInputManager::setDisplayViewport(int32_t type, const DisplayViewport& viewport) {
-    bool changed = false;
-    {
-        AutoMutex _l(mLock);
-
-        ViewportType viewportType = static_cast<ViewportType>(type);
-        DisplayViewport* v = NULL;
-        if (viewportType == ViewportType::VIEWPORT_EXTERNAL) {
-            v = &mLocked.externalViewport;
-        } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
-            v = &mLocked.internalViewport;
-        }
-
-        if (v != NULL && *v != viewport) {
-            changed = true;
-            *v = viewport;
-
-            if (viewportType == ViewportType::VIEWPORT_INTERNAL) {
-                sp<PointerController> controller = mLocked.pointerController.promote();
-                if (controller != NULL) {
-                    controller->setDisplayViewport(
-                            viewport.logicalRight - viewport.logicalLeft,
-                            viewport.logicalBottom - viewport.logicalTop,
-                            viewport.orientation);
-                }
-            }
-        }
-    }
-
-    if (changed) {
-        mInputManager->getReader()->requestRefreshConfiguration(
-                InputReaderConfiguration::CHANGE_DISPLAY_INFO);
-    }
-}
-
 status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
         const sp<InputChannel>& inputChannel,
         const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
@@ -479,7 +472,7 @@
         jsize length = env->GetArrayLength(excludedDeviceNames);
         for (jsize i = 0; i < length; i++) {
             jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i));
-            const char* deviceNameChars = env->GetStringUTFChars(item, NULL);
+            const char* deviceNameChars = env->GetStringUTFChars(item, nullptr);
             outConfig->excludedDeviceNames.push_back(deviceNameChars);
             env->ReleaseStringUTFChars(item, deviceNameChars);
             env->DeleteLocalRef(item);
@@ -526,11 +519,7 @@
 
         outConfig->pointerCapture = mLocked.pointerCapture;
 
-        outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL,
-                mLocked.internalViewport);
-        outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL,
-                mLocked.externalViewport);
-        outConfig->setVirtualDisplayViewports(mLocked.virtualViewports);
+        outConfig->setDisplayViewports(mLocked.viewports);
 
         outConfig->disabledDevices = mLocked.disabledInputDevices;
     } // release lock
@@ -541,25 +530,22 @@
     AutoMutex _l(mLock);
 
     sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller == NULL) {
+    if (controller == nullptr) {
         ensureSpriteControllerLocked();
 
         controller = new PointerController(this, mLooper, mLocked.spriteController);
         mLocked.pointerController = controller;
 
-        DisplayViewport& v = mLocked.internalViewport;
-        controller->setDisplayViewport(
-                v.logicalRight - v.logicalLeft,
-                v.logicalBottom - v.logicalTop,
-                v.orientation);
+        const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports);
+        updatePointerControllerFromViewport(controller, internalViewport);
 
         updateInactivityTimeoutLocked(controller);
     }
     return controller;
 }
 
-void NativeInputManager::ensureSpriteControllerLocked() {
-    if (mLocked.spriteController == NULL) {
+void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) {
+    if (mLocked.spriteController == nullptr) {
         JNIEnv* env = jniEnv();
         jint layer = env->CallIntMethod(mServiceObj, gServiceClassInfo.getPointerLayer);
         if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) {
@@ -575,7 +561,7 @@
 
     size_t count = inputDevices.size();
     jobjectArray inputDevicesObjArray = env->NewObjectArray(
-            count, gInputDeviceClassInfo.clazz, NULL);
+            count, gInputDeviceClassInfo.clazz, nullptr);
     if (inputDevicesObjArray) {
         bool error = false;
         for (size_t i = 0; i < count; i++) {
@@ -750,7 +736,7 @@
 
             sp<InputWindowHandle> windowHandle =
                     android_server_InputWindowHandle_getHandle(env, windowHandleObj);
-            if (windowHandle != NULL) {
+            if (windowHandle != nullptr) {
                 windowHandles.push(windowHandle);
             }
             env->DeleteLocalRef(windowHandleObj);
@@ -786,10 +772,15 @@
     }
 }
 
-void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationHandleObj) {
+void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
+        jobject applicationHandleObj) {
     sp<InputApplicationHandle> applicationHandle =
             android_server_InputApplicationHandle_getHandle(env, applicationHandleObj);
-    mInputManager->getDispatcher()->setFocusedApplication(applicationHandle);
+    mInputManager->getDispatcher()->setFocusedApplication(displayId, applicationHandle);
+}
+
+void NativeInputManager::setFocusedDisplay(JNIEnv* env, int32_t displayId) {
+    mInputManager->getDispatcher()->setFocusedDisplay(displayId);
 }
 
 void NativeInputManager::setInputDispatchMode(bool enabled, bool frozen) {
@@ -803,13 +794,14 @@
         mLocked.systemUiVisibility = visibility;
 
         sp<PointerController> controller = mLocked.pointerController.promote();
-        if (controller != NULL) {
+        if (controller != nullptr) {
             updateInactivityTimeoutLocked(controller);
         }
     }
 }
 
-void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) {
+void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller)
+        REQUIRES(mLock) {
     bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN;
     controller->setInactivityTimeout(lightsOut
             ? PointerController::INACTIVITY_TIMEOUT_SHORT
@@ -894,7 +886,7 @@
 void NativeInputManager::setPointerIconType(int32_t iconId) {
     AutoMutex _l(mLock);
     sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller != NULL) {
+    if (controller != nullptr) {
         controller->updatePointerIcon(iconId);
     }
 }
@@ -902,7 +894,7 @@
 void NativeInputManager::reloadPointerIcons() {
     AutoMutex _l(mLock);
     sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller != NULL) {
+    if (controller != nullptr) {
         controller->reloadPointerResources();
     }
 }
@@ -910,7 +902,7 @@
 void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) {
     AutoMutex _l(mLock);
     sp<PointerController> controller = mLocked.pointerController.promote();
-    if (controller != NULL) {
+    if (controller != nullptr) {
         controller->setCustomPointerIcon(icon);
     }
 }
@@ -1122,7 +1114,7 @@
                     gServiceClassInfo.dispatchUnhandledKey,
                     inputWindowHandleObj, keyEventObj, policyFlags);
             if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) {
-                fallbackKeyEventObj = NULL;
+                fallbackKeyEventObj = nullptr;
             }
             android_view_KeyEvent_recycle(env, keyEventObj);
             env->DeleteLocalRef(keyEventObj);
@@ -1235,7 +1227,7 @@
 static jlong nativeInit(JNIEnv* env, jclass /* clazz */,
         jobject serviceObj, jobject contextObj, jobject messageQueueObj) {
     sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
-    if (messageQueue == NULL) {
+    if (messageQueue == nullptr) {
         jniThrowRuntimeException(env, "MessageQueue is not initialized.");
         return 0;
     }
@@ -1255,37 +1247,10 @@
     }
 }
 
-static void nativeSetVirtualDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr,
+static void nativeSetDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr,
         jobjectArray viewportObjArray) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-    im->setVirtualDisplayViewports(env, viewportObjArray);
-}
-
-static void nativeSetDisplayViewport(JNIEnv* env, jclass /* clazz */, jlong ptr,
-        jint viewportType, jint displayId, jint orientation,
-        jint logicalLeft, jint logicalTop, jint logicalRight, jint logicalBottom,
-        jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom,
-        jint deviceWidth, jint deviceHeight, jstring uniqueId) {
-    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
-
-    DisplayViewport v;
-    v.displayId = displayId;
-    v.orientation = orientation;
-    v.logicalLeft = logicalLeft;
-    v.logicalTop = logicalTop;
-    v.logicalRight = logicalRight;
-    v.logicalBottom = logicalBottom;
-    v.physicalLeft = physicalLeft;
-    v.physicalTop = physicalTop;
-    v.physicalRight = physicalRight;
-    v.physicalBottom = physicalBottom;
-    v.deviceWidth = deviceWidth;
-    v.deviceHeight = deviceHeight;
-    if (uniqueId != nullptr) {
-        v.uniqueId = ScopedUtfChars(env, uniqueId).c_str();
-    }
-
-    im->setDisplayViewport(viewportType, v);
+    im->setDisplayViewports(env, viewportObjArray);
 }
 
 static jint nativeGetScanCodeState(JNIEnv* /* env */, jclass /* clazz */,
@@ -1316,8 +1281,8 @@
         jlong ptr, jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
-    int32_t* codes = env->GetIntArrayElements(keyCodes, NULL);
-    uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL);
+    int32_t* codes = env->GetIntArrayElements(keyCodes, nullptr);
+    uint8_t* flags = env->GetBooleanArrayElements(outFlags, nullptr);
     jsize numCodes = env->GetArrayLength(keyCodes);
     jboolean result;
     if (numCodes == env->GetArrayLength(keyCodes)) {
@@ -1356,7 +1321,7 @@
 
     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
             inputChannelObj);
-    if (inputChannel == NULL) {
+    if (inputChannel == nullptr) {
         throwInputChannelNotInitialized(env);
         return;
     }
@@ -1385,12 +1350,12 @@
 
     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
             inputChannelObj);
-    if (inputChannel == NULL) {
+    if (inputChannel == nullptr) {
         throwInputChannelNotInitialized(env);
         return;
     }
 
-    android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL);
+    android_view_InputChannel_setDisposeCallback(env, inputChannelObj, nullptr, nullptr);
 
     status_t status = im->unregisterInputChannel(env, inputChannel);
     if (status && status != BAD_VALUE) { // ignore already unregistered channel
@@ -1454,10 +1419,17 @@
 }
 
 static void nativeSetFocusedApplication(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobject applicationHandleObj) {
+        jlong ptr, jint displayId, jobject applicationHandleObj) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
-    im->setFocusedApplication(env, applicationHandleObj);
+    im->setFocusedApplication(env, displayId, applicationHandleObj);
+}
+
+static void nativeSetFocusedDisplay(JNIEnv* env, jclass /* clazz */,
+        jlong ptr, jint displayId) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+
+    im->setFocusedDisplay(env, displayId);
 }
 
 static void nativeSetPointerCapture(JNIEnv* env, jclass /* clazz */, jlong ptr,
@@ -1490,7 +1462,7 @@
     sp<InputChannel> toChannel =
             android_view_InputChannel_getInputChannel(env, toChannelObj);
 
-    if (fromChannel == NULL || toChannel == NULL) {
+    if (fromChannel == nullptr || toChannel == nullptr) {
         return JNI_FALSE;
     }
 
@@ -1543,7 +1515,7 @@
     }
 
     jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical(
-            patternObj, NULL));
+            patternObj, nullptr));
     nsecs_t pattern[patternSize];
     for (size_t i = 0; i < patternSize; i++) {
         pattern[i] = max(jlong(0), min(patternMillis[i],
@@ -1656,10 +1628,8 @@
             (void*) nativeInit },
     { "nativeStart", "(J)V",
             (void*) nativeStart },
-    { "nativeSetVirtualDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
-            (void*) nativeSetVirtualDisplayViewports },
-    { "nativeSetDisplayViewport", "(JIIIIIIIIIIIIILjava/lang/String;)V",
-            (void*) nativeSetDisplayViewport },
+    { "nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V",
+            (void*) nativeSetDisplayViewports },
     { "nativeGetScanCodeState", "(JIII)I",
             (void*) nativeGetScanCodeState },
     { "nativeGetKeyCodeState", "(JIII)I",
@@ -1681,8 +1651,10 @@
             (void*) nativeToggleCapsLock },
     { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;I)V",
             (void*) nativeSetInputWindows },
-    { "nativeSetFocusedApplication", "(JLcom/android/server/input/InputApplicationHandle;)V",
+    { "nativeSetFocusedApplication", "(JILcom/android/server/input/InputApplicationHandle;)V",
             (void*) nativeSetFocusedApplication },
+    { "nativeSetFocusedDisplay", "(JI)V",
+            (void*) nativeSetFocusedDisplay },
     { "nativeSetPointerCapture", "(JZ)V",
             (void*) nativeSetPointerCapture },
     { "nativeSetInputDispatchMode", "(JZZ)V",
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 0505204..47790ce 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -3,7 +3,6 @@
     srcs: ["java/**/*.java"],
 
     libs: [
-        "conscrypt",
         "services.core",
     ],
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 84de6b4..66cf48c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -15,20 +15,10 @@
  */
 package com.android.server.devicepolicy;
 
-import android.annotation.UserIdInt;
 import android.app.admin.IDevicePolicyManager;
-import android.content.ComponentName;
-import android.os.PersistableBundle;
-import android.security.keymaster.KeymasterCertificateChain;
-import android.security.keystore.ParcelableKeyGenParameterSpec;
-import android.telephony.data.ApnSetting;
 
 import com.android.server.SystemService;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 /**
  * Defines the required interface for IDevicePolicyManager implemenation.
  *
@@ -64,94 +54,10 @@
      */
     abstract void handleStopUser(int userId);
 
-    public void setSystemSetting(ComponentName who, String setting, String value){}
-
-    public void transferOwnership(ComponentName admin, ComponentName target, PersistableBundle bundle) {}
-
-    public PersistableBundle getTransferOwnershipBundle() {
-        return null;
-    }
-
-    public boolean generateKeyPair(ComponentName who, String callerPackage, String algorithm,
-            ParcelableKeyGenParameterSpec keySpec, int idAttestationFlags,
-            KeymasterCertificateChain attestationChain) {
-        return false;
-    }
-
-    public boolean isUsingUnifiedPassword(ComponentName who) {
-        return true;
-    }
-
-    public boolean setKeyPairCertificate(ComponentName who, String callerPackage, String alias,
-            byte[] cert, byte[] chain, boolean isUserSelectable) {
-        return false;
-    }
-
-    @Override
-    public void setStartUserSessionMessage(
-            ComponentName admin, CharSequence startUserSessionMessage) {}
-
-    @Override
-    public void setEndUserSessionMessage(ComponentName admin, CharSequence endUserSessionMessage) {}
-
-    @Override
-    public String getStartUserSessionMessage(ComponentName admin) {
-        return null;
-    }
-
-    @Override
-    public String getEndUserSessionMessage(ComponentName admin) {
-        return null;
-    }
-
-    @Override
-    public List<String> setMeteredDataDisabledPackages(ComponentName admin, List<String> packageNames) {
-        return packageNames;
-    }
-
-    @Override
-    public List<String> getMeteredDataDisabledPackages(ComponentName admin) {
-        return new ArrayList<>();
-    }
-
-    @Override
-    public int addOverrideApn(ComponentName admin, ApnSetting apnSetting) {
-        return -1;
-    }
-
-    @Override
-    public boolean updateOverrideApn(ComponentName admin, int apnId, ApnSetting apnSetting) {
-        return false;
-    }
-
-    @Override
-    public boolean removeOverrideApn(ComponentName admin, int apnId) {
-        return false;
-    }
-
-    @Override
-    public List<ApnSetting> getOverrideApns(ComponentName admin) {
-        return Collections.emptyList();
-    }
-
-    @Override
-    public void setOverrideApnsEnabled(ComponentName admin, boolean enabled) {}
-
-    @Override
-    public boolean isOverrideApnEnabled(ComponentName admin) {
-        return false;
-    }
-
     public void clearSystemUpdatePolicyFreezePeriodRecord() {
     }
 
     @Override
-    public boolean isMeteredDataDisabledPackageForUser(ComponentName admin,
-            String packageName, int userId) {
-        return false;
-    }
-
-    @Override
     public long forceNetworkLogs() {
         return 0;
     }
@@ -160,8 +66,4 @@
     public long forceSecurityLogs() {
         return 0;
     }
-
-    @Override
-    public void setDefaultSmsApplication(ComponentName admin, String packageName) {
-    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
index a8b9b0c..85ca52e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java
@@ -66,7 +66,8 @@
             super(TAG, mContext, mHandler, userId, componentName,
                     mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC,
                     mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE,
-                    mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+                    mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC,
+                    mConstants.DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
         }
 
         @Override
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
index 616c669..71fea02 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java
@@ -30,14 +30,17 @@
 public class DevicePolicyConstants {
     private static final String TAG = DevicePolicyManagerService.LOG_TAG;
 
-    private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY
-            = "das_died_service_reconnect_backoff_sec";
+    private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY =
+            "das_died_service_reconnect_backoff_sec";
 
-    private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY
-            = "das_died_service_reconnect_backoff_increase";
+    private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY =
+            "das_died_service_reconnect_backoff_increase";
 
-    private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY
-            = "das_died_service_reconnect_max_backoff_sec";
+    private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY =
+            "das_died_service_reconnect_max_backoff_sec";
+
+    private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY =
+            "das_died_service_stable_connection_threshold_sec";
 
     /**
      * The back-off before re-connecting, when a service binding died, due to the owner
@@ -55,6 +58,11 @@
      */
     public final long DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC;
 
+    /**
+     * If a connection lasts more than this duration, we reset the re-connect back-off time.
+     */
+    public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC;
+
     private DevicePolicyConstants(String settings) {
 
         final KeyValueListParser parser = new KeyValueListParser(',');
@@ -75,6 +83,10 @@
         long dasDiedServiceReconnectMaxBackoffSec = parser.getLong(
                 DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1));
 
+        long dasDiedServiceStableConnectionThresholdSec = parser.getLong(
+                DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY,
+                TimeUnit.MINUTES.toSeconds(2));
+
         // Set minimum: 5 seconds.
         dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec);
 
@@ -89,7 +101,8 @@
         DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC = dasDiedServiceReconnectBackoffSec;
         DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE = dasDiedServiceReconnectBackoffIncrease;
         DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec;
-
+        DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC =
+                dasDiedServiceStableConnectionThresholdSec;
     }
 
     public static DevicePolicyConstants loadFromString(String settings) {
@@ -102,14 +115,18 @@
 
         pw.print(prefix);
         pw.print("  DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC: ");
-        pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
+        pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC);
 
         pw.print(prefix);
         pw.print("  DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE: ");
-        pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
+        pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE);
 
         pw.print(prefix);
         pw.print("  DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC: ");
-        pw.println( DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+        pw.println(DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC);
+
+        pw.print(prefix);
+        pw.print("  DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: ");
+        pw.println(DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC);
     }
 }
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 77a3e21..6ba7d94 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -1,5 +1,8 @@
 package android.net.dhcp;
 
+import static android.net.util.NetworkConstants.IPV4_MAX_MTU;
+import static android.net.util.NetworkConstants.IPV4_MIN_MTU;
+
 import android.annotation.Nullable;
 import android.net.DhcpResults;
 import android.net.LinkAddress;
@@ -381,6 +384,26 @@
     }
 
     /**
+     * Returns whether a parameter is included in the parameter request list option of this packet.
+     *
+     * <p>If there is no parameter request list option in the packet, false is returned.
+     *
+     * @param paramId ID of the parameter, such as {@link #DHCP_MTU} or {@link #DHCP_HOST_NAME}.
+     */
+    public boolean hasRequestedParam(byte paramId) {
+        if (mRequestedParams == null) {
+            return false;
+        }
+
+        for (byte reqParam : mRequestedParams) {
+            if (reqParam == paramId) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Creates a new L3 packet (including IP header) containing the
      * DHCP udp packet.  This method relies upon the delegated method
      * finishPacket() to insert the per-packet contents.
@@ -696,7 +719,11 @@
         addTlv(buf, DHCP_ROUTER, mGateways);
         addTlv(buf, DHCP_DNS_SERVER, mDnsServers);
         addTlv(buf, DHCP_DOMAIN_NAME, mDomainName);
+        addTlv(buf, DHCP_HOST_NAME, mHostName);
         addTlv(buf, DHCP_VENDOR_INFO, mVendorInfo);
+        if (mMtu != null && Short.toUnsignedInt(mMtu) >= IPV4_MIN_MTU) {
+            addTlv(buf, DHCP_MTU, mMtu);
+        }
     }
 
     /**
@@ -1259,7 +1286,8 @@
         boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp,
         Inet4Address yourIp, byte[] mac, Integer timeout, Inet4Address netMask,
         Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
-        Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+        Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+        short mtu) {
         DhcpPacket pkt = new DhcpOfferPacket(
                 transactionId, (short) 0, broadcast, serverIpAddr, relayIp,
                 INADDR_ANY /* clientIp */, yourIp, mac);
@@ -1267,9 +1295,11 @@
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
         pkt.mDomainName = domainName;
+        pkt.mHostName = hostname;
         pkt.mServerIdentifier = dhcpServerIdentifier;
         pkt.mSubnetMask = netMask;
         pkt.mBroadcastAddress = bcAddr;
+        pkt.mMtu = mtu;
         if (metered) {
             pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
         }
@@ -1283,7 +1313,8 @@
         boolean broadcast, Inet4Address serverIpAddr, Inet4Address relayIp, Inet4Address yourIp,
         Inet4Address requestClientIp, byte[] mac, Integer timeout, Inet4Address netMask,
         Inet4Address bcAddr, List<Inet4Address> gateways, List<Inet4Address> dnsServers,
-        Inet4Address dhcpServerIdentifier, String domainName, boolean metered) {
+        Inet4Address dhcpServerIdentifier, String domainName, String hostname, boolean metered,
+        short mtu) {
         DhcpPacket pkt = new DhcpAckPacket(
                 transactionId, (short) 0, broadcast, serverIpAddr, relayIp, requestClientIp, yourIp,
                 mac);
@@ -1291,9 +1322,11 @@
         pkt.mDnsServers = dnsServers;
         pkt.mLeaseTime = timeout;
         pkt.mDomainName = domainName;
+        pkt.mHostName = hostname;
         pkt.mSubnetMask = netMask;
         pkt.mServerIdentifier = dhcpServerIdentifier;
         pkt.mBroadcastAddress = bcAddr;
+        pkt.mMtu = mtu;
         if (metered) {
             pkt.mVendorInfo = VENDOR_INFO_ANDROID_METERED;
         }
diff --git a/services/net/java/android/net/dhcp/DhcpServer.java b/services/net/java/android/net/dhcp/DhcpServer.java
index 2b3d577..cee6fa9 100644
--- a/services/net/java/android/net/dhcp/DhcpServer.java
+++ b/services/net/java/android/net/dhcp/DhcpServer.java
@@ -20,6 +20,7 @@
 import static android.net.NetworkUtils.getPrefixMaskAsInet4Address;
 import static android.net.TrafficStats.TAG_SYSTEM_DHCP_SERVER;
 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
 import static android.net.dhcp.DhcpPacket.DHCP_SERVER;
 import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
 import static android.net.dhcp.DhcpPacket.INFINITE_LEASE;
@@ -46,6 +47,7 @@
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.text.TextUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
@@ -350,6 +352,19 @@
         return isEmpty(request.mClientIp) && (request.mBroadcast || isEmpty(lease.getNetAddr()));
     }
 
+    /**
+     * Get the hostname from a lease if non-empty and requested in the incoming request.
+     * @param request The incoming request.
+     * @return The hostname, or null if not requested or empty.
+     */
+    @Nullable
+    private static String getHostnameIfRequested(@NonNull DhcpPacket request,
+            @NonNull DhcpLease lease) {
+        return request.hasRequestedParam(DHCP_HOST_NAME) && !TextUtils.isEmpty(lease.getHostname())
+                ? lease.getHostname()
+                : null;
+    }
+
     private boolean transmitOffer(@NonNull DhcpPacket request, @NonNull DhcpLease lease,
             @NonNull MacAddress clientMac) {
         final boolean broadcastFlag = getBroadcastFlag(request, lease);
@@ -358,12 +373,14 @@
                 getPrefixMaskAsInet4Address(mServingParams.serverAddr.getPrefixLength());
         final Inet4Address broadcastAddr = getBroadcastAddress(
                 mServingParams.getServerInet4Addr(), mServingParams.serverAddr.getPrefixLength());
+        final String hostname = getHostnameIfRequested(request, lease);
         final ByteBuffer offerPacket = DhcpPacket.buildOfferPacket(
                 ENCAP_BOOTP, request.mTransId, broadcastFlag, mServingParams.getServerInet4Addr(),
                 request.mRelayIp, lease.getNetAddr(), request.mClientMac, timeout, prefixMask,
                 broadcastAddr, new ArrayList<>(mServingParams.defaultRouters),
                 new ArrayList<>(mServingParams.dnsServers),
-                mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+                mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+                mServingParams.metered, (short) mServingParams.linkMtu);
 
         return transmitOfferOrAckPacket(offerPacket, request, lease, clientMac, broadcastFlag);
     }
@@ -374,13 +391,15 @@
         // transmitOffer above
         final boolean broadcastFlag = getBroadcastFlag(request, lease);
         final int timeout = getLeaseTimeout(lease);
+        final String hostname = getHostnameIfRequested(request, lease);
         final ByteBuffer ackPacket = DhcpPacket.buildAckPacket(ENCAP_BOOTP, request.mTransId,
                 broadcastFlag, mServingParams.getServerInet4Addr(), request.mRelayIp,
                 lease.getNetAddr(), request.mClientIp, request.mClientMac, timeout,
                 mServingParams.getPrefixMaskAsAddress(), mServingParams.getBroadcastAddress(),
                 new ArrayList<>(mServingParams.defaultRouters),
                 new ArrayList<>(mServingParams.dnsServers),
-                mServingParams.getServerInet4Addr(), null /* domainName */, mServingParams.metered);
+                mServingParams.getServerInet4Addr(), null /* domainName */, hostname,
+                mServingParams.metered, (short) mServingParams.linkMtu);
 
         return transmitOfferOrAckPacket(ackPacket, request, lease, clientMac, broadcastFlag);
     }
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
new file mode 100644
index 0000000..c5f9b10
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.testng.Assert.assertThrows;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import com.google.common.primitives.Bytes;
+import java.util.Arrays;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class EncryptedChunkTest {
+    private static final byte[] CHUNK_HASH_1_BYTES =
+            Arrays.copyOf(new byte[] {1}, ChunkHash.HASH_LENGTH_BYTES);
+    private static final byte[] NONCE_1 =
+            Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES);
+    private static final byte[] ENCRYPTED_BYTES_1 =
+            Arrays.copyOf(new byte[] {3}, EncryptedChunk.KEY_LENGTH_BYTES);
+
+    private static final byte[] CHUNK_HASH_2_BYTES =
+            Arrays.copyOf(new byte[] {4}, ChunkHash.HASH_LENGTH_BYTES);
+    private static final byte[] NONCE_2 =
+            Arrays.copyOf(new byte[] {5}, EncryptedChunk.NONCE_LENGTH_BYTES);
+    private static final byte[] ENCRYPTED_BYTES_2 =
+            Arrays.copyOf(new byte[] {6}, EncryptedChunk.KEY_LENGTH_BYTES);
+
+    @Test
+    public void testCreate_withIncorrectLength_throwsException() {
+        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+        byte[] shortNonce = Arrays.copyOf(new byte[] {2}, EncryptedChunk.NONCE_LENGTH_BYTES - 1);
+
+        assertThrows(
+                IllegalArgumentException.class,
+                () -> EncryptedChunk.create(chunkHash, shortNonce, ENCRYPTED_BYTES_1));
+    }
+
+    @Test
+    public void testEncryptedBytes_forNewlyCreatedObject_returnsCorrectValue() {
+        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+        EncryptedChunk encryptedChunk =
+                EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
+
+        byte[] returnedBytes = encryptedChunk.encryptedBytes();
+
+        assertThat(returnedBytes)
+                .asList()
+                .containsExactlyElementsIn(Bytes.asList(ENCRYPTED_BYTES_1))
+                .inOrder();
+    }
+
+    @Test
+    public void testKey_forNewlyCreatedObject_returnsCorrectValue() {
+        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+        EncryptedChunk encryptedChunk =
+                EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
+
+        ChunkHash returnedKey = encryptedChunk.key();
+
+        assertThat(returnedKey).isEqualTo(chunkHash);
+    }
+
+    @Test
+    public void testNonce_forNewlycreatedObject_returnCorrectValue() {
+        ChunkHash chunkHash = new ChunkHash(CHUNK_HASH_1_BYTES);
+        EncryptedChunk encryptedChunk =
+                EncryptedChunk.create(chunkHash, NONCE_1, ENCRYPTED_BYTES_1);
+
+        byte[] returnedNonce = encryptedChunk.nonce();
+
+        assertThat(returnedNonce).asList().containsExactlyElementsIn(Bytes.asList(NONCE_1));
+    }
+
+    @Test
+    public void testEquals() {
+        ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+        ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+        ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES);
+        EncryptedChunk encryptedChunk1 =
+                EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+        EncryptedChunk equalEncryptedChunk1 =
+                EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+        EncryptedChunk encryptedChunk2 =
+                EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2);
+
+        assertThat(encryptedChunk1).isEqualTo(equalEncryptedChunk1);
+        assertThat(encryptedChunk1).isNotEqualTo(encryptedChunk2);
+    }
+
+    @Test
+    public void testHashCode() {
+        ChunkHash chunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+        ChunkHash equalChunkHash1 = new ChunkHash(CHUNK_HASH_1_BYTES);
+        ChunkHash chunkHash2 = new ChunkHash(CHUNK_HASH_2_BYTES);
+        EncryptedChunk encryptedChunk1 =
+                EncryptedChunk.create(chunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+        EncryptedChunk equalEncryptedChunk1 =
+                EncryptedChunk.create(equalChunkHash1, NONCE_1, ENCRYPTED_BYTES_1);
+        EncryptedChunk encryptedChunk2 =
+                EncryptedChunk.create(chunkHash2, NONCE_2, ENCRYPTED_BYTES_2);
+
+        int hash1 = encryptedChunk1.hashCode();
+        int equalHash1 = equalEncryptedChunk1.hashCode();
+        int hash2 = encryptedChunk2.hashCode();
+
+        assertThat(hash1).isEqualTo(equalHash1);
+        assertThat(hash1).isNotEqualTo(hash2);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
new file mode 100644
index 0000000..b162557
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class InlineLengthsEncryptedChunkEncoderTest {
+
+    private static final byte[] TEST_NONCE =
+            Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES);
+    private static final byte[] TEST_KEY_DATA =
+            Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES);
+    private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9};
+
+    @Mock private BackupWriter mMockBackupWriter;
+    private ChunkHash mTestKey;
+    private EncryptedChunk mTestChunk;
+    private EncryptedChunkEncoder mEncoder;
+
+    @Before
+    public void setUp() throws Exception {
+        mMockBackupWriter = mock(BackupWriter.class);
+        mTestKey = new ChunkHash(TEST_KEY_DATA);
+        mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA);
+        mEncoder = new InlineLengthsEncryptedChunkEncoder();
+    }
+
+    @Test
+    public void writeChunkToWriter_writesLengthThenNonceThenData() throws Exception {
+        mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk);
+
+        InOrder inOrder = inOrder(mMockBackupWriter);
+        inOrder.verify(mMockBackupWriter)
+                .writeBytes(
+                        InlineLengthsEncryptedChunkEncoder.toByteArray(
+                                TEST_NONCE.length + TEST_DATA.length));
+        inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE);
+        inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA);
+    }
+
+    @Test
+    public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() {
+        int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk);
+
+        assertThat(encodedLength).isEqualTo(Integer.BYTES + TEST_NONCE.length + TEST_DATA.length);
+    }
+
+    @Test
+    public void getChunkOrderingType_returnsExplicitStartsType() {
+        assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.INLINE_LENGTHS);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
new file mode 100644
index 0000000..b61dbe9
--- /dev/null
+++ b/services/robotests/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2018 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.encryption.chunking;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+
+import android.platform.test.annotations.Presubmit;
+import com.android.server.backup.encryption.chunk.ChunkHash;
+import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+import java.util.Arrays;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.robolectric.annotation.Config;
+
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(manifest = Config.NONE, sdk = 26)
+@SystemLoaderPackages({"com.android.server.backup"})
+@Presubmit
+public class LengthlessEncryptedChunkEncoderTest {
+    private static final byte[] TEST_NONCE =
+            Arrays.copyOf(new byte[] {1}, EncryptedChunk.NONCE_LENGTH_BYTES);
+    private static final byte[] TEST_KEY_DATA =
+            Arrays.copyOf(new byte[] {2}, EncryptedChunk.KEY_LENGTH_BYTES);
+    private static final byte[] TEST_DATA = {5, 4, 5, 7, 10, 12, 1, 2, 9};
+
+    @Mock private BackupWriter mMockBackupWriter;
+    private ChunkHash mTestKey;
+    private EncryptedChunk mTestChunk;
+    private EncryptedChunkEncoder mEncoder;
+
+    @Before
+    public void setUp() throws Exception {
+        mMockBackupWriter = mock(BackupWriter.class);
+        mTestKey = new ChunkHash(TEST_KEY_DATA);
+        mTestChunk = EncryptedChunk.create(mTestKey, TEST_NONCE, TEST_DATA);
+        mEncoder = new LengthlessEncryptedChunkEncoder();
+    }
+
+    @Test
+    public void writeChunkToWriter_writesNonceThenData() throws Exception {
+        mEncoder.writeChunkToWriter(mMockBackupWriter, mTestChunk);
+
+        InOrder inOrder = inOrder(mMockBackupWriter);
+        inOrder.verify(mMockBackupWriter).writeBytes(TEST_NONCE);
+        inOrder.verify(mMockBackupWriter).writeBytes(TEST_DATA);
+    }
+
+    @Test
+    public void getEncodedLengthOfChunk_returnsSumOfNonceAndDataLengths() {
+        int encodedLength = mEncoder.getEncodedLengthOfChunk(mTestChunk);
+
+        assertThat(encodedLength).isEqualTo(TEST_NONCE.length + TEST_DATA.length);
+    }
+
+    @Test
+    public void getChunkOrderingType_returnsExplicitStartsType() throws Exception {
+        assertThat(mEncoder.getChunkOrderingType()).isEqualTo(ChunksMetadataProto.EXPLICIT_STARTS);
+    }
+}
diff --git a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
index 112e1e3..b771039 100644
--- a/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
+++ b/services/robotests/src/com/android/server/backup/fullbackup/AppMetadataBackupWriterTest.java
@@ -183,7 +183,6 @@
                                 new Signature[] {new Signature("1234"), new Signature("5678")},
                                 SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                                 null,
-                                null,
                                 null));
         File manifestFile = createFile(BACKUP_MANIFEST_FILENAME);
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
new file mode 100644
index 0000000..6665070
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2018 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;
+
+import static androidx.test.InstrumentationRegistry.getContext;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_ACTIVE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_IDLE_MAINTENANCE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_INACTIVE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_OVERRIDE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_PRE_IDLE;
+import static com.android.server.DeviceIdleController.LIGHT_STATE_WAITING_FOR_NETWORK;
+import static com.android.server.DeviceIdleController.STATE_ACTIVE;
+import static com.android.server.DeviceIdleController.STATE_IDLE;
+import static com.android.server.DeviceIdleController.STATE_IDLE_MAINTENANCE;
+import static com.android.server.DeviceIdleController.STATE_IDLE_PENDING;
+import static com.android.server.DeviceIdleController.STATE_INACTIVE;
+import static com.android.server.DeviceIdleController.STATE_LOCATING;
+import static com.android.server.DeviceIdleController.STATE_SENSING;
+import static com.android.server.DeviceIdleController.lightStateToString;
+import static com.android.server.DeviceIdleController.stateToString;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+
+import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
+import android.app.IActivityManager;
+import android.content.Context;
+import android.hardware.SensorManager;
+import android.location.LocationManager;
+import android.location.LocationProvider;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.net.NetworkPolicyManagerInternal;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+/**
+ * Tests for {@link com.android.server.DeviceIdleController}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class DeviceIdleControllerTest {
+    private DeviceIdleController mDeviceIdleController;
+    private AnyMotionDetectorForTest mAnyMotionDetector;
+    private AppStateTrackerForTest mAppStateTracker;
+
+    private MockitoSession mMockingSession;
+    @Mock
+    private PowerManager mPowerManager;
+    @Mock
+    private PowerManager.WakeLock mWakeLock;
+    @Mock
+    private AlarmManager mAlarmManager;
+    @Mock
+    private LocationManager mLocationManager;
+    @Mock
+    private IActivityManager mIActivityManager;
+
+    class InjectorForTest extends DeviceIdleController.Injector {
+
+        InjectorForTest(Context ctx) {
+            super(ctx);
+        }
+
+        @Override
+        AlarmManager getAlarmManager() {
+            return mAlarmManager;
+        }
+
+        @Override
+        AnyMotionDetector getAnyMotionDetector(Handler handler, SensorManager sm,
+                AnyMotionDetector.DeviceIdleCallback callback, float angleThreshold) {
+            return mAnyMotionDetector;
+        }
+
+        @Override
+        AppStateTracker getAppStateTracker(Context ctx, Looper loop) {
+            return mAppStateTracker;
+        }
+
+        @Override
+        ConnectivityService getConnectivityService() {
+            return null;
+        }
+
+        @Override
+        LocationManager getLocationManager() {
+            return mLocationManager;
+        }
+
+        @Override
+        DeviceIdleController.MyHandler getHandler(DeviceIdleController ctlr) {
+            return mock(DeviceIdleController.MyHandler.class);
+        }
+
+        @Override
+        PowerManager getPowerManager() {
+            return mPowerManager;
+        }
+    }
+
+    private class AnyMotionDetectorForTest extends AnyMotionDetector {
+        boolean isMonitoring = false;
+
+        AnyMotionDetectorForTest() {
+            super(mPowerManager, mock(Handler.class), mock(SensorManager.class),
+                    mock(DeviceIdleCallback.class), 0.5f);
+        }
+
+        @Override
+        public void checkForAnyMotion() {
+            isMonitoring = true;
+        }
+
+        @Override
+        public void stop() {
+            isMonitoring = false;
+        }
+    }
+
+    private class AppStateTrackerForTest extends AppStateTracker {
+        AppStateTrackerForTest(Context ctx, Looper looper) {
+            super(ctx, looper);
+        }
+
+        @Override
+        public void onSystemServicesReady() {
+            // Do nothing.
+        }
+
+        @Override
+        IActivityManager injectIActivityManager() {
+            return mIActivityManager;
+        }
+    }
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(LocalServices.class)
+                .startMocking();
+        doReturn(mock(ActivityManagerInternal.class))
+                .when(() -> LocalServices.getService(ActivityManagerInternal.class));
+        doReturn(mock(ActivityTaskManagerInternal.class))
+                .when(() -> LocalServices.getService(ActivityTaskManagerInternal.class));
+        doReturn(mock(PowerManagerInternal.class))
+                .when(() -> LocalServices.getService(PowerManagerInternal.class));
+        doReturn(mock(NetworkPolicyManagerInternal.class))
+                .when(() -> LocalServices.getService(NetworkPolicyManagerInternal.class));
+        when(mPowerManager.newWakeLock(anyInt(), anyString())).thenReturn(mWakeLock);
+        doNothing().when(mWakeLock).acquire();
+        mAppStateTracker = new AppStateTrackerForTest(getContext(), Looper.getMainLooper());
+        mAnyMotionDetector = new AnyMotionDetectorForTest();
+        mDeviceIdleController = new DeviceIdleController(getContext(),
+                new InjectorForTest(getContext()));
+        spyOn(mDeviceIdleController);
+        doNothing().when(mDeviceIdleController).publishBinderService(any(), any());
+        mDeviceIdleController.onStart();
+        mDeviceIdleController.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+        mDeviceIdleController.setDeepEnabledForTest(true);
+        mDeviceIdleController.setLightEnabledForTest(true);
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+        // DeviceIdleController adds this to LocalServices in the constructor, so we have to remove
+        // it after each test, otherwise, subsequent tests will fail.
+        LocalServices.removeServiceForTest(AppStateTracker.class);
+    }
+
+    @Test
+    public void testUpdateInteractivityLocked() {
+        doReturn(false).when(mPowerManager).isInteractive();
+        mDeviceIdleController.updateInteractivityLocked();
+        assertFalse(mDeviceIdleController.isScreenOn());
+
+        // Make sure setting false when screen is already off doesn't change anything.
+        doReturn(false).when(mPowerManager).isInteractive();
+        mDeviceIdleController.updateInteractivityLocked();
+        assertFalse(mDeviceIdleController.isScreenOn());
+
+        // Test changing from screen off to screen on.
+        doReturn(true).when(mPowerManager).isInteractive();
+        mDeviceIdleController.updateInteractivityLocked();
+        assertTrue(mDeviceIdleController.isScreenOn());
+
+        // Make sure setting true when screen is already on doesn't change anything.
+        doReturn(true).when(mPowerManager).isInteractive();
+        mDeviceIdleController.updateInteractivityLocked();
+        assertTrue(mDeviceIdleController.isScreenOn());
+
+        // Test changing from screen on to screen off.
+        doReturn(false).when(mPowerManager).isInteractive();
+        mDeviceIdleController.updateInteractivityLocked();
+        assertFalse(mDeviceIdleController.isScreenOn());
+    }
+
+    @Test
+    public void testUpdateChargingLocked() {
+        mDeviceIdleController.updateChargingLocked(false);
+        assertFalse(mDeviceIdleController.isCharging());
+
+        // Make sure setting false when charging is already off doesn't change anything.
+        mDeviceIdleController.updateChargingLocked(false);
+        assertFalse(mDeviceIdleController.isCharging());
+
+        // Test changing from charging off to charging on.
+        mDeviceIdleController.updateChargingLocked(true);
+        assertTrue(mDeviceIdleController.isCharging());
+
+        // Make sure setting true when charging is already on doesn't change anything.
+        mDeviceIdleController.updateChargingLocked(true);
+        assertTrue(mDeviceIdleController.isCharging());
+
+        // Test changing from charging on to charging off.
+        mDeviceIdleController.updateChargingLocked(false);
+        assertFalse(mDeviceIdleController.isCharging());
+    }
+
+    @Test
+    public void testStateActiveToStateInactive_ConditionsNotMet() {
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        verifyStateConditions(STATE_ACTIVE);
+
+        // State should stay ACTIVE with screen on and charging.
+        setChargingOn(true);
+        setScreenOn(true);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyStateConditions(STATE_ACTIVE);
+
+        // State should stay ACTIVE with charging on.
+        setChargingOn(true);
+        setScreenOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyStateConditions(STATE_ACTIVE);
+
+        // State should stay ACTIVE with screen on.
+        // Note the different operation order here makes sure the state doesn't change before test.
+        setScreenOn(true);
+        setChargingOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyStateConditions(STATE_ACTIVE);
+    }
+
+    @Test
+    public void testLightStateActiveToLightStateInactive_ConditionsNotMet() {
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+        // State should stay ACTIVE with screen on and charging.
+        setChargingOn(true);
+        setScreenOn(true);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+        // State should stay ACTIVE with charging on.
+        setChargingOn(true);
+        setScreenOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+        // State should stay ACTIVE with screen on.
+        // Note the different operation order here makes sure the state doesn't change before test.
+        setScreenOn(true);
+        setChargingOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+    }
+
+    @Test
+    public void testStateActiveToStateInactive_ConditionsMet() {
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        verifyStateConditions(STATE_ACTIVE);
+
+        setChargingOn(false);
+        setScreenOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyStateConditions(STATE_INACTIVE);
+    }
+
+    @Test
+    public void testLightStateActiveToLightStateInactive_ConditionsMet() {
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        verifyLightStateConditions(LIGHT_STATE_ACTIVE);
+
+        setChargingOn(false);
+        setScreenOn(false);
+
+        mDeviceIdleController.becomeInactiveIfAppropriateLocked();
+        verifyLightStateConditions(LIGHT_STATE_INACTIVE);
+    }
+
+    @Test
+    public void testStepIdleStateLocked_InvalidStates() {
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        // mDeviceIdleController.stepIdleStateLocked doesn't handle the ACTIVE case, so the state
+        // should stay as ACTIVE.
+        verifyStateConditions(STATE_ACTIVE);
+    }
+
+    @Test
+    public void testStepIdleStateLocked_ValidStates_NoLocationManager() {
+        mDeviceIdleController.setLocationManagerForTest(null);
+        // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+        doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyStateConditions(STATE_INACTIVE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_PENDING);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_SENSING);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        // No location manager, so SENSING should go straight to IDLE.
+        verifyStateConditions(STATE_IDLE);
+
+        // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+    }
+
+    @Test
+    public void testStepIdleStateLocked_ValidStates_WithLocationManager_NoProviders() {
+        // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+        doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyStateConditions(STATE_INACTIVE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_PENDING);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_SENSING);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        // Location manager exists but there isn't a network or GPS provider,
+        // so SENSING should go straight to IDLE.
+        verifyStateConditions(STATE_IDLE);
+
+        // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+    }
+
+    @Test
+    public void testStepIdleStateLocked_ValidStates_WithLocationManager_WithProviders() {
+        doReturn(mock(LocationProvider.class)).when(mLocationManager).getProvider(anyString());
+        // Make sure the controller doesn't think there's a wake-from-idle alarm coming soon.
+        // TODO: add tests for when there's a wake-from-idle alarm coming soon.
+        doReturn(Long.MAX_VALUE).when(mAlarmManager).getNextWakeFromIdleTime();
+        // Set state to INACTIVE.
+        mDeviceIdleController.becomeActiveLocked("testing", 0);
+        setChargingOn(false);
+        setScreenOn(false);
+        verifyStateConditions(STATE_INACTIVE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_PENDING);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_SENSING);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        // Location manager exists with a provider, so SENSING should go to LOCATING.
+        verifyStateConditions(STATE_LOCATING);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+
+        // Should just alternate between IDLE and IDLE_MAINTENANCE now.
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE);
+
+        mDeviceIdleController.stepIdleStateLocked("testing");
+        verifyStateConditions(STATE_IDLE_MAINTENANCE);
+    }
+
+    private void setChargingOn(boolean on) {
+        mDeviceIdleController.updateChargingLocked(on);
+    }
+
+    private void setScreenOn(boolean on) {
+        doReturn(on).when(mPowerManager).isInteractive();
+        mDeviceIdleController.updateInteractivityLocked();
+    }
+
+    private void verifyStateConditions(int expectedState) {
+        int curState = mDeviceIdleController.getState();
+        assertEquals(
+                "Expected " + stateToString(expectedState) + " but was " + stateToString(curState),
+                expectedState, curState);
+
+        switch (expectedState) {
+            case STATE_ACTIVE:
+                assertFalse(mDeviceIdleController.mMotionListener.isActive());
+                assertFalse(mAnyMotionDetector.isMonitoring);
+                break;
+            case STATE_INACTIVE:
+                assertFalse(mDeviceIdleController.mMotionListener.isActive());
+                assertFalse(mAnyMotionDetector.isMonitoring);
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                break;
+            case STATE_IDLE_PENDING:
+                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertFalse(mAnyMotionDetector.isMonitoring);
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                break;
+            case STATE_SENSING:
+                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertTrue(mAnyMotionDetector.isMonitoring);
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                break;
+            case STATE_LOCATING:
+                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertTrue(mAnyMotionDetector.isMonitoring);
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                break;
+            case STATE_IDLE:
+                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertFalse(mAnyMotionDetector.isMonitoring);
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                // Light state should be OVERRIDE at this point.
+                verifyLightStateConditions(LIGHT_STATE_OVERRIDE);
+                break;
+            case STATE_IDLE_MAINTENANCE:
+                assertTrue(mDeviceIdleController.mMotionListener.isActive());
+                assertFalse(mAnyMotionDetector.isMonitoring);
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                break;
+            default:
+                fail("Conditions for " + stateToString(expectedState) + " unknown.");
+        }
+    }
+
+    private void verifyLightStateConditions(int expectedLightState) {
+        int curLightState = mDeviceIdleController.getLightState();
+        assertEquals(
+                "Expected " + lightStateToString(expectedLightState)
+                        + " but was " + lightStateToString(curLightState),
+                expectedLightState, curLightState);
+
+        switch (expectedLightState) {
+            case LIGHT_STATE_ACTIVE:
+                assertTrue(
+                        mDeviceIdleController.isCharging() || mDeviceIdleController.isScreenOn());
+                break;
+            case LIGHT_STATE_INACTIVE:
+            case LIGHT_STATE_PRE_IDLE:
+            case LIGHT_STATE_IDLE:
+            case LIGHT_STATE_WAITING_FOR_NETWORK:
+            case LIGHT_STATE_IDLE_MAINTENANCE:
+            case LIGHT_STATE_OVERRIDE:
+                assertFalse(mDeviceIdleController.isCharging());
+                assertFalse(mDeviceIdleController.isScreenOn());
+                break;
+            default:
+                fail("Conditions for " + lightStateToString(expectedLightState) + " unknown.");
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
similarity index 79%
rename from services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
rename to services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
index 39cab8d..26e77eb 100644
--- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java
@@ -43,6 +43,8 @@
 
 @SmallTest
 public class PersistentConnectionTest extends AndroidTestCase {
+    private static final String TAG = "PersistentConnectionTest";
+    
     private static class MyConnection extends PersistentConnection<IDeviceAdminService> {
         public long uptimeMillis = 12345;
 
@@ -50,9 +52,11 @@
 
         public MyConnection(String tag, Context context, Handler handler, int userId,
                 ComponentName componentName, long rebindBackoffSeconds,
-                double rebindBackoffIncrease, long rebindMaxBackoffSeconds) {
+                double rebindBackoffIncrease, long rebindMaxBackoffSeconds,
+                long resetBackoffDelay) {
             super(tag, context, handler, userId, componentName,
-                    rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds);
+                    rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds,
+                    resetBackoffDelay);
         }
 
         @Override
@@ -113,10 +117,11 @@
         final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
         final Handler handler = new Handler(Looper.getMainLooper());
 
-        final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+        final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
                 /* rebindBackoffSeconds= */ 5,
                 /* rebindBackoffIncrease= */ 1.5,
-                /* rebindMaxBackoffSeconds= */ 11);
+                /* rebindMaxBackoffSeconds= */ 11,
+                /* resetBackoffDelay= */ 999);
 
         assertFalse(conn.isBound());
         assertFalse(conn.isConnected());
@@ -315,10 +320,11 @@
         final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
         final Handler handler = new Handler(Looper.getMainLooper());
 
-        final MyConnection conn = new MyConnection("tag", context, handler, userId, cn,
+        final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
                 /* rebindBackoffSeconds= */ 5,
                 /* rebindBackoffIncrease= */ 1.5,
-                /* rebindMaxBackoffSeconds= */ 11);
+                /* rebindMaxBackoffSeconds= */ 11,
+                /* resetBackoffDelay= */ 999);
 
         when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
                 any(Handler.class), any(UserHandle.class)))
@@ -356,4 +362,78 @@
         assertFalse(conn.isBound());
         assertFalse(conn.shouldBeBoundForTest());
     }
+
+    public void testResetBackoff() {
+        final Context context = mock(Context.class);
+        final int userId = 11;
+        final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def");
+        final Handler handler = new Handler(Looper.getMainLooper());
+
+        final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn,
+                /* rebindBackoffSeconds= */ 5,
+                /* rebindBackoffIncrease= */ 1.5,
+                /* rebindMaxBackoffSeconds= */ 11,
+                /* resetBackoffDelay= */ 20);
+
+        when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(),
+                any(Handler.class), any(UserHandle.class)))
+                .thenReturn(true);
+
+        // Bind.
+        conn.bind();
+
+        assertTrue(conn.isBound());
+        assertTrue(conn.shouldBeBoundForTest());
+        assertFalse(conn.isRebindScheduled());
+
+        conn.elapse(1000);
+
+        // Then the binding is "died"...
+        conn.getServiceConnectionForTest().onBindingDied(cn);
+
+        assertFalse(conn.isBound());
+        assertTrue(conn.shouldBeBoundForTest());
+        assertFalse(conn.isConnected());
+        assertNull(conn.getServiceBinder());
+        assertTrue(conn.isRebindScheduled());
+
+        assertEquals(7500, conn.getNextBackoffMsForTest());
+
+        assertEquals(
+                Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(),
+                        conn.uptimeMillis + 5000)),
+                conn.scheduledRunnables);
+
+        // 5000 ms later...
+        conn.elapse(5000);
+
+        assertTrue(conn.isBound());
+        assertTrue(conn.shouldBeBoundForTest());
+        assertFalse(conn.isConnected());
+        assertNull(conn.getServiceBinder());
+        assertFalse(conn.isRebindScheduled());
+
+        assertEquals(7500, conn.getNextBackoffMsForTest());
+
+        // Connected.
+        conn.getServiceConnectionForTest().onServiceConnected(cn,
+                new IDeviceAdminService.Stub() {});
+
+        assertTrue(conn.isBound());
+        assertTrue(conn.shouldBeBoundForTest());
+        assertTrue(conn.isConnected());
+        assertNotNull(conn.getServiceBinder());
+        assertFalse(conn.isRebindScheduled());
+
+        assertEquals(7500, conn.getNextBackoffMsForTest());
+
+        assertEquals(
+                Arrays.asList(Pair.create(conn.getStableCheckRunnableForTest(),
+                        conn.uptimeMillis + 20000)),
+                conn.scheduledRunnables);
+
+        conn.elapse(20000);
+
+        assertEquals(5000, conn.getNextBackoffMsForTest());
+    }
 }
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
index 7980dc2..f19cc5d 100755
--- a/services/tests/runtests.py
+++ b/services/tests/runtests.py
@@ -22,8 +22,7 @@
                                'android.support.test.runner.AndroidJUnitRunner')
 
 PACKAGE_WHITELIST = (
-    'android.net',
-    'com.android.server.connectivity',
+    "com.android.server",
 )
 
 COLOR_RED = '\033[0;31m'
@@ -37,14 +36,27 @@
                 COLOR_NONE)
     return subprocess.check_call(shell_command, shell=True)
 
-
+# usage:
+#   ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py : run tests in com.android.server
+#   ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e package [package name, e.g. com.android.server]
+#   ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py -e class [class name, e.g. com.android.server.MountServiceTests]
+#
+#   The available INSTRUMENTED_PACKAGE_RUNNER may differ in different environments.
+#   In this case, use "adb shell pm list instrumentation" to query available runners
+#   and use the environment variable INSTRUMENTED_PACKAGE_RUNNER to overwrite
+#   the default one, e.g.,
+#   INSTRUMENTED_PACKAGE_RUNNER=com.android.frameworks.servicestests/androidx.test.runner.AndroidJUnitRunner \
+#       ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/runtests.py
+#
 def main():
     build_top = os.environ.get('ANDROID_BUILD_TOP', None)
     out_dir = os.environ.get('OUT', None)
+    runner = os.environ.get('INSTRUMENTED_PACKAGE_RUNNER', None)
     if build_top is None or out_dir is None:
         print 'You need to source and lunch before you can use this script'
         return 1
-
+    if runner is None:
+        runner = INSTRUMENTED_PACKAGE_RUNNER
     print 'Building tests...'
     run('make -j32 -C %s -f build/core/main.mk '
         'MODULES-IN-frameworks-base-services-tests-servicestests' % build_top,
@@ -57,19 +69,19 @@
     apk_path = (
             '%s/data/app/FrameworksServicesTests/FrameworksServicesTests.apk' %
             out_dir)
-    run('adb install -r -g "%s"' % apk_path)
+    run('adb install -t -r -g "%s"' % apk_path)
 
     print 'Running tests...'
     if len(sys.argv) != 1:
         run('adb shell am instrument -w %s "%s"' %
-            (' '.join(sys.argv[1:]), INSTRUMENTED_PACKAGE_RUNNER))
+            (' '.join(sys.argv[1:]), runner))
         return 0
 
     # It would be nice if the activity manager accepted a list of packages, but
     # in lieu of that...
     for package in PACKAGE_WHITELIST:
         run('adb shell am instrument -w -e package %s %s' %
-            (package, INSTRUMENTED_PACKAGE_RUNNER))
+            (package, runner))
 
     return 0
 
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index 80307ee..2957267 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -58,6 +58,7 @@
     libbacktrace \
     libbase \
     libbinder \
+    libbinderthreadstate \
     libc++ \
     libcutils \
     liblog \
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers
new file mode 100644
index 0000000..509ea3b
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-2-signers
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers
new file mode 100644
index 0000000..bee71c0
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256-lineage-3-signers
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8
new file mode 100644
index 0000000..f781c30
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der
new file mode 100644
index 0000000..e611e3d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8
new file mode 100644
index 0000000..5e73f27
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der
new file mode 100644
index 0000000..7723bea
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_2.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8 b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8
new file mode 100644
index 0000000..d7309dd
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.pk8
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der
new file mode 100644
index 0000000..cc82af9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/certs/ec-p256_3.x509.der
Binary files differ
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/README b/services/tests/servicestests/assets/PackageSignaturesTest/xml/README
new file mode 100644
index 0000000..43d5bb8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/README
@@ -0,0 +1,58 @@
+The XML files in this directory are taken from the packages tag of a test APK signed with the
+certificates and keys under the certs/ directory. To recreate the XML files run the following:
+
+1. Build the test APK:
+mmm -j cts/hostsidetests/appsecurity/test-apps/tinyapp/
+
+2. Sign the APK with the first signer:
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+ --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8
+
+3. Install the APK on a device:
+adb install test.apk
+
+4. Pull the packages.xml file containing the new entry for the APK from the device:
+adb pull /data/system/packages.xml
+
+5. Search the packages.xml file for the package name 'android.appsecurity.cts.tinyapp'. Following is
+   the full entry when the APK is signed as above:
+
+    <package name="android.appsecurity.cts.tinyapp" codePath="/data/app/android.appsecurity.cts.tiny
+    app-4ix3umoWct_iD26jQ03Z_g==" nativeLibraryPath="/data/app/android.appsecurity.cts.tinyapp-4ix3u
+    moWct_iD26jQ03Z_g==/lib" publicFlags="805879364" privateFlags="0" ft="1663710dd00" it="1663710de
+    41" ut="1663710de41" version="10" userId="10051">
+        <sigs count="1" schemeVersion="3">
+            <cert index="16" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d
+            04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433
+            303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d0201
+            06082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2
+            b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d
+            0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b
+            30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d04030203490030
+            46022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea48297
+            99c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+        <proper-signing-keyset identifier="480" />
+    </package>
+
+The PackageSignatures#readXml and writeXml methods read and write everything within the sigs tag.
+The tags and attributes within the sigs tag can be modified and used to verify various good and
+error paths for the PackageSignaturesTest.
+
+Step 2 can be modified to sign with multiple signers by running one of the following commands:
+
+- To sign with two signers in the lineage (after the signing key has been rotated once):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+  --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+  certs/ec-p256_2.x509.der --key certs/ec-p256_2.pk8 --lineage certs/ec-p256-lineage-2-signers
+
+- To sign with three signers in the lineage (after the second key rotation):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+  --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+  certs/ec-p256_3.x509.der --key certs/ec-p256_3.pk8 --lineage certs/ec-p256-lineage-3-signers
+
+- To sign with two distinct signers (NOTE: The V3 signature scheme only supports a single signer,
+  so this method can only be used with signature schemes V1 and V2):
+apksigner sign --in ${OUT}/data/app/CtsPkgInstallTinyApp/CtsPkgInstallTinyApp.apk --out test.apk \
+  --cert certs/ec-p256.x509.der --key certs/ec-p256.pk8 --next-signer --cert \
+  certs/ec-p256_3.x509.der --key certs/ec-p256_3.pk8 --v3-signing-enabled false
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml
new file mode 100644
index 0000000..4d55bad
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-extra-cert-tag.xml
@@ -0,0 +1,5 @@
+        <sigs count="1" schemeVersion="3">
+          <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+            <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml
new file mode 100644
index 0000000..f7882b1
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-index.xml
@@ -0,0 +1,4 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="x" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml
new file mode 100644
index 0000000..af2c293
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-cert-key.xml
@@ -0,0 +1,4 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml
new file mode 100644
index 0000000..893402d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-public-key-cert-key.xml
@@ -0,0 +1,4 @@
+        <sigs count="1" schemeVersion="1">
+            <cert index="0" key="4082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml
new file mode 100644
index 0000000..1f81dac
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-invalid-tag.xml
@@ -0,0 +1,5 @@
+        <sigs count="1" schemeVersion="3">
+          <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+          <invalid />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml
new file mode 100644
index 0000000..c38e4d9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-index.xml
@@ -0,0 +1,4 @@
+        <sigs count="1" schemeVersion="3">
+            <cert key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml
new file mode 100644
index 0000000..8e8cbcf
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-key.xml
@@ -0,0 +1,4 @@
+        <sigs count="1" schemeVersion="3">
+          <cert index="0" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml
new file mode 100644
index 0000000..57e96a8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-cert-tag.xml
@@ -0,0 +1,3 @@
+        <sigs count="1" schemeVersion="3">
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml
new file mode 100644
index 0000000..d9f7a5f
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-scheme-version.xml
@@ -0,0 +1,4 @@
+        <sigs count="1">
+            <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml
new file mode 100644
index 0000000..4eefdd9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-missing-sigs-count.xml
@@ -0,0 +1,4 @@
+        <sigs schemeVersion="3">
+            <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml
new file mode 100644
index 0000000..2aeeb71
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer-previous-cert.xml
@@ -0,0 +1,4 @@
+        <sigs count="1" schemeVersion="2">
+          <cert index="0" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml
new file mode 100644
index 0000000..14471f8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/one-signer.xml
@@ -0,0 +1,4 @@
+        <sigs count="1" schemeVersion="1">
+            <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml
new file mode 100644
index 0000000..2b2e383
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-invalid-pastSigs-count.xml
@@ -0,0 +1,9 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+            <pastSigs count="x">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+                <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+                <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml
new file mode 100644
index 0000000..f992104
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml
@@ -0,0 +1,8 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+            <pastSigs count="3">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+                <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml
new file mode 100644
index 0000000..6ef0fe5
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-pastSigs-count.xml
@@ -0,0 +1,9 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+            <pastSigs>
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+                <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+                <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml
new file mode 100644
index 0000000..d98573d
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage-missing-scheme-version.xml
@@ -0,0 +1,9 @@
+        <sigs count="1">
+            <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+            <pastSigs count="3">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+                <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+                <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml
new file mode 100644
index 0000000..2ccf5060
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/three-signers-in-lineage.xml
@@ -0,0 +1,9 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d020106082a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e" />
+            <pastSigs count="3">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="3" />
+                <cert index="2" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" flags="7" />
+                <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml
new file mode 100644
index 0000000..6d567e9
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-certs-flags.xml
@@ -0,0 +1,8 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+            <pastSigs count="2">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="x" />
+                <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml
new file mode 100644
index 0000000..a2146b7
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml
@@ -0,0 +1,8 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+            <pastSigs count="2">
+                <cert index="x" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+                <cert index="0" flags="0" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml
new file mode 100644
index 0000000..90a4a84
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-certs-flags.xml
@@ -0,0 +1,8 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+            <pastSigs count="2">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+                <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml
new file mode 100644
index 0000000..6525e48
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml
@@ -0,0 +1,8 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+            <pastSigs count="2">
+                <cert key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+                <cert flags="0" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml
new file mode 100644
index 0000000..e06892c
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-multiple-pastSigs-tags.xml
@@ -0,0 +1,12 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+            <pastSigs count="2">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="23" />
+                <cert index="0" flags="23" />
+                <pastSigs count="2">
+                    <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="23" />
+                    <cert index="0" flags="23" />
+                </pastSigs>
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml
new file mode 100644
index 0000000..8081d2e
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-no-caps.xml
@@ -0,0 +1,8 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+            <pastSigs count="2">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="0" />
+                <cert index="0" flags="7" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml
new file mode 100644
index 0000000..127000a
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage-undefined-pastSigs-index.xml
@@ -0,0 +1,8 @@
+        <sigs count="2" schemeVersion="3">
+          <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+            <pastSigs count="2">
+              <cert index="1" flags="23" />
+              <cert index="0" flags="23" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml
new file mode 100644
index 0000000..6097ea6
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-in-lineage.xml
@@ -0,0 +1,8 @@
+        <sigs count="1" schemeVersion="3">
+            <cert index="0" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+            <pastSigs count="2">
+                <cert index="1" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" flags="7" />
+                <cert index="0" flags="3" />
+            </pastSigs>
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml
new file mode 100644
index 0000000..6ed3be8
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2-missing-cert-tag.xml
@@ -0,0 +1,4 @@
+        <sigs count="2" schemeVersion="1">
+            <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+        </sigs>
+
diff --git a/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml
new file mode 100644
index 0000000..ee4c4eb
--- /dev/null
+++ b/services/tests/servicestests/assets/PackageSignaturesTest/xml/two-signers-v1v2.xml
@@ -0,0 +1,5 @@
+        <sigs count="2" schemeVersion="2">
+            <cert index="0" key="3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3136303333313134353830365a170d3433303831373134353830365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd" />
+            <cert index="1" key="3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06035504030c0765632d70323536301e170d3138303731333137343135315a170d3238303731303137343135315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a8648ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e" />
+        </sigs>
+
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 2ec6830..4fbc587 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -73,6 +73,9 @@
     private static final int IGNORED_ACTION = 13;
     private static final int IGNORED_CODE = 1999;
     private static final int IGNORED_REPEAT = 42;
+    private static final int IGNORED_META_STATE = 0;
+    private static final int IGNORED_DEVICE_ID = 0;
+    private static final int IGNORED_SCANCODE = 0;
 
     private @Mock Context mContext;
     private @Mock Resources mResources;
@@ -369,6 +372,50 @@
     }
 
     @Test
+    public void testInterceptPowerKeyDown_longpress() {
+        withCameraDoubleTapPowerEnableConfigValue(true);
+        withCameraDoubleTapPowerDisableSettingValue(0);
+        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        withUserSetupCompleteValue(true);
+
+        long eventTime = INITIAL_EVENT_TIME_MILLIS;
+        KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT);
+        boolean interactive = true;
+        MutableBoolean outLaunched = new MutableBoolean(true);
+        boolean intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+
+        final long interval = GestureLauncherService.CAMERA_POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
+        eventTime += interval;
+        keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
+                IGNORED_REPEAT, IGNORED_META_STATE, IGNORED_DEVICE_ID, IGNORED_SCANCODE,
+                KeyEvent.FLAG_LONG_PRESS);
+        outLaunched.value = false;
+        intercepted = mGestureLauncherService.interceptPowerKeyDown(keyEvent, interactive,
+                outLaunched);
+        assertFalse(intercepted);
+        assertFalse(outLaunched.value);
+
+        verify(mMetricsLogger, never())
+                .action(eq(MetricsEvent.ACTION_DOUBLE_TAP_POWER_CAMERA_GESTURE), anyInt());
+
+        final ArgumentCaptor<Integer> intervalCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMetricsLogger, times(1)).histogram(
+                eq("power_double_tap_interval"), intervalCaptor.capture());
+        List<Integer> intervals = intervalCaptor.getAllValues();
+        assertEquals((int) INITIAL_EVENT_TIME_MILLIS, intervals.get(0).intValue());
+
+        final ArgumentCaptor<Integer> tapCountCaptor = ArgumentCaptor.forClass(Integer.class);
+        verify(mMetricsLogger, times(1)).histogram(
+                eq("power_consecutive_short_tap_count"), tapCountCaptor.capture());
+        List<Integer> tapCounts = tapCountCaptor.getAllValues();
+        assertEquals(1, tapCounts.get(0).intValue());
+    }
+
+    @Test
     public void
     testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOnInteractiveSetupIncomplete() {
         withCameraDoubleTapPowerEnableConfigValue(true);
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
new file mode 100644
index 0000000..44981b3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityDisplayTests.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
+
+import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
+
+import static org.junit.Assert.assertTrue;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for the {@link ActivityDisplay} class.
+ *
+ * Build/Install/Run:
+ *  atest WmTests:ActivityDisplayTests
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class ActivityDisplayTests extends ActivityTestsBase {
+
+    @Before
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        setupActivityTaskManagerService();
+    }
+
+    /**
+     * This test simulates the picture-in-picture menu activity launches an activity to fullscreen
+     * stack. The fullscreen stack should be the top focused for resuming correctly.
+     */
+    @Test
+    public void testFullscreenStackCanBeFocusedWhenFocusablePinnedStackExists() {
+        // Create a pinned stack and move to front.
+        final ActivityStack pinnedStack = mSupervisor.getDefaultDisplay().createStack(
+                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final TaskRecord pinnedTask = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(pinnedStack).build();
+        new ActivityBuilder(mService).setActivityFlags(FLAG_ALWAYS_FOCUSABLE)
+                .setTask(pinnedTask).build();
+        pinnedStack.moveToFront("movePinnedStackToFront");
+
+        // The focused stack should be the pinned stack.
+        assertTrue(pinnedStack.isFocusedStackOnDisplay());
+
+        // Create a fullscreen stack and move to front.
+        final ActivityStack fullscreenStack = createFullscreenStackWithSimpleActivityAt(
+                mSupervisor.getDefaultDisplay());
+        fullscreenStack.moveToFront("moveFullscreenStackToFront");
+
+        // The focused stack should be the fullscreen stack.
+        assertTrue(fullscreenStack.isFocusedStackOnDisplay());
+    }
+
+    /**
+     * Test {@link ActivityDisplay#mPreferredTopFocusableStack} will be cleared when the stack is
+     * removed or moved to back, and the focused stack will be according to z-order.
+     */
+    @Test
+    public void testStackShouldNotBeFocusedAfterMovingToBackOrRemoving() {
+        // Create a display which only contains 2 stacks.
+        final ActivityDisplay display = addNewActivityDisplayAt(ActivityDisplay.POSITION_TOP);
+        final ActivityStack stack1 = createFullscreenStackWithSimpleActivityAt(display);
+        final ActivityStack stack2 = createFullscreenStackWithSimpleActivityAt(display);
+
+        // Put stack1 and stack2 on top.
+        stack1.moveToFront("moveStack1ToFront");
+        stack2.moveToFront("moveStack2ToFront");
+        assertTrue(stack2.isFocusedStackOnDisplay());
+
+        // Stack1 should be focused after moving stack2 to back.
+        stack2.moveToBack("moveStack2ToBack", null /* task */);
+        assertTrue(stack1.isFocusedStackOnDisplay());
+
+        // Stack2 should be focused after removing stack1.
+        display.removeChild(stack1);
+        assertTrue(stack2.isFocusedStackOnDisplay());
+    }
+
+    private ActivityStack createFullscreenStackWithSimpleActivityAt(ActivityDisplay display) {
+        final ActivityStack fullscreenStack = display.createStack(
+                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, ON_TOP);
+        final TaskRecord fullscreenTask = new TaskBuilder(mService.mStackSupervisor)
+                .setStack(fullscreenStack).build();
+        new ActivityBuilder(mService).setTask(fullscreenTask).build();
+        return fullscreenStack;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
index 0345a81..81a0934 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java
@@ -33,10 +33,12 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
@@ -232,6 +234,7 @@
         doReturn(displaySleeping).when(display).isSleeping();
         doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
 
+        doReturn(isFocusedStack).when(stack).isFocusedStackOnDisplay();
         doReturn(isFocusedStack ? stack : null).when(display).getFocusedStack();
         mSupervisor.applySleepTokensLocked(true);
         verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
@@ -402,4 +405,32 @@
         assertEquals(primaryStack.getBounds(), STACK_SIZE);
         assertEquals(task.getBounds(), TASK_SIZE);
     }
+
+    /**
+     * Verify if a stack is not at the topmost position, it should be able to resume its activity if
+     * the stack is the top focused.
+     */
+    @Test
+    public void testResumeActivityWhenNonTopmostStackIsTopFocused() throws Exception {
+        // Create a stack at bottom.
+        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
+        final ActivityStack targetStack = spy(display.createStack(WINDOWING_MODE_FULLSCREEN,
+                ACTIVITY_TYPE_STANDARD, false /* onTop */));
+        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
+        final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
+        display.positionChildAtBottom(targetStack);
+
+        // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
+        // is the current top focused stack.
+        assertFalse(targetStack.isTopStackOnDisplay());
+        doReturn(targetStack).when(mSupervisor).getTopDisplayFocusedStack();
+
+        // Use the stack as target to resume.
+        mSupervisor.resumeFocusedStacksTopActivitiesLocked(
+                targetStack, activity, null /* targetOptions */);
+
+        // Verify the target stack should resume its activity.
+        verify(targetStack, times(1)).resumeTopActivityUncheckedLocked(
+                eq(activity), eq(null /* targetOptions */));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
index ab814ee..5fcd2aa 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackTests.java
@@ -38,6 +38,7 @@
 import static org.mockito.Mockito.anyInt;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 
 import android.content.pm.ActivityInfo;
 import android.os.UserHandle;
@@ -71,8 +72,8 @@
 
         setupActivityTaskManagerService();
         mDefaultDisplay = mSupervisor.getDefaultDisplay();
-        mStack = mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
-                true /* onTop */);
+        mStack = spy(mDefaultDisplay.createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD,
+                true /* onTop */));
         mTask = new TaskBuilder(mSupervisor).setStack(mStack).build();
     }
 
@@ -720,7 +721,7 @@
         doReturn(display).when(mSupervisor).getActivityDisplay(anyInt());
         doReturn(keyguardGoingAway).when(keyguardController).isKeyguardGoingAway();
         doReturn(displaySleeping).when(display).isSleeping();
-        doReturn(focusedStack ? mStack : null).when(mSupervisor).getTopDisplayFocusedStack();
+        doReturn(focusedStack).when(mStack).isFocusedStackOnDisplay();
 
         assertEquals(expected, mStack.shouldSleepActivities());
     }
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
index 22add01..2008861 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java
@@ -168,6 +168,9 @@
         // Makes sure the supervisor is using with the spy object.
         atm.mStackSupervisor.setService(atm);
         doReturn(mock(IPackageManager.class)).when(am).getPackageManager();
+        PackageManagerInternal mockPackageManager = mock(PackageManagerInternal.class);
+        doReturn(mockPackageManager).when(am).getPackageManagerInternalLocked();
+        doReturn(null).when(mockPackageManager).getDefaultHomeActivity(anyInt());
         doNothing().when(am).grantEphemeralAccessLocked(anyInt(), any(), anyInt(), anyInt());
         am.mWindowManager = prepareMockWindowManager();
         atm.setWindowManager(am.mWindowManager);
@@ -175,10 +178,9 @@
         // Put a home stack on the default display, so that we'll always have something focusable.
         final TestActivityStackSupervisor supervisor =
                 (TestActivityStackSupervisor) atm.mStackSupervisor;
-        supervisor.mHomeStack = supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_HOME, ON_TOP);
+        supervisor.mDisplay.createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
         final TaskRecord task = new TaskBuilder(atm.mStackSupervisor)
-                .setStack(supervisor.mHomeStack).build();
+                .setStack(supervisor.getDefaultDisplay().getHomeStack()).build();
         new ActivityBuilder(atm).setTask(task).build();
     }
 
@@ -447,9 +449,6 @@
             final ActivityStackSupervisor supervisor = spy(createTestSupervisor());
             final KeyguardController keyguardController = mock(KeyguardController.class);
 
-            // No home stack is set.
-            doNothing().when(supervisor).moveHomeStackToFront(any());
-            doReturn(true).when(supervisor).moveHomeStackTaskToTop(any());
             // Invoked during {@link ActivityStack} creation.
             doNothing().when(supervisor).updateUIDsPresentOnDisplay();
             // Always keep things awake.
diff --git a/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java b/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java
new file mode 100644
index 0000000..d7794b0
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/PersisterQueueTests.java
@@ -0,0 +1,300 @@
+/*
+ * Copyright (C) 2018 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.am;
+
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertSame;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Predicate;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+/**
+ * atest PersisterQueueTests
+ */
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+@Presubmit
+@FlakyTest(detail = "Confirm stable in post-submit before removing")
+public class PersisterQueueTests implements PersisterQueue.Listener {
+    private static final long INTER_WRITE_DELAY_MS = 50;
+    private static final long PRE_TASK_DELAY_MS = 300;
+    // We allow at most 1s more than the expected timeout.
+    private static final long TIMEOUT_ALLOWANCE = 100;
+
+    private static final Predicate<MatchingTestItem> TEST_ITEM_PREDICATE = item -> item.mMatching;
+
+    private AtomicInteger mItemCount;
+    private CountDownLatch mSetUpLatch;
+    private volatile CountDownLatch mLatch;
+    private List<Boolean> mProbablyDoneResults;
+
+    private PersisterQueue mTarget;
+
+    @Before
+    public void setUp() throws Exception {
+        mItemCount = new AtomicInteger(0);
+        mProbablyDoneResults = new ArrayList<>();
+        mSetUpLatch = new CountDownLatch(1);
+
+        mTarget = new PersisterQueue(INTER_WRITE_DELAY_MS, PRE_TASK_DELAY_MS);
+        mTarget.addListener(this);
+        mTarget.startPersisting();
+
+        assertTrue("Target didn't call callback on start up.",
+                mSetUpLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mTarget.stopPersisting();
+    }
+
+    @Test
+    public void testCallCallbackOnStartUp() throws Exception {
+        // The onPreProcessItem() must be called on start up.
+        assertEquals(1, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(0));
+    }
+
+    @Test
+    public void testProcessOneItem() throws Exception {
+        mLatch = new CountDownLatch(1);
+
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item. duration: "
+                        + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(2, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+    }
+
+    @Test
+    public void testProcessOneItem_Flush() throws Exception {
+        mLatch = new CountDownLatch(1);
+
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), true);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't process item immediately when flushing. duration: "
+                        + processDuration + "ms pretask delay: "
+                        + PRE_TASK_DELAY_MS + "ms",
+                processDuration < PRE_TASK_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(2, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(1));
+    }
+
+    @Test
+    public void testProcessTwoItems() throws Exception {
+        mLatch = new CountDownLatch(2);
+
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        mTarget.addItem(new TestItem(), false);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS + TIMEOUT_ALLOWANCE,
+                        TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process all items.", 2, mItemCount.get());
+        final long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item. duration: "
+                        + processDuration + "ms pretask delay: " + PRE_TASK_DELAY_MS
+                        + "ms inter write delay: " + INTER_WRITE_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS + INTER_WRITE_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(3, mProbablyDoneResults.size());
+        // The first one must be called with probably done being false.
+        assertFalse("The first probablyDone must be false.", mProbablyDoneResults.get(1));
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+    }
+
+    @Test
+    public void testProcessTwoItems_OneAfterAnother() throws Exception {
+        // First item
+        mLatch = new CountDownLatch(1);
+        long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        long processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item."
+                        + processDuration + "ms pretask delay: "
+                        + PRE_TASK_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS);
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+
+        // Second item
+        mLatch = new CountDownLatch(1);
+        dispatchTime = SystemClock.uptimeMillis();
+        // Synchronize on the instance to make sure we schedule the item after it starts to wait for
+        // task indefinitely.
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process all items.", 2, mItemCount.get());
+        processDuration = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Target didn't wait enough time before processing item."
+                        + processDuration + "ms pre task delay: "
+                        + PRE_TASK_DELAY_MS + "ms",
+                processDuration >= PRE_TASK_DELAY_MS);
+
+        // Once before processing this item, once after that.
+        assertEquals(3, mProbablyDoneResults.size());
+        // The last one must be called with probably done being true.
+        assertTrue("The last probablyDone must be true.", mProbablyDoneResults.get(2));
+    }
+
+    @Test
+    public void testFindLastItemNotReturnDifferentType() throws Exception {
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+        }
+    }
+
+    @Test
+    public void testFindLastItemNotReturnMismatchItem() throws Exception {
+        synchronized (mTarget) {
+            mTarget.addItem(new MatchingTestItem(false), false);
+            assertNull(mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+        }
+    }
+
+    @Test
+    public void testFindLastItemReturnMatchedItem() throws Exception {
+        synchronized (mTarget) {
+            final MatchingTestItem item = new MatchingTestItem(true);
+            mTarget.addItem(item, false);
+            assertSame(item, mTarget.findLastItem(TEST_ITEM_PREDICATE, MatchingTestItem.class));
+        }
+    }
+
+    @Test
+    public void testRemoveItemsNotRemoveDifferentType() throws Exception {
+        mLatch = new CountDownLatch(1);
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+    }
+
+    @Test
+    public void testRemoveItemsNotRemoveMismatchedItem() throws Exception {
+        mLatch = new CountDownLatch(1);
+        synchronized (mTarget) {
+            mTarget.addItem(new MatchingTestItem(false), false);
+            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+    }
+
+    @Test
+    public void testRemoveItemsRemoveMatchedItem() throws Exception {
+        mLatch = new CountDownLatch(1);
+        synchronized (mTarget) {
+            mTarget.addItem(new TestItem(), false);
+            mTarget.addItem(new MatchingTestItem(true), false);
+            mTarget.removeItems(TEST_ITEM_PREDICATE, MatchingTestItem.class);
+        }
+        assertTrue("Target didn't call callback enough times.",
+                mLatch.await(PRE_TASK_DELAY_MS + TIMEOUT_ALLOWANCE, TimeUnit.MILLISECONDS));
+        assertEquals("Target didn't process item.", 1, mItemCount.get());
+    }
+
+    @Test
+    public void testFlushWaitSynchronously() {
+        final long dispatchTime = SystemClock.uptimeMillis();
+        mTarget.addItem(new TestItem(), false);
+        mTarget.addItem(new TestItem(), false);
+        mTarget.flush();
+        assertEquals("Flush should wait until all items are processed before return.",
+                2, mItemCount.get());
+        final long processTime = SystemClock.uptimeMillis() - dispatchTime;
+        assertTrue("Flush should trigger immediate flush without delays. processTime: "
+                + processTime, processTime < TIMEOUT_ALLOWANCE);
+    }
+
+    @Override
+    public void onPreProcessItem(boolean queueEmpty) {
+        mProbablyDoneResults.add(queueEmpty);
+
+        final CountDownLatch latch = mLatch;
+        if (latch != null) {
+            latch.countDown();
+        }
+
+        mSetUpLatch.countDown();
+    }
+
+    private class TestItem implements PersisterQueue.WriteQueueItem {
+        @Override
+        public void process() {
+            mItemCount.getAndIncrement();
+        }
+    }
+
+    private class MatchingTestItem extends TestItem {
+        private boolean mMatching;
+
+        private MatchingTestItem(boolean matching) {
+            mMatching = matching;
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 70cfad1..1276f65 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -125,7 +125,6 @@
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
         mStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
-        ((MyTestActivityStackSupervisor) mService.mStackSupervisor).setHomeStack(mHomeStack);
         mCallbacksRecorder = new CallbacksRecorder();
         mRecentTasks.registerCallback(mCallbacksRecorder);
         QUIET_USER_INFO.flags = UserInfo.FLAG_MANAGED_PROFILE | UserInfo.FLAG_QUIET_MODE;
@@ -558,9 +557,8 @@
 
         final MyTestActivityStackSupervisor supervisor =
                 (MyTestActivityStackSupervisor) mService.mStackSupervisor;
-        final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack homeStack = mDisplay.getHomeStack();
         final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
-        supervisor.setHomeStack(homeStack);
 
         // Add a number of tasks (beyond the max) but ensure that nothing is trimmed because all
         // the tasks belong in stacks above the home stack
@@ -579,9 +577,8 @@
         final MyTestActivityStackSupervisor supervisor =
                 (MyTestActivityStackSupervisor) mService.mStackSupervisor;
         final ActivityStack behindHomeStack = new MyTestActivityStack(mDisplay, supervisor);
-        final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack homeStack = mDisplay.getHomeStack();
         final ActivityStack aboveHomeStack = new MyTestActivityStack(mDisplay, supervisor);
-        supervisor.setHomeStack(homeStack);
 
         // Add a number of tasks (beyond the max) but ensure that only the task in the stack behind
         // the home stack is trimmed once a new task is added
@@ -601,9 +598,8 @@
 
         final MyTestActivityStackSupervisor supervisor =
                 (MyTestActivityStackSupervisor) mService.mStackSupervisor;
-        final ActivityStack homeStack = new MyTestActivityStack(mDisplay, supervisor);
+        final ActivityStack homeStack = mDisplay.getHomeStack();
         final ActivityStack otherDisplayStack = new MyTestActivityStack(mOtherDisplay, supervisor);
-        supervisor.setHomeStack(homeStack);
 
         // Add a number of tasks (beyond the max) on each display, ensure that the tasks are not
         // removed
@@ -870,7 +866,7 @@
         @Override
         public void initialize() {
             super.initialize();
-            mDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY);
+            mDisplay = getActivityDisplay(DEFAULT_DISPLAY);
             mOtherDisplay = TestActivityDisplay.create(this, DEFAULT_DISPLAY + 1);
             addChild(mOtherDisplay, ActivityDisplay.POSITION_TOP);
             addChild(mDisplay, ActivityDisplay.POSITION_TOP);
@@ -881,10 +877,6 @@
             mRunningTasks = new TestRunningTasks();
             return mRunningTasks;
         }
-
-        void setHomeStack(ActivityStack stack) {
-            mHomeStack = stack;
-        }
     }
 
     private class MyTestActivityStack extends TestActivityStack {
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index 9fcdf2d..d52051eec 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -436,7 +436,6 @@
                         new Signature[] {SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -456,7 +455,6 @@
                         new Signature[] {SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -537,7 +535,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -560,7 +557,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -583,7 +579,6 @@
                         new Signature[] {signature1Copy, signature2Copy},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -606,7 +601,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -629,7 +623,6 @@
                         new Signature[] {SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -654,8 +647,7 @@
                         new Signature[] {SIGNATURE_2},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
-                        new int[] {0, 0}));
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2}));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         // we know signature1Copy is in history, and we want to assume it has
@@ -682,8 +674,7 @@
                         new Signature[] {SIGNATURE_2},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        new Signature[] {SIGNATURE_1, SIGNATURE_2},
-                        new int[] {0, 0}));
+                        new Signature[] {SIGNATURE_1, SIGNATURE_2}));
         packageInfo.applicationInfo = new ApplicationInfo();
 
         // we know signature1Copy is in history, but we want to assume it does not have
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
index 12f2991..4774985 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/TarBackupReaderTest.java
@@ -377,7 +377,6 @@
                         new Signature[] {FAKE_SIGNATURE_2},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         PackageManagerStub.sPackageInfo = packageInfo;
 
@@ -414,7 +413,6 @@
                         new Signature[] {FAKE_SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         PackageManagerStub.sPackageInfo = packageInfo;
 
@@ -452,7 +450,6 @@
                         new Signature[] {FAKE_SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         PackageManagerStub.sPackageInfo = packageInfo;
 
@@ -493,7 +490,6 @@
                         new Signature[] {FAKE_SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.versionCode = 2;
         PackageManagerStub.sPackageInfo = packageInfo;
@@ -537,7 +533,6 @@
                         new Signature[] {FAKE_SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.versionCode = 1;
         PackageManagerStub.sPackageInfo = packageInfo;
@@ -577,7 +572,6 @@
                         new Signature[] {FAKE_SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.versionCode = 1;
         PackageManagerStub.sPackageInfo = packageInfo;
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index ceee60c..b421280 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.display;
 
+import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX;
+
 import android.content.Context;
 import android.hardware.display.BrightnessConfiguration;
 import android.hardware.display.Curve;
@@ -25,35 +27,48 @@
 import android.hardware.input.InputManagerInternal;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.UserHandle;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.SmallTest;
+import android.view.Display;
+import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.display.DisplayDeviceInfo;
 import com.android.server.display.DisplayManagerService.SyncRoot;
-import com.android.server.display.VirtualDisplayAdapter.SurfaceControlDisplayFactory;
 import com.android.server.lights.LightsManager;
 import com.android.server.wm.WindowManagerInternal;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.Arrays;
 import java.util.List;
 
-import static org.mockito.Matchers.any;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.mockito.Mockito.mock;
 
 @SmallTest
-public class DisplayManagerServiceTest extends AndroidTestCase {
+@RunWith(AndroidJUnit4.class)
+public class DisplayManagerServiceTest {
     private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1;
     private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10;
 
+    private Context mContext;
+
     private final DisplayManagerService.Injector mShortMockedInjector =
             new DisplayManagerService.Injector() {
                 @Override
@@ -86,8 +101,8 @@
     @Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter;
     @Mock IBinder mMockDisplayToken;
 
-    @Override
-    protected void setUp() throws Exception {
+    @Before
+    public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
 
         LocalServices.removeServiceForTest(InputManagerInternal.class);
@@ -96,15 +111,12 @@
         LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal);
         LocalServices.removeServiceForTest(LightsManager.class);
         LocalServices.addService(LightsManager.class, mMockLightsManager);
-        super.setUp();
+
+        mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
     }
 
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-    }
-
-    public void testCreateVirtualDisplay_sentToInputManager() throws Exception {
+    @Test
+    public void testCreateVirtualDisplay_sentToInputManager() {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mBasicInjector);
         registerDefaultDisplays(displayManager);
@@ -115,7 +127,7 @@
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
 
         String uniqueId = "uniqueId --- Test";
-        String uniqueIdPrefix = "virtual:" + mContext.getPackageName() + ":";
+        String uniqueIdPrefix = UNIQUE_ID_PREFIX + mContext.getPackageName() + ":";
         int width = 600;
         int height = 800;
         int dpi = 320;
@@ -132,19 +144,113 @@
         // flush the handler
         displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
 
-        ArgumentCaptor<List<DisplayViewport>> virtualViewportCaptor =
-                ArgumentCaptor.forClass(List.class);
-        verify(mMockInputManagerInternal).setDisplayViewports(
-                any(), any(), virtualViewportCaptor.capture());
+        ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class);
+        verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
+        List<DisplayViewport> viewports = viewportCaptor.getValue();
 
-        assertEquals(1, virtualViewportCaptor.getValue().size());
-        DisplayViewport dv = virtualViewportCaptor.getValue().get(0);
-        assertEquals(height, dv.deviceHeight);
-        assertEquals(width, dv.deviceWidth);
-        assertEquals(uniqueIdPrefix + uniqueId, dv.uniqueId);
-        assertEquals(displayId, dv.displayId);
+        // Expect to receive 3 viewports: internal, external, and virtual
+        assertEquals(3, viewports.size());
+
+        DisplayViewport virtualViewport = null;
+        DisplayViewport internalViewport = null;
+        DisplayViewport externalViewport = null;
+        for (int i = 0; i < viewports.size(); i++) {
+            DisplayViewport v = viewports.get(i);
+            switch (v.type) {
+                case DisplayViewport.VIEWPORT_INTERNAL: {
+                    internalViewport = v;
+                    break;
+                }
+                case DisplayViewport.VIEWPORT_EXTERNAL: {
+                    externalViewport = v;
+                    break;
+                }
+                case DisplayViewport.VIEWPORT_VIRTUAL: {
+                    virtualViewport = v;
+                    break;
+                }
+            }
+        }
+        // INTERNAL and EXTERNAL viewports get created upon access
+        assertNotNull(internalViewport);
+        assertNotNull(externalViewport);
+        assertNotNull(virtualViewport);
+
+        // INTERNAL and EXTERNAL
+        assertTrue(internalViewport.valid);
+        assertTrue(externalViewport.valid);
+
+        // VIRTUAL
+        assertEquals(height, virtualViewport.deviceHeight);
+        assertEquals(width, virtualViewport.deviceWidth);
+        assertEquals(uniqueIdPrefix + uniqueId, virtualViewport.uniqueId);
+        assertEquals(displayId, virtualViewport.displayId);
     }
 
+    @Test
+    public void testPhysicalViewports() {
+        DisplayManagerService displayManager =
+                new DisplayManagerService(mContext, mBasicInjector);
+        registerDefaultDisplays(displayManager);
+        displayManager.systemReady(false /* safeMode */, true /* onlyCore */);
+        displayManager.windowManagerAndInputReady();
+
+        // This is effectively the DisplayManager service published to ServiceManager.
+        DisplayManagerService.BinderService bs = displayManager.new BinderService();
+
+        when(mMockAppToken.asBinder()).thenReturn(mMockAppToken);
+
+        final int displayIds[] = bs.getDisplayIds();
+        assertEquals(1, displayIds.length);
+        final int displayId = displayIds[0];
+        DisplayInfo info = bs.getDisplayInfo(displayId);
+        assertEquals(info.type, Display.TYPE_BUILT_IN);
+
+        displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
+
+        // flush the handler
+        displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */);
+
+        ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class);
+        verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture());
+        List<DisplayViewport> viewports = viewportCaptor.getValue();
+
+        // Expect to receive 2 viewports: 1 internal, 1 external
+        assertEquals(2, viewports.size());
+
+        DisplayViewport internalViewport = null;
+        DisplayViewport externalViewport = null;
+        for (int i = 0; i < viewports.size(); i++) {
+            DisplayViewport v = viewports.get(i);
+            switch (v.type) {
+                case DisplayViewport.VIEWPORT_INTERNAL: {
+                    internalViewport = v;
+                    break;
+                }
+                case DisplayViewport.VIEWPORT_EXTERNAL: {
+                    externalViewport = v;
+                    break;
+                }
+                default: {
+                    fail("Unexpected viewport type: " + DisplayViewport.typeToString(v.type));
+                    break;
+                }
+            }
+        }
+        // INTERNAL and EXTERNAL viewports get created upon access
+        assertNotNull(internalViewport);
+        assertNotNull(externalViewport);
+        assertTrue(internalViewport.valid);
+        assertEquals(displayId, internalViewport.displayId);
+
+        // To simplify comparison, override the type for external Viewport
+        // TODO (b/116850516) remove this
+        externalViewport.type = internalViewport.type;
+        assertEquals(internalViewport, externalViewport);
+        externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; // undo the changes above
+    }
+
+    @Test
     public void testCreateVirtualDisplayRotatesWithContent() throws Exception {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mBasicInjector);
@@ -178,6 +284,7 @@
     /**
      * Tests that the virtual display is created along-side the default display.
      */
+    @Test
     public void testStartVirtualDisplayWithDefaultDisplay_Succeeds() throws Exception {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mShortMockedInjector);
@@ -188,6 +295,7 @@
     /**
      * Tests that we get a Runtime exception when we cannot initialize the default display.
      */
+    @Test
     public void testStartVirtualDisplayWithDefDisplay_NoDefaultDisplay() throws Exception {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mShortMockedInjector);
@@ -206,6 +314,7 @@
     /**
      * Tests that we get a Runtime exception when we cannot initialize the virtual display.
      */
+    @Test
     public void testStartVirtualDisplayWithDefDisplay_NoVirtualDisplayAdapter() throws Exception {
         DisplayManagerService displayManager = new DisplayManagerService(mContext,
                 new DisplayManagerService.Injector() {
@@ -232,6 +341,7 @@
     /**
      * Tests that an exception is raised for too dark a brightness configuration.
      */
+    @Test
     public void testTooDarkBrightnessConfigurationThrowException() {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mShortMockedInjector);
@@ -266,6 +376,7 @@
     /**
      * Tests that no exception is raised for not too dark a brightness configuration.
      */
+    @Test
     public void testBrightEnoughBrightnessConfigurationDoesNotThrowException() {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mShortMockedInjector);
@@ -279,6 +390,7 @@
     /**
      * Tests that null brightness configurations are alright.
      */
+    @Test
     public void testNullBrightnessConfiguration() {
         DisplayManagerService displayManager =
                 new DisplayManagerService(mContext, mShortMockedInjector);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 2de5d87..a3348c2 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -1052,7 +1052,6 @@
                         genSignatures(signatures),
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         return pi;
     }
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index 318ed3a..9af7b1c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -504,7 +504,6 @@
                         new Signature[] { new Signature(new byte[16]) },
                         2,
                         new ArraySet<>(),
-                        null,
                         null);
         pkg.mExtras = new Bundle();
         pkg.mRestrictedAccountType = "foo19";
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
new file mode 100644
index 0000000..d3a77d3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageSignaturesTest.java
@@ -0,0 +1,474 @@
+/*
+ * Copyright (C) 2018 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.Context;
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.util.Xml;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.HexDump;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(AndroidJUnit4.class)
+public class PackageSignaturesTest {
+    private static final String TEST_RESOURCES_FOLDER = "PackageSignaturesTest";
+
+    private Context mContext;
+
+    private PackageSetting mPackageSetting;
+
+    // These signatures are the DER encoding of the ec-p256[_X] X509 certificates in the certs/
+    // directory. The apksigner tool was used to sign a test APK with these certificates and the
+    // corresponding ec-p256{_X].pk8 private key file. For the lineage tests the
+    // ec-p256-lineage-X-signers file was provided as the parameter to the --lineage option when
+    // signing the APK. The APK was then installed on a test device, the packages.xml file was
+    // pulled from the device, and the APK's <sig> tag was used as the basis for these tests.
+    // For more details see the README under the xml/ directory.
+    private static final String FIRST_EXPECTED_SIGNATURE =
+            "3082016c30820111a003020102020900ca0fb64dfb66e772300a06082a8648ce3d04030230123110300e06"
+            + "035504030c0765632d70323536301e170d3136303333313134353830365a170d34333038313731343538"
+            + "30365a30123110300e06035504030c0765632d703235363059301306072a8648ce3d020106082a8648ce"
+            + "3d03010703420004a65f113d22cb4913908307ac31ee2ba0e9138b785fac6536d14ea2ce90d2b4bfe194"
+            + "b50cdc8e169f54a73a991ef0fa76329825be078cc782740703da44b4d7eba350304e301d0603551d0e04"
+            + "160414d4133568b95b30158b322071ea8c43ff5b05ccc8301f0603551d23041830168014d4133568b95b"
+            + "30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d0403020349"
+            + "003046022100f504a0866caef029f417142c5cb71354c79ffcd1d640618dfca4f19e16db78d6022100f8"
+            + "eea4829799c06cad08c6d3d2d2ec05e0574154e747ea0fdbb8042cb655aadd";
+    private static final String SECOND_EXPECTED_SIGNATURE =
+            "3082016d30820113a0030201020209008855bd1dd2b2b225300a06082a8648ce3d04030230123110300e06"
+            + "035504030c0765632d70323536301e170d3138303731333137343135315a170d32383037313031373431"
+            + "35315a30143112301006035504030c0965632d703235365f323059301306072a8648ce3d020106082a86"
+            + "48ce3d030107034200041d4cca0472ad97ee3cecef0da93d62b450c6788333b36e7553cde9f74ab5df00"
+            + "bbba6ba950e68461d70bbc271b62151dad2de2bf6203cd2076801c7a9d4422e1a350304e301d0603551d"
+            + "0e041604147991d92b0208fc448bf506d4efc9fff428cb5e5f301f0603551d23041830168014d4133568"
+            + "b95b30158b322071ea8c43ff5b05ccc8300c0603551d13040530030101ff300a06082a8648ce3d040302"
+            + "034800304502202769abb1b49fc2f53479c4ae92a6631dabfd522c9acb0bba2b43ebeb99c63011022100"
+            + "d260fb1d1f176cf9b7fa60098bfd24319f4905a3e5fda100a6fe1a2ab19ff09e";
+    private static final String THIRD_EXPECTED_SIGNATURE =
+            "3082016e30820115a0030201020209008394f5cad16a89a7300a06082a8648ce3d04030230143112301006"
+            + "035504030c0965632d703235365f32301e170d3138303731343030303532365a170d3238303731313030"
+            + "303532365a30143112301006035504030c0965632d703235365f333059301306072a8648ce3d02010608"
+            + "2a8648ce3d03010703420004f31e62430e9db6fc5928d975fc4e47419bacfcb2e07c89299e6cd7e344dd"
+            + "21adfd308d58cb49a1a2a3fecacceea4862069f30be1643bcc255040d8089dfb3743a350304e301d0603"
+            + "551d0e041604146f8d0828b13efaf577fc86b0e99fa3e54bcbcff0301f0603551d230418301680147991"
+            + "d92b0208fc448bf506d4efc9fff428cb5e5f300c0603551d13040530030101ff300a06082a8648ce3d04"
+            + "030203470030440220256bdaa2784c273e4cc291a595a46779dee9de9044dc9f7ab820309567df9fe902"
+            + "201a4ad8c69891b5a8c47434fe9540ed1f4979b5fad3483f3fa04d5677355a579e";
+
+    // When running tests using the pastSigs tag / lineage the past signers and their capabilities
+    // should be returned in the SigningDetails. The flags attribute of the cert tag under the
+    // pastSigs tag contains these capabilities; for tests that verify the lineage the capabilities
+    // of the signers should be set to the values in this Map.
+    private static final Map<String, Integer> SIGNATURE_TO_CAPABILITY_MAP;
+
+    static {
+        SIGNATURE_TO_CAPABILITY_MAP = new HashMap<>();
+        SIGNATURE_TO_CAPABILITY_MAP.put(FIRST_EXPECTED_SIGNATURE, 3);
+        SIGNATURE_TO_CAPABILITY_MAP.put(SECOND_EXPECTED_SIGNATURE, 7);
+        SIGNATURE_TO_CAPABILITY_MAP.put(THIRD_EXPECTED_SIGNATURE, 23);
+    }
+
+    private static final int[] CAPABILITIES =
+            {PackageParser.SigningDetails.CertCapabilities.INSTALLED_DATA,
+                    PackageParser.SigningDetails.CertCapabilities.SHARED_USER_ID,
+                    PackageParser.SigningDetails.CertCapabilities.PERMISSION,
+                    PackageParser.SigningDetails.CertCapabilities.ROLLBACK};
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mPackageSetting = createPackageSetting();
+    }
+
+    @Test
+    public void testReadXmlWithOneSignerCompletesSuccessfully() throws Exception {
+        // Verifies the good path of reading a single sigs tag with one signer returns the
+        // expected signature and scheme version.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer.xml", 1, FIRST_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithTwoV1V2Signers() throws Exception {
+        // Verifies the good path of reading a single sigs tag with multiple signers returns the
+        // expected signatures and scheme version.
+        verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2.xml", 2,
+                FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlFromTwoSigsTagsWithSameSigner() throws Exception {
+        // Verifies the good path of reading two separate packages tags from the same signer. The
+        // first call to readXml should return the list with the expected signature, then the second
+        // call should reference this signature and complete successfully with no new entries in the
+        // List.
+        XmlPullParser parser = getXMLFromResources("xml/one-signer.xml");
+        ArrayList<Signature> signatures = new ArrayList<>();
+        mPackageSetting.signatures.readXml(parser, signatures);
+        Set<String> expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
+        verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+        parser = getXMLFromResources("xml/one-signer-previous-cert.xml");
+        mPackageSetting.signatures.readXml(parser, signatures);
+        expectedSignatures = createSetOfSignatures(FIRST_EXPECTED_SIGNATURE);
+        verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+    }
+
+    @Test
+    public void testReadXmlWithSigningLineage() throws Exception {
+        // Verifies the good path of reading a single sigs tag including pastSigs with the
+        // signing lineage returns the expected signatures and lineage for two and three signers
+        // in the lineage.
+        verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/two-signers-in-lineage.xml", 3,
+                FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+        verifyReadXmlReturnsExpectedSignaturesAndLineage("xml/three-signers-in-lineage.xml", 3,
+                FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithInvalidPublicKeyInCertKey() throws Exception {
+        // If the cert tag key attribute does not contain a valid public key then a
+        // CertificateException should be thrown when attempting to build the SigningDetails; in
+        // this case the signing details should be set to UNKNOWN.
+        XmlPullParser parser = getXMLFromResources(
+                "xml/one-signer-invalid-public-key-cert-key.xml");
+        ArrayList<Signature> signatures = new ArrayList<>();
+        mPackageSetting.signatures.readXml(parser, signatures);
+        assertEquals(
+                "The signing details was not UNKNOWN after parsing an invalid public key cert key"
+                        + " attribute",
+                PackageParser.SigningDetails.UNKNOWN, mPackageSetting.signatures.mSigningDetails);
+    }
+
+    @Test
+    public void testReadXmlWithMissingSigsCount() throws Exception {
+        // Verifies if the sigs count attribute is missing then the signature cannot be read but the
+        // method does not throw an exception.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-sigs-count.xml",
+                PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN);
+    }
+
+    @Test
+    public void testReadXmlWithMissingSchemeVersion() throws Exception {
+        // Verifies if the schemeVersion is an invalid value the signature can still be obtained.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-scheme-version.xml",
+                PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+                FIRST_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithSigningLineageWithMissingSchemeVersion() throws Exception {
+        // Verifies if the scheme version cannot be read the signers in the lineage can still be
+        // obtained.
+        verifyReadXmlReturnsExpectedSignaturesAndLineage(
+                "xml/three-signers-in-lineage-missing-scheme-version.xml",
+                PackageParser.SigningDetails.SignatureSchemeVersion.UNKNOWN,
+                FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithInvalidCertIndex() throws Exception {
+        // If the cert index attribute is invalid the signature will not be read but the call
+        // should exit gracefully.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-index.xml", 3);
+    }
+
+    @Test
+    public void testReadXmlWithMissingCertIndex() throws Exception {
+        // If the cert index attribute is missing the signature will not be read but the call should
+        // exit gracefully.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-index.xml", 3);
+    }
+
+    @Test
+    public void testReadXmlWithInvalidCertKey() throws Exception {
+        // If the cert key value is invalid the signature cannot be read but the call should exit
+        // gracefully.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-cert-key.xml", 3);
+    }
+
+    @Test
+    public void testReadXmlWithMissingCertKey() throws Exception {
+        // If the cert key is missing the signature cannot be read but the call should exit
+        // gracefully.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-key.xml", 3);
+    }
+
+    @Test
+    public void testReadXmlWithMissingCertTag() throws Exception {
+        // If the cert tag is missing there is no signature to read but the call should exit
+        // gracefully.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-missing-cert-tag.xml", 3);
+    }
+
+    @Test
+    public void testReadXmlWithTooFewCertTags() throws Exception {
+        // If the number of cert tags is less than that specified in the count attribute then the
+        // signatures that could be read are copied to a smaller array to be used when building
+        // the SigningDetails object. This test verifies if there are too few cert tags the
+        // available signatures can still be obtained.
+        verifyReadXmlReturnsExpectedSignatures("xml/two-signers-v1v2-missing-cert-tag.xml", 1,
+                FIRST_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithExtraCertTag() throws Exception {
+        // Verifies if there are more cert tags than specified by the count attribute the extra cert
+        // tag is ignored and the expected signature from the first cert tag is returned.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-extra-cert-tag.xml", 3,
+                FIRST_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithInvalidTag() throws Exception {
+        // Verifies an invalid tag under sigs is ignored and the expected signature is returned.
+        verifyReadXmlReturnsExpectedSignatures("xml/one-signer-invalid-tag.xml", 3,
+                FIRST_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithInvalidPastSigsCount() throws Exception {
+        // Verifies if the pastSigs tag contains an invalid count attribute the current signature
+        // is still returned; in this case the third expected signature is the most recent signer.
+        verifyReadXmlReturnsExpectedSignatures(
+                "xml/three-signers-in-lineage-invalid-pastSigs-count.xml", 3,
+                THIRD_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithMissingPastSigsCount() throws Exception {
+        // Verifies if the pastSigs tag is missing the count attribute the current signature is
+        // still returned; in this case the third expected signature is the most recent signer.
+        verifyReadXmlReturnsExpectedSignaturesAndLineage(
+                "xml/three-signers-in-lineage-missing-pastSigs-count.xml", 3,
+                THIRD_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithInvalidCertFlags() throws Exception {
+        // Verifies if the cert tag contains an invalid flags attribute the expected signatures
+        // are still returned, although since the flags could not be read these signatures will not
+        // include the capabilities of the previous signers in the lineage.
+        verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-invalid-certs-flags.xml",
+                3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithMissingCertFlags() throws Exception {
+        // Verifies if the cert tag does not contain a flags attribute the expected signatures are
+        // still returned, although since there are no flags to read these signatures will not
+        // include the capabilities of the previous signers in the lineage.
+        verifyReadXmlReturnsExpectedSignatures("xml/two-signers-in-lineage-missing-certs-flags.xml",
+                3, FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithMultiplePastSigsTags() throws Exception {
+        // Verifies if multiple pastSigs tags are found under the sigs tag the additional pastSigs
+        // tag is ignored and the expected signatures are returned along with the previous signer in
+        // the lineage.
+        verifyReadXmlReturnsExpectedSignaturesAndLineage(
+                "xml/two-signers-in-lineage-multiple-pastSigs-tags.xml", 3,
+                FIRST_EXPECTED_SIGNATURE, SECOND_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithInvalidPastSigsCertIndex() throws Exception {
+        // If the pastSigs cert tag contains an invalid index attribute that signature cannot be
+        // read but the current signature should still be returned.
+        verifyReadXmlReturnsExpectedSignaturesAndLineage(
+                "xml/two-signers-in-lineage-invalid-pastSigs-cert-index.xml", 3,
+                SECOND_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithMissingPastSigsCertIndex() throws Exception {
+        // If the pastSigs cert tag does not contain an index attribute that signature cannot be
+        // read but the current signature should still be returned.
+        verifyReadXmlReturnsExpectedSignaturesAndLineage(
+                "xml/two-signers-in-lineage-missing-pastSigs-cert-index.xml", 3,
+                SECOND_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithUndefinedPastSigsIndex() throws Exception {
+        // If a cert tag does not contain a key attribute it is assumed that the index attribute
+        // refers to a previously seen signature. If a signature does not yet exist at this index
+        // then the current signature cannot be read but any other signatures should still be
+        // returned.
+        verifyReadXmlReturnsExpectedSignatures(
+                "xml/two-signers-in-lineage-undefined-pastSigs-index.xml", 3,
+                FIRST_EXPECTED_SIGNATURE, null);
+    }
+
+    @Test
+    public void testReadXmlWithTooFewPastSigsCertTags() throws Exception {
+        // If the number of cert tags is less than that specified in the count attribute of the
+        // pastSigs tag then the signatures that could be read are copied to a smaller array to be
+        // used when building the SigningDetails object. This test verifies if there are too few
+        // cert tags the available signatures and lineage can still be obtained.
+        verifyReadXmlReturnsExpectedSignaturesAndLineage(
+                "xml/three-signers-in-lineage-missing-pastSigs-cert-tag.xml", 3,
+                FIRST_EXPECTED_SIGNATURE, THIRD_EXPECTED_SIGNATURE);
+    }
+
+    @Test
+    public void testReadXmlWithPastSignerWithNoCapabilities() throws Exception {
+        // When rotating the signing key a developer is able to specify the capabilities granted to
+        // the apps signed with the previous key. This test verifies a previous signing certificate
+        // with the flags set to 0 does not have any capabilities.
+        XmlPullParser parser = getXMLFromResources("xml/two-signers-in-lineage-no-caps.xml");
+        ArrayList<Signature> signatures = new ArrayList<>();
+        mPackageSetting.signatures.readXml(parser, signatures);
+        // obtain the Signature in the list matching the previous signing certificate
+        Signature previousSignature = null;
+        for (Signature signature : signatures) {
+            String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
+            if (signatureValue.equals(FIRST_EXPECTED_SIGNATURE)) {
+                previousSignature = signature;
+                break;
+            }
+        }
+        assertNotNull("Unable to find the expected previous signer", previousSignature);
+        for (int capability : CAPABILITIES) {
+            assertFalse("The previous signer should not have the " + capability + " capability",
+                    mPackageSetting.signatures.mSigningDetails.hasCertificate(previousSignature,
+                            capability));
+        }
+    }
+
+    /**
+     * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
+     * version and the provided signatures.
+     */
+    private void verifyReadXmlReturnsExpectedSignatures(String xmlFile, int expectedSchemeVersion,
+            String... expectedSignatureValues) throws Exception {
+        XmlPullParser parser = getXMLFromResources(xmlFile);
+        ArrayList<Signature> signatures = new ArrayList<>();
+        mPackageSetting.signatures.readXml(parser, signatures);
+        Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
+        verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+        assertEquals("The returned signature scheme is not the expected value",
+                expectedSchemeVersion,
+                mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+    }
+
+    /**
+     * Verifies reading the sigs tag of the provided XML file returns the specified signature scheme
+     * version, the provided signatures, and that the previous signers have the expected
+     * capabilities.
+     */
+    private void verifyReadXmlReturnsExpectedSignaturesAndLineage(String xmlFile,
+            int schemeVersion, String... expectedSignatureValues) throws Exception {
+        XmlPullParser parser = getXMLFromResources(xmlFile);
+        ArrayList<Signature> signatures = new ArrayList<>();
+        mPackageSetting.signatures.readXml(parser, signatures);
+        Set<String> expectedSignatures = createSetOfSignatures(expectedSignatureValues);
+        verifySignaturesContainExpectedValues(signatures, expectedSignatures);
+        assertEquals("The returned signature scheme is not the expected value", schemeVersion,
+                mPackageSetting.signatures.mSigningDetails.signatureSchemeVersion);
+        for (Signature signature : signatures) {
+            String signatureValue = HexDump.toHexString(signature.toByteArray(), false);
+            int expectedCapabilities = SIGNATURE_TO_CAPABILITY_MAP.get(signatureValue);
+            assertTrue("The signature " + signatureValue
+                            + " was not found with the expected capabilities of " +
+                            expectedCapabilities
+                            + " in the signing details",
+                    mPackageSetting.signatures.mSigningDetails.hasCertificate(signature,
+                            expectedCapabilities));
+        }
+    }
+
+    /**
+     * Verifies the provided {@code List} contains Signatures that match the provided hex encoded
+     * signature values.
+     *
+     * The provided {@code Set} will be modified by this method as elements will be removed to
+     * ensure duplicate expected Signatures are not in the {@code List}.
+     */
+    private static void verifySignaturesContainExpectedValues(ArrayList<Signature> signatures,
+            Set<String> expectedSignatures) {
+        assertEquals("The number of signatures does not equal the expected number of signatures",
+                expectedSignatures.size(), signatures.size());
+        for (Signature signature : signatures) {
+            String signatureString = null;
+            if (signature != null) {
+                signatureString = HexDump.toHexString(signature.toByteArray(), false);
+            }
+            // If the signature is in the expected set then remove it so that duplicate matching
+            // signatures are reported.
+            if (expectedSignatures.contains(signatureString)) {
+                expectedSignatures.remove(signatureString);
+            } else {
+                fail("The following unexpected signature was returned: " + signatureString);
+            }
+        }
+    }
+
+    private static Set<String> createSetOfSignatures(String... signatures) {
+        Set<String> result = new HashSet<String>();
+        for (String signature : signatures) {
+            result.add(signature);
+        }
+        return result;
+    }
+
+    private XmlPullParser getXMLFromResources(String xmlFile) throws Exception {
+        InputStream xmlStream = mContext.getResources().getAssets().open(
+                TEST_RESOURCES_FOLDER + "/" + xmlFile);
+        XmlPullParser result = Xml.newPullParser();
+        result.setInput(xmlStream, StandardCharsets.UTF_8.name());
+        int type;
+        // advance the parser to the first tag
+        while ((type = result.next()) != XmlPullParser.START_TAG
+                && type != XmlPullParser.END_DOCUMENT) {
+            ;
+        }
+        return result;
+    }
+
+    private static PackageSetting createPackageSetting() {
+        // Generic PackageSetting object with values from a test app installed on a device to be
+        // used to test the methods under the PackageSignatures signatures data member.
+        File appPath = new File("/data/app/app");
+        PackageSetting result = new PackageSetting("test.app", null, appPath, appPath,
+                "/data/app/app", null, null, null,
+                1, 940097092, 0, null,
+                null, 0 /*userId*/, null, null);
+        return result;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
index 13612a1..182760b 100644
--- a/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -99,7 +99,6 @@
                         new Signature[] {SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -119,7 +118,6 @@
                         new Signature[] {SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -203,7 +201,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -226,7 +223,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -248,7 +244,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -271,7 +266,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2, SIGNATURE_3},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -295,7 +289,6 @@
                         new Signature[] {SIGNATURE_1},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -320,7 +313,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
@@ -348,7 +340,6 @@
                         new Signature[] {SIGNATURE_1, SIGNATURE_2},
                         PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V3,
                         null,
-                        null,
                         null));
         packageInfo.applicationInfo = new ApplicationInfo();
 
diff --git a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
index acd065e..e16f118 100644
--- a/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
+++ b/services/tests/servicestests/src/com/android/server/policy/PhoneWindowManagerTestBase.java
@@ -16,6 +16,10 @@
 
 package com.android.server.policy;
 
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.Surface.ROTATION_0;
 import static android.view.Surface.ROTATION_180;
 import static android.view.Surface.ROTATION_270;
@@ -179,8 +183,25 @@
         transformPhysicalToLogicalCoordinates(rotation, DISPLAY_WIDTH, DISPLAY_HEIGHT, m);
         m.mapRect(rectF);
 
+        int pos = -1;
+        switch (rotation) {
+            case ROTATION_0:
+                pos = BOUNDS_POSITION_TOP;
+                break;
+            case ROTATION_90:
+                pos = BOUNDS_POSITION_LEFT;
+                break;
+            case ROTATION_180:
+                pos = BOUNDS_POSITION_BOTTOM;
+                break;
+            case ROTATION_270:
+                pos = BOUNDS_POSITION_RIGHT;
+                break;
+        }
+
+
         return DisplayCutout.fromBoundingRect((int) rectF.left, (int) rectF.top,
-                (int) rectF.right, (int) rectF.bottom);
+                (int) rectF.right, (int) rectF.bottom, pos);
     }
 
     static class TestContextWrapper extends ContextWrapper {
diff --git a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
index 54ac6fc..bd4a356 100644
--- a/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/BatterySaverPolicyTest.java
@@ -84,9 +84,6 @@
     }
 
     @Mock
-    Handler mHandler;
-
-    @Mock
     MetricsLogger mMetricsLogger = mock(MetricsLogger.class);
 
     private BatterySaverPolicyForTest mBatterySaverPolicy;
diff --git a/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java b/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
index 3766d24..ef945e6 100644
--- a/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/textservices/LocaleUtilsTest.java
@@ -19,11 +19,17 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Locale;
 
+@SmallTest
+@RunWith(AndroidJUnit4.class)
 public class LocaleUtilsTest {
     private static final Locale LOCALE_EN = new Locale("en");
     private static final Locale LOCALE_EN_US = new Locale("en", "US");
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 12be0b3..e6e08bb 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -90,7 +90,7 @@
         private AppTransitionListener mListener;
 
         MockAppTransition(Context context) {
-            super(context, null);
+            super(context, sWm);
         }
 
         @Override
diff --git a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
index b330304..3dcdd23 100644
--- a/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/DisplayContentTests.java
@@ -24,6 +24,8 @@
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -32,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
 
 import static com.android.server.wm.WindowContainer.POSITION_TOP;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
@@ -303,7 +306,8 @@
                 createTapEvent(dm0.widthPixels / 2, dm0.heightPixels / 2, false));
 
         // Check focus is on primary display.
-        assertEquals(sWm.mCurrentFocus, dc0.findFocusedWindow());
+        assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+                dc0.findFocusedWindow());
 
         // Tap on secondary display
         DisplayMetrics dm1 = dc1.getDisplayMetrics();
@@ -313,7 +317,8 @@
                 createTapEvent(dm1.widthPixels / 2, dm1.heightPixels / 2, false));
 
         // Check focus is on secondary.
-        assertEquals(sWm.mCurrentFocus, dc1.findFocusedWindow());
+        assertEquals(sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus,
+                dc1.findFocusedWindow());
     }
 
     @Test
@@ -321,34 +326,29 @@
         // Create a focusable window and check that focus is calculated correctly
         final WindowState window1 =
                 createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, "window1");
-        assertEquals(window1, sWm.mRoot.computeFocusedWindow());
+        updateFocusedWindow();
+        assertTrue(window1.isFocused());
+        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Check that a new display doesn't affect focus
         final DisplayContent dc = createNewDisplay();
-        assertEquals(window1, sWm.mRoot.computeFocusedWindow());
+        updateFocusedWindow();
+        assertTrue(window1.isFocused());
+        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Add a window to the second display, and it should be focused
         final WindowState window2 = createWindow(null, TYPE_BASE_APPLICATION, dc, "window2");
-        assertEquals(window2, sWm.mRoot.computeFocusedWindow());
+        updateFocusedWindow();
+        assertTrue(window1.isFocused());
+        assertTrue(window2.isFocused());
+        assertEquals(window2, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
 
         // Move the first window to the to including parents, and make sure focus is updated
         window1.getParent().positionChildAt(POSITION_TOP, window1, true);
-        assertEquals(window1, sWm.mRoot.computeFocusedWindow());
-    }
-
-    @Test
-    public void testKeyguard_preventsSecondaryDisplayFocus() throws Exception {
-        final WindowState keyguard = createWindow(null, TYPE_STATUS_BAR,
-                sWm.getDefaultDisplayContentLocked(), "keyguard");
-        assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
-
-        // Add a window to a second display, and it should be focused
-        final DisplayContent dc = createNewDisplay();
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, dc, "win");
-        assertEquals(win, sWm.mRoot.computeFocusedWindow());
-
-        mWmRule.getWindowManagerPolicy().keyguardShowingAndNotOccluded = true;
-        assertEquals(keyguard, sWm.mRoot.computeFocusedWindow());
+        updateFocusedWindow();
+        assertTrue(window1.isFocused());
+        assertTrue(window2.isFocused());
+        assertEquals(window1, sWm.mRoot.getTopFocusedDisplayContent().mCurrentFocus);
     }
 
     /**
@@ -454,7 +454,7 @@
             dc.mInitialDisplayHeight = 400;
             Rect r = new Rect(80, 0, 120, 10);
             final DisplayCutout cutout = new WmDisplayCutout(
-                    fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
+                    fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_TOP), null)
                     .computeSafeInsets(200, 400).getDisplayCutout();
 
             dc.mInitialDisplayCutout = cutout;
@@ -484,7 +484,7 @@
 
             final Rect r1 = new Rect(left, top, right, bottom);
             final DisplayCutout cutout = new WmDisplayCutout(
-                    fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom), null)
+                    fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
                     .computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
 
             dc.mInitialDisplayCutout = cutout;
@@ -501,7 +501,7 @@
             // |             |      -------------
             final Rect r = new Rect(top, left, bottom, right);
             assertEquals(new WmDisplayCutout(
-                    fromBoundingRect(r.left, r.top, r.right, r.bottom), null)
+                    fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
                     .computeSafeInsets(displayHeight, displayWidth)
                     .getDisplayCutout(), dc.getDisplayInfo().displayCutout);
         }
@@ -590,6 +590,12 @@
         assertEquals(displayContent.mBaseDisplayDensity, expectedBaseDensity);
     }
 
+    private void updateFocusedWindow() {
+        synchronized (sWm.mWindowMap) {
+            sWm.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, false);
+        }
+    }
+
     /**
      * Create DisplayContent that does not update display base/initial values from device to keep
      * the values set by test.
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
index 0886729..7cd1314 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowFrameTests.java
@@ -16,12 +16,12 @@
 
 package com.android.server.wm;
 
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.WindowManager.LayoutParams.FILL_PARENT;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
 
 import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
@@ -474,7 +474,8 @@
         final Rect pf = new Rect(0, 0, 1000, 2000);
         // Create a display cutout of size 50x50, aligned top-center
         final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
+                fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
+                pf.width(), pf.height());
 
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
@@ -499,7 +500,8 @@
         final Rect pf = new Rect(0, -500, 1000, 1500);
         // Create a display cutout of size 50x50, aligned top-center
         final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(500, 0, 550, 50), pf.width(), pf.height());
+                fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
+                pf.width(), pf.height());
 
         final WindowFrames windowFrames = w.getWindowFrames();
         windowFrames.setFrames(pf, pf, pf, pf, pf, pf, pf, pf);
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
index b7cc9ce..3637baf 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowStateTests.java
@@ -49,6 +49,7 @@
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
+import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
@@ -404,8 +405,12 @@
         WindowFrames wf = app.getWindowFrames();
         wf.mParentFrame.set(7, 10, 185, 380);
         wf.mDisplayFrame.set(wf.mParentFrame);
-        final DisplayCutout cutout = new DisplayCutout(new Rect(0, 15, 0, 22),
-                Arrays.asList(new Rect(95, 0, 105, 15), new Rect(95, 378, 105, 400)));
+        final DisplayCutout cutout = new DisplayCutout(
+                Insets.of(0, 15, 0, 22) /* safeInset */,
+                null /* boundLeft */,
+                new Rect(95, 0, 105, 15),
+                null /* boundRight */,
+                new Rect(95, 378, 105, 400));
         wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
 
         app.computeFrameLw();
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
new file mode 100644
index 0000000..ba8869b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/DisplayRotationUtilTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2018 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.wm.utils;
+
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
+import static android.view.Surface.ROTATION_0;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
+import static com.android.server.wm.utils.DisplayRotationUtil.getBoundIndexFromRotation;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+import android.graphics.Rect;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+
+
+/**
+ * Tests for {@link DisplayRotationUtil}
+ *
+ * Run with: atest DisplayRotationUtilTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class DisplayRotationUtilTest {
+    private static Rect ZERO_RECT = new Rect();
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot0() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_0),
+                equalTo(BOUNDS_POSITION_LEFT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_0),
+                equalTo(BOUNDS_POSITION_TOP));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_0),
+                equalTo(BOUNDS_POSITION_RIGHT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_0),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+    }
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot90() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_90),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_90),
+                equalTo(BOUNDS_POSITION_LEFT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_90),
+                equalTo(BOUNDS_POSITION_TOP));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_90),
+                equalTo(BOUNDS_POSITION_RIGHT));
+    }
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot180() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_180),
+                equalTo(BOUNDS_POSITION_RIGHT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_180),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_180),
+                equalTo(BOUNDS_POSITION_LEFT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_180),
+                equalTo(BOUNDS_POSITION_TOP));
+    }
+
+    @Test
+    public void testGetBoundIndexFromRotation_rot270() {
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_LEFT, ROTATION_270),
+                equalTo(BOUNDS_POSITION_TOP));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_TOP, ROTATION_270),
+                equalTo(BOUNDS_POSITION_RIGHT));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_RIGHT, ROTATION_270),
+                equalTo(BOUNDS_POSITION_BOTTOM));
+        assertThat(getBoundIndexFromRotation(BOUNDS_POSITION_BOTTOM, ROTATION_270),
+                equalTo(BOUNDS_POSITION_LEFT));
+
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot0() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 200, 300),
+                equalTo(bounds));
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot90() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 200, 300),
+                equalTo(new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT }));
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot180() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 200, 300),
+                equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
+    }
+
+    @Test
+    public void testGetRotatedBounds_top_rot270() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { ZERO_RECT, new Rect(50,0,150,10), ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 200, 300),
+                equalTo(new Rect[] { ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot0() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_0, 300, 200),
+                equalTo(bounds));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot90() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_90, 300, 200),
+                equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(50, 290, 150, 300) }));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot180() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_180, 300, 200),
+                equalTo(new Rect[]{ ZERO_RECT, ZERO_RECT, new Rect(290, 50, 300, 150), ZERO_RECT }));
+    }
+
+    @Test
+    public void testGetRotatedBounds_left_rot270() {
+        DisplayRotationUtil util = new DisplayRotationUtil();
+        Rect[] bounds = new Rect[] { new Rect(0, 50, 10, 150), ZERO_RECT, ZERO_RECT, ZERO_RECT };
+        assertThat(util.getRotatedBounds(bounds, ROTATION_270, 300, 200),
+                equalTo(new Rect[]{ ZERO_RECT, new Rect(50, 0, 150, 10), ZERO_RECT, ZERO_RECT }));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index 9ce3dca..c5e35e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -18,11 +18,19 @@
 
 
 import static android.view.DisplayCutout.NO_CUTOUT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM;
+import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_RIGHT;
+import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
 import static android.view.DisplayCutout.fromBoundingRect;
 
+import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
 
+
+import android.graphics.Insets;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.util.Size;
@@ -45,15 +53,17 @@
 @SmallTest
 @Presubmit
 public class WmDisplayCutoutTest {
+    private static final Rect ZERO_RECT = new Rect();
 
     private final DisplayCutout mCutoutTop = new DisplayCutout(
-            new Rect(0, 100, 0, 0),
-            Arrays.asList(new Rect(50, 0, 75, 100)));
+            Insets.of(0, 100, 0, 0),
+            null /* boundLeft */, new Rect(50, 0, 75, 100) /* boundTop */,
+            null /* boundRight */, null /* boundBottom */);
 
     @Test
     public void calculateRelativeTo_top() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20), 200, 400)
+                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
                 .calculateRelativeTo(new Rect(5, 5, 95, 195));
 
         assertEquals(new Rect(0, 15, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -62,7 +72,7 @@
     @Test
     public void calculateRelativeTo_left() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 20, 100), 400, 200)
+                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200)
                 .calculateRelativeTo(new Rect(5, 5, 195, 95));
 
         assertEquals(new Rect(15, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -71,7 +81,7 @@
     @Test
     public void calculateRelativeTo_bottom() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 180, 100, 200), 100, 200)
+                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200)
                 .calculateRelativeTo(new Rect(5, 5, 95, 195));
 
         assertEquals(new Rect(0, 0, 0, 15), cutout.getDisplayCutout().getSafeInsets());
@@ -80,7 +90,7 @@
     @Test
     public void calculateRelativeTo_right() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(180, 0, 200, 100), 200, 100)
+                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100)
                 .calculateRelativeTo(new Rect(5, 5, 195, 95));
 
         assertEquals(new Rect(0, 0, 15, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -89,16 +99,17 @@
     @Test
     public void calculateRelativeTo_bounds() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20), 200, 400)
+                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
                 .calculateRelativeTo(new Rect(5, 10, 95, 180));
 
-        assertEquals(new Rect(-5, -10, 95, 10), cutout.getDisplayCutout().getBounds().getBounds());
+        assertThat(cutout.getDisplayCutout().getBoundingRectTop(),
+                equalTo(new Rect(-5, -10, 95, 10)));
     }
 
     @Test
     public void computeSafeInsets_top() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20), 200, 400);
+                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400);
 
         assertEquals(new Rect(0, 20, 0, 0), cutout.getDisplayCutout().getSafeInsets());
     }
@@ -106,7 +117,7 @@
     @Test
     public void computeSafeInsets_left() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 20, 100), 400, 200);
+                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200);
 
         assertEquals(new Rect(20, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
     }
@@ -114,7 +125,7 @@
     @Test
     public void computeSafeInsets_bottom() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 180, 100, 200), 100, 200);
+                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200);
 
         assertEquals(new Rect(0, 0, 0, 20), cutout.getDisplayCutout().getSafeInsets());
     }
@@ -122,7 +133,7 @@
     @Test
     public void computeSafeInsets_right() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(180, 0, 200, 100), 200, 100);
+                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100);
 
         assertEquals(new Rect(0, 0, 20, 0), cutout.getDisplayCutout().getSafeInsets());
     }
@@ -132,8 +143,7 @@
         DisplayCutout cutout = WmDisplayCutout.computeSafeInsets(mCutoutTop, 1000,
                 2000).getDisplayCutout();
 
-        assertEquals(mCutoutTop.getBounds().getBounds(),
-                cutout.getBounds().getBounds());
+        assertEquals(mCutoutTop.getBoundingRects(), cutout.getBoundingRects());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk
index 8405179..f3f4355 100644
--- a/services/tests/uiservicestests/Android.mk
+++ b/services/tests/uiservicestests/Android.mk
@@ -45,6 +45,7 @@
     libbacktrace \
     libbase \
     libbinder \
+    libbinderthreadstate \
     libc++ \
     libcutils \
     liblog \
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 58aae2b..4e007c2d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.notification;
 
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
 import static android.app.NotificationManager.EXTRA_BLOCKED_STATE;
 import static android.app.NotificationManager.IMPORTANCE_HIGH;
@@ -74,6 +76,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
+import android.app.ITransientNotification;
 import android.app.IUriGrantsManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.usage.UsageStatsManagerInternal;
@@ -118,12 +121,14 @@
 import com.android.internal.R;
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.server.LocalServices;
+import com.android.server.SystemService;
 import com.android.server.UiServiceTestCase;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 import com.android.server.notification.NotificationManagerService.NotificationListeners;
 import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
 import org.junit.Before;
@@ -160,6 +165,8 @@
     private IPackageManager mPackageManager;
     @Mock
     private PackageManager mPackageManagerClient;
+    @Mock
+    private WindowManagerInternal mWindowManagerInternal;
     private TestableContext mContext = spy(getContext());
     private final String PKG = mContext.getPackageName();
     private TestableLooper mTestableLooper;
@@ -238,6 +245,16 @@
         }
     }
 
+    private class TestableToastCallback extends ITransientNotification.Stub {
+        @Override
+        public void show(IBinder windowToken) {
+        }
+
+        @Override
+        public void hide() {
+        }
+    }
+
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
@@ -249,6 +266,8 @@
 
         LocalServices.removeServiceForTest(UriGrantsManagerInternal.class);
         LocalServices.addService(UriGrantsManagerInternal.class, mUgmInternal);
+        LocalServices.removeServiceForTest(WindowManagerInternal.class);
+        LocalServices.addService(WindowManagerInternal.class, mWindowManagerInternal);
 
         mService = new TestableNotificationManagerService(mContext);
 
@@ -302,6 +321,7 @@
                     mGroupHelper, mAm, mAppUsageStats,
                     mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
                     mAppOpsManager);
+            mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
         } catch (SecurityException e) {
             if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) {
                 throw e;
@@ -3548,4 +3568,93 @@
 
         assertEquals(0, captor.getValue().getNotification().flags);
     }
+
+    @Test
+    public void testAllowForegroundToasts() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        // notifications from this package are blocked by the user
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+        // this app is in the foreground
+        when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_FOREGROUND);
+
+        // enqueue toast -> toast should still enqueue
+        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+                new TestableToastCallback(), 2000, 0);
+        assertEquals(1, mService.mToastQueue.size());
+    }
+
+    @Test
+    public void testDisallowToastsFromSuspendedPackages() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(true);
+
+        // notifications from this package are NOT blocked by the user
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_LOW);
+
+        // enqueue toast -> no toasts enqueued
+        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+                new TestableToastCallback(), 2000, 0);
+        assertEquals(0, mService.mToastQueue.size());
+    }
+
+    @Test
+    public void testDisallowToastsFromBlockedApps() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = false;
+
+        // package is not suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(false);
+
+        // notifications from this package are blocked by the user
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+        // this app is NOT in the foreground
+        when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
+
+        // enqueue toast -> no toasts enqueued
+        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+                new TestableToastCallback(), 2000, 0);
+        assertEquals(0, mService.mToastQueue.size());
+    }
+
+    @Test
+    public void testAlwaysAllowSystemToasts() throws Exception {
+        final String testPackage = "testPackageName";
+        assertEquals(0, mService.mToastQueue.size());
+        mService.isSystemUid = true;
+
+        // package is suspended
+        when(mPackageManager.isPackageSuspendedForUser(testPackage, UserHandle.getUserId(mUid)))
+                .thenReturn(true);
+
+        // notifications from this package ARE blocked by the user
+        mService.setPreferencesHelper(mPreferencesHelper);
+        when(mPreferencesHelper.getImportance(testPackage, mUid)).thenReturn(IMPORTANCE_NONE);
+
+        // this app is NOT in the foreground
+        when(mActivityManager.getUidImportance(mUid)).thenReturn(IMPORTANCE_GONE);
+
+        // enqueue toast -> system toast can still be enqueued
+        ((INotificationManager)mService.mService).enqueueToast(testPackage,
+                new TestableToastCallback(), 2000, 0);
+        assertEquals(1, mService.mToastQueue.size());
+    }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index c616685..8946d25 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -79,7 +79,7 @@
  * and downgrades have taken place. The original file version denotes how to parse the file.
  */
 public class UsageStatsDatabase {
-    private static final int DEFAULT_CURRENT_VERSION = 4;
+    private static final int DEFAULT_CURRENT_VERSION = 3;
 
     // Current version of the backup schema
     static final int BACKUP_VERSION = 1;
@@ -856,6 +856,9 @@
             Slog.e(TAG, "UsageStatsDatabase", e);
             throw e;
         }
+        // If old version, don't bother sanity checking
+        if (version < 4) return;
+
         // STOPSHIP: b/111422946, b/115429334
         // Everything below this comment is sanity check against the new database version.
         // After the new version has soaked for some time the following should removed.
diff --git a/startop/iorap/Android.bp b/startop/iorap/Android.bp
new file mode 100644
index 0000000..b3b0900
--- /dev/null
+++ b/startop/iorap/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2018 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.
+
+java_library_static {
+  name: "libiorap-java",
+
+  aidl: {
+    include_dirs: [
+      "system/iorap/binder",
+    ],
+  },
+
+  srcs: [
+      ":iorap-aidl",
+      "**/*.java",
+  ],
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
new file mode 100644
index 0000000..1d38f4c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityHintEvent.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Provide a hint to iorapd that an activity has transitioned state.<br /><br />
+ *
+ * Knowledge of when an activity starts/stops can be used by iorapd to increase system
+ * performance (e.g. by launching perfetto tracing to record an io profile, or by
+ * playing back an ioprofile via readahead) over the long run.<br /><br />
+ *
+ * /@see com.google.android.startop.iorap.IIorap#onActivityHintEvent<br /><br />
+ *
+ * Once an activity hint is in {@link #TYPE_STARTED} it must transition to another type.
+ * All other states could be terminal, see below: <br /><br />
+ *
+ * <pre>
+ *
+ *          ┌──────────────────────────────────────┐
+ *          │                                      ▼
+ *        ┌─────────┐     ╔════════════════╗     ╔═══════════╗
+ *    ──▶ │ STARTED │ ──▶ ║   COMPLETED    ║ ──▶ ║ CANCELLED ║
+ *        └─────────┘     ╚════════════════╝     ╚═══════════╝
+ *                          │
+ *                          │
+ *                          ▼
+ *                        ╔════════════════╗
+ *                        ║ POST_COMPLETED ║
+ *                        ╚════════════════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/ActivityHint.dot -->
+ *
+ * @hide
+ */
+public class ActivityHintEvent implements Parcelable {
+
+    public static final int TYPE_STARTED = 0;
+    public static final int TYPE_CANCELLED = 1;
+    public static final int TYPE_COMPLETED = 2;
+    public static final int TYPE_POST_COMPLETED = 3;
+    private static final int TYPE_MAX = TYPE_POST_COMPLETED;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_STARTED,
+            TYPE_CANCELLED,
+            TYPE_COMPLETED,
+            TYPE_POST_COMPLETED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+    public final ActivityInfo activityInfo;
+
+    public ActivityHintEvent(@Type int type, ActivityInfo activityInfo) {
+        this.type = type;
+        this.activityInfo = activityInfo;
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        Objects.requireNonNull(activityInfo, "activityInfo");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{type: %d, activityInfo: %s}", type, activityInfo);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof ActivityHintEvent) {
+            return equals((ActivityHintEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(ActivityHintEvent other) {
+        return type == other.type &&
+                Objects.equals(activityInfo, other.activityInfo);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        activityInfo.writeToParcel(out, flags);
+    }
+
+    private ActivityHintEvent(Parcel in) {
+        this.type = in.readInt();
+        this.activityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<ActivityHintEvent> CREATOR
+            = new Parcelable.Creator<ActivityHintEvent>() {
+        public ActivityHintEvent createFromParcel(Parcel in) {
+            return new ActivityHintEvent(in);
+        }
+
+        public ActivityHintEvent[] newArray(int size) {
+            return new ActivityHintEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
new file mode 100644
index 0000000..f47a42c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/ActivityInfo.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import java.util.Objects;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+/**
+ * Provide minimal information for launched activities to iorap.<br /><br />
+ *
+ * This uniquely identifies a system-wide activity by providing the {@link #packageName} and
+ * {@link #activityName}.
+ *
+ * @see ActivityHintEvent
+ * @see AppIntentEvent
+ *
+ * @hide
+ */
+public class ActivityInfo implements Parcelable {
+
+    /** The name of the package, for example {@code com.android.calculator}. */
+    public final String packageName;
+    /** The name of the activity, for example {@code .activities.activity.MainActivity} */
+    public final String activityName;
+
+    public ActivityInfo(String packageName, String activityName) {
+        this.packageName = packageName;
+        this.activityName = activityName;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        Objects.requireNonNull(packageName, "packageName");
+        Objects.requireNonNull(activityName, "activityName");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{packageName: %s, activityName: %s}", packageName, activityName);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof ActivityInfo) {
+            return equals((ActivityInfo) other);
+        }
+        return false;
+    }
+
+    private boolean equals(ActivityInfo other) {
+        return Objects.equals(packageName, other.packageName) &&
+                Objects.equals(activityName, other.activityName);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(packageName);
+        out.writeString(activityName);
+    }
+
+    private ActivityInfo(Parcel in) {
+        packageName = in.readString();
+        activityName = in.readString();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<ActivityInfo> CREATOR
+            = new Parcelable.Creator<ActivityInfo>() {
+        public ActivityInfo createFromParcel(Parcel in) {
+            return new ActivityInfo(in);
+        }
+
+        public ActivityInfo[] newArray(int size) {
+            return new ActivityInfo[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
new file mode 100644
index 0000000..1cd37b5
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppIntentEvent.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Notifications for iorapd specifying when a system-wide intent defaults change.<br /><br />
+ *
+ * Intent defaults provide a mechanism for an app to register itself as an automatic handler.
+ * For example the camera app might be registered as the default handler for
+ * {@link android.provider.MediaStore#INTENT_ACTION_STILL_IMAGE_CAMERA} intent. Subsequently,
+ * if an arbitrary other app requests for a still image camera photo to be taken, the system
+ * will launch the respective default camera app to be launched to handle that request.<br /><br />
+ *
+ * In some cases iorapd might need to know default intents, e.g. for boot-time pinning of
+ * applications that resolve from the default intent. If the application would now be resolved
+ * differently, iorapd would unpin the old application and pin the new application.<br /><br />
+ *
+ * @hide
+ */
+public class AppIntentEvent implements Parcelable {
+
+    /** @see android.content.Intent#CATEGORY_DEFAULT */
+    public static final int TYPE_DEFAULT_INTENT_CHANGED = 0;
+    private static final int TYPE_MAX = 0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_DEFAULT_INTENT_CHANGED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+
+    public final ActivityInfo oldActivityInfo;
+    public final ActivityInfo newActivityInfo;
+
+    // TODO: Probably need the corresponding action here as well.
+
+    public static AppIntentEvent createDefaultIntentChanged(ActivityInfo oldActivityInfo,
+            ActivityInfo newActivityInfo) {
+        return new AppIntentEvent(TYPE_DEFAULT_INTENT_CHANGED, oldActivityInfo,
+                newActivityInfo);
+    }
+
+    private AppIntentEvent(@Type int type, ActivityInfo oldActivityInfo,
+            ActivityInfo newActivityInfo) {
+        this.type = type;
+        this.oldActivityInfo = oldActivityInfo;
+        this.newActivityInfo = newActivityInfo;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        Objects.requireNonNull(oldActivityInfo, "oldActivityInfo");
+        Objects.requireNonNull(oldActivityInfo, "newActivityInfo");
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{oldActivityInfo: %s, newActivityInfo: %s}", oldActivityInfo,
+                newActivityInfo);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof AppIntentEvent) {
+            return equals((AppIntentEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(AppIntentEvent other) {
+        return type == other.type &&
+                Objects.equals(oldActivityInfo, other.oldActivityInfo) &&
+                Objects.equals(newActivityInfo, other.newActivityInfo);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        oldActivityInfo.writeToParcel(out, flags);
+        newActivityInfo.writeToParcel(out, flags);
+    }
+
+    private AppIntentEvent(Parcel in) {
+        this.type = in.readInt();
+        this.oldActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+        this.newActivityInfo = ActivityInfo.CREATOR.createFromParcel(in);
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<AppIntentEvent> CREATOR
+            = new Parcelable.Creator<AppIntentEvent>() {
+        public AppIntentEvent createFromParcel(Parcel in) {
+            return new AppIntentEvent(in);
+        }
+
+        public AppIntentEvent[] newArray(int size) {
+            return new AppIntentEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
new file mode 100644
index 0000000..34aedd7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/CheckHelpers.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+/**
+ * Convenience short-hand to throw {@link IllegalAccessException} when the arguments
+ * are out-of-range.
+ */
+public class CheckHelpers {
+    /** @throws IllegalAccessException if {@param type} is not in {@code [0..maxValue]} */
+    public static void checkTypeInRange(int type, int maxValue) {
+        if (type < 0) {
+            throw new IllegalArgumentException(
+                    String.format("type must be non-negative (value=%d)", type));
+        }
+        if (type > maxValue) {
+            throw new IllegalArgumentException(
+                    String.format("type out of range (value=%d, max=%d)", type, maxValue));
+        }
+    }
+
+    /** @throws IllegalAccessException if {@param state} is not in {@code [0..maxValue]} */
+    public static void checkStateInRange(int state, int maxValue) {
+        if (state < 0) {
+            throw new IllegalArgumentException(
+                    String.format("state must be non-negative (value=%d)", state));
+        }
+        if (state > maxValue) {
+            throw new IllegalArgumentException(
+                    String.format("state out of range (value=%d, max=%d)", state, maxValue));
+        }
+    }
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
new file mode 100644
index 0000000..aa4eea7
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/PackageEvent.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.annotation.NonNull;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.net.Uri;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Forward package manager events to iorapd. <br /><br />
+ *
+ * Knowing when packages are modified by the system are a useful tidbit to help with performance:
+ * for example when a package is replaced, it could be a hint used to invalidate any collected
+ * io profiles used for prefetching or pinning.
+ *
+ * @hide
+ */
+public class PackageEvent implements Parcelable {
+
+    /** @see android.content.Intent#ACTION_PACKAGE_REPLACED */
+    public static final int TYPE_REPLACED = 0;
+    private static final int TYPE_MAX = 0;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_REPLACED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+
+    /** The path that a package is installed in, for example {@code /data/app/.../base.apk}. */
+    public final Uri packageUri;
+    /** The name of the package, for example {@code com.android.calculator}. */
+    public final String packageName;
+
+    @NonNull
+    public static PackageEvent createReplaced(Uri packageUri, String packageName) {
+        return new PackageEvent(TYPE_REPLACED, packageUri, packageName);
+    }
+
+    private PackageEvent(@Type int type, Uri packageUri, String packageName) {
+        this.type = type;
+        this.packageUri = packageUri;
+        this.packageName = packageName;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        Objects.requireNonNull(packageUri, "packageUri");
+        Objects.requireNonNull(packageName, "packageName");
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof PackageEvent) {
+            return equals((PackageEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(PackageEvent other) {
+        return type == other.type &&
+                Objects.equals(packageUri, other.packageUri) &&
+                Objects.equals(packageName, other.packageName);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{packageUri: %s, packageName: %s}", packageUri, packageName);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        packageUri.writeToParcel(out, flags);
+        out.writeString(packageName);
+    }
+
+    private PackageEvent(Parcel in) {
+        this.type = in.readInt();
+        this.packageUri = Uri.CREATOR.createFromParcel(in);
+        this.packageName = in.readString();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<PackageEvent> CREATOR
+            = new Parcelable.Creator<PackageEvent>() {
+        public PackageEvent createFromParcel(Parcel in) {
+            return new PackageEvent(in);
+        }
+
+        public PackageEvent[] newArray(int size) {
+            return new PackageEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/RequestId.java b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
new file mode 100644
index 0000000..2c79319
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/RequestId.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.NonNull;
+
+/**
+ * Uniquely identify an {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * used for asynchronous callbacks by the server. <br /><br />
+ *
+ * As all system server binder calls must be {@code oneway}, this means all invocations
+ * into {@link com.google.android.startop.iorap.IIorap} are non-blocking. The request ID
+ * exists to associate all calls with their respective callbacks in
+ * {@link com.google.android.startop.iorap.ITaskListener}.
+ *
+ * @see com.google.android.startop.iorap.IIorap
+ *
+ * @hide
+ */
+public class RequestId implements Parcelable {
+
+    public final long requestId;
+
+    private static Object mLock = new Object();
+    private static long mNextRequestId = 0;
+
+    /**
+     * Create a monotonically increasing request ID.<br /><br />
+     *
+     * It is invalid to re-use the same request ID for multiple method calls on
+     * {@link com.google.android.startop.iorap.IIorap}; a new request ID must be created
+     * each time.
+     */
+    @NonNull public static RequestId nextValueForSequence() {
+        long currentRequestId;
+        synchronized (mLock) {
+            currentRequestId = mNextRequestId;
+            ++mNextRequestId;
+        }
+        return new RequestId(currentRequestId);
+    }
+
+    private RequestId(long requestId) {
+        this.requestId = requestId;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        if (requestId < 0) {
+            throw new IllegalArgumentException("request id must be non-negative");
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{requestId: %ld}", requestId);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof RequestId) {
+            return equals((RequestId) other);
+        }
+        return false;
+    }
+
+    private boolean equals(RequestId other) {
+        return requestId == other.requestId;
+    }
+
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(requestId);
+    }
+
+    private RequestId(Parcel in) {
+        requestId = in.readLong();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<RequestId> CREATOR
+            = new Parcelable.Creator<RequestId>() {
+        public RequestId createFromParcel(Parcel in) {
+            return new RequestId(in);
+        }
+
+        public RequestId[] newArray(int size) {
+            return new RequestId[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
new file mode 100644
index 0000000..75d47f9
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceEvent.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward system service events to iorapd.
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceEvent implements Parcelable {
+
+    /** @see com.android.server.SystemService#onBootPhase */
+    public static final int TYPE_BOOT_PHASE = 0;
+    /** @see com.android.server.SystemService#onStart */
+    public static final int TYPE_START = 1;
+    private static final int TYPE_MAX = TYPE_START;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_BOOT_PHASE,
+            TYPE_START,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+
+    // TODO: do we want to pass the exact build phase enum?
+
+    public SystemServiceEvent(@Type int type) {
+        this.type = type;
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{type: %d}", type);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof SystemServiceEvent) {
+            return equals((SystemServiceEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(SystemServiceEvent other) {
+        return type == other.type;
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+    }
+
+    private SystemServiceEvent(Parcel in) {
+        this.type = in.readInt();
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<SystemServiceEvent> CREATOR
+            = new Parcelable.Creator<SystemServiceEvent>() {
+        public SystemServiceEvent createFromParcel(Parcel in) {
+            return new SystemServiceEvent(in);
+        }
+
+        public SystemServiceEvent[] newArray(int size) {
+            return new SystemServiceEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
new file mode 100644
index 0000000..b77c03c
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/SystemServiceUserEvent.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Forward user events to iorapd.<br /><br />
+ *
+ * Knowledge of the logged-in user is reserved to be used to set-up appropriate policies
+ * by iorapd (e.g. to handle user default pinned applications changing).
+ *
+ * @see com.android.server.SystemService
+ *
+ * @hide
+ */
+public class SystemServiceUserEvent implements Parcelable {
+
+    /** @see com.android.server.SystemService#onStartUser */
+    public static final int TYPE_START_USER = 0;
+    /** @see com.android.server.SystemService#onUnlockUser */
+    public static final int TYPE_UNLOCK_USER = 1;
+    /** @see com.android.server.SystemService#onSwitchUser*/
+    public static final int TYPE_SWITCH_USER = 2;
+    /** @see com.android.server.SystemService#onStopUser */
+    public static final int TYPE_STOP_USER = 3;
+    /** @see com.android.server.SystemService#onCleanupUser */
+    public static final int TYPE_CLEANUP_USER = 4;
+    private static final int TYPE_MAX = TYPE_CLEANUP_USER;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_START_USER,
+            TYPE_UNLOCK_USER,
+            TYPE_SWITCH_USER,
+            TYPE_STOP_USER,
+            TYPE_CLEANUP_USER,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Type {}
+
+    @Type public final int type;
+    public final int userHandle;
+
+    public SystemServiceUserEvent(@Type int type, int userHandle) {
+        this.type = type;
+        this.userHandle = userHandle;
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkTypeInRange(type, TYPE_MAX);
+        if (userHandle < 0) {
+            throw new IllegalArgumentException("userHandle must be non-negative");
+        }
+    }
+
+    @Override
+    public String toString() {
+        return String.format("{type: %d, userHandle: %d}", type, userHandle);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof SystemServiceUserEvent) {
+            return equals((SystemServiceUserEvent) other);
+        }
+        return false;
+    }
+
+    private boolean equals(SystemServiceUserEvent other) {
+        return type == other.type &&
+                userHandle == other.userHandle;
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(type);
+        out.writeInt(userHandle);
+    }
+
+    private SystemServiceUserEvent(Parcel in) {
+        this.type = in.readInt();
+        this.userHandle = in.readInt();
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<SystemServiceUserEvent> CREATOR
+            = new Parcelable.Creator<SystemServiceUserEvent>() {
+        public SystemServiceUserEvent createFromParcel(Parcel in) {
+            return new SystemServiceUserEvent(in);
+        }
+
+        public SystemServiceUserEvent[] newArray(int size) {
+            return new SystemServiceUserEvent[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
new file mode 100644
index 0000000..b5fd6d8
--- /dev/null
+++ b/startop/iorap/src/com/google/android/startop/iorap/TaskResult.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Result data accompanying a request for {@link com.google.android.startop.iorap.ITaskListener}
+ * callbacks.<br /><br />
+ *
+ * Following {@link com.google.android.startop.iorap.IIorap} method invocation,
+ * iorapd will issue in-order callbacks for that corresponding {@link RequestId}.<br /><br />
+ *
+ * State transitions are as follows: <br /><br />
+ *
+ * <pre>
+ *          ┌─────────────────────────────┐
+ *          │                             ▼
+ *        ┌───────┐     ┌─────────┐     ╔═══════════╗
+ *    ──▶ │ BEGAN │ ──▶ │ ONGOING │ ──▶ ║ COMPLETED ║
+ *        └───────┘     └─────────┘     ╚═══════════╝
+ *          │             │
+ *          │             │
+ *          ▼             │
+ *        ╔═══════╗       │
+ *    ──▶ ║ ERROR ║ ◀─────┘
+ *        ╚═══════╝
+ *
+ * </pre> <!-- system/iorap/docs/binder/TaskResult.dot -->
+ *
+ * @hide
+ */
+public class TaskResult implements Parcelable {
+
+    public static final int STATE_BEGAN = 0;
+    public static final int STATE_ONGOING = 1;
+    public static final int STATE_COMPLETED = 2;
+    public static final int STATE_ERROR = 3;
+    private static final int STATE_MAX = STATE_ERROR;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "STATE_" }, value = {
+            STATE_BEGAN,
+            STATE_ONGOING,
+            STATE_COMPLETED,
+            STATE_ERROR,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface State {}
+
+    @State public final int state;
+
+    @Override
+    public String toString() {
+        return String.format("{state: %d}", state);
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        } else if (other instanceof TaskResult) {
+            return equals((TaskResult) other);
+        }
+        return false;
+    }
+
+    private boolean equals(TaskResult other) {
+        return state == other.state;
+    }
+
+    public TaskResult(@State int state) {
+        this.state = state;
+
+        checkConstructorArguments();
+    }
+
+    private void checkConstructorArguments() {
+        CheckHelpers.checkStateInRange(state, STATE_MAX);
+    }
+
+    //<editor-fold desc="Binder boilerplate">
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(state);
+    }
+
+    private TaskResult(Parcel in) {
+        state = in.readInt();
+
+        checkConstructorArguments();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Parcelable.Creator<TaskResult> CREATOR
+            = new Parcelable.Creator<TaskResult>() {
+        public TaskResult createFromParcel(Parcel in) {
+            return new TaskResult(in);
+        }
+
+        public TaskResult[] newArray(int size) {
+            return new TaskResult[size];
+        }
+    };
+    //</editor-fold>
+}
diff --git a/startop/iorap/tests/Android.bp b/startop/iorap/tests/Android.bp
new file mode 100644
index 0000000..7605784
--- /dev/null
+++ b/startop/iorap/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright (C) 2018 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.
+
+// TODO: once b/80095087 is fixed, rewrite this back to android_test
+java_library {
+    name: "libiorap-java-test-lib",
+    srcs: ["src/**/*.kt"],
+
+    static_libs: [
+      // non-test dependencies
+      "libiorap-java",
+      // test android dependencies
+      "platform-test-annotations",
+      "android-support-test",
+      // test framework dependencies
+      "mockito-target-inline-minus-junit4",
+      // "mockito-target-minus-junit4",
+        // Mockito also requires JNI (see Android.mk)
+        // and android:debuggable=true (see AndroidManifest.xml)
+      "truth-prebuilt",
+    ],
+
+    // sdk_version: "current",
+    // certificate: "platform",
+
+    libs: ["android.test.base", "android.test.runner"],
+
+    // test_suites: ["device-tests"],
+}
diff --git a/startop/iorap/tests/Android.mk b/startop/iorap/tests/Android.mk
new file mode 100644
index 0000000..1b2aa46
--- /dev/null
+++ b/startop/iorap/tests/Android.mk
@@ -0,0 +1,46 @@
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# android_test does not support JNI libraries
+# TODO: once b/80095087 is fixed, rewrite this back to android_test
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
+
+LOCAL_PACKAGE_NAME := libiorap-java-tests
+LOCAL_COMPATIBILITY_SUITE := device-tests
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    libiorap-java-test-lib
+
+LOCAL_MULTILIB := both
+
+LOCAL_JNI_SHARED_LIBRARIES := \
+    libdexmakerjvmtiagent \
+    libstaticjvmtiagent \
+    libmultiplejvmtiagentsinterferenceagent
+
+LOCAL_JAVA_LIBRARIES := \
+    android.test.base \
+    android.test.runner
+
+# Use private APIs
+LOCAL_CERTIFICATE := platform
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
diff --git a/startop/iorap/tests/AndroidManifest.xml b/startop/iorap/tests/AndroidManifest.xml
new file mode 100644
index 0000000..99f4add
--- /dev/null
+++ b/startop/iorap/tests/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+<!--suppress AndroidUnknownAttribute -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.startop.iorap.tests"
+    android:sharedUserId="com.google.android.startop.iorap.tests"
+    android:versionCode="1"
+    android:versionName="1.0" >
+
+    <!--suppress AndroidDomInspection -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.google.android.startop.iorap.tests" />
+
+      <!--
+       'debuggable=true' is required to properly load mockito jvmti dependencies,
+         otherwise it gives the following error at runtime:
+
+       Openjdkjvmti plugin was loaded on a non-debuggable Runtime.
+       Plugin was loaded too late to change runtime state to DEBUGGABLE. -->
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+    </application>
+</manifest>
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
new file mode 100644
index 0000000..4ba44a9
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/IIorapIntegrationTest.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.ServiceManager
+import android.support.test.filters.MediumTest
+import org.junit.Test
+import org.junit.Ignore
+import org.mockito.Mockito.*
+
+// @Ignore("Test is disabled until iorapd is added to init and there's selinux policies for it")
+@MediumTest
+class IIorapIntegrationTest {
+    /**
+     * @throws ServiceManager.ServiceNotFoundException if iorapd service could not be found
+     */
+    private val iorapService : IIorap by lazy {
+        // TODO: connect to 'iorapd.stub' which doesn't actually do any work other than reply.
+        IIorap.Stub.asInterface(ServiceManager.getServiceOrThrow("iorapd"))
+
+        // Use 'adb shell setenforce 0' otherwise this whole test fails,
+        // because the servicemanager is not allowed to hand out the binder token for iorapd.
+
+        // TODO: implement the selinux policies for iorapd.
+    }
+
+    // A dummy binder stub implementation is required to use with mockito#spy.
+    // Mockito overrides the methods at runtime and tracks how methods were invoked.
+    open class DummyTaskListener : ITaskListener.Stub()  {
+        // Note: make parameters nullable to avoid the kotlin IllegalStateExceptions
+        // from using the mockito matchers (eq, argThat, etc).
+        override fun onProgress(requestId: RequestId?, result: TaskResult?) {
+        }
+
+        override fun onComplete(requestId: RequestId?, result: TaskResult?) {
+        }
+    }
+
+    private fun testAnyMethod(func : (RequestId) -> Unit) {
+        val taskListener = spy(DummyTaskListener())!!
+
+        try {
+            iorapService.setTaskListener(taskListener)
+            // Note: Binder guarantees total order for oneway messages sent to the same binder
+            // interface, so we don't need any additional blocking here before sending later calls.
+
+            // Every new method call should have a unique request id.
+            val requestId = RequestId.nextValueForSequence()!!
+
+            // Apply the specific function under test.
+            func(requestId)
+
+            // Typical mockito behavior is to allow any-order callbacks, but we want to test order.
+            val inOrder = inOrder(taskListener)
+
+            // The "stub" behavior of iorapd is that every request immediately gets a response of
+            //   BEGAN,ONGOING,COMPLETED
+            inOrder.verify(taskListener, timeout(100)).
+                  onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_BEGAN })
+            inOrder.verify(taskListener, timeout(100)).
+                  onProgress(eq(requestId), argThat { it!!.state == TaskResult.STATE_ONGOING })
+            inOrder.verify(taskListener, timeout(100)).
+                  onComplete(eq(requestId), argThat { it!!.state == TaskResult.STATE_COMPLETED })
+            inOrder.verifyNoMoreInteractions()
+
+        } finally {
+            iorapService.setTaskListener(null)
+        }
+    }
+
+    @Test
+    fun testOnPackageEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onPackageEvent(requestId,
+                    PackageEvent.createReplaced(
+                            Uri.parse("https://www.google.com"), "com.fake.package"))
+        }
+    }
+
+    @Test
+    fun testOnAppIntentEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onAppIntentEvent(requestId, AppIntentEvent.createDefaultIntentChanged(
+                    ActivityInfo("dont care", "dont care"),
+                    ActivityInfo("dont care 2", "dont care 2")))
+        }
+    }
+
+    @Test
+    fun testOnSystemServiceEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onSystemServiceEvent(requestId,
+                    SystemServiceEvent(SystemServiceEvent.TYPE_START))
+        }
+    }
+
+    @Test
+    fun testOnSystemServiceUserEvent() {
+        testAnyMethod { requestId : RequestId ->
+            iorapService.onSystemServiceUserEvent(requestId,
+                    SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER,0))
+        }
+    }
+}
diff --git a/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
new file mode 100644
index 0000000..4abbb3e
--- /dev/null
+++ b/startop/iorap/tests/src/com/google/android/startop/iorap/ParcelablesTest.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 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.google.android.startop.iorap
+
+import android.net.Uri
+import android.os.Parcel
+import android.os.Parcelable
+import android.support.test.filters.SmallTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import com.google.common.truth.Truth.assertThat
+import org.junit.runners.Parameterized
+
+/**
+ * Basic unit tests to ensure that all of the [Parcelable]s in [com.google.android.startop.iorap]
+ * have a valid-conforming interface implementation.
+ */
+@SmallTest
+@RunWith(Parameterized::class)
+class ParcelablesTest<T : Parcelable>(private val inputData : InputData<T>) {
+    companion object {
+        private val initialRequestId = RequestId.nextValueForSequence()!!
+
+        @JvmStatic
+        @Parameterized.Parameters
+        fun data() = listOf(
+                InputData(
+                        newActivityInfo(),
+                        newActivityInfo(),
+                        ActivityInfo("some package", "some other activity")),
+                InputData(
+                        ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+                        ActivityHintEvent(ActivityHintEvent.TYPE_COMPLETED, newActivityInfo()),
+                        ActivityHintEvent(ActivityHintEvent.TYPE_POST_COMPLETED,
+                                newActivityInfo())),
+                InputData(
+                        AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+                                newActivityInfoOther()),
+                        AppIntentEvent.createDefaultIntentChanged(newActivityInfo(),
+                                newActivityInfoOther()),
+                        AppIntentEvent.createDefaultIntentChanged(newActivityInfoOther(),
+                                newActivityInfo())),
+                InputData(
+                        PackageEvent.createReplaced(newUri(), "some package"),
+                        PackageEvent.createReplaced(newUri(), "some package"),
+                        PackageEvent.createReplaced(newUri(), "some other package")
+                ),
+                InputData(initialRequestId, cloneRequestId(initialRequestId),
+                        RequestId.nextValueForSequence()),
+                InputData(
+                        SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+                        SystemServiceEvent(SystemServiceEvent.TYPE_BOOT_PHASE),
+                        SystemServiceEvent(SystemServiceEvent.TYPE_START)),
+                InputData(
+                        SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+                        SystemServiceUserEvent(SystemServiceUserEvent.TYPE_START_USER, 12345),
+                        SystemServiceUserEvent(SystemServiceUserEvent.TYPE_CLEANUP_USER, 12345)),
+                InputData(
+                        TaskResult(TaskResult.STATE_COMPLETED),
+                        TaskResult(TaskResult.STATE_COMPLETED),
+                        TaskResult(TaskResult.STATE_ONGOING))
+        )
+
+        private fun newActivityInfo() : ActivityInfo {
+            return ActivityInfo("some package", "some activity")
+        }
+
+        private fun newActivityInfoOther() : ActivityInfo {
+            return ActivityInfo("some package 2", "some activity 2")
+        }
+
+        private fun newUri() : Uri {
+            return Uri.parse("https://www.google.com")
+        }
+
+        private fun cloneRequestId(requestId: RequestId) : RequestId {
+            val constructor = requestId::class.java.declaredConstructors[0]
+            constructor.isAccessible = true
+            return constructor.newInstance(requestId.requestId) as RequestId
+        }
+    }
+
+    /**
+     * Test for [Object.equals] implementation.
+     */
+    @Test
+    fun testEquality() {
+        assertThat(inputData.valid).isEqualTo(inputData.valid)
+        assertThat(inputData.valid).isEqualTo(inputData.validCopy)
+        assertThat(inputData.valid).isNotEqualTo(inputData.validOther)
+    }
+
+    /**
+     * Test for [Parcelable] implementation.
+     */
+    @Test
+    fun testParcelRoundTrip() {
+        // calling writeToParcel and then T::CREATOR.createFromParcel would return the same data.
+        val assertParcels = { it : T, data : InputData<T> ->
+            val parcel = Parcel.obtain()
+            it.writeToParcel(parcel, 0)
+            parcel.setDataPosition(0) // future reads will see all previous writes.
+            assertThat(it).isEqualTo(data.createFromParcel(parcel))
+            parcel.recycle()
+        }
+
+        assertParcels(inputData.valid, inputData)
+        assertParcels(inputData.validCopy, inputData)
+        assertParcels(inputData.validOther, inputData)
+    }
+
+    data class InputData<T : Parcelable>(val valid : T, val validCopy : T, val validOther : T) {
+        val kls = valid.javaClass
+        init {
+            assertThat(valid).isNotSameAs(validCopy)
+            // Don't use isInstanceOf because of phantom warnings in intellij about Class!
+            assertThat(validCopy.javaClass).isEqualTo(valid.javaClass)
+            assertThat(validOther.javaClass).isEqualTo(valid.javaClass)
+        }
+
+        fun createFromParcel(parcel : Parcel) : T {
+            val field  = kls.getDeclaredField("CREATOR")
+            val creator = field.get(null) as Parcelable.Creator<T>
+
+            return creator.createFromParcel(parcel)
+        }
+    }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 8c37a21..d33a537 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1905,6 +1905,22 @@
         return false;
     }
 
+    /**
+     * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
+     * @param intent The {@link Intent#ACTION_CALL} intent to handle.
+     * @hide
+     */
+    public void handleCallIntent(Intent intent) {
+        try {
+            if (isServiceConnected()) {
+                getTelecomService().handleCallIntent(intent);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException handleCallIntent: " + e);
+        }
+
+    }
+
     private ITelecomService getTelecomService() {
         if (mTelecomServiceOverride != null) {
             return mTelecomServiceOverride;
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 38247bc..df7d683 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -284,4 +284,9 @@
      * @see TelecomServiceImpl#isInEmergencyCall
      */
     boolean isInEmergencyCall();
+
+    /**
+     * @see TelecomServiceImpl#handleCallIntent
+     */
+    void handleCallIntent(in Intent intent);
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 57b652e..9b5b700 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -1633,11 +1633,21 @@
      * When {@code false}, use default title for Enhanced 4G LTE Mode settings.
      * When {@code true}, use the variant.
      * @hide
+     * @deprecated use {@link #KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT}.
      */
+    @Deprecated
     public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL =
             "enhanced_4g_lte_title_variant_bool";
 
     /**
+     * The index indicates the carrier specified title string of Enahnce 4G LTE Mode settings.
+     * Default value is 0, which indicates the default title string.
+     * @hide
+     */
+    public static final String KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT =
+            "enhanced_4g_lte_title_variant_int";
+
+    /**
      * Indicates whether the carrier wants to notify the user when handover of an LTE video call to
      * WIFI fails.
      * <p>
@@ -2419,6 +2429,7 @@
 
         sDefaults.putStringArray(KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL, false);
+        sDefaults.putInt(KEY_ENHANCED_4G_LTE_TITLE_VARIANT_INT, 0);
         sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
         sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
         sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
diff --git a/telephony/java/android/telephony/DisconnectCause.java b/telephony/java/android/telephony/DisconnectCause.java
index ee5cdc2..d7169b2 100644
--- a/telephony/java/android/telephony/DisconnectCause.java
+++ b/telephony/java/android/telephony/DisconnectCause.java
@@ -319,6 +319,29 @@
      */
     public static final int IMS_SIP_ALTERNATE_EMERGENCY_CALL = 71;
 
+    /**
+     * Indicates that a new outgoing call cannot be placed because there is already an outgoing
+     * call dialing out.
+     */
+    public static final int ALREADY_DIALING = 72;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed while there is a ringing call.
+     */
+    public static final int CANT_CALL_WHILE_RINGING = 73;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because calling has been disabled using
+     * the ro.telephony.disable-call system property.
+     */
+    public static final int CALLING_DISABLED = 74;
+
+    /**
+     * Indicates that a new outgoing call cannot be placed because there is currently an ongoing
+     * foreground and background call.
+     */
+    public static final int TOO_MANY_ONGOING_CALLS = 75;
+
     //*********************************************************************************************
     // When adding a disconnect type:
     // 1) Update toString() with the newly added disconnect type.
@@ -474,6 +497,14 @@
             return "NORMAL_UNSPECIFIED";
         case IMS_SIP_ALTERNATE_EMERGENCY_CALL:
             return "IMS_SIP_ALTERNATE_EMERGENCY_CALL";
+        case ALREADY_DIALING:
+            return "ALREADY_DIALING";
+        case CANT_CALL_WHILE_RINGING:
+            return "CANT_CALL_WHILE_RINGING";
+        case CALLING_DISABLED:
+            return "CALLING_DISABLED";
+        case TOO_MANY_ONGOING_CALLS:
+            return "TOO_MANY_ONGOING_CALLS";
         default:
             return "INVALID: " + cause;
         }
diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java
index 5e4518f..79298fd 100644
--- a/telephony/java/android/telephony/NeighboringCellInfo.java
+++ b/telephony/java/android/telephony/NeighboringCellInfo.java
@@ -33,8 +33,9 @@
  * Represents the neighboring cell information, including
  * Received Signal Strength and Cell ID location.
  *
- * @deprecated This class should not be used by anyone targeting SDK level 29 (Q) or higher.
- *      Instead callers should use {@Link android.telephony.CellInfo}.
+ * @deprecated This class should not be used by any app targeting
+ *     {@link Build.VERSION_CODES.Q Android Q} or higher. Instead callers should use
+ *     {@Link android.telephony.CellInfo CellInfo}.
  */
 @Deprecated
 public class NeighboringCellInfo implements Parcelable
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index f2b73dc..7469186 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -24,6 +24,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.telephony.AccessNetworkConstants.AccessNetworkType;
+import android.telephony.NetworkRegistrationState.Domain;
 import android.text.TextUtils;
 
 import java.lang.annotation.Retention;
@@ -1595,7 +1596,7 @@
     /**
      * Get all of the available network registration states.
      *
-     * @return List of registration states
+     * @return List of {@link NetworkRegistrationState}
      * @hide
      */
     @SystemApi
@@ -1606,14 +1607,30 @@
     }
 
     /**
-     * Get the network registration states with given transport type.
+     * Get the network registration states for the transport type.
      *
-     * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
-     * @return List of registration states.
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return List of {@link NetworkRegistrationState}
+     * @hide
+     *
+     * @deprecated Use {@link #getNetworkRegistrationStatesFromTransportType(int)}
+     */
+    @Deprecated
+    @SystemApi
+    public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+        return getNetworkRegistrationStatesForTransportType(transportType);
+    }
+
+    /**
+     * Get the network registration states for the transport type.
+     *
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return List of {@link NetworkRegistrationState}
      * @hide
      */
     @SystemApi
-    public List<NetworkRegistrationState> getNetworkRegistrationStates(int transportType) {
+    public List<NetworkRegistrationState> getNetworkRegistrationStatesForTransportType(
+            int transportType) {
         List<NetworkRegistrationState> list = new ArrayList<>();
 
         synchronized (mNetworkRegistrationStates) {
@@ -1628,16 +1645,57 @@
     }
 
     /**
-     * Get the network registration states with given transport type and domain.
+     * Get the network registration states for the network domain.
      *
-     * @param domain The network domain. Must be {@link NetworkRegistrationState#DOMAIN_CS} or
-     * {@link NetworkRegistrationState#DOMAIN_PS}.
-     * @param transportType The transport type. See {@link AccessNetworkConstants.TransportType}
-     * @return The matching NetworkRegistrationState.
+     * @param domain The network {@link NetworkRegistrationState.Domain domain}
+     * @return List of {@link NetworkRegistrationState}
      * @hide
      */
     @SystemApi
-    public NetworkRegistrationState getNetworkRegistrationStates(int domain, int transportType) {
+    public List<NetworkRegistrationState> getNetworkRegistrationStatesForDomain(
+            @Domain int domain) {
+        List<NetworkRegistrationState> list = new ArrayList<>();
+
+        synchronized (mNetworkRegistrationStates) {
+            for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
+                if (networkRegistrationState.getDomain() == domain) {
+                    list.add(networkRegistrationState);
+                }
+            }
+        }
+
+        return list;
+    }
+
+    /**
+     * Get the network registration state for the transport type and network domain.
+     *
+     * @param domain The network {@link NetworkRegistrationState.Domain domain}
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return The matching {@link NetworkRegistrationState}
+     * @hide
+     *
+     * @deprecated Use {@link #getNetworkRegistrationState(int, int)}
+     */
+    @Deprecated
+    @SystemApi
+    public NetworkRegistrationState getNetworkRegistrationStates(@Domain int domain,
+                                                                 int transportType) {
+        return getNetworkRegistrationState(domain, transportType);
+    }
+
+    /**
+     * Get the network registration state for the transport type and network domain.
+     *
+     * @param domain The network {@link NetworkRegistrationState.Domain domain}
+     * @param transportType The {@link AccessNetworkConstants.TransportType transport type}
+     * @return The matching {@link NetworkRegistrationState}
+     * @hide
+     *
+     */
+    @SystemApi
+    public NetworkRegistrationState getNetworkRegistrationState(@Domain int domain,
+                                                                int transportType) {
         synchronized (mNetworkRegistrationStates) {
             for (NetworkRegistrationState networkRegistrationState : mNetworkRegistrationStates) {
                 if (networkRegistrationState.getTransportType() == transportType
@@ -1669,5 +1727,4 @@
             mNetworkRegistrationStates.add(regState);
         }
     }
-
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fdf8231..52c8f5a 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -167,7 +167,6 @@
     /** @hide */
     static public final int OTASP_SIM_UNPROVISIONED = 5;
 
-
     /** @hide */
     static public final int KEY_TYPE_EPDG = 1;
 
@@ -2924,7 +2923,7 @@
      * of time the mode may be unknown.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
      * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
@@ -5929,7 +5928,7 @@
      * Sets the network selection mode to automatic.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -5954,7 +5953,7 @@
      * Perform a radio scan and return the list of available networks.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p> Note that this scan can take a long time (sometimes minutes) to happen.
      *
@@ -6033,7 +6032,7 @@
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -6058,7 +6057,7 @@
      * Ask the radio to connect to the input network and change selection mode to manual.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission:
      * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
@@ -6091,7 +6090,7 @@
      * Get the network selection mode.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
 
      * @return the network selection mode.
      *
@@ -6981,7 +6980,8 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                isDataRoamingEnabled = telephony.isDataRoamingEnabled(getSubId());
+                isDataRoamingEnabled = telephony.isDataRoamingEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()));
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#isDataRoamingEnabled", e);
@@ -6993,7 +6993,7 @@
      * Gets the roaming mode for CDMA phone.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @return one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT}, {@link #CDMA_ROAMING_MODE_HOME},
      * {@link #CDMA_ROAMING_MODE_AFFILIATED}, {@link #CDMA_ROAMING_MODE_ANY}.
@@ -7018,7 +7018,7 @@
      * Sets the roaming mode for CDMA phone to the given mode {@code mode}.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @param mode should be one of {@link #CDMA_ROAMING_MODE_RADIO_DEFAULT},
      * {@link #CDMA_ROAMING_MODE_HOME}, {@link #CDMA_ROAMING_MODE_AFFILIATED},
@@ -7087,7 +7087,8 @@
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                telephony.setDataRoamingEnabled(getSubId(), isEnabled);
+                telephony.setDataRoamingEnabled(
+                        getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), isEnabled);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Error calling ITelephony#setDataRoamingEnabled", e);
@@ -7880,7 +7881,7 @@
      * Returns the current {@link ServiceState} information.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -8349,7 +8350,7 @@
      * Checks if phone is in emergency callback mode.
      *
      * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
-     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultDataSubscriptionId()}
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
      *
      * @return true if phone is in emergency callback mode.
      * @hide
@@ -8380,6 +8381,29 @@
     }
 
     /**
+     * Checks if manual network selection is allowed.
+     *
+     * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+     * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
+     *
+     * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+     *
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+    public boolean isManualNetworkSelectionAllowed() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.isManualNetworkSelectionAllowed(getSubId());
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#isManualNetworkSelectionAllowed", e);
+        }
+        return true;
+    }
+
+    /**
      * Get the most recently available signal strength information.
      *
      * Get the most recent SignalStrength information reported by the modem. Due
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index c2c93da..8379f8c 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1206,7 +1206,8 @@
 
     /** @hide */
     public static int getMvnoTypeIntFromString(String mvnoType) {
-        Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoType);
+        String mvnoTypeString = TextUtils.isEmpty(mvnoType) ? mvnoType : mvnoType.toLowerCase();
+        Integer mvnoTypeInt = MVNO_TYPE_STRING_MAP.get(mvnoTypeString);
         return  mvnoTypeInt == null ? UNSPECIFIED_INT : mvnoTypeInt;
     }
 
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.aidl b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl
new file mode 100644
index 0000000..bfb0a59
--- /dev/null
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, 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.telephony.emergency;
+
+parcelable EmergencyNumber;
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
new file mode 100644
index 0000000..d6a08543
--- /dev/null
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2018 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.telephony.emergency;
+
+import android.annotation.IntDef;
+import android.hardware.radio.V1_3.EmergencyNumberSource;
+import android.hardware.radio.V1_3.EmergencyServiceCategory;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A parcelable class that wraps and retrieves the information of number, service category(s) and
+ * country code for a specific emergency number.
+ */
+public final class EmergencyNumber implements Parcelable {
+
+    private static final String LOG_TAG = "EmergencyNumber";
+
+    /**
+     * Defining Emergency Service Category as follows:
+     *  - General emergency call, all categories;
+     *  - Police;
+     *  - Ambulance;
+     *  - Fire Brigade;
+     *  - Marine Guard;
+     *  - Mountain Rescue;
+     *  - Manually Initiated eCall (MIeC);
+     *  - Automatically Initiated eCall (AIeC);
+     *
+     * Category UNSPECIFIED (General emergency call, all categories) indicates that no specific
+     * services are associated with this emergency number; if the emergency number is specified,
+     * it has one or more defined emergency service categories.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "EMERGENCY_SERVICE_CATEGORY_" }, value = {
+            EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED,
+            EMERGENCY_SERVICE_CATEGORY_POLICE,
+            EMERGENCY_SERVICE_CATEGORY_AMBULANCE,
+            EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE,
+            EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD,
+            EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE,
+            EMERGENCY_SERVICE_CATEGORY_MIEC,
+            EMERGENCY_SERVICE_CATEGORY_AIEC
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyServiceCategories {}
+
+    /**
+     * Emergency Service Category UNSPECIFIED (General emergency call, all categories) bit-field
+     * indicates that no specific services are associated with this emergency number; if the
+     * emergency number is specified, it has one or more defined emergency service categories.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED =
+            EmergencyServiceCategory.UNSPECIFIED;
+    /**
+     * Bit-field that indicates Emergency Service Category for Police.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_POLICE = EmergencyServiceCategory.POLICE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Ambulance.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_AMBULANCE =
+            EmergencyServiceCategory.AMBULANCE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Fire Brigade.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE =
+            EmergencyServiceCategory.FIRE_BRIGADE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Marine Guard.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD =
+            EmergencyServiceCategory.MARINE_GUARD;
+    /**
+     * Bit-field that indicates Emergency Service Category for Mountain Rescue.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE =
+            EmergencyServiceCategory.MOUNTAIN_RESCUE;
+    /**
+     * Bit-field that indicates Emergency Service Category for Manually Initiated eCall (MIeC)
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_MIEC = EmergencyServiceCategory.MIEC;
+    /**
+     * Bit-field that indicates Emergency Service Category for Automatically Initiated eCall (AIeC)
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_SERVICE_CATEGORY_AIEC = EmergencyServiceCategory.AIEC;
+
+    private static final Set<Integer> EMERGENCY_SERVICE_CATEGORY_SET;
+    static {
+        EMERGENCY_SERVICE_CATEGORY_SET = new HashSet<Integer>();
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_POLICE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AMBULANCE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_MIEC);
+        EMERGENCY_SERVICE_CATEGORY_SET.add(EMERGENCY_SERVICE_CATEGORY_AIEC);
+    }
+
+    /**
+     * The source to tell where the corresponding @1.3::EmergencyNumber comes from.
+     *
+     * The emergency number has one or more defined emergency number sources.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     *
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
+            EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
+            EMERGENCY_NUMBER_SOURCE_SIM,
+            EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
+            EMERGENCY_NUMBER_SOURCE_DEFAULT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface EmergencyNumberSources {}
+
+    /**
+     * Bit-field which indicates the number is from the network signaling.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING =
+            EmergencyNumberSource.NETWORK_SIGNALING;
+    /**
+     * Bit-field which indicates the number is from the sim.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
+    /** Bit-field which indicates the number is from the modem config. */
+    public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
+            EmergencyNumberSource.MODEM_CONFIG;
+    /**
+     * Bit-field which indicates the number is available as default.
+     *
+     * 112, 911 must always be available; additionally, 000, 08, 110, 999, 118 and 119 must be
+     * available when sim is not present.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = EmergencyNumberSource.DEFAULT;
+
+    private static final Set<Integer> EMERGENCY_NUMBER_SOURCE_SET;
+    static {
+        EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
+    }
+
+    private final String mNumber;
+    private final String mCountryIso;
+    private final int mEmergencyServiceCategoryBitmask;
+    private final int mEmergencyNumberSourceBitmask;
+
+    /** @hide */
+    public EmergencyNumber(String number, String countryIso,
+                           int emergencyServiceCategories,
+                           int emergencyNumberSources) {
+        this.mNumber = number;
+        this.mCountryIso = countryIso;
+        this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
+        this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
+    }
+
+    /** @hide */
+    public EmergencyNumber(Parcel source) {
+        mNumber = source.readString();
+        mCountryIso = source.readString();
+        mEmergencyServiceCategoryBitmask = source.readInt();
+        mEmergencyNumberSourceBitmask = source.readInt();
+    }
+
+    /**
+     * Get the dialing number of the emergency number.
+     *
+     * The character in the number string is only the dial pad
+     * character('0'-'9', '*', or '#'). For example: 911.
+     *
+     * @return the dialing number.
+     */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /**
+     * Get the country code string (lowercase character) in ISO 3166 format of the emergency number.
+     *
+     * @return the country code string (lowercase character) in ISO 3166 format.
+     */
+    public String getCountryIso() {
+        return mCountryIso;
+    }
+
+    /**
+     * Returns the bitmask of emergency service categories {@link EmergencyServiceCategories} of
+     * the emergency number.
+     *
+     * @return bitmask of the emergency service categories {@link EmergencyServiceCategories}
+     */
+    public @EmergencyServiceCategories int getEmergencyServiceCategoryBitmask() {
+        return mEmergencyServiceCategoryBitmask;
+    }
+
+    /**
+     * Returns the emergency service categories {@link EmergencyServiceCategories} of the emergency
+     * number.
+     *
+     * @return a list of the emergency service categories {@link EmergencyServiceCategories}
+     */
+    public List<Integer> getEmergencyServiceCategories() {
+        List<Integer> categories = new ArrayList<>();
+        if (serviceUnspecified()) {
+            categories.add(EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
+            return categories;
+        }
+        for (Integer category : EMERGENCY_SERVICE_CATEGORY_SET) {
+            if (isInEmergencyServiceCategories(category)) {
+                categories.add(category);
+            }
+        }
+        return categories;
+    }
+
+    /**
+     * Checks if the emergency service category is unspecified for the emergency number
+     * {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}.
+     *
+     * @return {@code true} if the emergency service category is unspecified for the emergency
+     * number {@link #EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED}; {@code false} otherwise.
+     */
+    private boolean serviceUnspecified() {
+        return mEmergencyServiceCategoryBitmask == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
+    }
+
+    /**
+     * Checks if the emergency number is in the specified emergency service category(s)
+     * {@link EmergencyServiceCategories}.
+     *
+     * @return {@code true} if the emergency number is in the specified emergency service
+     * category(s) {@link EmergencyServiceCategories}; {@code false} otherwise.
+     *
+     * @param categories - emergency service categories {@link EmergencyServiceCategories}
+     */
+    public boolean isInEmergencyServiceCategories(@EmergencyServiceCategories int categories) {
+        if (categories == EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED) {
+            return serviceUnspecified();
+        }
+        return (mEmergencyServiceCategoryBitmask & categories) == categories;
+    }
+
+    /**
+     * Returns the bitmask of the sources {@link EmergencyNumberSources} of the emergency number.
+     *
+     * @return bitmask of the emergency number sources {@link EmergencyNumberSources}
+     */
+    public @EmergencyNumberSources int getEmergencyNumberSourceBitmask() {
+        return mEmergencyNumberSourceBitmask;
+    }
+
+    /**
+     * Returns a list of {@link EmergencyNumberSources} of the emergency number.
+     *
+     * @return a list of {@link EmergencyNumberSources}
+     */
+    public List<Integer> getEmergencyNumberSources() {
+        List<Integer> sources = new ArrayList<>();
+        for (Integer source : EMERGENCY_NUMBER_SOURCE_SET) {
+            if ((mEmergencyNumberSourceBitmask & source) == source) {
+                sources.add(source);
+            }
+        }
+        return sources;
+    }
+
+    /**
+     * Checks if the emergency number is from the specified emergency number source(s)
+     * {@link EmergencyNumberSources}.
+     *
+     * @return {@code true} if the emergency number is from the specified emergency number
+     * source(s) {@link EmergencyNumberSources}; {@code false} otherwise.
+     *
+     * @param sources - {@link EmergencyNumberSources}
+     */
+    public boolean isFromSources(@EmergencyNumberSources int sources) {
+        return (mEmergencyNumberSourceBitmask & sources) == sources;
+    }
+
+    @Override
+    /** @hide */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mNumber);
+        dest.writeString(mCountryIso);
+        dest.writeInt(mEmergencyServiceCategoryBitmask);
+        dest.writeInt(mEmergencyNumberSourceBitmask);
+    }
+
+    @Override
+    /** @hide */
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public String toString() {
+        return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso
+                + " / [ServiceCategories]"
+                + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
+                + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (!EmergencyNumber.class.isInstance(o)) {
+            return false;
+        }
+        return (o == this || toString().equals(o.toString()));
+    }
+
+    public static final Parcelable.Creator<EmergencyNumber> CREATOR =
+            new Parcelable.Creator<EmergencyNumber>() {
+        @Override
+        public EmergencyNumber createFromParcel(Parcel in) {
+            return new EmergencyNumber(in);
+        }
+
+        @Override
+        public EmergencyNumber[] newArray(int size) {
+            return new EmergencyNumber[size];
+        }
+    };
+}
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 5d6a8c1..89ef339 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -117,12 +117,14 @@
      * @hide
      */
     public static final String EXTRA_CONFERENCE = "conference";
+
     /**
      * Boolean extra property set on an {@link ImsCallProfile} to indicate that this call is an
      * emergency call.  The {@link ImsService} sets this on a call to indicate that the network has
      * identified the call as an emergency call.
      */
-    public static final String EXTRA_E_CALL = "e_call";
+    public static final String EXTRA_EMERGENCY_CALL = "e_call";
+
     /**
      * @hide
      */
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index a20d4f5..df903cc2 100644
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -16,22 +16,16 @@
 
 package android.telephony.ims;
 
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
 import android.os.Message;
 import android.os.RemoteException;
 import android.telephony.ims.aidl.IImsCallSessionListener;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-
-import android.telephony.ims.stub.ImsCallSessionImplBase;
 import android.util.Log;
 
 import com.android.ims.internal.IImsCallSession;
 import com.android.ims.internal.IImsVideoCallProvider;
 
+import java.util.Objects;
+
 /**
  * Provides the call initiation/termination, and media exchange between two IMS endpoints.
  * It directly communicates with IMS service which implements the IMS protocol behavior.
@@ -42,7 +36,8 @@
     private static final String TAG = "ImsCallSession";
 
     /**
-     * Defines IMS call session state. Please use {@link ImsCallSessionImplBase.State} definition.
+     * Defines IMS call session state. Please use
+     * {@link android.telephony.ims.stub.ImsCallSessionImplBase.State} definition.
      * This is kept around for capability reasons.
      */
     public static class State {
@@ -1027,9 +1022,9 @@
     }
 
     /**
-     * Sends RTT Upgrade request
+     * Sends RTT Upgrade or downgrade request
      *
-     * @param to   : expected profile
+     * @param to Profile with the RTT flag set to the desired value
      */
     public void sendRttModifyRequest(ImsCallProfile to) {
         if (mClosed) {
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index 3138180..cecf2e2 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -21,12 +21,11 @@
 import android.net.Uri;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.telephony.ims.ImsReasonInfo;
 import android.telephony.ims.aidl.IImsRegistration;
 import android.telephony.ims.aidl.IImsRegistrationCallback;
 import android.util.Log;
 
-import android.telephony.ims.ImsReasonInfo;
-
 import com.android.internal.annotations.VisibleForTesting;
 
 import java.lang.annotation.Retention;
@@ -81,13 +80,14 @@
      * Callback class for receiving Registration callback events.
      * @hide
      */
-    public static class Callback {
+    public static class Callback extends IImsRegistrationCallback.Stub {
         /**
          * Notifies the framework when the IMS Provider is connected to the IMS network.
          *
          * @param imsRadioTech the radio access technology. Valid values are defined in
          * {@link ImsRegistrationTech}.
          */
+        @Override
         public void onRegistered(@ImsRegistrationTech int imsRadioTech) {
         }
 
@@ -97,6 +97,7 @@
          * @param imsRadioTech the radio access technology. Valid values are defined in
          * {@link ImsRegistrationTech}.
          */
+        @Override
         public void onRegistering(@ImsRegistrationTech int imsRadioTech) {
         }
 
@@ -105,6 +106,7 @@
          *
          * @param info the {@link ImsReasonInfo} associated with why registration was disconnected.
          */
+        @Override
         public void onDeregistered(ImsReasonInfo info) {
         }
 
@@ -115,6 +117,7 @@
          * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed
          * @param info A {@link ImsReasonInfo} that identifies the reason for failure.
          */
+        @Override
         public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
                 ImsReasonInfo info) {
         }
@@ -125,6 +128,7 @@
          * @param uris new array of subscriber {@link Uri}s that are associated with this IMS
          *         subscription.
          */
+        @Override
         public void onSubscriberAssociatedUriChanged(Uri[] uris) {
 
         }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ca2bcff..32eb12b 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -808,6 +808,13 @@
      */
     boolean isDataEnabled(int subId);
 
+     /**
+     * Checks if manual network selection is allowed.
+     *
+     * @return {@code true} if manual network selection is allowed, otherwise return {@code false}.
+     */
+     boolean isManualNetworkSelectionAllowed(int subId);
+
     /**
      * Get P-CSCF address from PCO after data connection is established or modified.
      * @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
diff --git a/telephony/java/com/android/internal/telephony/SmsApplication.java b/telephony/java/com/android/internal/telephony/SmsApplication.java
index 39722c6..5ed283e 100644
--- a/telephony/java/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/java/com/android/internal/telephony/SmsApplication.java
@@ -584,7 +584,8 @@
 
         // We only make the change if the new package is valid
         PackageManager packageManager = context.getPackageManager();
-        Collection<SmsApplicationData> applications = getApplicationCollection(context);
+        Collection<SmsApplicationData> applications = getApplicationCollectionInternal(
+                context, userId);
         SmsApplicationData oldAppData = oldPackageName != null ?
                 getApplicationForPackage(applications, oldPackageName) : null;
         SmsApplicationData applicationData = getApplicationForPackage(applications, packageName);
diff --git a/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
index c86f06e..51302ce 100644
--- a/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
+++ b/tests/NativeProcessesMemoryTest/src/com/android/tests/nativeprocesses/NativeProcessesMemoryTest.java
@@ -19,11 +19,15 @@
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ByteArrayInputStreamSource;
-import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.LogDataType;
-import com.android.tradefed.result.TestDescription;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestLogData;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
 import com.android.tradefed.testtype.IDeviceTest;
-import com.android.tradefed.testtype.IRemoteTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -58,7 +62,12 @@
  *   - memory usage of each native process (one measurement for each process)
  * </pre>
  */
-public class NativeProcessesMemoryTest implements IDeviceTest, IRemoteTest {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class NativeProcessesMemoryTest implements IDeviceTest {
+
+    @Rule public TestMetrics metrics = new TestMetrics();
+    @Rule public TestLogData logs = new TestLogData();
+
     // the dumpsys process comes and go as we run this test, changing pids, so ignore it
     private static final List<String> PROCESSES_TO_IGNORE = Arrays.asList("dumpsys");
 
@@ -68,38 +77,25 @@
     private static final String SEPARATOR = ",";
     private static final String LINE_SEPARATOR = "\\n";
 
-    // name of this test run, used for reporting
-    private static final String RUN_NAME = "NativeProcessesTest";
     // key used to report the number of native processes
     private static final String NUM_NATIVE_PROCESSES_KEY = "Num_native_processes";
 
-    private final Map<String, String> mNativeProcessToMemory = new HashMap<String, String>();
     // identity for summing over MemoryMetric
     private final MemoryMetric mZero = new MemoryMetric(0, 0, 0);
 
     private ITestDevice mTestDevice;
-    private ITestInvocationListener mListener;
 
-    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
-        mListener = listener;
+    @Test
+    public void run() throws DeviceNotAvailableException {
         // showmap requires root, we enable it here for the rest of the test
-        mTestDevice.enableAdbRoot();
-
-        listener.testRunStarted(RUN_NAME, 1 /* testCount */);
-
-        TestDescription testDescription = new TestDescription(getClass().getName(), "run");
-        listener.testStarted(testDescription);
-
+        getDevice().enableAdbRoot();
         // process name -> list of pids with that name
         Map<String, List<String>> nativeProcesses = collectNativeProcesses();
         sampleAndLogAllProcesses(nativeProcesses);
 
         // want to also record the number of native processes
-        mNativeProcessToMemory.put(
+        metrics.addTestMetric(
                 NUM_NATIVE_PROCESSES_KEY, Integer.toString(nativeProcesses.size()));
-
-        listener.testEnded(testDescription, mNativeProcessToMemory);
-        listener.testRunEnded(0, new HashMap<String, String>());
     }
 
     /** Samples memory of all processes and logs the memory use. */
@@ -148,7 +144,7 @@
      */
     private Map<String, List<String>> collectNativeProcesses() throws DeviceNotAvailableException {
         HashMap<String, List<String>> nativeProcesses = new HashMap<>();
-        String memInfo = mTestDevice.executeShellCommand(DUMPSYS_MEMINFO_OOM_CMD);
+        String memInfo = getDevice().executeShellCommand(DUMPSYS_MEMINFO_OOM_CMD);
 
         for (String line : memInfo.split(LINE_SEPARATOR)) {
             String[] splits = line.split(SEPARATOR);
@@ -172,7 +168,7 @@
     private void logShowmap(String label, String showmap) {
         try (ByteArrayInputStreamSource source =
                 new ByteArrayInputStreamSource(showmap.getBytes())) {
-            mListener.testLog(label + "_showmap", LogDataType.TEXT, source);
+            logs.addTestLog(label + "_showmap", LogDataType.TEXT, source);
         }
     }
 
@@ -183,7 +179,7 @@
     private Optional<MemoryMetric> snapMemoryUsage(String processName, String pid)
             throws DeviceNotAvailableException {
         // TODO(zhin): copied from com.android.tests.sysmem.host.Metrics#sample(), extract?
-        String showmap = mTestDevice.executeShellCommand("showmap " + pid);
+        String showmap = getDevice().executeShellCommand("showmap " + pid);
         logShowmap(processName + "_" + pid, showmap);
 
         // CHECKSTYLE:OFF Generated code
@@ -214,9 +210,9 @@
 
     /** Logs a MemoryMetric of a process. */
     private void logMemoryMetric(String processName, MemoryMetric memoryMetric) {
-        mNativeProcessToMemory.put(processName + "_pss", Long.toString(memoryMetric.pss));
-        mNativeProcessToMemory.put(processName + "_rss", Long.toString(memoryMetric.rss));
-        mNativeProcessToMemory.put(processName + "_vss", Long.toString(memoryMetric.vss));
+        metrics.addTestMetric(processName + "_pss", Long.toString(memoryMetric.pss));
+        metrics.addTestMetric(processName + "_rss", Long.toString(memoryMetric.rss));
+        metrics.addTestMetric(processName + "_vss", Long.toString(memoryMetric.vss));
     }
 
     /** Container of memory numbers we want to log. */
diff --git a/tests/NetworkSecurityConfigTest/Android.mk b/tests/NetworkSecurityConfigTest/Android.mk
index fe65ecc..c225e17 100644
--- a/tests/NetworkSecurityConfigTest/Android.mk
+++ b/tests/NetworkSecurityConfigTest/Android.mk
@@ -7,7 +7,6 @@
 
 LOCAL_JAVA_LIBRARIES := \
     android.test.runner \
-    bouncycastle \
     conscrypt \
     android.test.base \
 
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index e529b93..750e2fb 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -38,6 +38,7 @@
     libbacktrace \
     libbase \
     libbinder \
+    libbinderthreadstate \
     libc++ \
     libcrypto \
     libcutils \
diff --git a/tests/net/java/android/net/dhcp/DhcpPacketTest.java b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
index 312b3d1..a592809 100644
--- a/tests/net/java/android/net/dhcp/DhcpPacketTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpPacketTest.java
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.annotation.Nullable;
 import android.net.DhcpResults;
 import android.net.LinkAddress;
 import android.net.NetworkUtils;
@@ -37,6 +38,7 @@
 import java.io.ByteArrayOutputStream;
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -56,6 +58,8 @@
     private static final Inet4Address NETMASK = getPrefixMaskAsInet4Address(PREFIX_LENGTH);
     private static final Inet4Address BROADCAST_ADDR = getBroadcastAddress(
             SERVER_ADDR, PREFIX_LENGTH);
+    private static final String HOSTNAME = "testhostname";
+    private static final short MTU = 1500;
     // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
     // doesn't use == instead of equals when comparing addresses.
     private static final Inet4Address ANY = (Inet4Address) v4Address("0.0.0.0");
@@ -960,7 +964,8 @@
         assertTrue(msg, Arrays.equals(expected, actual));
     }
 
-    public void checkBuildOfferPacket(int leaseTimeSecs) throws Exception {
+    public void checkBuildOfferPacket(int leaseTimeSecs, @Nullable String hostname)
+            throws Exception {
         final int renewalTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) / 2);
         final int rebindingTime = (int) (Integer.toUnsignedLong(leaseTimeSecs) * 875 / 1000);
         final int transactionId = 0xdeadbeef;
@@ -971,7 +976,8 @@
                 CLIENT_MAC, leaseTimeSecs, NETMASK /* netMask */,
                 BROADCAST_ADDR /* bcAddr */, Collections.singletonList(SERVER_ADDR) /* gateways */,
                 Collections.singletonList(SERVER_ADDR) /* dnsServers */,
-                SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, false /* metered */);
+                SERVER_ADDR /* dhcpServerIdentifier */, null /* domainName */, hostname,
+                false /* metered */, MTU);
 
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         // BOOTP headers
@@ -1027,12 +1033,22 @@
         // Nameserver
         bos.write(new byte[] { (byte) 0x06, (byte) 0x04 });
         bos.write(SERVER_ADDR.getAddress());
+        // Hostname
+        if (hostname != null) {
+            bos.write(new byte[]{(byte) 0x0c, (byte) hostname.length()});
+            bos.write(hostname.getBytes(Charset.forName("US-ASCII")));
+        }
+        // MTU
+        bos.write(new byte[] { (byte) 0x1a, (byte) 0x02 });
+        bos.write(shortToByteArray(MTU));
         // End options.
         bos.write(0xff);
 
-        final byte[] expected = bos.toByteArray();
-        assertTrue((expected.length & 1) == 0);
+        if ((bos.size() & 1) != 0) {
+            bos.write(0x00);
+        }
 
+        final byte[] expected = bos.toByteArray();
         final byte[] actual = new byte[packet.limit()];
         packet.get(actual);
         final String msg = "Expected:\n  " + HexDump.dumpHexString(expected) +
@@ -1042,13 +1058,18 @@
 
     @Test
     public void testOfferPacket() throws Exception {
-        checkBuildOfferPacket(3600);
-        checkBuildOfferPacket(Integer.MAX_VALUE);
-        checkBuildOfferPacket(0x80000000);
-        checkBuildOfferPacket(INFINITE_LEASE);
+        checkBuildOfferPacket(3600, HOSTNAME);
+        checkBuildOfferPacket(Integer.MAX_VALUE, HOSTNAME);
+        checkBuildOfferPacket(0x80000000, HOSTNAME);
+        checkBuildOfferPacket(INFINITE_LEASE, HOSTNAME);
+        checkBuildOfferPacket(3600, null);
     }
 
     private static byte[] intToByteArray(int val) {
         return ByteBuffer.allocate(4).putInt(val).array();
     }
+
+    private static byte[] shortToByteArray(short val) {
+        return ByteBuffer.allocate(2).putShort(val).array();
+    }
 }
diff --git a/tests/net/java/android/net/dhcp/DhcpServerTest.java b/tests/net/java/android/net/dhcp/DhcpServerTest.java
index 45a50d9..df34c73 100644
--- a/tests/net/java/android/net/dhcp/DhcpServerTest.java
+++ b/tests/net/java/android/net/dhcp/DhcpServerTest.java
@@ -17,6 +17,7 @@
 package android.net.dhcp;
 
 import static android.net.dhcp.DhcpPacket.DHCP_CLIENT;
+import static android.net.dhcp.DhcpPacket.DHCP_HOST_NAME;
 import static android.net.dhcp.DhcpPacket.ENCAP_BOOTP;
 import static android.net.dhcp.DhcpPacket.INADDR_ANY;
 import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
@@ -87,6 +88,7 @@
             Arrays.asList(parseAddr("192.168.0.200"), parseAddr("192.168.0.201")));
     private static final long TEST_LEASE_TIME_SECS = 3600L;
     private static final int TEST_MTU = 1500;
+    private static final String TEST_HOSTNAME = "testhostname";
 
     private static final int TEST_TRANSACTION_ID = 123;
     private static final byte[] TEST_CLIENT_MAC_BYTES = new byte [] { 1, 2, 3, 4, 5, 6 };
@@ -96,7 +98,10 @@
     private static final long TEST_CLOCK_TIME = 1234L;
     private static final int TEST_LEASE_EXPTIME_SECS = 3600;
     private static final DhcpLease TEST_LEASE = new DhcpLease(null, TEST_CLIENT_MAC,
-            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS*1000L + TEST_CLOCK_TIME, null /* hostname */);
+            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME,
+            null /* hostname */);
+    private static final DhcpLease TEST_LEASE_WITH_HOSTNAME = new DhcpLease(null, TEST_CLIENT_MAC,
+            TEST_CLIENT_ADDR, TEST_LEASE_EXPTIME_SECS * 1000L + TEST_CLOCK_TIME, TEST_HOSTNAME);
 
     @NonNull @Mock
     private Dependencies mDeps;
@@ -217,15 +222,17 @@
     public void testRequest_Selecting_Ack() throws Exception {
         when(mRepository.requestLease(isNull() /* clientId */, eq(TEST_CLIENT_MAC),
                 eq(INADDR_ANY) /* clientAddr */, eq(INADDR_ANY) /* relayAddr */,
-                eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, isNull() /* hostname */))
-                .thenReturn(TEST_LEASE);
+                eq(TEST_CLIENT_ADDR) /* reqAddr */, eq(true) /* sidSet */, eq(TEST_HOSTNAME)))
+                .thenReturn(TEST_LEASE_WITH_HOSTNAME);
 
         final DhcpRequestPacket request = makeRequestSelectingPacket();
+        request.mHostName = TEST_HOSTNAME;
+        request.mRequestedParams = new byte[] { DHCP_HOST_NAME };
         mServer.processPacket(request, DHCP_CLIENT);
 
         assertResponseSentTo(TEST_CLIENT_ADDR);
         final DhcpAckPacket packet = assertAck(getPacket());
-        assertMatchesTestLease(packet);
+        assertMatchesTestLease(packet, TEST_HOSTNAME);
     }
 
     @Test
@@ -270,14 +277,18 @@
      *  - other request states (init-reboot/renewing/rebinding)
      */
 
-    private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+    private void assertMatchesTestLease(@NonNull DhcpPacket packet, @Nullable String hostname) {
         assertMatchesClient(packet);
         assertFalse(packet.hasExplicitClientId());
         assertEquals(TEST_SERVER_ADDR, packet.mServerIdentifier);
         assertEquals(TEST_CLIENT_ADDR, packet.mYourIp);
         assertNotNull(packet.mLeaseTime);
         assertEquals(TEST_LEASE_EXPTIME_SECS, (int) packet.mLeaseTime);
-        assertNull(packet.mHostName);
+        assertEquals(hostname, packet.mHostName);
+    }
+
+    private void assertMatchesTestLease(@NonNull DhcpPacket packet) {
+        assertMatchesTestLease(packet, null);
     }
 
     private void assertMatchesClient(@NonNull DhcpPacket packet) {
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 99a5a69..9b919abf 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
@@ -34,8 +36,10 @@
 import android.net.IpSecConfig;
 import android.net.IpSecManager;
 import android.net.IpSecSpiResponse;
+import android.net.IpSecTransform;
 import android.net.IpSecTransformResponse;
 import android.net.IpSecTunnelInterfaceResponse;
+import android.net.IpSecUdpEncapResponse;
 import android.net.LinkAddress;
 import android.net.Network;
 import android.net.NetworkUtils;
@@ -62,16 +66,17 @@
 
     private static final int TEST_SPI = 0xD1201D;
 
-    private final String mDestinationAddr;
     private final String mSourceAddr;
+    private final String mDestinationAddr;
     private final LinkAddress mLocalInnerAddress;
+    private final int mFamily;
 
     @Parameterized.Parameters
     public static Collection ipSecConfigs() {
         return Arrays.asList(
                 new Object[][] {
-                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24"},
-                {"2601::2", "2601::10", "2001:db8::1/64"}
+                {"1.2.3.4", "8.8.4.4", "10.0.1.1/24", AF_INET},
+                {"2601::2", "2601::10", "2001:db8::1/64", AF_INET6}
         });
     }
 
@@ -129,12 +134,14 @@
             new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
     private static final IpSecAlgorithm AEAD_ALGO =
             new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
+    private static final int REMOTE_ENCAP_PORT = 4500;
 
     public IpSecServiceParameterizedTest(
-            String sourceAddr, String destAddr, String localInnerAddr) {
+            String sourceAddr, String destAddr, String localInnerAddr, int family) {
         mSourceAddr = sourceAddr;
         mDestinationAddr = destAddr;
         mLocalInnerAddress = new LinkAddress(localInnerAddr);
+        mFamily = family;
     }
 
     @Before
@@ -157,6 +164,8 @@
             .thenReturn(AppOpsManager.MODE_IGNORED);
     }
 
+    //TODO: Add a test to verify SPI.
+
     @Test
     public void testIpSecServiceReserveSpi() throws Exception {
         when(mMockNetd.ipSecAllocateSpi(anyInt(), anyString(), eq(mDestinationAddr), eq(TEST_SPI)))
@@ -257,6 +266,47 @@
         config.setAuthentication(AUTH_ALGO);
     }
 
+    private void addEncapSocketToIpSecConfig(int resourceId, IpSecConfig config) throws Exception {
+        config.setEncapType(IpSecTransform.ENCAP_ESPINUDP);
+        config.setEncapSocketResourceId(resourceId);
+        config.setEncapRemotePort(REMOTE_ENCAP_PORT);
+    }
+
+    private void verifyTransformNetdCalledForCreatingSA(
+            IpSecConfig config, IpSecTransformResponse resp) throws Exception {
+        verifyTransformNetdCalledForCreatingSA(config, resp, 0);
+    }
+
+    private void verifyTransformNetdCalledForCreatingSA(
+            IpSecConfig config, IpSecTransformResponse resp, int encapSocketPort) throws Exception {
+        IpSecAlgorithm auth = config.getAuthentication();
+        IpSecAlgorithm crypt = config.getEncryption();
+        IpSecAlgorithm authCrypt = config.getAuthenticatedEncryption();
+
+        verify(mMockNetd, times(1))
+                .ipSecAddSecurityAssociation(
+                        eq(mUid),
+                        eq(config.getMode()),
+                        eq(config.getSourceAddress()),
+                        eq(config.getDestinationAddress()),
+                        eq((config.getNetwork() != null) ? config.getNetwork().netId : 0),
+                        eq(TEST_SPI),
+                        eq(0),
+                        eq(0),
+                        eq((auth != null) ? auth.getName() : ""),
+                        eq((auth != null) ? auth.getKey() : new byte[] {}),
+                        eq((auth != null) ? auth.getTruncationLengthBits() : 0),
+                        eq((crypt != null) ? crypt.getName() : ""),
+                        eq((crypt != null) ? crypt.getKey() : new byte[] {}),
+                        eq((crypt != null) ? crypt.getTruncationLengthBits() : 0),
+                        eq((authCrypt != null) ? authCrypt.getName() : ""),
+                        eq((authCrypt != null) ? authCrypt.getKey() : new byte[] {}),
+                        eq((authCrypt != null) ? authCrypt.getTruncationLengthBits() : 0),
+                        eq(config.getEncapType()),
+                        eq(encapSocketPort),
+                        eq(config.getEncapRemotePort()));
+    }
+
     @Test
     public void testCreateTransform() throws Exception {
         IpSecConfig ipSecConfig = new IpSecConfig();
@@ -267,28 +317,7 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(mUid),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        anyInt(),
-                        eq(TEST_SPI),
-                        anyInt(),
-                        anyInt(),
-                        eq(IpSecAlgorithm.AUTH_HMAC_SHA256),
-                        eq(AUTH_KEY),
-                        anyInt(),
-                        eq(IpSecAlgorithm.CRYPT_AES_CBC),
-                        eq(CRYPT_KEY),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
     }
 
     @Test
@@ -302,28 +331,59 @@
                 mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
         assertEquals(IpSecManager.Status.OK, createTransformResp.status);
 
-        verify(mMockNetd)
-                .ipSecAddSecurityAssociation(
-                        eq(mUid),
-                        anyInt(),
-                        anyString(),
-                        anyString(),
-                        anyInt(),
-                        eq(TEST_SPI),
-                        anyInt(),
-                        anyInt(),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(""),
-                        eq(new byte[] {}),
-                        eq(0),
-                        eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
-                        eq(AEAD_KEY),
-                        anyInt(),
-                        anyInt(),
-                        anyInt(),
-                        anyInt());
+        verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp);
+    }
+
+    @Test
+    public void testCreateTransportModeTransformWithEncap() throws Exception {
+        IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TRANSPORT);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+        addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+        if (mFamily == AF_INET) {
+            IpSecTransformResponse createTransformResp =
+                    mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+            assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+            verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+        } else {
+            try {
+                IpSecTransformResponse createTransformResp =
+                        mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+                fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
+    }
+
+    @Test
+    public void testCreateTunnelModeTransformWithEncap() throws Exception {
+        IpSecUdpEncapResponse udpSock = mIpSecService.openUdpEncapsulationSocket(0, new Binder());
+
+        IpSecConfig ipSecConfig = new IpSecConfig();
+        ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL);
+        addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig);
+        addAuthAndCryptToIpSecConfig(ipSecConfig);
+        addEncapSocketToIpSecConfig(udpSock.resourceId, ipSecConfig);
+
+        if (mFamily == AF_INET) {
+            IpSecTransformResponse createTransformResp =
+                    mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+            assertEquals(IpSecManager.Status.OK, createTransformResp.status);
+
+            verifyTransformNetdCalledForCreatingSA(ipSecConfig, createTransformResp, udpSock.port);
+        } else {
+            try {
+                IpSecTransformResponse createTransformResp =
+                        mIpSecService.createTransform(ipSecConfig, new Binder(), "blessedPackage");
+                fail("Expected IllegalArgumentException on attempt to use UDP Encap in IPv6");
+            } catch (IllegalArgumentException expected) {
+            }
+        }
     }
 
     @Test
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 9c4da1f..14312cf 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -83,16 +83,6 @@
         }
 
         try {
-            mWm.setFocusedApp(null, false);
-            fail("IWindowManager.setFocusedApp did not throw SecurityException as"
-                    + " expected");
-        } catch (SecurityException e) {
-            // expected
-        } catch (RemoteException e) {
-            fail("Unexpected remote exception");
-        }
-
-        try {
             mWm.prepareAppTransition(0, false);
             fail("IWindowManager.prepareAppTransition did not throw SecurityException as"
                     + " expected");
diff --git a/tests/testables/src/android/testing/TestableContext.java b/tests/testables/src/android/testing/TestableContext.java
index cf84c79..fff9635 100644
--- a/tests/testables/src/android/testing/TestableContext.java
+++ b/tests/testables/src/android/testing/TestableContext.java
@@ -53,7 +53,7 @@
  * Like the following:</p>
  * <pre class="prettyprint">
  * &#064;Rule
- * private final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
+ * public final TestableContext mContext = new TestableContext(InstrumentationRegister.getContext());
  * </pre>
  */
 public class TestableContext extends ContextWrapper implements TestRule {
diff --git a/tools/aapt2/ResourceUtils.cpp b/tools/aapt2/ResourceUtils.cpp
index c48765b..de00fca 100644
--- a/tools/aapt2/ResourceUtils.cpp
+++ b/tools/aapt2/ResourceUtils.cpp
@@ -38,6 +38,8 @@
 namespace aapt {
 namespace ResourceUtils {
 
+constexpr int32_t kNonBreakingSpace = 0xa0;
+
 Maybe<ResourceName> ToResourceName(
     const android::ResTable::resource_name& name_in) {
   ResourceName name_out;
@@ -810,7 +812,7 @@
   Utf8Iterator iter(text);
   while (iter.HasNext()) {
     char32_t codepoint = iter.Next();
-    if (!preserve_spaces && !quote_ && iswspace(codepoint)) {
+    if (!preserve_spaces && !quote_ && codepoint != kNonBreakingSpace && iswspace(codepoint)) {
       if (!last_codepoint_was_space_) {
         // Emit a space if it's the first.
         xml_string_.text += ' ';
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index 62c19fb..8060a8de 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -73,9 +73,9 @@
 };
 
 // Resource file paths are expected to look like: [--/res/]type[-config]/name
-static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path,
+static Maybe<ResourcePathData> ExtractResourcePathData(const std::string& path, const char dir_sep,
                                                        std::string* out_error) {
-  std::vector<std::string> parts = util::Split(path, file::sDirSep);
+  std::vector<std::string> parts = util::Split(path, dir_sep);
   if (parts.size() < 2) {
     if (out_error) *out_error = "bad resource path";
     return {};
@@ -656,7 +656,7 @@
     // Extract resource type information from the full path
     std::string err_str;
     ResourcePathData path_data;
-    if (auto maybe_path_data = ExtractResourcePathData(path, &err_str)) {
+    if (auto maybe_path_data = ExtractResourcePathData(path, inputs->GetDirSeparator(), &err_str)) {
       path_data = maybe_path_data.value();
     } else {
       context->GetDiagnostics()->Error(DiagMessage(file->GetSource()) << err_str);
@@ -731,7 +731,7 @@
   // Collect the resources files to compile
   if (options_.res_dir && options_.res_zip) {
     context.GetDiagnostics()->Error(DiagMessage()
-                                        << "only one of --dir and --zip can be specified");
+                                      << "only one of --dir and --zip can be specified");
     return 1;
   } else if (options_.res_dir) {
     if (!args.empty()) {
diff --git a/tools/aapt2/cmd/Compile_test.cpp b/tools/aapt2/cmd/Compile_test.cpp
index dd5198c..c0c05cd 100644
--- a/tools/aapt2/cmd/Compile_test.cpp
+++ b/tools/aapt2/cmd/Compile_test.cpp
@@ -17,6 +17,9 @@
 #include "Compile.h"
 
 #include "android-base/file.h"
+#include "android-base/stringprintf.h"
+#include "android-base/utf8.h"
+
 #include "io/StringStream.h"
 #include "io/ZipArchive.h"
 #include "java/AnnotationProcessor.h"
@@ -24,8 +27,20 @@
 
 namespace aapt {
 
+std::string BuildPath(std::vector<std::string> args) {
+  std::string out;
+  if (args.empty()) {
+    return out;
+  }
+  out = args[0];
+  for (int i = 1; i < args.size(); i++) {
+    file::AppendPath(&out, args[i]);
+  }
+  return out;
+}
+
 int TestCompile(const std::string& path, const std::string& outDir, bool legacy,
-    StdErrDiagnostics& diag) {
+                StdErrDiagnostics& diag) {
   std::vector<android::StringPiece> args;
   args.push_back(path);
   args.push_back("-o");
@@ -39,95 +54,101 @@
 TEST(CompilerTest, MultiplePeriods) {
   StdErrDiagnostics diag;
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-  const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath())
-      + "/integration-tests/CompileTest/res";
+  const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
+                                         "integration-tests", "CompileTest", "res"});
 
   // Resource files without periods in the file name should not throw errors
-  const std::string path0 = kResDir + "/values/values.xml";
-  const std::string path0_out = kResDir + "/values_values.arsc.flat";
-
-  remove(path0_out.c_str());
+  const std::string path0 = BuildPath({kResDir, "values", "values.xml"});
+  const std::string path0_out = BuildPath({kResDir, "values_values.arsc.flat"});
+  ::android::base::utf8::unlink(path0_out.c_str());
   ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ false, diag), 0);
-  ASSERT_EQ(remove(path0_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
   ASSERT_EQ(TestCompile(path0, kResDir, /** legacy */ true, diag), 0);
-  ASSERT_EQ(remove(path0_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path0_out.c_str()), 0);
 
-  const std::string path1 = kResDir + "/drawable/image.png";
-  const std::string path1_out = kResDir + "/drawable_image.png.flat";
-  remove(path1_out.c_str());
+  const std::string path1 = BuildPath({kResDir, "drawable", "image.png"});
+  const std::string path1_out = BuildPath({kResDir, "drawable_image.png.flat"});
+  ::android::base::utf8::unlink(path1_out.c_str());
   ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ false, diag), 0);
-  ASSERT_EQ(remove(path1_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
   ASSERT_EQ(TestCompile(path1, kResDir, /** legacy */ true, diag), 0);
-  ASSERT_EQ(remove(path1_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path1_out.c_str()), 0);
 
-  const std::string path2 = kResDir + "/drawable/image.9.png";
-  const std::string path2_out = kResDir + "/drawable_image.9.png.flat";
-  remove(path2_out.c_str());
+  const std::string path2 = BuildPath({kResDir, "drawable", "image.9.png"});
+  const std::string path2_out = BuildPath({kResDir, "drawable_image.9.png.flat"});
+  ::android::base::utf8::unlink(path2_out.c_str());
   ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ false, diag), 0);
-  ASSERT_EQ(remove(path2_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
   ASSERT_EQ(TestCompile(path2, kResDir, /** legacy */ true, diag), 0);
-  ASSERT_EQ(remove(path2_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path2_out.c_str()), 0);
 
   // Resource files with periods in the file name should fail on non-legacy compilations
-  const std::string path3 = kResDir + "/values/values.all.xml";
-  const std::string path3_out = kResDir + "/values_values.all.arsc.flat";
-  remove(path3_out.c_str());
+  const std::string path3 = BuildPath({kResDir, "values", "values.all.xml"});
+  const std::string path3_out = BuildPath({kResDir, "values_values.all.arsc.flat"});
+  ::android::base::utf8::unlink(path3_out.c_str());
   ASSERT_NE(TestCompile(path3, kResDir, /** legacy */ false, diag), 0);
-  ASSERT_NE(remove(path3_out.c_str()), 0);
+  ASSERT_NE(::android::base::utf8::unlink(path3_out.c_str()), 0);
   ASSERT_EQ(TestCompile(path3, kResDir, /** legacy */ true, diag), 0);
-  ASSERT_EQ(remove(path3_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path3_out.c_str()), 0);
 
-  const std::string path4 = kResDir + "/drawable/image.small.png";
-  const std::string path4_out = (kResDir + std::string("/drawable_image.small.png.flat")).c_str();
-  remove(path4_out.c_str());
+  const std::string path4 = BuildPath({kResDir, "drawable", "image.small.png"});
+  const std::string path4_out = BuildPath({kResDir, "drawable_image.small.png.flat"});
+  ::android::base::utf8::unlink(path4_out.c_str());
   ASSERT_NE(TestCompile(path4, kResDir, /** legacy */ false, diag), 0);
-  ASSERT_NE(remove(path4_out.c_str()), 0);
+  ASSERT_NE(::android::base::utf8::unlink(path4_out.c_str()), 0);
   ASSERT_EQ(TestCompile(path4, kResDir, /** legacy */ true, diag), 0);
-  ASSERT_EQ(remove(path4_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path4_out.c_str()), 0);
 
-  const std::string path5 = kResDir + "/drawable/image.small.9.png";
-  const std::string path5_out = (kResDir + std::string("/drawable_image.small.9.png.flat")).c_str();
-  remove(path5_out.c_str());
+  const std::string path5 = BuildPath({kResDir, "drawable", "image.small.9.png"});
+  const std::string path5_out = BuildPath({kResDir, "drawable_image.small.9.png.flat"});
+  ::android::base::utf8::unlink(path5_out.c_str());
   ASSERT_NE(TestCompile(path5, kResDir, /** legacy */ false, diag), 0);
-  ASSERT_NE(remove(path5_out.c_str()), 0);
+  ASSERT_NE(::android::base::utf8::unlink(path5_out.c_str()), 0);
   ASSERT_EQ(TestCompile(path5, kResDir, /** legacy */ true, diag), 0);
-  ASSERT_EQ(remove(path5_out.c_str()), 0);
+  ASSERT_EQ(::android::base::utf8::unlink(path5_out.c_str()), 0);
 }
 
 TEST(CompilerTest, DirInput) {
   StdErrDiagnostics diag;
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-  const std::string kResDir = android::base::Dirname(android::base::GetExecutablePath())
-                            + "/integration-tests/CompileTest/DirInput/res";
-  const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
-                                 + "/integration-tests/CompileTest/DirInput/compiled.flata";
-  remove(kOutputFlata.c_str());
+  const std::string kResDir = BuildPath({android::base::Dirname(android::base::GetExecutablePath()),
+                                         "integration-tests", "CompileTest", "DirInput", "res"});
+  const std::string kOutputFlata =
+      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+                 "CompileTest", "DirInput", "compiled.flata"});
+  ::android::base::utf8::unlink(kOutputFlata.c_str());
 
   std::vector<android::StringPiece> args;
   args.push_back("--dir");
   args.push_back(kResDir);
   args.push_back("-o");
   args.push_back(kOutputFlata);
+  args.push_back("-v");
   ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
 
-  // Check for the presence of the compiled files
-  std::string err;
-  std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
-  ASSERT_NE(zip, nullptr) << err;
-  ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
-  ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
-  ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
-  ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
+  {
+    // Check for the presence of the compiled files
+    std::string err;
+    std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
+    ASSERT_NE(zip, nullptr) << err;
+    ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
+    ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
+    ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
+  }
+  ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
 }
 
 TEST(CompilerTest, ZipInput) {
   StdErrDiagnostics diag;
   std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build();
-  const std::string kResZip = android::base::Dirname(android::base::GetExecutablePath())
-                            + "/integration-tests/CompileTest/ZipInput/res.zip";
-  const std::string kOutputFlata = android::base::Dirname(android::base::GetExecutablePath())
-                                 + "/integration-tests/CompileTest/ZipInput/compiled.flata";
-  remove(kOutputFlata.c_str());
+  const std::string kResZip =
+      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+                 "CompileTest", "ZipInput", "res.zip"});
+  const std::string kOutputFlata =
+      BuildPath({android::base::Dirname(android::base::GetExecutablePath()), "integration-tests",
+                 "CompileTest", "ZipInput", "compiled.flata"});
+
+  ::android::base::utf8::unlink(kOutputFlata.c_str());
 
   std::vector<android::StringPiece> args;
   args.push_back("--zip");
@@ -136,14 +157,16 @@
   args.push_back(kOutputFlata);
   ASSERT_EQ(CompileCommand(&diag).Execute(args, &std::cerr), 0);
 
-  // Check for the presence of the compiled files
-  std::string err;
-  std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
-  ASSERT_NE(zip, nullptr) << err;
-  ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
-  ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
-  ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
-  ASSERT_EQ(remove(kOutputFlata.c_str()), 0);
+  {
+    // Check for the presence of the compiled files
+    std::string err;
+    std::unique_ptr<io::ZipFileCollection> zip = io::ZipFileCollection::Create(kOutputFlata, &err);
+    ASSERT_NE(zip, nullptr) << err;
+    ASSERT_NE(zip->FindFile("drawable_image.png.flat"), nullptr);
+    ASSERT_NE(zip->FindFile("layout_layout.xml.flat"), nullptr);
+    ASSERT_NE(zip->FindFile("values_values.arsc.flat"), nullptr);
+  }
+  ASSERT_EQ(::android::base::utf8::unlink(kOutputFlata.c_str()), 0);
 }
 
-} // namespace aapt
\ No newline at end of file
+}  // namespace aapt
\ No newline at end of file
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index 13c1047..20ea3cb6 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -1260,7 +1260,7 @@
       return false;
     }
 
-    proguard::WriteKeepSet(keep_set, &fout);
+    proguard::WriteKeepSet(keep_set, &fout, options_.generate_minimal_proguard_rules);
     fout.Flush();
 
     if (fout.HadError()) {
diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h
index e58a93e..a42c0f2 100644
--- a/tools/aapt2/cmd/Link.h
+++ b/tools/aapt2/cmd/Link.h
@@ -49,6 +49,7 @@
   Maybe<std::string> generate_proguard_rules_path;
   Maybe<std::string> generate_main_dex_proguard_rules_path;
   bool generate_conditional_proguard_rules = false;
+  bool generate_minimal_proguard_rules = false;
   bool generate_non_final_ids = false;
   std::vector<std::string> javadoc_annotations;
   Maybe<std::string> private_symbols;
@@ -119,6 +120,9 @@
     AddOptionalSwitch("--proguard-conditional-keep-rules",
         "Generate conditional Proguard keep rules.",
         &options_.generate_conditional_proguard_rules);
+    AddOptionalSwitch("--proguard-minimal-keep-rules",
+        "Generate a minimal set of Proguard keep rules.",
+        &options_.generate_minimal_proguard_rules);
     AddOptionalSwitch("--no-auto-version", "Disables automatic style and layout SDK versioning.",
         &options_.no_auto_version);
     AddOptionalSwitch("--no-version-vectors",
diff --git a/tools/aapt2/cmd/Util_test.cpp b/tools/aapt2/cmd/Util_test.cpp
index 158ef29..6aeff08 100644
--- a/tools/aapt2/cmd/Util_test.cpp
+++ b/tools/aapt2/cmd/Util_test.cpp
@@ -16,12 +16,22 @@
 
 #include "Util.h"
 
+#include "android-base/stringprintf.h"
+
 #include "AppInfo.h"
 #include "split/TableSplitter.h"
 #include "test/Builders.h"
 #include "test/Test.h"
+#include "util/Files.h"
 
 namespace aapt {
+
+#ifdef _WIN32
+#define CREATE_PATH(path) android::base::StringPrintf(";%s", path)
+#else
+#define CREATE_PATH(path) android::base::StringPrintf(":%s", path)
+#endif
+
 #define EXPECT_CONFIG_EQ(constraints, config) \
     EXPECT_EQ(constraints.configs.size(), 1); \
     EXPECT_EQ(*constraints.configs.begin(), config); \
@@ -89,7 +99,7 @@
 }
 
 
-TEST (UtilTest, ParseSplitParameter) {
+TEST (UtilTest, ParseSplitParameters) {
   IDiagnostics* diagnostics = test::ContextBuilder().Build().get()->GetDiagnostics();
   std::string path;
   SplitConstraints constraints;
@@ -98,14 +108,14 @@
   // ========== Test IMSI ==========
   // mcc: 'mcc[0-9]{3}'
   // mnc: 'mnc[0-9]{1,3}'
-  ASSERT_TRUE(ParseSplitParameter(":mcc310",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setMcc(0x0136)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc004",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310-mnc004"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setMcc(0x0136)
@@ -113,7 +123,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":mcc310-mnc000",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("mcc310-mnc000"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setMcc(0x0136)
@@ -124,14 +134,14 @@
   // ========== Test LOCALE ==========
   // locale: '[a-z]{2,3}(-r[a-z]{2})?'
   // locale: 'b+[a-z]{2,3}(+[a-z[0-9]]{2})?'
-  ASSERT_TRUE(ParseSplitParameter(":es",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("es"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setLanguage(0x6573)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":fr-rCA",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("fr-rCA"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setLanguage(0x6672)
@@ -139,7 +149,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":b+es+419",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("b+es+419"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setLanguage(0x6573)
@@ -151,21 +161,21 @@
   // orientation: '(port|land|square)'
   // touchscreen: '(notouch|stylus|finger)'
   // density" '(anydpi|nodpi|ldpi|mdpi|tvdpi|hdpi|xhdpi|xxhdpi|xxxhdpi|[0-9]*dpi)'
-  ASSERT_TRUE(ParseSplitParameter(":square",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("square"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setOrientation(0x03)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":stylus",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("stylus"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setTouchscreen(0x02)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":xxxhdpi",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("xxxhdpi"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setDensity(0x0280)
@@ -173,7 +183,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":land-xhdpi-finger",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("land-xhdpi-finger"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setOrientation(0x02)
@@ -188,28 +198,28 @@
   // navigation: '(nonav|dpad|trackball|wheel)'
   // inputFlags: '(keysexposed|keyshidden|keyssoft)'
   // inputFlags: '(navexposed|navhidden)'
-  ASSERT_TRUE(ParseSplitParameter(":qwerty",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("qwerty"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setKeyboard(0x02)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":dpad",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("dpad"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setNavigation(0x02)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":keyssoft-navhidden",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("keyssoft-navhidden"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setInputFlags(0x0B)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":keyshidden-nokeys-navexposed-trackball",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("keyshidden-nokeys-navexposed-trackball"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setKeyboard(0x01)
@@ -220,7 +230,7 @@
 
   // ========== Test SCREEN_SIZE ==========
   // screenWidth/screenHeight: '[0-9]+x[0-9]+'
-  ASSERT_TRUE(ParseSplitParameter(":1920x1080",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("1920x1080"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenWidth(0x0780)
@@ -238,14 +248,14 @@
   // uiMode [type]: '(desk|car|television|appliance|watch|vrheadset)'
   // uiMode [night]: '(night|notnight)'
   // smallestScreenWidthDp: 'sw[0-9]dp'
-  ASSERT_TRUE(ParseSplitParameter(":ldrtl",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("ldrtl"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenLayout(0x80)
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":small",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("small"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenLayout(0x01)
@@ -253,7 +263,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":notlong",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("notlong"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenLayout(0x10)
@@ -261,15 +271,15 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":ldltr-normal-long",
-                                  diagnostics, &path, &constraints));
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("ldltr-normal-long"),
+                                      diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenLayout(0x62)
       .setSdkVersion(0x0004) // screenLayout (size|long) requires donut
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":car",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("car"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setUiMode(0x03)
@@ -277,7 +287,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":vrheadset",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("vrheadset"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setUiMode(0x07)
@@ -285,7 +295,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":television-night",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("television-night"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setUiMode(0x24)
@@ -293,7 +303,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":sw1920dp",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("sw1920dp"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setSmallestScreenWidthDp(0x0780)
@@ -304,7 +314,7 @@
   // ========== Test SCREEN_SIZE_DP ==========
   // screenWidthDp: 'w[0-9]dp'
   // screenHeightDp: 'h[0-9]dp'
-  ASSERT_TRUE(ParseSplitParameter(":w1920dp",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("w1920dp"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenWidthDp(0x0780)
@@ -312,7 +322,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":h1080dp",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("h1080dp"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenHeightDp(0x0438)
@@ -324,7 +334,7 @@
   // screenLayout2: '(round|notround)'
   // colorMode: '(widecg|nowidecg)'
   // colorMode: '(highhdr|lowdr)'
-  ASSERT_TRUE(ParseSplitParameter(":round",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("round"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setScreenLayout2(0x02)
@@ -332,7 +342,7 @@
       .Build();
   EXPECT_CONFIG_EQ(constraints, expected_configuration);
 
-  ASSERT_TRUE(ParseSplitParameter(":widecg-highdr",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("widecg-highdr"),
                                   diagnostics, &path, &constraints));
   expected_configuration = test::ConfigDescriptionBuilder()
       .setColorMode(0x0A)
@@ -349,10 +359,10 @@
   std::string path;
 
   test_constraints.push_back({});
-  ASSERT_TRUE(ParseSplitParameter(":v7",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("v7"),
                                   diagnostics, &path, &test_constraints.back()));
   test_constraints.push_back({});
-  ASSERT_TRUE(ParseSplitParameter(":xhdpi",
+  ASSERT_TRUE(ParseSplitParameter(CREATE_PATH("xhdpi"),
                                   diagnostics, &path, &test_constraints.back()));
   EXPECT_EQ(test_constraints.size(), 2);
   EXPECT_EQ(test_constraints[0].name, "v7");
diff --git a/tools/aapt2/dump/DumpManifest.cpp b/tools/aapt2/dump/DumpManifest.cpp
index 2c356d1..b6a984f 100644
--- a/tools/aapt2/dump/DumpManifest.cpp
+++ b/tools/aapt2/dump/DumpManifest.cpp
@@ -16,6 +16,8 @@
 
 #include "DumpManifest.h"
 
+#include <algorithm>
+
 #include "LoadedApk.h"
 #include "SdkConstants.h"
 #include "ValueVisitor.h"
@@ -70,10 +72,14 @@
   CATEGORY_ATTR = 0x010103e8,
   BANNER_ATTR = 0x10103f2,
   ISGAME_ATTR = 0x10103f4,
+  VERSION_ATTR = 0x01010519,
+  CERT_DIGEST_ATTR = 0x01010548,
   REQUIRED_FEATURE_ATTR = 0x1010557,
   REQUIRED_NOT_FEATURE_ATTR = 0x1010558,
   COMPILE_SDK_VERSION_ATTR = 0x01010572,
   COMPILE_SDK_VERSION_CODENAME_ATTR = 0x01010573,
+  VERSION_MAJOR_ATTR = 0x01010577,
+  PACKAGE_TYPE_ATTR = 0x01010587,
 };
 
 const std::string& kAndroidNamespace = "http://schemas.android.com/apk/res/android";
@@ -1318,6 +1324,70 @@
   }
 };
 
+/** Represents <static-library> elements. **/
+class StaticLibrary : public ManifestExtractor::Element {
+ public:
+  StaticLibrary() = default;
+  std::string name;
+  int version;
+  int versionMajor;
+
+  void Extract(xml::Element* element) override {
+    auto parent_stack = extractor()->parent_stack();
+    if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+      name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+      version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+      versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    printer.Print(StringPrintf(
+      "static-library: name='%s' version='%d' versionMajor='%d'\n",
+      name.data(), version, versionMajor));
+  }
+};
+
+/** Represents <uses-static-library> elements. **/
+class UsesStaticLibrary : public ManifestExtractor::Element {
+ public:
+  UsesStaticLibrary() = default;
+  std::string name;
+  int version;
+  int versionMajor;
+  std::vector<std::string> certDigests;
+
+  void Extract(xml::Element* element) override {
+    auto parent_stack = extractor()->parent_stack();
+    if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+      name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
+      version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+      versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+      AddCertDigest(element);
+    }
+  }
+
+  void AddCertDigest(xml::Element* element) {
+    std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+    // We allow ":" delimiters in the SHA declaration as this is the format
+    // emitted by the certtool making it easy for developers to copy/paste.
+    digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+    if (!digest.empty()) {
+      certDigests.push_back(digest);
+    }
+  }
+
+  void Print(text::Printer& printer) override {
+    printer.Print(StringPrintf(
+      "uses-static-library: name='%s' version='%d' versionMajor='%d'",
+      name.data(), version, versionMajor));
+    for (size_t i = 0; i < certDigests.size(); i++) {
+      printer.Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+    }
+    printer.Print("\n");
+  }
+};
+
 /**
  * Represents <meta-data> elements. These tags are only printed when a flag is passed in to
  * explicitly enable meta data printing.
@@ -1326,29 +1396,29 @@
  public:
   MetaData() = default;
   std::string name;
-  const std::string* value;
+  std::string value;
   const int* value_int;
-  const std::string* resource;
+  std::string resource;
   const int* resource_int;
 
   void Extract(xml::Element* element) override {
     name = GetAttributeStringDefault(FindAttribute(element, NAME_ATTR), "");
-    value = GetAttributeString(FindAttribute(element, VALUE_ATTR));
+    value = GetAttributeStringDefault(FindAttribute(element, VALUE_ATTR), "");
     value_int = GetAttributeInteger(FindAttribute(element, VALUE_ATTR));
-    resource = GetAttributeString(FindAttribute(element, RESOURCE_ATTR));
+    resource = GetAttributeStringDefault(FindAttribute(element, RESOURCE_ATTR), "");
     resource_int = GetAttributeInteger(FindAttribute(element, RESOURCE_ATTR));
   }
 
   void Print(text::Printer& printer) override {
     if (extractor()->options_.include_meta_data && !name.empty()) {
       printer.Print(StringPrintf("meta-data: name='%s' ", name.data()));
-      if (value) {
-        printer.Print(StringPrintf("value='%s' ", value->data()));
+      if (!value.empty()) {
+        printer.Print(StringPrintf("value='%s' ", value.data()));
       } else if (value_int) {
         printer.Print(StringPrintf("value='%d' ", *value_int));
       } else {
-        if (resource) {
-          printer.Print(StringPrintf("resource='%s' ", resource->data()));
+        if (!resource.empty()) {
+          printer.Print(StringPrintf("resource='%s' ", resource.data()));
         } else if (resource_int) {
           printer.Print(StringPrintf("resource='%d' ", *resource_int));
         }
@@ -1544,15 +1614,65 @@
 class UsesPackage : public ManifestExtractor::Element {
  public:
   UsesPackage() = default;
+  const std::string* packageType = nullptr;
   const std::string* name = nullptr;
+  int version;
+  int versionMajor;
+  std::vector<std::string> certDigests;
 
   void Extract(xml::Element* element) override {
-    name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+    auto parent_stack = extractor()->parent_stack();
+    if (parent_stack.size() > 0 && ElementCast<Application>(parent_stack[0])) {
+      packageType = GetAttributeString(FindAttribute(element, PACKAGE_TYPE_ATTR));
+      name = GetAttributeString(FindAttribute(element, NAME_ATTR));
+      version = GetAttributeIntegerDefault(FindAttribute(element, VERSION_ATTR), 0);
+      versionMajor = GetAttributeIntegerDefault(FindAttribute(element, VERSION_MAJOR_ATTR), 0);
+      AddCertDigest(element);
+    }
+  }
+
+  void AddCertDigest(xml::Element* element) {
+    std::string digest = GetAttributeStringDefault(FindAttribute(element, CERT_DIGEST_ATTR), "");
+    // We allow ":" delimiters in the SHA declaration as this is the format
+    // emitted by the certtool making it easy for developers to copy/paste.
+    digest.erase(std::remove(digest.begin(), digest.end(), ':'), digest.end());
+    if (!digest.empty()) {
+      certDigests.push_back(digest);
+    }
   }
 
   void Print(text::Printer& printer) override {
     if (name) {
-      printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+      if (packageType) {
+        printer.Print(StringPrintf(
+          "uses-typed-package: type='%s' name='%s' version='%d' versionMajor='%d'",
+          packageType->data(), name->data(), version, versionMajor));
+        for (size_t i = 0; i < certDigests.size(); i++) {
+          printer.Print(StringPrintf(" certDigest='%s'", certDigests[i].data()));
+        }
+        printer.Print("\n");
+      } else {
+        printer.Print(StringPrintf("uses-package:'%s'\n", name->data()));
+      }
+    }
+  }
+};
+
+/** Represents <additional-certificate> elements. **/
+class AdditionalCertificate : public ManifestExtractor::Element {
+ public:
+  AdditionalCertificate() = default;
+
+  void Extract(xml::Element* element) override {
+    auto parent_stack = extractor()->parent_stack();
+    if (parent_stack.size() > 0) {
+      if (ElementCast<UsesPackage>(parent_stack[0])) {
+        UsesPackage* uses = ElementCast<UsesPackage>(parent_stack[0]);
+        uses->AddCertDigest(element);
+      } else if (ElementCast<UsesStaticLibrary>(parent_stack[0])) {
+        UsesStaticLibrary* uses = ElementCast<UsesStaticLibrary>(parent_stack[0]);
+        uses->AddCertDigest(element);
+      }
     }
   }
 };
@@ -1837,10 +1957,10 @@
                   && offhost_apdu_action)) {
 
             // Attempt to load the resource file
-            if (!meta_data->resource) {
+            if (!meta_data->resource.empty()) {
               return;
             }
-            auto resource = apk->LoadXml(*meta_data->resource, diag);
+            auto resource = apk->LoadXml(meta_data->resource, diag);
             if (!resource) {
               return;
             }
@@ -2065,6 +2185,9 @@
     {"uses-permission-sdk-23", std::is_base_of<UsesPermissionSdk23, T>::value},
     {"uses-library", std::is_base_of<UsesLibrary, T>::value},
     {"uses-package", std::is_base_of<UsesPackage, T>::value},
+    {"static-library", std::is_base_of<StaticLibrary, T>::value},
+    {"uses-static-library", std::is_base_of<UsesStaticLibrary, T>::value},
+    {"additional-certificate", std::is_base_of<AdditionalCertificate, T>::value},
     {"uses-sdk", std::is_base_of<UsesSdkBadging, T>::value},
   };
 
@@ -2110,7 +2233,10 @@
     {"uses-permission", &CreateType<UsesPermission>},
     {"uses-permission-sdk-23", &CreateType<UsesPermissionSdk23>},
     {"uses-library", &CreateType<UsesLibrary>},
+    {"static-library", &CreateType<StaticLibrary>},
+    {"uses-static-library", &CreateType<UsesStaticLibrary>},
     {"uses-package", &CreateType<UsesPackage>},
+    {"additional-certificate", &CreateType<AdditionalCertificate>},
     {"uses-sdk", &CreateType<UsesSdkBadging>},
   };
 
diff --git a/tools/aapt2/format/proto/ProtoSerialize_test.cpp b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
index 21fdbd8..74295ab 100644
--- a/tools/aapt2/format/proto/ProtoSerialize_test.cpp
+++ b/tools/aapt2/format/proto/ProtoSerialize_test.cpp
@@ -33,6 +33,7 @@
  public:
   MOCK_METHOD1(FindFile, io::IFile*(const StringPiece& path));
   MOCK_METHOD0(Iterator, std::unique_ptr<io::IFileCollectionIterator>());
+  MOCK_METHOD0(GetDirSeparator, char());
 };
 
 TEST(ProtoSerializeTest, SerializeSinglePackage) {
diff --git a/tools/aapt2/io/File.h b/tools/aapt2/io/File.h
index f06e28c..565aad6 100644
--- a/tools/aapt2/io/File.h
+++ b/tools/aapt2/io/File.h
@@ -25,6 +25,7 @@
 
 #include "Source.h"
 #include "io/Data.h"
+#include "util/Files.h"
 #include "util/Util.h"
 
 namespace aapt {
@@ -103,6 +104,7 @@
 
   virtual IFile* FindFile(const android::StringPiece& path) = 0;
   virtual std::unique_ptr<IFileCollectionIterator> Iterator() = 0;
+  virtual char GetDirSeparator() = 0;
 };
 
 }  // namespace io
diff --git a/tools/aapt2/io/FileSystem.cpp b/tools/aapt2/io/FileSystem.cpp
index 16a20f4c..51cc903 100644
--- a/tools/aapt2/io/FileSystem.cpp
+++ b/tools/aapt2/io/FileSystem.cpp
@@ -128,5 +128,9 @@
   return util::make_unique<FileCollectionIterator>(this);
 }
 
+char FileCollection::GetDirSeparator() {
+  return file::sDirSep;
+}
+
 }  // namespace io
 }  // namespace aapt
diff --git a/tools/aapt2/io/FileSystem.h b/tools/aapt2/io/FileSystem.h
index fb6bf6e..04c6fa1 100644
--- a/tools/aapt2/io/FileSystem.h
+++ b/tools/aapt2/io/FileSystem.h
@@ -67,6 +67,7 @@
   IFile* InsertFile(const android::StringPiece& path);
   IFile* FindFile(const android::StringPiece& path) override;
   std::unique_ptr<IFileCollectionIterator> Iterator() override;
+  char GetDirSeparator() override;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(FileCollection);
diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp
index 866e96d..427dc92 100644
--- a/tools/aapt2/io/ZipArchive.cpp
+++ b/tools/aapt2/io/ZipArchive.cpp
@@ -159,6 +159,13 @@
   return util::make_unique<ZipFileCollectionIterator>(this);
 }
 
+char ZipFileCollection::GetDirSeparator() {
+  // According to the zip file specification, section  4.4.17.1:
+  // "All slashes MUST be forward slashes '/' as opposed to backwards slashes '\' for compatibility
+  // with Amiga and UNIX file systems etc."
+  return '/';
+}
+
 ZipFileCollection::~ZipFileCollection() {
   if (handle_) {
     CloseArchive(handle_);
diff --git a/tools/aapt2/io/ZipArchive.h b/tools/aapt2/io/ZipArchive.h
index 8381259..b283e57 100644
--- a/tools/aapt2/io/ZipArchive.h
+++ b/tools/aapt2/io/ZipArchive.h
@@ -66,6 +66,7 @@
 
   io::IFile* FindFile(const android::StringPiece& path) override;
   std::unique_ptr<IFileCollectionIterator> Iterator() override;
+  char GetDirSeparator() override;
 
   ~ZipFileCollection() override;
 
diff --git a/tools/aapt2/java/ProguardRules.cpp b/tools/aapt2/java/ProguardRules.cpp
index d40795a..52e168e 100644
--- a/tools/aapt2/java/ProguardRules.cpp
+++ b/tools/aapt2/java/ProguardRules.cpp
@@ -384,7 +384,7 @@
   return true;
 }
 
-void WriteKeepSet(const KeepSet& keep_set, OutputStream* out) {
+void WriteKeepSet(const KeepSet& keep_set, OutputStream* out, bool minimal_keep) {
   Printer printer(out);
   for (const auto& entry : keep_set.manifest_class_set_) {
     for (const UsageLocation& location : entry.second) {
@@ -406,15 +406,19 @@
         printer.Print("-if class **.R$layout { int ")
             .Print(JavaClassGenerator::TransformToFieldName(location.name.entry))
             .Println("; }");
-        printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
-            .Print(entry.first.signature).Println("); }");
+
+        printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
+        printer.Print((minimal_keep) ? entry.first.signature : "...");
+        printer.Println("); }");
       }
     } else {
       for (const UsageLocation& location : entry.second) {
         printer.Print("# Referenced at ").Println(location.source.to_string());
       }
-      printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(")
-          .Print(entry.first.signature).Println("); }");
+
+      printer.Print("-keep class ").Print(entry.first.name).Print(" { <init>(");
+      printer.Print((minimal_keep) ? entry.first.signature : "...");
+      printer.Println("); }");
     }
     printer.Println();
   }
diff --git a/tools/aapt2/java/ProguardRules.h b/tools/aapt2/java/ProguardRules.h
index 01dad0b..38b4860 100644
--- a/tools/aapt2/java/ProguardRules.h
+++ b/tools/aapt2/java/ProguardRules.h
@@ -70,7 +70,7 @@
   }
 
  private:
-  friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+  friend void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
 
   friend bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                                std::set<UsageLocation>* locations);
@@ -89,7 +89,7 @@
 
 bool CollectResourceReferences(IAaptContext* context, ResourceTable* table, KeepSet* keep_set);
 
-void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out);
+void WriteKeepSet(const KeepSet& keep_set, io::OutputStream* out, bool minimal_keep);
 
 bool CollectLocations(const UsageLocation& location, const KeepSet& keep_set,
                       std::set<UsageLocation>* locations);
diff --git a/tools/aapt2/java/ProguardRules_test.cpp b/tools/aapt2/java/ProguardRules_test.cpp
index 83c72d8..3d93cb1 100644
--- a/tools/aapt2/java/ProguardRules_test.cpp
+++ b/tools/aapt2/java/ProguardRules_test.cpp
@@ -26,10 +26,10 @@
 
 namespace aapt {
 
-std::string GetKeepSetString(const proguard::KeepSet& set) {
+std::string GetKeepSetString(const proguard::KeepSet& set, bool minimal_rules) {
   std::string out;
   StringOutputStream sout(&out);
-  proguard::WriteKeepSet(set, &sout);
+  proguard::WriteKeepSet(set, &sout, minimal_rules);
   sout.Flush();
   return out;
 }
@@ -53,8 +53,17 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRulesForManifest(manifest.get(), &set, false));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarActivity { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarService { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarReceiver { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarProvider { <init>(); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarInstrumentation { <init>(); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarAppComponentFactory { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarBackupAgent { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.BarApplication { <init>(); }"));
@@ -75,8 +84,10 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
 }
 
@@ -89,8 +100,10 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
 }
 
@@ -105,8 +118,11 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(); }"));
 }
@@ -133,7 +149,12 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), navigation.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
+
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr("-keep class com.package.Foo { <init>(...); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.package.Bar { <init>(...); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.base.Nested { <init>(...); }"));
@@ -150,8 +171,10 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr(
       "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
 }
@@ -188,11 +211,16 @@
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), bar_layout.get(), &set));
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), foo_layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("int foo"));
+  EXPECT_THAT(actual, HasSubstr("int bar"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
   EXPECT_THAT(actual, HasSubstr(
-      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+    "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
 }
@@ -209,10 +237,16 @@
   set.AddReference({test::ParseNameOrDie("layout/bar"), {}}, layout->file.name);
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
-
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
   EXPECT_THAT(actual, HasSubstr(
-      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+      "-keep class com.foo.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
+  EXPECT_THAT(actual, HasSubstr("int foo"));
+  EXPECT_THAT(actual, HasSubstr("int bar"));
+
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
+  EXPECT_THAT(actual, HasSubstr(
+    "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
   EXPECT_THAT(actual, HasSubstr("-if class **.R$layout"));
   EXPECT_THAT(actual, HasSubstr("int foo"));
   EXPECT_THAT(actual, HasSubstr("int bar"));
@@ -230,11 +264,14 @@
   set.AddReference({test::ParseNameOrDie("style/MyStyle"), {}}, layout->file.name);
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, Not(HasSubstr("-if")));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, Not(HasSubstr("-if")));
   EXPECT_THAT(actual, HasSubstr(
-      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+    "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
 }
 
 TEST(ProguardRulesTest, ViewOnClickRuleIsEmitted) {
@@ -247,10 +284,13 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), layout.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
-
+  std::string actual = GetKeepSetString(set,  /** minimal_rules */ false);
   EXPECT_THAT(actual, HasSubstr(
       "-keepclassmembers class * { *** bar_method(android.view.View); }"));
+
+  actual = GetKeepSetString(set,  /** minimal_rules */ true);
+  EXPECT_THAT(actual, HasSubstr(
+    "-keepclassmembers class * { *** bar_method(android.view.View); }"));
 }
 
 TEST(ProguardRulesTest, MenuRulesAreEmitted) {
@@ -267,10 +307,16 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), menu.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
-
+  std::string actual = GetKeepSetString(set,  /** minimal_rules */ false);
   EXPECT_THAT(actual, HasSubstr(
-      "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
+    "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(...); }"));
+  EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
+
+  actual = GetKeepSetString(set,  /** minimal_rules */ true);
+  EXPECT_THAT(actual, HasSubstr(
+    "-keepclassmembers class * { *** on_click(android.view.MenuItem); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(android.content.Context); }"));
   EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Baz { <init>(android.content.Context); }"));
   EXPECT_THAT(actual, Not(HasSubstr("com.foo.Bat")));
@@ -287,10 +333,12 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transition.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr(
-      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+    "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
 }
 
 TEST(ProguardRulesTest, TransitionRulesAreEmitted) {
@@ -304,10 +352,12 @@
   proguard::KeepSet set;
   ASSERT_TRUE(proguard::CollectProguardRules(context.get(), transitionSet.get(), &set));
 
-  std::string actual = GetKeepSetString(set);
+  std::string actual = GetKeepSetString(set, /** minimal_rules */ false);
+  EXPECT_THAT(actual, HasSubstr("-keep class com.foo.Bar { <init>(...); }"));
 
+  actual = GetKeepSetString(set, /** minimal_rules */ true);
   EXPECT_THAT(actual, HasSubstr(
-      "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
+    "-keep class com.foo.Bar { <init>(android.content.Context, android.util.AttributeSet); }"));
 }
 
 }  // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index fa6538d..85bf6f2 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -393,6 +393,10 @@
   uses_static_library_action.Action(RequiredAndroidAttribute("certDigest"));
   uses_static_library_action["additional-certificate"];
 
+  xml::XmlNodeAction& uses_package_action = application_action["uses-package"];
+  uses_package_action.Action(RequiredNameIsJavaPackage);
+  uses_package_action["additional-certificate"];
+
   if (options_.debug_mode) {
     application_action.Action([&](xml::Element* el) -> bool {
       xml::Attribute *attr = el->FindOrCreateAttribute(xml::kSchemaAndroid, "debuggable");
diff --git a/tools/aapt2/util/Files_test.cpp b/tools/aapt2/util/Files_test.cpp
index 219c183..202cc26 100644
--- a/tools/aapt2/util/Files_test.cpp
+++ b/tools/aapt2/util/Files_test.cpp
@@ -18,11 +18,21 @@
 
 #include <sstream>
 
+#include "android-base/stringprintf.h"
+
 #include "test/Test.h"
 
+using ::android::base::StringPrintf;
+
 namespace aapt {
 namespace file {
 
+#ifdef _WIN32
+constexpr const char sTestDirSep = '\\';
+#else
+constexpr const char sTestDirSep = '/';
+#endif
+
 class FilesTest : public ::testing::Test {
  public:
   void SetUp() override {
@@ -42,16 +52,16 @@
 }
 
 TEST_F(FilesTest, AppendPathWithLeadingOrTrailingSeparators) {
-  std::string base = "hello/";
+  std::string base = StringPrintf("hello%c", sTestDirSep);
   AppendPath(&base, "there");
   EXPECT_EQ(expected_path_, base);
 
   base = "hello";
-  AppendPath(&base, "/there");
+  AppendPath(&base, StringPrintf("%cthere", sTestDirSep));
   EXPECT_EQ(expected_path_, base);
 
-  base = "hello/";
-  AppendPath(&base, "/there");
+  base = StringPrintf("hello%c", sTestDirSep);
+  AppendPath(&base, StringPrintf("%cthere", sTestDirSep));
   EXPECT_EQ(expected_path_, base);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 58c1300..7472278 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -513,7 +513,7 @@
     /**
      * @hide
      * Universal name for app creating the configuration
-     *    see {#link {@link PackageManager#getNameForUid(int)}
+     *    see {@link PackageManager#getNameForUid(int)}
      */
     @SystemApi
     public String creatorName;
@@ -521,7 +521,7 @@
     /**
      * @hide
      * Universal name for app updating the configuration
-     *    see {#link {@link PackageManager#getNameForUid(int)}
+     *    see {@link PackageManager#getNameForUid(int)}
      */
     @SystemApi
     public String lastUpdateName;