Merge "Use boundsOnScreen to offset the translate"
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 50d23ad2..4616ced 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -197,20 +197,17 @@
             api_file: "api/module-lib-current.txt",
             removed_api_file: "api/module-lib-removed.txt",
         },
-        // TODO(b/147559833) enable the compatibility check against the last release API
-        // and the API lint
-        //last_released: {
-        //    api_file: ":last-released-module-lib-api",
-        //    removed_api_file: "api/module-lib-removed.txt",
-        //    baseline_file: ":module-lib-api-incompatibilities-with-last-released"
-        //},
-        //api_lint: {
-        //    enabled: true,
-        //    new_since: ":last-released-module-lib-api",
-        //    baseline_file: "api/module-lib-lint-baseline.txt",
-        //},
+        last_released: {
+            api_file: ":last-released-module-lib-api",
+            removed_api_file: "api/module-lib-removed.txt",
+            baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+        },
+        api_lint: {
+            enabled: true,
+            new_since: ":last-released-module-lib-api",
+            baseline_file: "api/module-lib-lint-baseline.txt",
+        },
     },
-    //jdiff_enabled: true,
 }
 
 
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 8a9c774..b905273 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -606,10 +606,6 @@
         return hasLateConstraint;
     }
 
-    private static boolean kindofEqualsBundle(BaseBundle a, BaseBundle b) {
-        return (a == b) || (a != null && a.kindofEquals(b));
-    }
-
     @Override
     public boolean equals(Object o) {
         if (!(o instanceof JobInfo)) {
@@ -620,11 +616,11 @@
             return false;
         }
         // XXX won't be correct if one is parcelled and the other not.
-        if (!kindofEqualsBundle(extras, j.extras)) {
+        if (!BaseBundle.kindofEquals(extras, j.extras)) {
             return false;
         }
         // XXX won't be correct if one is parcelled and the other not.
-        if (!kindofEqualsBundle(transientExtras, j.transientExtras)) {
+        if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) {
             return false;
         }
         // XXX for now we consider two different clip data objects to be different,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index cf7f380..789f20b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1648,11 +1648,11 @@
                     pw.println();
                 }
             }
-            if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+            if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
                 pw.print(prefix); pw.print("  Extras: ");
                 pw.println(job.getExtras().toShortString());
             }
-            if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+            if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
                 pw.print(prefix); pw.print("  Transient extras: ");
                 pw.println(job.getTransientExtras().toShortString());
             }
@@ -1869,10 +1869,10 @@
                             job.getTriggerContentMaxDelay());
                 }
             }
-            if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+            if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
                 job.getExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.EXTRAS);
             }
-            if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+            if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
                 job.getTransientExtras().dumpDebug(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
             }
             if (job.getClipData() != null) {
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index baa1c25..1f9f18c 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -20,8 +20,6 @@
 
 apex_defaults {
     native_shared_libs: [
-        "libstatssocket",
-        "libstatspull",
         "libstats_jni",
     ],
     // binaries: ["vold"],
@@ -30,7 +28,6 @@
         "service-statsd",
     ],
     // prebuilts: ["my_prebuilt"],
-    compile_multilib: "both",
     name: "com.android.os.statsd-defaults",
     key: "com.android.os.statsd.key",
     certificate: ":com.android.os.statsd.certificate",
@@ -77,4 +74,4 @@
         //TODO (b/148620413): remove platform.
          "//apex_available:platform",
     ],
-}
+}
\ No newline at end of file
diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp
index 4ccdd7e..7c93bc7 100644
--- a/apex/statsd/aidl/Android.bp
+++ b/apex/statsd/aidl/Android.bp
@@ -38,10 +38,6 @@
         },
         ndk: {
             enabled: true,
-            apex_available: [
-                "com.android.os.statsd",
-            ],
         }
-
-    },
+    }
 }
diff --git a/api/current.txt b/api/current.txt
index 8394416..7bfd9a4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -289,7 +289,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 allowNativeHeapPointerTagging = 16844311; // 0x1010617
+    field public static final int allowNativeHeapPointerTagging = 16844307; // 0x1010613
     field public static final int allowParallelSyncs = 16843570; // 0x1010332
     field public static final int allowSingleTap = 16843353; // 0x1010259
     field public static final int allowTaskReparenting = 16843268; // 0x1010204
@@ -335,9 +335,6 @@
     field public static final int autoUrlDetect = 16843404; // 0x101028c
     field public static final int autoVerify = 16844014; // 0x10104ee
     field public static final int autofillHints = 16844118; // 0x1010556
-    field public static final int autofillInlineSuggestionChip = 16844307; // 0x1010613
-    field public static final int autofillInlineSuggestionSubtitle = 16844309; // 0x1010615
-    field public static final int autofillInlineSuggestionTitle = 16844308; // 0x1010614
     field public static final int autofilledHighlight = 16844136; // 0x1010568
     field public static final int background = 16842964; // 0x10100d4
     field public static final int backgroundDimAmount = 16842802; // 0x1010032
@@ -1082,6 +1079,7 @@
     field public static final int preferenceScreenStyle = 16842891; // 0x101008b
     field public static final int preferenceStyle = 16842894; // 0x101008e
     field public static final int presentationTheme = 16843712; // 0x10103c0
+    field public static final int preserveLegacyExternalStorage = 16844308; // 0x1010614
     field public static final int previewImage = 16843482; // 0x10102da
     field public static final int primaryContentAlpha = 16844114; // 0x1010552
     field public static final int priority = 16842780; // 0x101001c
@@ -2259,7 +2257,6 @@
     field public static final int ThemeOverlay_Material_Dialog = 16974550; // 0x10302d6
     field public static final int ThemeOverlay_Material_Dialog_Alert = 16974551; // 0x10302d7
     field public static final int ThemeOverlay_Material_Light = 16974410; // 0x103024a
-    field public static final int Theme_AutofillInlineSuggestion = 16974565; // 0x10302e5
     field public static final int Theme_Black = 16973832; // 0x1030008
     field public static final int Theme_Black_NoTitleBar = 16973833; // 0x1030009
     field public static final int Theme_Black_NoTitleBar_Fullscreen = 16973834; // 0x103000a
@@ -6426,9 +6423,13 @@
   public class StatusBarManager {
   }
 
-  public final class SyncNotedAppOp {
+  public final class SyncNotedAppOp implements android.os.Parcelable {
+    ctor public SyncNotedAppOp(@IntRange(from=0L) int, @Nullable String);
+    method public int describeContents();
     method @Nullable public String getFeatureId();
     method @NonNull public String getOp();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.SyncNotedAppOp> CREATOR;
   }
 
   @Deprecated public class TabActivity extends android.app.ActivityGroup {
@@ -7242,7 +7243,7 @@
   public final class FactoryResetProtectionPolicy implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<java.lang.String> getFactoryResetProtectionAccounts();
-    method public boolean isFactoryResetProtectionDisabled();
+    method public boolean isFactoryResetProtectionEnabled();
     method public void writeToParcel(@NonNull android.os.Parcel, @Nullable int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.FactoryResetProtectionPolicy> CREATOR;
   }
@@ -7251,7 +7252,7 @@
     ctor public FactoryResetProtectionPolicy.Builder();
     method @NonNull public android.app.admin.FactoryResetProtectionPolicy build();
     method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionAccounts(@NonNull java.util.List<java.lang.String>);
-    method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionDisabled(boolean);
+    method @NonNull public android.app.admin.FactoryResetProtectionPolicy.Builder setFactoryResetProtectionEnabled(boolean);
   }
 
   public class FreezePeriod {
@@ -27044,7 +27045,7 @@
     method @NonNull public String getId();
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectableRoutes();
     method @NonNull public java.util.List<android.media.MediaRoute2Info> getSelectedRoutes();
-    method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferrableRoutes();
+    method @NonNull public java.util.List<android.media.MediaRoute2Info> getTransferableRoutes();
     method public int getVolume();
     method public int getVolumeHandling();
     method public int getVolumeMax();
@@ -27424,7 +27425,7 @@
   public final class RouteDiscoveryPreference implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<java.lang.String> getPreferredFeatures();
-    method public boolean isActiveScan();
+    method public boolean shouldPerformActiveScan();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.RouteDiscoveryPreference> CREATOR;
   }
@@ -27433,8 +27434,8 @@
     ctor public RouteDiscoveryPreference.Builder(@NonNull java.util.List<java.lang.String>, boolean);
     ctor public RouteDiscoveryPreference.Builder(@NonNull android.media.RouteDiscoveryPreference);
     method @NonNull public android.media.RouteDiscoveryPreference build();
-    method @NonNull public android.media.RouteDiscoveryPreference.Builder setActiveScan(boolean);
     method @NonNull public android.media.RouteDiscoveryPreference.Builder setPreferredFeatures(@NonNull java.util.List<java.lang.String>);
+    method @NonNull public android.media.RouteDiscoveryPreference.Builder setShouldPerformActiveScan(boolean);
   }
 
   public final class RoutingSessionInfo implements android.os.Parcelable {
@@ -27445,7 +27446,7 @@
     method @NonNull public String getId();
     method @NonNull public java.util.List<java.lang.String> getSelectableRoutes();
     method @NonNull public java.util.List<java.lang.String> getSelectedRoutes();
-    method @NonNull public java.util.List<java.lang.String> getTransferrableRoutes();
+    method @NonNull public java.util.List<java.lang.String> getTransferableRoutes();
     method public int getVolume();
     method public int getVolumeHandling();
     method public int getVolumeMax();
@@ -27459,16 +27460,16 @@
     method @NonNull public android.media.RoutingSessionInfo.Builder addDeselectableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder addSelectableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder addSelectedRoute(@NonNull String);
-    method @NonNull public android.media.RoutingSessionInfo.Builder addTransferrableRoute(@NonNull String);
+    method @NonNull public android.media.RoutingSessionInfo.Builder addTransferableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo build();
     method @NonNull public android.media.RoutingSessionInfo.Builder clearDeselectableRoutes();
     method @NonNull public android.media.RoutingSessionInfo.Builder clearSelectableRoutes();
     method @NonNull public android.media.RoutingSessionInfo.Builder clearSelectedRoutes();
-    method @NonNull public android.media.RoutingSessionInfo.Builder clearTransferrableRoutes();
+    method @NonNull public android.media.RoutingSessionInfo.Builder clearTransferableRoutes();
     method @NonNull public android.media.RoutingSessionInfo.Builder removeDeselectableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder removeSelectedRoute(@NonNull String);
-    method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferrableRoute(@NonNull String);
+    method @NonNull public android.media.RoutingSessionInfo.Builder removeTransferableRoute(@NonNull String);
     method @NonNull public android.media.RoutingSessionInfo.Builder setControlHints(@Nullable android.os.Bundle);
     method @NonNull public android.media.RoutingSessionInfo.Builder setVolume(int);
     method @NonNull public android.media.RoutingSessionInfo.Builder setVolumeHandling(int);
@@ -30816,7 +30817,7 @@
 
   public class AudioGroup {
     ctor @Deprecated public AudioGroup();
-    ctor public AudioGroup(@Nullable android.content.Context);
+    ctor public AudioGroup(@NonNull android.content.Context);
     method public void clear();
     method public int getMode();
     method public android.net.rtp.AudioStream[] getStreams();
@@ -37314,6 +37315,7 @@
     method public void setCacheBehaviorTombstone(java.io.File, boolean) throws java.io.IOException;
     method public boolean unmountObb(String, boolean, android.os.storage.OnObbStateChangeListener);
     method public void unregisterStorageVolumeCallback(@NonNull android.os.storage.StorageManager.StorageVolumeCallback);
+    field @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE";
     field public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
     field public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES";
     field public static final String EXTRA_UUID = "android.os.storage.extra.UUID";
@@ -43047,6 +43049,7 @@
     method @NonNull public android.service.autofill.FillResponse build();
     method @NonNull public android.service.autofill.FillResponse.Builder disableAutofill(long);
     method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews);
+    method @NonNull public android.service.autofill.FillResponse.Builder setAuthentication(@NonNull android.view.autofill.AutofillId[], @Nullable android.content.IntentSender, @Nullable android.widget.RemoteViews, @Nullable android.service.autofill.InlinePresentation);
     method @NonNull public android.service.autofill.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
     method @NonNull public android.service.autofill.FillResponse.Builder setFieldClassificationIds(@NonNull android.view.autofill.AutofillId...);
     method @NonNull public android.service.autofill.FillResponse.Builder setFlags(int);
@@ -43964,7 +43967,7 @@
 
 package android.service.quickaccesswallet {
 
-  public final class GetWalletCardsCallback {
+  public interface GetWalletCardsCallback {
     method public void onFailure(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
     method public void onSuccess(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
   }
@@ -44005,7 +44008,6 @@
     method public abstract void onWalletCardsRequested(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.GetWalletCardsCallback);
     method public abstract void onWalletDismissed();
     method public final void sendWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
-    field public static final String ACTION_DISMISS_WALLET = "android.service.quickaccesswallet.action.DISMISS_WALLET";
     field public static final String ACTION_VIEW_WALLET = "android.service.quickaccesswallet.action.VIEW_WALLET";
     field public static final String ACTION_VIEW_WALLET_SETTINGS = "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
     field public static final String SERVICE_INTERFACE = "android.service.quickaccesswallet.QuickAccessWalletService";
@@ -46893,7 +46895,7 @@
   }
 
   public final class CellIdentityGsm extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method public int getArfcn();
     method public int getBsic();
     method public int getCid();
@@ -46909,7 +46911,7 @@
   }
 
   public final class CellIdentityLte extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method @NonNull public java.util.List<java.lang.Integer> getBands();
     method public int getBandwidth();
     method public int getCi();
@@ -46927,7 +46929,7 @@
   }
 
   public final class CellIdentityNr extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method @NonNull public java.util.List<java.lang.Integer> getBands();
     method @Nullable public String getMccString();
     method @Nullable public String getMncString();
@@ -46940,7 +46942,7 @@
   }
 
   public final class CellIdentityTdscdma extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method public int getCid();
     method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
     method public int getCpid();
@@ -46954,7 +46956,7 @@
   }
 
   public final class CellIdentityWcdma extends android.telephony.CellIdentity {
-    method @NonNull public java.util.List<java.lang.String> getAdditionalPlmns();
+    method @NonNull public java.util.Set<java.lang.String> getAdditionalPlmns();
     method public int getCid();
     method @Nullable public android.telephony.ClosedSubscriberGroupInfo getClosedSubscriberGroupInfo();
     method public int getLac();
@@ -47244,7 +47246,7 @@
     method @Nullable public android.telephony.CellIdentity getCellIdentity();
     method public int getDomain();
     method public int getNrState();
-    method @NonNull public String getRegisteredPlmn();
+    method @Nullable public String getRegisteredPlmn();
     method public int getTransportType();
     method public boolean isRegistered();
     method public boolean isRoaming();
@@ -52155,6 +52157,8 @@
     field public static final int CONTEXT_CLICK = 6; // 0x6
     field public static final int FLAG_IGNORE_GLOBAL_SETTING = 2; // 0x2
     field public static final int FLAG_IGNORE_VIEW_SETTING = 1; // 0x1
+    field public static final int GESTURE_END = 13; // 0xd
+    field public static final int GESTURE_START = 12; // 0xc
     field public static final int KEYBOARD_PRESS = 3; // 0x3
     field public static final int KEYBOARD_RELEASE = 7; // 0x7
     field public static final int KEYBOARD_TAP = 3; // 0x3
diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..6e59596
--- /dev/null
+++ b/api/module-lib-lint-baseline.txt
@@ -0,0 +1,33 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringConstants#EXTRA_ADD_TETHER_TYPE:
+    Inconsistent extra value; expected `android.net.extra.ADD_TETHER_TYPE`, was `extraAddTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_PROVISION_CALLBACK:
+    Inconsistent extra value; expected `android.net.extra.PROVISION_CALLBACK`, was `extraProvisionCallback`
+ActionValue: android.net.TetheringConstants#EXTRA_REM_TETHER_TYPE:
+    Inconsistent extra value; expected `android.net.extra.REM_TETHER_TYPE`, was `extraRemTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_RUN_PROVISION:
+    Inconsistent extra value; expected `android.net.extra.RUN_PROVISION`, was `extraRunProvision`
+ActionValue: android.net.TetheringConstants#EXTRA_SET_ALARM:
+    Inconsistent extra value; expected `android.net.extra.SET_ALARM`, was `extraSetAlarm`
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+    Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+    Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+    Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+ManagerConstructor: android.net.TetheringManager#TetheringManager(android.content.Context, java.util.function.Supplier<android.os.IBinder>):
+    Managers must always be obtained from Context; no direct constructors
+
+
+PrivateSuperclass: android.location.GnssAntennaInfo.PhaseCenterVariationCorrections:
+    Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections:
+    Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+
+
+StaticUtils: android.net.TetheringConstants:
+    Fully-static utility classes must not have constructor
diff --git a/api/system-current.txt b/api/system-current.txt
index 8725538..773cd4d 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -18,6 +18,7 @@
     field public static final String ACCESS_SHARED_LIBRARIES = "android.permission.ACCESS_SHARED_LIBRARIES";
     field public static final String ACCESS_SHORTCUTS = "android.permission.ACCESS_SHORTCUTS";
     field public static final String ACCESS_SURFACE_FLINGER = "android.permission.ACCESS_SURFACE_FLINGER";
+    field public static final String ACCESS_TV_DESCRAMBLER = "android.permission.ACCESS_TV_DESCRAMBLER";
     field public static final String ACCESS_TV_TUNER = "android.permission.ACCESS_TV_TUNER";
     field public static final String ACCESS_VIBRATOR_STATE = "android.permission.ACCESS_VIBRATOR_STATE";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
@@ -256,7 +257,6 @@
 
   public static final class R.attr {
     field public static final int allowClearUserDataOnFailedRestore = 16844288; // 0x1010600
-    field public static final int isAutofillInlineSuggestionTheme = 16844310; // 0x1010616
     field public static final int isVrOnly = 16844152; // 0x1010578
     field public static final int minExtensionVersion = 16844306; // 0x1010612
     field public static final int requiredSystemPropertyName = 16844133; // 0x1010565
@@ -364,6 +364,7 @@
   }
 
   public class AppOpsManager {
+    method @Nullable @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
     method @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static String[] getOpStrs();
     method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, @NonNull String, @Nullable java.lang.String...);
@@ -680,6 +681,19 @@
     method public void setNotificationAssistantAccessGranted(@Nullable android.content.ComponentName, boolean);
   }
 
+  public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
+    ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
+    method public int describeContents();
+    method @Nullable public String getFeatureId();
+    method @NonNull public String getMessage();
+    method @NonNull public String getOp();
+    method @NonNull public String getPackageName();
+    method public int getSamplingStrategy();
+    method @IntRange(from=0L) public int getUid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
+  }
+
   public class SearchManager implements android.content.DialogInterface.OnCancelListener android.content.DialogInterface.OnDismissListener {
     method public void launchAssist(@Nullable android.os.Bundle);
   }
@@ -4877,7 +4891,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public static android.media.tv.tuner.DemuxCapabilities getDemuxCapabilities(@NonNull android.content.Context);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.frontend.FrontendInfo getFrontendInfo();
     method @Nullable public android.media.tv.tuner.frontend.FrontendStatus getFrontendStatus(@NonNull int[]);
-    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.Descrambler openDescrambler();
+    method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER) public android.media.tv.tuner.Descrambler openDescrambler();
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrPlayback openDvrPlayback(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnPlaybackStatusChangedListener);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.dvr.DvrRecorder openDvrRecorder(long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.dvr.OnRecordStatusChangedListener);
     method @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER) public android.media.tv.tuner.filter.Filter openFilter(int, int, long, @Nullable java.util.concurrent.Executor, @Nullable android.media.tv.tuner.filter.FilterCallback);
@@ -4897,8 +4911,10 @@
   }
 
   public final class TunerConstants {
-    field public static final int INVALID_STREAM_ID = -1; // 0xffffffff
-    field public static final int INVALID_TS_PID = -1; // 0xffffffff
+    field public static final int INVALID_AV_SYNC_ID = -1; // 0xffffffff
+    field public static final int INVALID_FILTER_ID = -1; // 0xffffffff
+    field public static final int INVALID_STREAM_ID = 65535; // 0xffff
+    field public static final int INVALID_TS_PID = 65535; // 0xffff
     field public static final int RESULT_INVALID_ARGUMENT = 4; // 0x4
     field public static final int RESULT_INVALID_STATE = 3; // 0x3
     field public static final int RESULT_NOT_INITIALIZED = 2; // 0x2
@@ -7598,7 +7614,7 @@
     field @Deprecated public int numAssociation;
     field @Deprecated public int numScorerOverride;
     field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
-    field @Deprecated public boolean requirePMF;
+    field @Deprecated public boolean requirePmf;
     field @Deprecated @Nullable public String saePasswordId;
     field @Deprecated public boolean shared;
     field @Deprecated public boolean useExternalScores;
@@ -9680,6 +9696,7 @@
     field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE";
     field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS";
     field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI";
+    field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
   }
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
@@ -10186,6 +10203,7 @@
     method public void onConnected();
     method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
+    method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId);
     field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
   }
 
@@ -14063,6 +14081,14 @@
 
 }
 
+package android.view.inline {
+
+  public final class InlinePresentationSpec implements android.os.Parcelable {
+    method @Nullable public String getStyle();
+  }
+
+}
+
 package android.webkit {
 
   public abstract class CookieManager {
diff --git a/api/system-lint-baseline.txt b/api/system-lint-baseline.txt
index dfb0d74..f440381 100644
--- a/api/system-lint-baseline.txt
+++ b/api/system-lint-baseline.txt
@@ -206,7 +206,7 @@
     
 MutableBareField: android.net.wifi.WifiConfiguration#meteredOverride:
     
-MutableBareField: android.net.wifi.WifiConfiguration#requirePMF:
+MutableBareField: android.net.wifi.WifiConfiguration#requirePmf:
     
 MutableBareField: android.net.wifi.WifiConfiguration#saePasswordId:
     
diff --git a/api/test-current.txt b/api/test-current.txt
index 4c8bb02..0a821e4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -164,6 +164,7 @@
   public class AppOpsManager {
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void addHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOps);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void clearHistory();
+    method @Nullable @RequiresPermission("android.permission.GET_APP_OPS_STATS") public android.app.RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
     method @RequiresPermission("android.permission.GET_APP_OPS_STATS") public void getHistoricalOps(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method @RequiresPermission("android.permission.MANAGE_APPOPS") public void getHistoricalOpsFromDiskRaw(@NonNull android.app.AppOpsManager.HistoricalOpsRequest, @Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.app.AppOpsManager.HistoricalOps>);
     method public static int getNumOps();
@@ -447,6 +448,19 @@
     method public android.graphics.Rect getSourceRectHint();
   }
 
+  public final class RuntimeAppOpAccessMessage implements android.os.Parcelable {
+    ctor public RuntimeAppOpAccessMessage(@IntRange(from=0L) int, @IntRange(from=0L) int, @NonNull String, @Nullable String, @NonNull String, int);
+    method public int describeContents();
+    method @Nullable public String getFeatureId();
+    method @NonNull public String getMessage();
+    method @NonNull public String getOp();
+    method @NonNull public String getPackageName();
+    method public int getSamplingStrategy();
+    method @IntRange(from=0L) public int getUid();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.app.RuntimeAppOpAccessMessage> CREATOR;
+  }
+
   public class StatusBarManager {
     method public void collapsePanels();
     method public void expandNotificationsPanel();
@@ -2962,6 +2976,9 @@
     field public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
     field public static final String LOCATION_ACCESS_CHECK_DELAY_MILLIS = "location_access_check_delay_millis";
     field public static final String LOCATION_ACCESS_CHECK_INTERVAL_MILLIS = "location_access_check_interval_millis";
+    field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
+    field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
+    field public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
     field public static final String NOTIFICATION_BADGING = "notification_badging";
     field @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static final String SYNC_PARENT_SOUNDS = "sync_parent_sounds";
     field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
@@ -3188,6 +3205,7 @@
     method public void onConnected();
     method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
+    method public final boolean requestAutofill(@NonNull android.content.ComponentName, @NonNull android.view.autofill.AutofillId);
     field public static final String SERVICE_INTERFACE = "android.service.autofill.augmented.AugmentedAutofillService";
   }
 
@@ -3385,6 +3403,34 @@
 
 }
 
+package android.service.quickaccesswallet {
+
+  public interface QuickAccessWalletClient {
+    method public void addWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+    method @NonNull public static android.service.quickaccesswallet.QuickAccessWalletClient create(@NonNull android.content.Context);
+    method @Nullable public android.content.Intent createWalletIntent();
+    method @Nullable public android.content.Intent createWalletSettingsIntent();
+    method public void disconnect();
+    method public void getWalletCards(@NonNull android.service.quickaccesswallet.GetWalletCardsRequest, @NonNull android.service.quickaccesswallet.QuickAccessWalletClient.OnWalletCardsRetrievedCallback);
+    method public boolean isWalletFeatureAvailable();
+    method public boolean isWalletFeatureAvailableWhenDeviceLocked();
+    method public boolean isWalletServiceAvailable();
+    method public void notifyWalletDismissed();
+    method public void removeWalletServiceEventListener(@NonNull android.service.quickaccesswallet.QuickAccessWalletClient.WalletServiceEventListener);
+    method public void selectWalletCard(@NonNull android.service.quickaccesswallet.SelectWalletCardRequest);
+  }
+
+  public static interface QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+    method public void onWalletCardRetrievalError(@NonNull android.service.quickaccesswallet.GetWalletCardsError);
+    method public void onWalletCardsRetrieved(@NonNull android.service.quickaccesswallet.GetWalletCardsResponse);
+  }
+
+  public static interface QuickAccessWalletClient.WalletServiceEventListener {
+    method public void onWalletServiceEvent(@NonNull android.service.quickaccesswallet.WalletServiceEvent);
+  }
+
+}
+
 package android.service.quicksettings {
 
   public class TileService extends android.app.Service {
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 1bcf44e..2ee055d 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -120,15 +120,14 @@
         "libstatslog",
         "libstatsmetadata",
         "libsysutils",
-	// TODO(b/145923087): move to shared when statsd is moved to the apex
-        "libstatssocket",
         "libutils",
     ],
     shared_libs: [
-        "libbinder",
+        "libbinder_ndk",
         "libincident",
         "liblog",
-        "statsd-aidl-cpp",
+        "libstatssocket",
+        "statsd-aidl-ndk_platform",
     ],
 }
 
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 6b9d0e4..d95d594 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -25,6 +25,7 @@
 
 using std::string;
 using std::vector;
+using android::base::StringPrintf;
 
 // These constants must be kept in sync with those in StatsDimensionsValue.java
 const static int STATS_DIMENSIONS_VALUE_STRING_TYPE = 2;
@@ -73,7 +74,7 @@
                     break;
                 case STRING:
                     childParcel.valueType = STATS_DIMENSIONS_VALUE_STRING_TYPE;
-                    childParcel.stringValue = String16(dim.mValue.str_value.c_str());
+                    childParcel.stringValue = dim.mValue.str_value;
                     break;
                 default:
                     ALOGE("Encountered FieldValue with unsupported value type.");
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 4adcf96..a766bba 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <android/os/StatsDimensionsValueParcel.h>
+#include <aidl/android/os/StatsDimensionsValueParcel.h>
 #include <utils/JenkinsHash.h>
 #include <vector>
 #include "android-base/stringprintf.h"
@@ -27,7 +27,7 @@
 namespace os {
 namespace statsd {
 
-using android::base::StringPrintf;
+using ::aidl::android::os::StatsDimensionsValueParcel;
 
 struct Metric2Condition {
     int64_t conditionId;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 168833f..7087c68 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -28,8 +28,6 @@
 
 #include <android-base/file.h>
 #include <android-base/strings.h>
-#include <binder/IPCThreadState.h>
-#include <binder/PermissionController.h>
 #include <cutils/multiuser.h>
 #include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
 #include <frameworks/base/cmds/statsd/src/uid_data.pb.h>
@@ -47,85 +45,42 @@
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_MESSAGE;
 
+using Status = ::ndk::ScopedAStatus;
+
 namespace android {
 namespace os {
 namespace statsd {
 
 constexpr const char* kPermissionDump = "android.permission.DUMP";
-constexpr const char* kPermissionUsage = "android.permission.PACKAGE_USAGE_STATS";
-
-constexpr const char* kOpUsage = "android:get_usage_stats";
 
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
 // for StatsDataDumpProto
 const int FIELD_ID_REPORTS_LIST = 1;
 
-static binder::Status ok() {
-    return binder::Status::ok();
-}
-
-static binder::Status exception(uint32_t code, const std::string& msg) {
+static Status exception(int32_t code, const std::string& msg) {
     ALOGE("%s (%d)", msg.c_str(), code);
-    return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
+    return ::ndk::ScopedAStatus(AStatus_fromExceptionCodeWithMessage(code, msg.c_str()));
 }
 
 static bool checkPermission(const char* permission) {
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    uid_t uid = IPCThreadState::self()->getCallingUid();
+    pid_t pid = AIBinder_getCallingPid();
+    uid_t uid = AIBinder_getCallingUid();
     return checkPermissionForIds(permission, pid, uid);
 }
 
-binder::Status checkUid(uid_t expectedUid) {
-    uid_t uid = IPCThreadState::self()->getCallingUid();
+Status checkUid(uid_t expectedUid) {
+    uid_t uid = AIBinder_getCallingUid();
     if (uid == expectedUid || uid == AID_ROOT) {
-        return ok();
+        return Status::ok();
     } else {
-        return exception(binder::Status::EX_SECURITY,
-                StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
-    }
-}
-
-binder::Status checkDumpAndUsageStats(const String16& packageName) {
-    pid_t pid = IPCThreadState::self()->getCallingPid();
-    uid_t uid = IPCThreadState::self()->getCallingUid();
-
-    // Root, system, and shell always have access
-    if (uid == AID_ROOT || uid == AID_SYSTEM || uid == AID_SHELL) {
-        return ok();
-    }
-
-    // Caller must be granted these permissions
-    if (!checkPermission(kPermissionDump)) {
-        return exception(binder::Status::EX_SECURITY,
-                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
-    }
-    if (!checkPermission(kPermissionUsage)) {
-        return exception(binder::Status::EX_SECURITY,
-                StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
-    }
-
-    // Caller must also have usage stats op granted
-    PermissionController pc;
-    switch (pc.noteOp(String16(kOpUsage), uid, packageName)) {
-        case PermissionController::MODE_ALLOWED:
-        case PermissionController::MODE_DEFAULT:
-            return ok();
-        default:
-            return exception(binder::Status::EX_SECURITY,
-                    StringPrintf("UID %d / PID %d lacks app-op %s", uid, pid, kOpUsage));
+        return exception(EX_SECURITY,
+                         StringPrintf("UID %d is not expected UID %d", uid, expectedUid));
     }
 }
 
 #define ENFORCE_UID(uid) {                                        \
-    binder::Status status = checkUid((uid));                      \
-    if (!status.isOk()) {                                         \
-        return status;                                            \
-    }                                                             \
-}
-
-#define ENFORCE_DUMP_AND_USAGE_STATS(packageName) {               \
-    binder::Status status = checkDumpAndUsageStats(packageName);  \
+    Status status = checkUid((uid));                              \
     if (!status.isOk()) {                                         \
         return status;                                            \
     }                                                             \
@@ -134,13 +89,13 @@
 StatsService::StatsService(const sp<Looper>& handlerLooper, shared_ptr<LogEventQueue> queue)
     : mAnomalyAlarmMonitor(new AlarmMonitor(
               MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
-              [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+              [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
                   if (sc != nullptr) {
                       sc->setAnomalyAlarm(timeMillis);
                       StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
                   }
               },
-              [](const sp<IStatsCompanionService>& sc) {
+              [](const shared_ptr<IStatsCompanionService>& sc) {
                   if (sc != nullptr) {
                       sc->cancelAnomalyAlarm();
                       StatsdStats::getInstance().noteRegisteredAnomalyAlarmChanged();
@@ -148,19 +103,21 @@
               })),
       mPeriodicAlarmMonitor(new AlarmMonitor(
               MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS,
-              [](const sp<IStatsCompanionService>& sc, int64_t timeMillis) {
+              [](const shared_ptr<IStatsCompanionService>& sc, int64_t timeMillis) {
                   if (sc != nullptr) {
                       sc->setAlarmForSubscriberTriggering(timeMillis);
                       StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
                   }
               },
-              [](const sp<IStatsCompanionService>& sc) {
+              [](const shared_ptr<IStatsCompanionService>& sc) {
                   if (sc != nullptr) {
                       sc->cancelAlarmForSubscriberTriggering();
                       StatsdStats::getInstance().noteRegisteredPeriodicAlarmChanged();
                   }
               })),
-      mEventQueue(queue) {
+      mEventQueue(queue),
+      mStatsCompanionServiceDeathRecipient(AIBinder_DeathRecipient_new(
+              StatsService::statsCompanionServiceDied)) {
     mUidMap = UidMap::getInstance();
     mPullerManager = new StatsPullerManager();
     StatsPuller::SetUidMap(mUidMap);
@@ -169,22 +126,20 @@
             mUidMap, mPullerManager, mAnomalyAlarmMonitor, mPeriodicAlarmMonitor,
             getElapsedRealtimeNs(),
             [this](const ConfigKey& key) {
-                sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+                shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
                 if (receiver == nullptr) {
-                    VLOG("Could not find a broadcast receiver for %s",
-                        key.ToString().c_str());
+                    VLOG("Could not find a broadcast receiver for %s", key.ToString().c_str());
                     return false;
                 } else if (receiver->sendDataBroadcast(
                            mProcessor->getLastReportTimeNs(key)).isOk()) {
                     return true;
                 } else {
-                    VLOG("Failed to send a broadcast for receiver %s",
-                        key.ToString().c_str());
+                    VLOG("Failed to send a broadcast for receiver %s", key.ToString().c_str());
                     return false;
                 }
             },
             [this](const int& uid, const vector<int64_t>& activeConfigs) {
-                sp<IPendingIntentRef> receiver =
+                shared_ptr<IPendingIntentRef> receiver =
                     mConfigManager->GetActiveConfigsChangedReceiver(uid);
                 if (receiver == nullptr) {
                     VLOG("Could not find receiver for uid %d", uid);
@@ -245,36 +200,6 @@
 }
 
 /**
- * Implement our own because the default binder implementation isn't
- * properly handling SHELL_COMMAND_TRANSACTION.
- */
-status_t StatsService::onTransact(uint32_t code, const Parcel& data, Parcel* reply,
-                                  uint32_t flags) {
-    switch (code) {
-        case SHELL_COMMAND_TRANSACTION: {
-            int in = data.readFileDescriptor();
-            int out = data.readFileDescriptor();
-            int err = data.readFileDescriptor();
-            int argc = data.readInt32();
-            Vector<String8> args;
-            for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
-                args.add(String8(data.readString16()));
-            }
-            sp<IShellCallback> shellCallback = IShellCallback::asInterface(data.readStrongBinder());
-            sp<IResultReceiver> resultReceiver =
-                    IResultReceiver::asInterface(data.readStrongBinder());
-
-            err = command(in, out, err, args, resultReceiver);
-            if (resultReceiver != nullptr) {
-                resultReceiver->send(err);
-            }
-            return NO_ERROR;
-        }
-        default: { return BnStatsd::onTransact(code, data, reply, flags); }
-    }
-}
-
-/**
  * Write data from statsd.
  * Format for statsdStats:  adb shell dumpsys stats --metadata [-v] [--proto]
  * Format for data report:  adb shell dumpsys stats [anything other than --metadata] [--proto]
@@ -283,20 +208,21 @@
  *     (bugreports call "adb shell dumpsys stats --dump-priority NORMAL -a --proto")
  * TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
  */
-status_t StatsService::dump(int fd, const Vector<String16>& args) {
+status_t StatsService::dump(int fd, const char** args, uint32_t numArgs) {
     if (!checkPermission(kPermissionDump)) {
         return PERMISSION_DENIED;
     }
-    int lastArg = args.size() - 1;
+
+    int lastArg = numArgs - 1;
     bool asProto = false;
-    if (lastArg >= 0 && !args[lastArg].compare(String16("--proto"))) { // last argument
+    if (lastArg >= 0 && string(args[lastArg]) == "--proto") { // last argument
         asProto = true;
         lastArg--;
     }
-    if (args.size() > 0 && !args[0].compare(String16("--metadata"))) { // first argument
+    if (numArgs > 0 && string(args[0]) == "--metadata") { // first argument
         // Request is to dump statsd stats.
         bool verbose = false;
-        if (lastArg >= 0 && !args[lastArg].compare(String16("-v"))) {
+        if (lastArg >= 0 && string(args[lastArg]) == "-v") {
             verbose = true;
             lastArg--;
         }
@@ -351,67 +277,72 @@
 /**
  * Implementation of the adb shell cmd stats command.
  */
-status_t StatsService::command(int in, int out, int err, Vector<String8>& args,
-                               sp<IResultReceiver> resultReceiver) {
-    uid_t uid = IPCThreadState::self()->getCallingUid();
-    if (uid != AID_ROOT && uid != AID_SHELL) {
+status_t StatsService::handleShellCommand(int in, int out, int err, const char** argv,
+                                          uint32_t argc) {
+    uid_t uid = AIBinder_getCallingUid();
+    if (uid != AID_ROOT || uid != AID_SHELL) {
         return PERMISSION_DENIED;
     }
 
-    const int argCount = args.size();
-    if (argCount >= 1) {
+    Vector<String8> utf8Args;
+    utf8Args.setCapacity(argc);
+    for (uint32_t i = 0; i < argc; i++) {
+        utf8Args.push(String8(argv[i]));
+    }
+
+    if (argc >= 1) {
         // adb shell cmd stats config ...
-        if (!args[0].compare(String8("config"))) {
-            return cmd_config(in, out, err, args);
+        if (!utf8Args[0].compare(String8("config"))) {
+            return cmd_config(in, out, err, utf8Args);
         }
 
-        if (!args[0].compare(String8("print-uid-map"))) {
-            return cmd_print_uid_map(out, args);
+        if (!utf8Args[0].compare(String8("print-uid-map"))) {
+            return cmd_print_uid_map(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("dump-report"))) {
-            return cmd_dump_report(out, args);
+        if (!utf8Args[0].compare(String8("dump-report"))) {
+            return cmd_dump_report(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("pull-source")) && args.size() > 1) {
-            return cmd_print_pulled_metrics(out, args);
+        if (!utf8Args[0].compare(String8("pull-source")) && argc > 1) {
+            return cmd_print_pulled_metrics(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("send-broadcast"))) {
-            return cmd_trigger_broadcast(out, args);
+        if (!utf8Args[0].compare(String8("send-broadcast"))) {
+            return cmd_trigger_broadcast(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("print-stats"))) {
-            return cmd_print_stats(out, args);
+        if (!utf8Args[0].compare(String8("print-stats"))) {
+            return cmd_print_stats(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("meminfo"))) {
+        if (!utf8Args[0].compare(String8("meminfo"))) {
             return cmd_dump_memory_info(out);
         }
 
-        if (!args[0].compare(String8("write-to-disk"))) {
+        if (!utf8Args[0].compare(String8("write-to-disk"))) {
             return cmd_write_data_to_disk(out);
         }
 
-        if (!args[0].compare(String8("log-app-breadcrumb"))) {
-            return cmd_log_app_breadcrumb(out, args);
+        if (!utf8Args[0].compare(String8("log-app-breadcrumb"))) {
+            return cmd_log_app_breadcrumb(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("log-binary-push"))) {
-            return cmd_log_binary_push(out, args);
+        if (!utf8Args[0].compare(String8("log-binary-push"))) {
+            return cmd_log_binary_push(out, utf8Args);
         }
 
-        if (!args[0].compare(String8("clear-puller-cache"))) {
+        if (!utf8Args[0].compare(String8("clear-puller-cache"))) {
             return cmd_clear_puller_cache(out);
         }
 
-        if (!args[0].compare(String8("print-logs"))) {
-            return cmd_print_logs(out, args);
+        if (!utf8Args[0].compare(String8("print-logs"))) {
+            return cmd_print_logs(out, utf8Args);
         }
-        if (!args[0].compare(String8("send-active-configs"))) {
-            return cmd_trigger_active_config_broadcast(out, args);
+        if (!utf8Args[0].compare(String8("send-active-configs"))) {
+            return cmd_trigger_active_config_broadcast(out, utf8Args);
         }
-        if (!args[0].compare(String8("data-subscribe"))) {
+        if (!utf8Args[0].compare(String8("data-subscribe"))) {
             {
                 std::lock_guard<std::mutex> lock(mShellSubscriberMutex);
                 if (mShellSubscriber == nullptr) {
@@ -419,14 +350,10 @@
                 }
             }
             int timeoutSec = -1;
-            if (argCount >= 2) {
-                timeoutSec = atoi(args[1].c_str());
+            if (argc >= 2) {
+                timeoutSec = atoi(utf8Args[1].c_str());
             }
-            if (resultReceiver == nullptr) {
-                ALOGI("Null resultReceiver given, no subscription will be started");
-                return UNEXPECTED_NULL;
-            }
-            mShellSubscriber->startNewSubscription(in, out, resultReceiver, timeoutSec);
+            mShellSubscriber->startNewSubscription(in, out, timeoutSec);
             return NO_ERROR;
         }
     }
@@ -556,7 +483,7 @@
     const int argCount = args.size();
     if (argCount == 2) {
         // Automatically pick the UID
-        uid = IPCThreadState::self()->getCallingUid();
+        uid = AIBinder_getCallingUid();
         name.assign(args[1].c_str(), args[1].size());
         good = true;
     } else if (argCount == 3) {
@@ -572,17 +499,15 @@
         return UNKNOWN_ERROR;
     }
     ConfigKey key(uid, StrToInt64(name));
-    sp<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
+    shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetConfigReceiver(key);
     if (receiver == nullptr) {
         VLOG("Could not find receiver for %s, %s", args[1].c_str(), args[2].c_str());
         return UNKNOWN_ERROR;
-    } else if (receiver->sendDataBroadcast(
-               mProcessor->getLastReportTimeNs(key)).isOk()) {
+    } else if (receiver->sendDataBroadcast(mProcessor->getLastReportTimeNs(key)).isOk()) {
         VLOG("StatsService::trigger broadcast succeeded to %s, %s", args[1].c_str(),
              args[2].c_str());
     } else {
-        VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(),
-             args[2].c_str());
+        VLOG("StatsService::trigger broadcast failed to %s, %s", args[1].c_str(), args[2].c_str());
         return UNKNOWN_ERROR;
     }
     return NO_ERROR;
@@ -594,7 +519,7 @@
     vector<int64_t> configIds;
     if (argCount == 1) {
         // Automatically pick the uid and send a broadcast that has no active configs.
-        uid = IPCThreadState::self()->getCallingUid();
+        uid = AIBinder_getCallingUid();
         mProcessor->GetActiveConfigs(uid, configIds);
     } else {
         int curArg = 1;
@@ -608,7 +533,7 @@
             }
             curArg++;
         } else {
-            uid = IPCThreadState::self()->getCallingUid();
+            uid = AIBinder_getCallingUid();
         }
         if (curArg == argCount || args[curArg] != "--configs") {
             VLOG("Reached end of args, or specify configs not set. Sending actual active configs,");
@@ -628,7 +553,7 @@
             }
         }
     }
-    sp<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
+    shared_ptr<IPendingIntentRef> receiver = mConfigManager->GetActiveConfigsChangedReceiver(uid);
     if (receiver == nullptr) {
         VLOG("Could not find receiver for uid %d", uid);
         return UNKNOWN_ERROR;
@@ -651,7 +576,7 @@
 
             if (argCount == 3) {
                 // Automatically pick the UID
-                uid = IPCThreadState::self()->getCallingUid();
+                uid = AIBinder_getCallingUid();
                 name.assign(args[2].c_str(), args[2].size());
                 good = true;
             } else if (argCount == 4) {
@@ -734,7 +659,7 @@
         }
         if (argCount == 2) {
             // Automatically pick the UID
-            uid = IPCThreadState::self()->getCallingUid();
+            uid = AIBinder_getCallingUid();
             name.assign(args[1].c_str(), args[1].size());
             good = true;
         } else if (argCount == 3) {
@@ -826,7 +751,7 @@
     const int argCount = args.size();
     if (argCount == 3) {
         // Automatically pick the UID
-        uid = IPCThreadState::self()->getCallingUid();
+        uid = AIBinder_getCallingUid();
         label = atoi(args[1].c_str());
         state = atoi(args[2].c_str());
         good = true;
@@ -903,9 +828,8 @@
 }
 
 status_t StatsService::cmd_clear_puller_cache(int out) {
-    IPCThreadState* ipc = IPCThreadState::self();
     VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
-            ipc->getCallingPid(), ipc->getCallingUid());
+            AIBinder_getCallingPid(), AIBinder_getCallingUid());
     if (checkPermission(kPermissionDump)) {
         int cleared = mPullerManager->ForceClearPullerCache();
         dprintf(out, "Puller removed %d cached data!\n", cleared);
@@ -916,9 +840,8 @@
 }
 
 status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
-    IPCThreadState* ipc = IPCThreadState::self();
-    VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
-         ipc->getCallingUid());
+    VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
+         AIBinder_getCallingUid());
     if (checkPermission(kPermissionDump)) {
         bool enabled = true;
         if (args.size() >= 2) {
@@ -950,24 +873,24 @@
     }
     uid = goodUid;
 
-    int32_t callingUid = IPCThreadState::self()->getCallingUid();
+    int32_t callingUid = AIBinder_getCallingUid();
     return mEngBuild // UserDebug/EngBuild are allowed to impersonate uids.
             || (callingUid == goodUid) // Anyone can 'impersonate' themselves.
             || (callingUid == AID_ROOT && goodUid == AID_SHELL); // ROOT can impersonate SHELL.
 }
 
-Status StatsService::informAllUidData(const ParcelFileDescriptor& fd) {
+Status StatsService::informAllUidData(const ScopedFileDescriptor& fd) {
     ENFORCE_UID(AID_SYSTEM);
     // Read stream into buffer.
     string buffer;
     if (!android::base::ReadFdToString(fd.get(), &buffer)) {
-        return exception(Status::EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
+        return exception(EX_ILLEGAL_ARGUMENT, "Failed to read all data from the pipe.");
     }
 
     // Parse buffer.
     UidData uidData;
     if (!uidData.ParseFromString(buffer)) {
-        return exception(Status::EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
+        return exception(EX_ILLEGAL_ARGUMENT, "Error parsing proto stream for UidData.");
     }
 
     vector<String16> versionStrings;
@@ -1002,20 +925,28 @@
     return Status::ok();
 }
 
-Status StatsService::informOnePackage(const String16& app, int32_t uid, int64_t version,
-                                      const String16& version_string, const String16& installer) {
+Status StatsService::informOnePackage(const string& app, int32_t uid, int64_t version,
+                                      const string& versionString, const string& installer) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::informOnePackage was called");
-    mUidMap->updateApp(getElapsedRealtimeNs(), app, uid, version, version_string, installer);
+    // TODO(b/149254662): This is gross. We should consider changing statsd
+    // internals to use std::string.
+    String16 utf16App = String16(app.c_str());
+    String16 utf16VersionString = String16(versionString.c_str());
+    String16 utf16Installer = String16(installer.c_str());
+
+    mUidMap->updateApp(getElapsedRealtimeNs(), utf16App, uid, version, utf16VersionString,
+                       utf16Installer);
     return Status::ok();
 }
 
-Status StatsService::informOnePackageRemoved(const String16& app, int32_t uid) {
+Status StatsService::informOnePackageRemoved(const string& app, int32_t uid) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::informOnePackageRemoved was called");
-    mUidMap->removeApp(getElapsedRealtimeNs(), app, uid);
+    String16 utf16App = String16(app.c_str());
+    mUidMap->removeApp(getElapsedRealtimeNs(), utf16App, uid);
     mConfigManager->RemoveConfigs(uid);
     return Status::ok();
 }
@@ -1079,7 +1010,7 @@
 }
 
 void StatsService::sayHiToStatsCompanion() {
-    sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+    shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
     if (statsCompanion != nullptr) {
         VLOG("Telling statsCompanion that statsd is ready");
         statsCompanion->statsdReady();
@@ -1092,14 +1023,14 @@
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::statsCompanionReady was called");
-    sp<IStatsCompanionService> statsCompanion = getStatsCompanionService();
+    shared_ptr<IStatsCompanionService> statsCompanion = getStatsCompanionService();
     if (statsCompanion == nullptr) {
-        return Status::fromExceptionCode(
-                Status::EX_NULL_POINTER,
-                "statscompanion unavailable despite it contacting statsd!");
+        return exception(EX_NULL_POINTER,
+                         "StatsCompanion unavailable despite it contacting statsd.");
     }
     VLOG("StatsService::statsCompanionReady linking to statsCompanion.");
-    IInterface::asBinder(statsCompanion)->linkToDeath(this);
+    AIBinder_linkToDeath(statsCompanion->asBinder().get(),
+                         mStatsCompanionServiceDeathRecipient.get(), this);
     mPullerManager->SetStatsCompanionService(statsCompanion);
     mAnomalyAlarmMonitor->setStatsCompanionService(statsCompanion);
     mPeriodicAlarmMonitor->setStatsCompanionService(statsCompanion);
@@ -1127,39 +1058,47 @@
     }
 }
 
-Status StatsService::getData(int64_t key, const int32_t callingUid, vector<uint8_t>* output) {
+Status StatsService::getData(int64_t key, const int32_t callingUid, vector<int8_t>* output) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::getData with Uid %i", callingUid);
     ConfigKey configKey(callingUid, key);
+    // TODO(b/149254662): Since libbinder_ndk uses int8_t instead of uint8_t,
+    // there are inconsistencies with internal statsd logic. Instead of
+    // modifying lots of files, we create a temporary output array of int8_t and
+    // copy its data into output. This is a bad hack, but hopefully
+    // libbinder_ndk will transition to using uint8_t soon: progress is tracked
+    // in b/144957764. Same applies to StatsService::getMetadata.
+    vector<uint8_t> unsignedOutput;
     // The dump latency does not matter here since we do not include the current bucket, we do not
     // need to pull any new data anyhow.
     mProcessor->onDumpReport(configKey, getElapsedRealtimeNs(), false /* include_current_bucket*/,
-                             true /* erase_data */, GET_DATA_CALLED, FAST, output);
+                             true /* erase_data */, GET_DATA_CALLED, FAST, &unsignedOutput);
+    *output = vector<int8_t>(unsignedOutput.begin(), unsignedOutput.end());
     return Status::ok();
 }
 
-Status StatsService::getMetadata(vector<uint8_t>* output) {
+Status StatsService::getMetadata(vector<int8_t>* output) {
     ENFORCE_UID(AID_SYSTEM);
 
-    StatsdStats::getInstance().dumpStats(output, false); // Don't reset the counters.
+    vector<uint8_t> unsignedOutput;
+    StatsdStats::getInstance().dumpStats(&unsignedOutput, false); // Don't reset the counters.
+    *output = vector<int8_t>(unsignedOutput.begin(), unsignedOutput.end());
     return Status::ok();
 }
 
-Status StatsService::addConfiguration(int64_t key, const vector <uint8_t>& config,
+Status StatsService::addConfiguration(int64_t key, const vector <int8_t>& config,
                                       const int32_t callingUid) {
     ENFORCE_UID(AID_SYSTEM);
 
     if (addConfigurationChecked(callingUid, key, config)) {
         return Status::ok();
     } else {
-        ALOGE("Could not parse malformatted StatsdConfig");
-        return Status::fromExceptionCode(binder::Status::EX_ILLEGAL_ARGUMENT,
-                                         "config does not correspond to a StatsdConfig proto");
+        return exception(EX_ILLEGAL_ARGUMENT, "Could not parse malformatted StatsdConfig.");
     }
 }
 
-bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config) {
+bool StatsService::addConfigurationChecked(int uid, int64_t key, const vector<int8_t>& config) {
     ConfigKey configKey(uid, key);
     StatsdConfig cfg;
     if (config.size() > 0) {  // If the config is empty, skip parsing.
@@ -1180,7 +1119,7 @@
 }
 
 Status StatsService::setDataFetchOperation(int64_t key,
-                                           const sp<IPendingIntentRef>& pir,
+                                           const shared_ptr<IPendingIntentRef>& pir,
                                            const int32_t callingUid) {
     ENFORCE_UID(AID_SYSTEM);
 
@@ -1194,7 +1133,7 @@
     return Status::ok();
 }
 
-Status StatsService::setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+Status StatsService::setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
                                                       const int32_t callingUid,
                                                       vector<int64_t>* output) {
     ENFORCE_UID(AID_SYSTEM);
@@ -1225,7 +1164,7 @@
 
 Status StatsService::setBroadcastSubscriber(int64_t configId,
                                             int64_t subscriberId,
-                                            const sp<IPendingIntentRef>& pir,
+                                            const shared_ptr<IPendingIntentRef>& pir,
                                             const int32_t callingUid) {
     ENFORCE_UID(AID_SYSTEM);
 
@@ -1252,14 +1191,14 @@
     // Permission check not necessary as it's meant for applications to write to
     // statsd.
     android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
-                               (int32_t) IPCThreadState::self()->getCallingUid(), label,
+                               (int32_t) AIBinder_getCallingUid(), label,
                                state);
     return Status::ok();
 }
 
 Status StatsService::registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
                                     int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-                                    const sp<android::os::IPullAtomCallback>& pullerCallback) {
+                                    const shared_ptr<IPullAtomCallback>& pullerCallback) {
     ENFORCE_UID(AID_SYSTEM);
 
     VLOG("StatsService::registerPullAtomCallback called.");
@@ -1270,10 +1209,10 @@
 
 Status StatsService::registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs,
                                     int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-                                    const sp<android::os::IPullAtomCallback>& pullerCallback) {
+                                    const shared_ptr<IPullAtomCallback>& pullerCallback) {
 
     VLOG("StatsService::registerNativePullAtomCallback called.");
-    int32_t uid = IPCThreadState::self()->getCallingUid();
+    int32_t uid = AIBinder_getCallingUid();
     mPullerManager->RegisterPullAtomCallback(uid, atomTag, coolDownNs, timeoutNs, additiveFields,
                                              pullerCallback);
     return Status::ok();
@@ -1288,7 +1227,7 @@
 
 Status StatsService::unregisterNativePullAtomCallback(int32_t atomTag) {
     VLOG("StatsService::unregisterNativePullAtomCallback called.");
-    int32_t uid = IPCThreadState::self()->getCallingUid();
+    int32_t uid = AIBinder_getCallingUid();
     mPullerManager->UnregisterPullAtomCallback(uid, atomTag);
     return Status::ok();
 }
@@ -1314,7 +1253,13 @@
     return Status::ok();
 }
 
-void StatsService::binderDied(const wp <IBinder>& who) {
+
+void StatsService::statsCompanionServiceDied(void* cookie) {
+    auto thiz = static_cast<StatsService*>(cookie);
+    thiz->statsCompanionServiceDiedImpl();
+}
+
+void StatsService::statsCompanionServiceDiedImpl() {
     ALOGW("statscompanion service died");
     StatsdStats::getInstance().noteSystemServerRestart(getWallClockSec());
     if (mProcessor != nullptr) {
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 0527d43..e6723c8 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -27,27 +27,30 @@
 #include "shell/ShellSubscriber.h"
 #include "statscompanion_util.h"
 
-#include <android/os/BnStatsd.h>
-#include <android/os/IPendingIntentRef.h>
-#include <android/os/IStatsCompanionService.h>
-#include <android/os/IStatsd.h>
-#include <binder/IResultReceiver.h>
-#include <binder/ParcelFileDescriptor.h>
+#include <aidl/android/os/BnStatsd.h>
+#include <aidl/android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPullAtomCallback.h>
 #include <utils/Looper.h>
 
 #include <mutex>
 
 using namespace android;
-using namespace android::binder;
 using namespace android::os;
 using namespace std;
 
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnStatsd;
+using aidl::android::os::IPendingIntentRef;
+using aidl::android::os::IPullAtomCallback;
+using ::ndk::ScopedAIBinder_DeathRecipient;
+using ::ndk::ScopedFileDescriptor;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
 
-class StatsService : public BnStatsd,
-                     public IBinder::DeathRecipient {
+class StatsService : public BnStatsd {
 public:
     StatsService(const sp<Looper>& handlerLooper, std::shared_ptr<LogEventQueue> queue);
     virtual ~StatsService();
@@ -55,10 +58,9 @@
     /** The anomaly alarm registered with AlarmManager won't be updated by less than this. */
     const uint32_t MIN_DIFF_TO_UPDATE_REGISTERED_ALARM_SECS = 5;
 
-    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
-    virtual status_t dump(int fd, const Vector<String16>& args);
-    virtual status_t command(int inFd, int outFd, int err, Vector<String8>& args,
-                             sp<IResultReceiver> resultReceiver);
+    virtual status_t dump(int fd, const char** args, uint32_t numArgs) override;
+    virtual status_t handleShellCommand(int in, int out, int err, const char** argv,
+                                        uint32_t argc) override;
 
     virtual Status systemRunning();
     virtual Status statsCompanionReady();
@@ -66,10 +68,10 @@
     virtual Status informPollAlarmFired();
     virtual Status informAlarmForSubscriberTriggeringFired();
 
-    virtual Status informAllUidData(const ParcelFileDescriptor& fd);
-    virtual Status informOnePackage(const String16& app, int32_t uid, int64_t version,
-                                    const String16& version_string, const String16& installer);
-    virtual Status informOnePackageRemoved(const String16& app, int32_t uid);
+    virtual Status informAllUidData(const ScopedFileDescriptor& fd);
+    virtual Status informOnePackage(const string& app, int32_t uid, int64_t version,
+                                    const string& versionString, const string& installer);
+    virtual Status informOnePackageRemoved(const string& app, int32_t uid);
     virtual Status informDeviceShutdown();
 
     /**
@@ -92,13 +94,13 @@
      */
     virtual Status getData(int64_t key,
                            const int32_t callingUid,
-                           vector<uint8_t>* output) override;
+                           vector<int8_t>* output) override;
 
 
     /**
      * Binder call for clients to get metadata across all configs in statsd.
      */
-    virtual Status getMetadata(vector<uint8_t>* output) override;
+    virtual Status getMetadata(vector<int8_t>* output) override;
 
 
     /**
@@ -106,14 +108,14 @@
      * should requestData for this configuration.
      */
     virtual Status addConfiguration(int64_t key,
-                                    const vector<uint8_t>& config,
+                                    const vector<int8_t>& config,
                                     const int32_t callingUid) override;
 
     /**
      * Binder call to let clients register the data fetch operation for a configuration.
      */
     virtual Status setDataFetchOperation(int64_t key,
-                                         const sp<IPendingIntentRef>& pir,
+                                         const shared_ptr<IPendingIntentRef>& pir,
                                          const int32_t callingUid) override;
 
     /**
@@ -125,7 +127,7 @@
     /**
      * Binder call to let clients register the active configs changed operation.
      */
-    virtual Status setActiveConfigsChangedOperation(const sp<IPendingIntentRef>& pir,
+    virtual Status setActiveConfigsChangedOperation(const shared_ptr<IPendingIntentRef>& pir,
                                                     const int32_t callingUid,
                                                     vector<int64_t>* output) override;
 
@@ -144,7 +146,7 @@
      */
     virtual Status setBroadcastSubscriber(int64_t configId,
                                           int64_t subscriberId,
-                                          const sp<IPendingIntentRef>& pir,
+                                          const shared_ptr<IPendingIntentRef>& pir,
                                           const int32_t callingUid) override;
 
     /**
@@ -167,14 +169,14 @@
      */
     virtual Status registerPullAtomCallback(int32_t uid, int32_t atomTag, int64_t coolDownNs,
             int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-            const sp<android::os::IPullAtomCallback>& pullerCallback) override;
+            const shared_ptr<IPullAtomCallback>& pullerCallback) override;
 
     /**
      * Binder call to register a callback function for a pulled atom.
      */
     virtual Status registerNativePullAtomCallback(int32_t atomTag, int64_t coolDownNs,
             int64_t timeoutNs, const std::vector<int32_t>& additiveFields,
-            const sp<android::os::IPullAtomCallback>& pullerCallback) override;
+            const shared_ptr<IPullAtomCallback>& pullerCallback) override;
 
     /**
      * Binder call to unregister any existing callback for the given uid and atom.
@@ -191,9 +193,6 @@
      */
     virtual Status getRegisteredExperimentIds(std::vector<int64_t>* expIdsOut);
 
-    /** IBinder::DeathRecipient */
-    virtual void binderDied(const wp<IBinder>& who) override;
-
 private:
     /**
      * Load system properties at init.
@@ -317,7 +316,7 @@
     /**
      * Adds a configuration after checking permissions and obtaining UID from binder call.
      */
-    bool addConfigurationChecked(int uid, int64_t key, const vector<uint8_t>& config);
+    bool addConfigurationChecked(int uid, int64_t key, const vector<int8_t>& config);
 
     /**
      * Update a configuration.
@@ -325,6 +324,17 @@
     void set_config(int uid, const string& name, const StatsdConfig& config);
 
     /**
+     * Death recipient callback that is called when StatsCompanionService dies.
+     * The cookie is a pointer to a StatsService object.
+     */
+    static void statsCompanionServiceDied(void* cookie);
+
+    /**
+     * Implementation of statsCompanionServiceDied.
+     */
+    void statsCompanionServiceDiedImpl();
+
+    /**
      * Tracks the uid <--> package name mapping.
      */
     sp<UidMap> mUidMap;
@@ -367,6 +377,8 @@
     mutable mutex mShellSubscriberMutex;
     std::shared_ptr<LogEventQueue> mEventQueue;
 
+    ScopedAIBinder_DeathRecipient mStatsCompanionServiceDeathRecipient;
+
     FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_simple);
     FRIEND_TEST(StatsServiceTest, TestAddConfig_empty);
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.cpp b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
index bc36dad..02291181 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.cpp
@@ -26,17 +26,21 @@
 
 AlarmMonitor::AlarmMonitor(
         uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
-        const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
-        const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm)
-    : mRegisteredAlarmTimeSec(0), mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
+        const std::function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>& updateAlarm,
+        const std::function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm)
+    : mRegisteredAlarmTimeSec(0),
+      mMinUpdateTimeSec(minDiffToUpdateRegisteredAlarmTimeSec),
       mUpdateAlarm(updateAlarm),
       mCancelAlarm(cancelAlarm) {}
 
 AlarmMonitor::~AlarmMonitor() {}
 
-void AlarmMonitor::setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService) {
+void AlarmMonitor::setStatsCompanionService(
+        shared_ptr<IStatsCompanionService> statsCompanionService) {
     std::lock_guard<std::mutex> lock(mLock);
-    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+    // TODO(b/149254662): determine if tmpForLock is needed now that we have moved
+    // from sp to shared_ptr
+    shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
     if (statsCompanionService == nullptr) {
         VLOG("Erasing link to statsCompanionService");
diff --git a/cmds/statsd/src/anomaly/AlarmMonitor.h b/cmds/statsd/src/anomaly/AlarmMonitor.h
index 219695e..5c34e38 100644
--- a/cmds/statsd/src/anomaly/AlarmMonitor.h
+++ b/cmds/statsd/src/anomaly/AlarmMonitor.h
@@ -18,7 +18,7 @@
 
 #include "anomaly/indexed_priority_queue.h"
 
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
 
 #include <unordered_set>
@@ -26,7 +26,9 @@
 
 using namespace android;
 
-using android::os::IStatsCompanionService;
+using aidl::android::os::IStatsCompanionService;
+using std::function;
+using std::shared_ptr;
 using std::unordered_set;
 
 namespace android {
@@ -64,8 +66,9 @@
      * alarm.
      */
     AlarmMonitor(uint32_t minDiffToUpdateRegisteredAlarmTimeSec,
-                 const std::function<void(const sp<IStatsCompanionService>&, int64_t)>& updateAlarm,
-                 const std::function<void(const sp<IStatsCompanionService>&)>& cancelAlarm);
+                 const function<void(const shared_ptr<IStatsCompanionService>&, int64_t)>&
+                         updateAlarm,
+                 const function<void(const shared_ptr<IStatsCompanionService>&)>& cancelAlarm);
     ~AlarmMonitor();
 
     /**
@@ -74,7 +77,7 @@
      * If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
      * update IStatsCompanionService (until such time as it is set non-null).
      */
-    void setStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+    void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
 
     /**
      * Adds the given alarm (reference) to the queue.
@@ -123,7 +126,7 @@
     /**
      * Binder interface for communicating with StatsCompanionService.
      */
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
 
     /**
      * Amount by which the soonest projected alarm must differ from
@@ -147,10 +150,10 @@
     int64_t secToMs(uint32_t timeSec);
 
     // Callback function to update the alarm via StatsCompanionService.
-    std::function<void(const sp<IStatsCompanionService>, int64_t)> mUpdateAlarm;
+    std::function<void(const shared_ptr<IStatsCompanionService>, int64_t)> mUpdateAlarm;
 
     // Callback function to cancel the alarm via StatsCompanionService.
-    std::function<void(const sp<IStatsCompanionService>)> mCancelAlarm;
+    std::function<void(const shared_ptr<IStatsCompanionService>)> mCancelAlarm;
 
 };
 
diff --git a/cmds/statsd/src/anomaly/AlarmTracker.h b/cmds/statsd/src/anomaly/AlarmTracker.h
index 2fb3e3b..2da4a186 100644
--- a/cmds/statsd/src/anomaly/AlarmTracker.h
+++ b/cmds/statsd/src/anomaly/AlarmTracker.h
@@ -22,12 +22,9 @@
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // Alarm
 
-#include <android/os/IStatsCompanionService.h>
 #include <stdlib.h>
 #include <utils/RefBase.h>
 
-using android::os::IStatsCompanionService;
-
 namespace android {
 namespace os {
 namespace statsd {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 23a4437..27c4f4e 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -8000,6 +8000,18 @@
     // of this pull. If this number grows prohibitively large, then this can
     // cause jank due to resource contention.
     optional int32 event_connection_count = 6;
+    // Set of timings measured from when SurfaceFlinger began compositing a
+    // frame, until the frame was requested to be presented to the display. This
+    // measures SurfaceFlinger's total CPU walltime on the critical path per
+    // frame.
+    optional FrameTimingHistogram frame_duration = 7
+        [(android.os.statsd.log_mode) = MODE_BYTES];
+    // Set of timings measured from when SurfaceFlinger first began using the
+    // GPU to composite a frame, until the GPU has finished compositing that
+    // frame. This measures the total additional time SurfaceFlinger needed to
+    // perform due to falling back into GPU composition.
+    optional FrameTimingHistogram render_engine_timing = 8
+        [(android.os.statsd.log_mode) = MODE_BYTES];
 }
 
 /**
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index 986955b..9bdb588 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -42,42 +42,67 @@
 using android::base::StringPrintf;
 using std::unique_ptr;
 
-class ConfigReceiverDeathRecipient : public android::IBinder::DeathRecipient {
-    public:
-        ConfigReceiverDeathRecipient(sp<ConfigManager> configManager, const ConfigKey& configKey):
-            mConfigManager(configManager),
-            mConfigKey(configKey) {}
-        ~ConfigReceiverDeathRecipient() override = default;
-    private:
-        sp<ConfigManager> mConfigManager;
-        ConfigKey mConfigKey;
+struct ConfigReceiverDeathCookie {
+    ConfigReceiverDeathCookie(sp<ConfigManager> configManager, const ConfigKey& configKey,
+                              const shared_ptr<IPendingIntentRef>& pir):
+        mConfigManager(configManager),
+        mConfigKey(configKey),
+        mPir(pir) {}
 
-    void binderDied(const android::wp<android::IBinder>& who) override {
-        if (IInterface::asBinder(mConfigManager->GetConfigReceiver(mConfigKey)) == who.promote()) {
-             mConfigManager->RemoveConfigReceiver(mConfigKey);
-        }
-    }
+    sp<ConfigManager> mConfigManager;
+    ConfigKey mConfigKey;
+    shared_ptr<IPendingIntentRef> mPir;
 };
 
-class ActiveConfigChangedReceiverDeathRecipient : public android::IBinder::DeathRecipient {
-    public:
-        ActiveConfigChangedReceiverDeathRecipient(sp<ConfigManager> configManager, const int uid):
-            mConfigManager(configManager),
-            mUid(uid) {}
-        ~ActiveConfigChangedReceiverDeathRecipient() override = default;
-    private:
-        sp<ConfigManager> mConfigManager;
-        int mUid;
+static void configReceiverDied(void* cookie) {
+    auto cookie_ = static_cast<ConfigReceiverDeathCookie*>(cookie);
+    sp<ConfigManager> configManager = cookie_->mConfigManager;
+    ConfigKey configKey = cookie_->mConfigKey;
+    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
 
-    void binderDied(const android::wp<android::IBinder>& who) override {
-        if (IInterface::asBinder(mConfigManager->GetActiveConfigsChangedReceiver(mUid))
-              == who.promote()) {
-            mConfigManager->RemoveActiveConfigsChangedReceiver(mUid);
-        }
+    // TODO(b/149254662): Fix threading. This currently fails if a new
+    // pir gets set between the get and the remove.
+    if (configManager->GetConfigReceiver(configKey) == pir) {
+        configManager->RemoveConfigReceiver(configKey);
     }
+    // The death recipient corresponding to this specific pir can never
+    // be triggered again, so free up resources.
+    // TODO(b/149254662): Investigate other options to manage the memory.
+    delete cookie_;
+}
+
+struct ActiveConfigChangedReceiverDeathCookie {
+    ActiveConfigChangedReceiverDeathCookie(sp<ConfigManager> configManager, const int uid,
+                                           const shared_ptr<IPendingIntentRef>& pir):
+        mConfigManager(configManager),
+        mUid(uid),
+        mPir(pir) {}
+
+    sp<ConfigManager> mConfigManager;
+    int mUid;
+    shared_ptr<IPendingIntentRef> mPir;
 };
 
-ConfigManager::ConfigManager() {
+static void activeConfigChangedReceiverDied(void* cookie) {
+    auto cookie_ = static_cast<ActiveConfigChangedReceiverDeathCookie*>(cookie);
+    sp<ConfigManager> configManager = cookie_->mConfigManager;
+    int uid = cookie_->mUid;
+    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+
+    // TODO(b/149254662): Fix threading. This currently fails if a new
+    // pir gets set between the get and the remove.
+    if (configManager->GetActiveConfigsChangedReceiver(uid) == pir) {
+        configManager->RemoveActiveConfigsChangedReceiver(uid);
+    }
+    // The death recipient corresponding to this specific pir can never
+    // be triggered again, so free up resources.
+    delete cookie_;
+}
+
+ConfigManager::ConfigManager():
+    mConfigReceiverDeathRecipient(AIBinder_DeathRecipient_new(configReceiverDied)),
+    mActiveConfigChangedReceiverDeathRecipient(
+        AIBinder_DeathRecipient_new(activeConfigChangedReceiverDied)) {
 }
 
 ConfigManager::~ConfigManager() {
@@ -150,10 +175,11 @@
 }
 
 void ConfigManager::SetConfigReceiver(const ConfigKey& key,
-                                      const sp<IPendingIntentRef>& pir) {
+                                      const shared_ptr<IPendingIntentRef>& pir) {
     lock_guard<mutex> lock(mMutex);
     mConfigReceivers[key] = pir;
-    IInterface::asBinder(pir)->linkToDeath(new ConfigReceiverDeathRecipient(this, key));
+    AIBinder_linkToDeath(pir->asBinder().get(), mConfigReceiverDeathRecipient.get(),
+                         new ConfigReceiverDeathCookie(this, key, pir));
 }
 
 void ConfigManager::RemoveConfigReceiver(const ConfigKey& key) {
@@ -162,11 +188,11 @@
 }
 
 void ConfigManager::SetActiveConfigsChangedReceiver(const int uid,
-                                                    const sp<IPendingIntentRef>& pir) {
+                                                    const shared_ptr<IPendingIntentRef>& pir) {
     lock_guard<mutex> lock(mMutex);
     mActiveConfigsChangedReceivers[uid] = pir;
-    IInterface::asBinder(pir)->linkToDeath(
-        new ActiveConfigChangedReceiverDeathRecipient(this, uid));
+    AIBinder_linkToDeath(pir->asBinder().get(), mActiveConfigChangedReceiverDeathRecipient.get(),
+                         new ActiveConfigChangedReceiverDeathCookie(this, uid, pir));
 }
 
 void ConfigManager::RemoveActiveConfigsChangedReceiver(const int uid) {
@@ -279,7 +305,7 @@
     return ret;
 }
 
-const sp<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetConfigReceiver(const ConfigKey& key) const {
     lock_guard<mutex> lock(mMutex);
 
     auto it = mConfigReceivers.find(key);
@@ -290,7 +316,8 @@
     }
 }
 
-const sp<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid) const {
+const shared_ptr<IPendingIntentRef> ConfigManager::GetActiveConfigsChangedReceiver(const int uid)
+        const {
     lock_guard<mutex> lock(mMutex);
 
     auto it = mActiveConfigsChangedReceivers.find(uid);
diff --git a/cmds/statsd/src/config/ConfigManager.h b/cmds/statsd/src/config/ConfigManager.h
index 2095173..824e588 100644
--- a/cmds/statsd/src/config/ConfigManager.h
+++ b/cmds/statsd/src/config/ConfigManager.h
@@ -19,12 +19,15 @@
 #include "config/ConfigKey.h"
 #include "config/ConfigListener.h"
 
-#include <android/os/IPendingIntentRef.h>
+#include <aidl/android/os/IPendingIntentRef.h>
 #include <mutex>
 #include <string>
 
 #include <stdio.h>
 
+using aidl::android::os::IPendingIntentRef;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -62,12 +65,12 @@
     /**
      * Sets the broadcast receiver for a configuration key.
      */
-    void SetConfigReceiver(const ConfigKey& key, const sp<IPendingIntentRef>& pir);
+    void SetConfigReceiver(const ConfigKey& key, const shared_ptr<IPendingIntentRef>& pir);
 
     /**
      * Returns the package name and class name representing the broadcast receiver for this config.
      */
-    const sp<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
+    const shared_ptr<IPendingIntentRef> GetConfigReceiver(const ConfigKey& key) const;
 
     /**
      * Returns all config keys registered.
@@ -83,13 +86,13 @@
      * Sets the broadcast receiver that is notified whenever the list of active configs
      * changes for this uid.
      */
-    void SetActiveConfigsChangedReceiver(const int uid, const sp<IPendingIntentRef>& pir);
+    void SetActiveConfigsChangedReceiver(const int uid, const shared_ptr<IPendingIntentRef>& pir);
 
     /**
      * Returns the broadcast receiver for active configs changed for this uid.
      */
 
-    const sp<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
+    const shared_ptr<IPendingIntentRef> GetActiveConfigsChangedReceiver(const int uid) const;
 
     /**
      * Erase any active configs changed broadcast receiver associated with this uid.
@@ -141,18 +144,23 @@
     /**
      * Each config key can be subscribed by up to one receiver, specified as IPendingIntentRef.
      */
-    std::map<ConfigKey, sp<IPendingIntentRef>> mConfigReceivers;
+    std::map<ConfigKey, shared_ptr<IPendingIntentRef>> mConfigReceivers;
 
     /**
      * Each uid can be subscribed by up to one receiver to notify that the list of active configs
      * for this uid has changed. The receiver is specified as IPendingIntentRef.
      */
-     std::map<int, sp<IPendingIntentRef>> mActiveConfigsChangedReceivers;
+     std::map<int, shared_ptr<IPendingIntentRef>> mActiveConfigsChangedReceivers;
 
     /**
      * The ConfigListeners that will be told about changes.
      */
     std::vector<sp<ConfigListener>> mListeners;
+
+    // Death recipients that are triggered when the host process holding an
+    // IPendingIntentRef dies.
+    ::ndk::ScopedAIBinder_DeathRecipient mConfigReceiverDeathRecipient;
+    ::ndk::ScopedAIBinder_DeathRecipient mActiveConfigChangedReceiverDeathRecipient;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/PullResultReceiver.cpp b/cmds/statsd/src/external/PullResultReceiver.cpp
index 6b6fe7d..8aa4792 100644
--- a/cmds/statsd/src/external/PullResultReceiver.cpp
+++ b/cmds/statsd/src/external/PullResultReceiver.cpp
@@ -16,17 +16,12 @@
 
 #include "PullResultReceiver.h"
 
-using namespace android::binder;
-using namespace android::util;
-using namespace std;
-
 namespace android {
 namespace os {
 namespace statsd {
 
 PullResultReceiver::PullResultReceiver(
-        std::function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
-             pullFinishCb)
+        std::function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCb)
     : pullFinishCallback(std::move(pullFinishCb)) {
 }
 
diff --git a/cmds/statsd/src/external/PullResultReceiver.h b/cmds/statsd/src/external/PullResultReceiver.h
index 17d06e4..ceaae80 100644
--- a/cmds/statsd/src/external/PullResultReceiver.h
+++ b/cmds/statsd/src/external/PullResultReceiver.h
@@ -14,29 +14,33 @@
  * limitations under the License.
  */
 
-#include <android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
 
 using namespace std;
 
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+
 namespace android {
 namespace os {
 namespace statsd {
 
 class PullResultReceiver : public BnPullAtomResultReceiver {
 public:
-    PullResultReceiver(function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
+    PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
                                pullFinishCallback);
     ~PullResultReceiver();
 
     /**
      * Binder call for finishing a pull.
      */
-    binder::Status pullFinished(int32_t atomTag, bool success,
-                                const vector<android::util::StatsEventParcel>& output) override;
+    Status pullFinished(int32_t atomTag, bool success,
+                        const vector<StatsEventParcel>& output) override;
 
 private:
-    function<void(int32_t, bool, const vector<android::util::StatsEventParcel>&)>
-            pullFinishCallback;
+    function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 1a11f0e..b6c8e34 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -18,24 +18,24 @@
 #include "Log.h"
 
 #include "StatsCallbackPuller.h"
-
-#include <android/os/IPullAtomCallback.h>
-#include <android/util/StatsEventParcel.h>
-
 #include "PullResultReceiver.h"
 #include "StatsPullerManager.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
 
-using namespace android::binder;
-using namespace android::util;
+#include <aidl/android/util/StatsEventParcel.h>
+
 using namespace std;
 
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
+
 namespace android {
 namespace os {
 namespace statsd {
 
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
                                          const int64_t coolDownNs, int64_t timeoutNs,
                                          const vector<int> additiveFields)
     : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
@@ -57,7 +57,7 @@
     shared_ptr<vector<shared_ptr<LogEvent>>> sharedData =
             make_shared<vector<shared_ptr<LogEvent>>>();
 
-    sp<PullResultReceiver> resultReceiver = new PullResultReceiver(
+    shared_ptr<PullResultReceiver> resultReceiver = SharedRefBase::make<PullResultReceiver>(
             [cv_mutex, cv, pullFinish, pullSuccess, sharedData](
                     int32_t atomTag, bool success, const vector<StatsEventParcel>& output) {
                 // This is the result of the pull, executing in a statsd binder thread.
@@ -66,9 +66,11 @@
                 {
                     lock_guard<mutex> lk(*cv_mutex);
                     for (const StatsEventParcel& parcel: output) {
+                        uint8_t* buf = reinterpret_cast<uint8_t*>(
+                                const_cast<int8_t*>(parcel.buffer.data()));
                         shared_ptr<LogEvent> event = make_shared<LogEvent>(
-                                const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(),
-                                /*uid=*/-1, /*pid=*/-1, /*useNewSchema=*/true);
+                                buf, parcel.buffer.size(), /*uid=*/-1, /*pid=*/-1,
+                                /*useNewSchema=*/true);
                         sharedData->push_back(event);
                     }
                     *pullSuccess = success;
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
index fe6af19..e82e8bb 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -16,23 +16,25 @@
 
 #pragma once
 
-#include <android/os/IPullAtomCallback.h>
-
+#include <aidl/android/os/IPullAtomCallback.h>
 #include "StatsPuller.h"
 
+using aidl::android::os::IPullAtomCallback;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
 
 class StatsCallbackPuller : public StatsPuller {
 public:
-    explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+    explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
                                  const int64_t coolDownNs, const int64_t timeoutNs,
                                  const std::vector<int> additiveFields);
 
 private:
-    bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
-    const sp<IPullAtomCallback> mCallback;
+    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+    const shared_ptr<IPullAtomCallback> mCallback;
 
     FRIEND_TEST(StatsCallbackPullerTest, PullFail);
     FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
diff --git a/cmds/statsd/src/external/StatsPuller.h b/cmds/statsd/src/external/StatsPuller.h
index 9c21695..fee571c 100644
--- a/cmds/statsd/src/external/StatsPuller.h
+++ b/cmds/statsd/src/external/StatsPuller.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
 #include <mutex>
 #include <vector>
@@ -26,6 +26,9 @@
 #include "logd/LogEvent.h"
 #include "puller_util.h"
 
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -58,7 +61,8 @@
 
     static void SetUidMap(const sp<UidMap>& uidMap);
 
-    virtual void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService){};
+    virtual void SetStatsCompanionService(
+            shared_ptr<IStatsCompanionService> statsCompanionService) {};
 
 protected:
     const int mTagId;
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 3ceff75..1c38542 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -19,8 +19,6 @@
 
 #include "StatsPullerManager.h"
 
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IStatsCompanionService.h>
 #include <cutils/log.h>
 #include <math.h>
 #include <stdint.h>
@@ -87,7 +85,10 @@
         return;
     }
 
-    sp<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
+    // TODO(b/149254662): Why are we creating a copy here? This is different
+    // from the other places where we create a copy because we don't reassign
+    // mStatsCompanionService so a destructor can't implicitly be called...
+    shared_ptr<IStatsCompanionService> statsCompanionServiceCopy = mStatsCompanionService;
     if (statsCompanionServiceCopy != nullptr) {
         statsCompanionServiceCopy->setPullingAlarm(mNextPullTimeNs / 1000000);
     } else {
@@ -97,9 +98,11 @@
 }
 
 void StatsPullerManager::SetStatsCompanionService(
-        sp<IStatsCompanionService> statsCompanionService) {
+        shared_ptr<IStatsCompanionService> statsCompanionService) {
+    // TODO(b/149254662): Why are we using AutoMutex instead of lock_guard?
+    // Additionally, do we need the temporary shared_ptr to prevent deadlocks?
     AutoMutex _l(mLock);
-    sp<IStatsCompanionService> tmpForLock = mStatsCompanionService;
+    shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
     for (const auto& pulledAtom : kAllPullAtomInfo) {
         pulledAtom.second->SetStatsCompanionService(statsCompanionService);
@@ -250,10 +253,11 @@
 void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
                                                   const int64_t coolDownNs, const int64_t timeoutNs,
                                                   const vector<int32_t>& additiveFields,
-                                                  const sp<IPullAtomCallback>& callback) {
+                                                  const shared_ptr<IPullAtomCallback>& callback) {
     AutoMutex _l(mLock);
     VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
-    // TODO: linkToDeath with the callback so that we can remove it and delete the puller.
+    // TODO(b/146439412): linkToDeath with the callback so that we can remove it
+    // and delete the puller.
     StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
     kAllPullAtomInfo[{.atomTag = atomTag}] =
             new StatsCallbackPuller(atomTag, callback, coolDownNs, timeoutNs, additiveFields);
diff --git a/cmds/statsd/src/external/StatsPullerManager.h b/cmds/statsd/src/external/StatsPullerManager.h
index aef16dc..e067766 100644
--- a/cmds/statsd/src/external/StatsPullerManager.h
+++ b/cmds/statsd/src/external/StatsPullerManager.h
@@ -16,9 +16,8 @@
 
 #pragma once
 
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IStatsCompanionService.h>
-#include <binder/IServiceManager.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <utils/RefBase.h>
 #include <utils/threads.h>
 
@@ -30,11 +29,14 @@
 #include "guardrail/StatsdStats.h"
 #include "logd/LogEvent.h"
 
+using aidl::android::os::IPullAtomCallback;
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
 
-
 typedef struct PullerKey {
     // The uid of the process that registers this puller.
     const int uid = -1;
@@ -93,18 +95,18 @@
     // Clear pull data cache if it is beyond respective cool down time.
     int ClearPullerCacheIfNecessary(int64_t timestampNs);
 
-    void SetStatsCompanionService(sp<IStatsCompanionService> statsCompanionService);
+    void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
 
     void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
                                   const int64_t timeoutNs, const vector<int32_t>& additiveFields,
-                                  const sp<IPullAtomCallback>& callback);
+                                  const shared_ptr<IPullAtomCallback>& callback);
 
     void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
 
     std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
 
 private:
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    shared_ptr<IStatsCompanionService> mStatsCompanionService = nullptr;
 
     typedef struct {
         int64_t nextPullTimeNs;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 103eb0c..9c50846 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -20,7 +20,8 @@
 #include "stats_log_util.h"
 #include "statslog.h"
 
-#include <binder/IPCThreadState.h>
+#include <android/binder_ibinder.h>
+#include <android-base/stringprintf.h>
 #include <private/android_filesystem_config.h>
 
 namespace android {
@@ -31,6 +32,7 @@
 const int FIELD_ID_EXPERIMENT_ID = 1;
 
 using namespace android::util;
+using android::base::StringPrintf;
 using android::util::ProtoOutputStream;
 using std::string;
 using std::vector;
@@ -180,8 +182,8 @@
     mLogdTimestampNs = getWallClockNs();
     mElapsedTimestampNs = getElapsedRealtimeNs();
     mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
-    mLogUid = android::IPCThreadState::self()->getCallingUid();
-    mLogPid = android::IPCThreadState::self()->getCallingPid();
+    mLogUid = AIBinder_getCallingUid();
+    mLogPid = AIBinder_getCallingPid();
 
     mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
     mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
@@ -290,9 +292,9 @@
 }
 
 bool LogEvent::writeBytes(const string& value) {
-    if (mContext) {
+    /* if (mContext) {
         return android_log_write_char_array(mContext, value.c_str(), value.length()) >= 0;
-    }
+    }*/
     return false;
 }
 
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index a67bef4..ea449b0 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -20,7 +20,6 @@
 
 #include <android/util/ProtoOutputStream.h>
 #include <private/android_logger.h>
-#include <stats_event_list.h>
 #include <stats_event.h>
 
 #include <string>
diff --git a/cmds/statsd/src/main.cpp b/cmds/statsd/src/main.cpp
index 140ef4e..d79b6a2 100644
--- a/cmds/statsd/src/main.cpp
+++ b/cmds/statsd/src/main.cpp
@@ -20,9 +20,9 @@
 #include "StatsService.h"
 #include "socket/StatsSocketListener.h"
 
-#include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_process.h>
+#include <android/binder_manager.h>
 #include <utils/Looper.h>
 
 #include <stdio.h>
@@ -32,16 +32,11 @@
 
 using namespace android;
 using namespace android::os::statsd;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
+using std::make_shared;
 
-/**
- * Thread function data.
- */
-struct log_reader_thread_data {
-    sp<StatsService> service;
-};
-
-
-sp<StatsService> gStatsService = nullptr;
+shared_ptr<StatsService> gStatsService = nullptr;
 
 void sigHandler(int sig) {
     if (gStatsService != nullptr) {
@@ -68,20 +63,18 @@
     sp<Looper> looper(Looper::prepare(0 /* opts */));
 
     // Set up the binder
-    sp<ProcessState> ps(ProcessState::self());
-    ps->setThreadPoolMaxThreadCount(9);
-    ps->startThreadPool();
-    ps->giveThreadPoolName();
-    IPCThreadState::self()->disableBackgroundScheduling(true);
+    ABinderProcess_setThreadPoolMaxThreadCount(9);
+    ABinderProcess_startThreadPool();
 
     std::shared_ptr<LogEventQueue> eventQueue =
             std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
 
     // Create the service
-    gStatsService = new StatsService(looper, eventQueue);
-    if (defaultServiceManager()->addService(String16("stats"), gStatsService, false,
-                IServiceManager::DUMP_FLAG_PRIORITY_NORMAL | IServiceManager::DUMP_FLAG_PROTO)
-            != 0) {
+    gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
+    // TODO(b/149582373): Set DUMP_FLAG_PROTO once libbinder_ndk supports
+    // setting dumpsys priorities.
+    binder_status_t status = AServiceManager_addService(gStatsService->asBinder().get(), "stats");
+    if (status != STATUS_OK) {
         ALOGE("Failed to add service as AIDL service");
         return -1;
     }
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index bfac6e3..4e3c506 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -21,10 +21,11 @@
 #include "packages/PackageInfoListener.h"
 #include "stats_util.h"
 
-#include <binder/IShellCallback.h>
 #include <gtest/gtest_prod.h>
 #include <stdio.h>
 #include <utils/RefBase.h>
+#include <utils/String16.h>
+
 #include <list>
 #include <mutex>
 #include <set>
diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp
index d6a0433..a861a3b 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.cpp
+++ b/cmds/statsd/src/shell/ShellSubscriber.cpp
@@ -29,49 +29,88 @@
 
 const static int FIELD_ID_ATOM = 1;
 
-void ShellSubscriber::startNewSubscription(int in, int out, sp<IResultReceiver> resultReceiver,
-                                           int timeoutSec) {
+void ShellSubscriber::startNewSubscription(int in, int out, int timeoutSec) {
     VLOG("start new shell subscription");
+    int64_t subscriberId = getElapsedRealtimeNs();
+
     {
         std::lock_guard<std::mutex> lock(mMutex);
-        if (mResultReceiver != nullptr) {
+        if (mSubscriberId> 0) {
             VLOG("Only one shell subscriber is allowed.");
             return;
         }
+        mSubscriberId = subscriberId;
         mInput = in;
         mOutput = out;
-        mResultReceiver = resultReceiver;
-        IInterface::asBinder(mResultReceiver)->linkToDeath(this);
     }
 
-    // Note that the following is blocking, and it's intended as we cannot return until the shell
-    // cmd exits, otherwise all resources & FDs will be automatically closed.
+    bool success = readConfig();
+    if (!success) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        cleanUpLocked();
+    }
 
-    // Read config forever until EOF is reached. Clients may send multiple configs -- each new
-    // config replace the previous one.
-    readConfig(in);
-    VLOG("timeout : %d", timeoutSec);
-
-    // Now we have read an EOF we now wait for the semaphore until the client exits.
-    VLOG("Now wait for client to exit");
+    VLOG("Wait for client to exit or timeout (%d sec)", timeoutSec);
     std::unique_lock<std::mutex> lk(mMutex);
 
+    // Note that the following is blocking, and it's intended as we cannot return until the shell
+    // cmd exits or we time out.
     if (timeoutSec > 0) {
         mShellDied.wait_for(lk, timeoutSec * 1s,
-                            [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+                            [this, subscriberId] { return mSubscriberId != subscriberId; });
     } else {
-        mShellDied.wait(lk, [this, resultReceiver] { return mResultReceiver != resultReceiver; });
+        mShellDied.wait(lk, [this, subscriberId] { return mSubscriberId != subscriberId; });
+    }
+}
+
+
+// Read configs until EOF is reached. There may be multiple configs in the input
+// -- each new config should replace the previous one.
+//
+// Returns a boolean indicating whether the input was read successfully.
+bool ShellSubscriber::readConfig() {
+    if (mInput < 0) {
+        return false;
+    }
+
+    while (true) {
+        // Read the size of the config.
+        size_t bufferSize = 0;
+        ssize_t bytesRead = read(mInput, &bufferSize, sizeof(bufferSize));
+        if (bytesRead == 0) {
+            VLOG("We have reached the end of the input.");
+            return true;
+        } else if (bytesRead < 0 || (size_t)bytesRead != sizeof(bufferSize)) {
+            ALOGE("Error reading config size");
+            return false;
+        }
+
+        // Read and parse the config.
+        vector<uint8_t> buffer(bufferSize);
+        bytesRead = read(mInput, buffer.data(), bufferSize);
+        if (bytesRead > 0 && (size_t)bytesRead == bufferSize) {
+            ShellSubscription config;
+            if (config.ParseFromArray(buffer.data(), bufferSize)) {
+                updateConfig(config);
+            } else {
+                ALOGE("Error parsing the config");
+                return false;
+            }
+        } else {
+            VLOG("Error reading the config, expected bytes: %zu, actual bytes: %zu", bufferSize, 
+                 bytesRead);
+            return false;
+        }
     }
 }
 
 void ShellSubscriber::updateConfig(const ShellSubscription& config) {
-    std::lock_guard<std::mutex> lock(mMutex);
     mPushedMatchers.clear();
     mPulledInfo.clear();
 
     for (const auto& pushed : config.pushed()) {
         mPushedMatchers.push_back(pushed);
-        VLOG("adding matcher for atom %d", pushed.atom_id());
+        VLOG("adding matcher for pushed atom %d", pushed.atom_id());
     }
 
     int64_t token = getElapsedRealtimeNs();
@@ -89,46 +128,20 @@
     }
 
     if (mPulledInfo.size() > 0 && minInterval > 0) {
-        // This thread is guaranteed to terminate after it detects the token is different or
-        // cleaned up.
+        // This thread is guaranteed to terminate after it detects the token is
+        // different.
         std::thread puller([token, minInterval, this] { startPull(token, minInterval); });
         puller.detach();
     }
 }
 
-void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
-                                          const SimpleAtomMatcher& matcher) {
-    if (mOutput == 0) return;
-    int count = 0;
-    mProto.clear();
-    for (const auto& event : data) {
-        VLOG("%s", event->ToString().c_str());
-        if (matchesSimple(*mUidMap, matcher, *event)) {
-            VLOG("matched");
-            count++;
-            uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
-                                              util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
-            event->ToProto(mProto);
-            mProto.end(atomToken);
-        }
-    }
-
-    if (count > 0) {
-        // First write the payload size.
-        size_t bufferSize = mProto.size();
-        write(mOutput, &bufferSize, sizeof(bufferSize));
-        VLOG("%d atoms, proto size: %zu", count, bufferSize);
-        // Then write the payload.
-        mProto.flush(mOutput);
-    }
-    mProto.clear();
-}
-
 void ShellSubscriber::startPull(int64_t token, int64_t intervalMillis) {
-    while (1) {
+    while (true) {
         int64_t nowMillis = getElapsedRealtimeMillis();
         {
             std::lock_guard<std::mutex> lock(mMutex);
+            // If the token has changed, the config has changed, so this
+            // puller can now stop.
             if (mPulledInfo.size() == 0 || mPullToken != token) {
                 VLOG("Pulling thread %lld done!", (long long)token);
                 return;
@@ -152,55 +165,47 @@
     }
 }
 
-void ShellSubscriber::readConfig(int in) {
-    if (in <= 0) {
+// Must be called with the lock acquired, so that mProto isn't being written to
+// at the same time by multiple threads.
+void ShellSubscriber::writeToOutputLocked(const vector<std::shared_ptr<LogEvent>>& data,
+                                          const SimpleAtomMatcher& matcher) {
+    if (mOutput < 0) {
         return;
     }
-
-    while (1) {
-        size_t bufferSize = 0;
-        int result = 0;
-        if ((result = read(in, &bufferSize, sizeof(bufferSize))) == 0) {
-            VLOG("Done reading");
-            break;
-        } else if (result < 0 || result != sizeof(bufferSize)) {
-            ALOGE("Error reading config size");
-            break;
-        }
-
-        vector<uint8_t> buffer(bufferSize);
-        if ((result = read(in, buffer.data(), bufferSize)) > 0 && ((size_t)result) == bufferSize) {
-            ShellSubscription config;
-            if (config.ParseFromArray(buffer.data(), bufferSize)) {
-                updateConfig(config);
-            } else {
-                ALOGE("error parsing the config");
-                break;
-            }
-        } else {
-            VLOG("Error reading the config, returned: %d, expecting %zu", result, bufferSize);
-            break;
+    int count = 0;
+    mProto.clear();
+    for (const auto& event : data) {
+        VLOG("%s", event->ToString().c_str());
+        if (matchesSimple(*mUidMap, matcher, *event)) {
+            VLOG("matched");
+            count++;
+            uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
+                                              util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
+            event->ToProto(mProto);
+            mProto.end(atomToken);
         }
     }
-}
 
-void ShellSubscriber::cleanUpLocked() {
-    // The file descriptors will be closed by binder.
-    mInput = 0;
-    mOutput = 0;
-    mResultReceiver = nullptr;
-    mPushedMatchers.clear();
-    mPulledInfo.clear();
-    mPullToken = 0;
-    VLOG("done clean up");
+    if (count > 0) {
+        // First write the payload size.
+        size_t bufferSize = mProto.size();
+        write(mOutput, &bufferSize, sizeof(bufferSize));
+
+        VLOG("%d atoms, proto size: %zu", count, bufferSize);
+        // Then write the payload.
+        mProto.flush(mOutput);
+    }
 }
 
 void ShellSubscriber::onLogEvent(const LogEvent& event) {
+    // Acquire a lock to prevent corruption from multiple threads writing to
+    // mProto.
     std::lock_guard<std::mutex> lock(mMutex);
-
-    if (mOutput <= 0) {
+    if (mOutput < 0) {
         return;
     }
+
+    mProto.clear();
     for (const auto& matcher : mPushedMatchers) {
         if (matchesSimple(*mUidMap, matcher, event)) {
             VLOG("%s", event.ToString().c_str());
@@ -208,25 +213,27 @@
                                               util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
             event.ToProto(mProto);
             mProto.end(atomToken);
+
             // First write the payload size.
             size_t bufferSize = mProto.size();
             write(mOutput, &bufferSize, sizeof(bufferSize));
 
             // Then write the payload.
             mProto.flush(mOutput);
-            mProto.clear();
-            break;
         }
     }
 }
 
-void ShellSubscriber::binderDied(const wp<IBinder>& who) {
-    {
-        VLOG("Shell exits");
-        std::lock_guard<std::mutex> lock(mMutex);
-        cleanUpLocked();
-    }
-    mShellDied.notify_all();
+void ShellSubscriber::cleanUpLocked() {
+    // The file descriptors will be closed by binder.
+    mInput = -1;
+    mOutput = -1;
+    mSubscriberId = 0;
+    mPushedMatchers.clear();
+    mPulledInfo.clear();
+    // Setting mPullToken == 0 tells pull thread that its work is done.
+    mPullToken = 0;
+    VLOG("done clean up");
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/shell/ShellSubscriber.h b/cmds/statsd/src/shell/ShellSubscriber.h
index 86d8590..eaf2ad1 100644
--- a/cmds/statsd/src/shell/ShellSubscriber.h
+++ b/cmds/statsd/src/shell/ShellSubscriber.h
@@ -19,7 +19,6 @@
 #include "logd/LogEvent.h"
 
 #include <android/util/ProtoOutputStream.h>
-#include <binder/IResultReceiver.h>
 #include <condition_variable>
 #include <mutex>
 #include <thread>
@@ -56,7 +55,7 @@
  * Only one shell subscriber allowed at a time, because each shell subscriber blocks one thread
  * until it exits.
  */
-class ShellSubscriber : public virtual IBinder::DeathRecipient {
+class ShellSubscriber : public virtual RefBase {
 public:
     ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr)
         : mUidMap(uidMap), mPullerMgr(pullerMgr){};
@@ -64,10 +63,7 @@
     /**
      * Start a new subscription.
      */
-    void startNewSubscription(int inFd, int outFd, sp<IResultReceiver> resultReceiver,
-                              int timeoutSec);
-
-    void binderDied(const wp<IBinder>& who);
+    void startNewSubscription(int inFd, int outFd, int timeoutSec);
 
     void onLogEvent(const LogEvent& event);
 
@@ -80,7 +76,7 @@
         int64_t mInterval;
         int64_t mPrevPullElapsedRealtimeMs;
     };
-    void readConfig(int in);
+    bool readConfig();
 
     void updateConfig(const ShellSubscription& config);
 
@@ -101,16 +97,16 @@
 
     std::condition_variable mShellDied;  // semaphore for waiting until shell exits.
 
-    int mInput;  // The input file descriptor
+    int mInput = -1;  // The input file descriptor
 
-    int mOutput;  // The output file descriptor
-
-    sp<IResultReceiver> mResultReceiver;
+    int mOutput = -1;  // The output file descriptor
 
     std::vector<SimpleAtomMatcher> mPushedMatchers;
 
     std::vector<PullInfo> mPulledInfo;
 
+    int64_t mSubscriberId = 0; // A unique id to identify a subscriber.
+
     int64_t mPullToken = 0;  // A unique token to identify a puller thread.
 };
 
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 73f640e..3d02ffb 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -17,6 +17,7 @@
 #include "hash.h"
 #include "stats_log_util.h"
 
+#include <aidl/android/os/IStatsCompanionService.h>
 #include <private/android_filesystem_config.h>
 #include <set>
 #include <utils/SystemClock.h>
@@ -35,6 +36,10 @@
 using android::util::FIELD_TYPE_UINT64;
 using android::util::ProtoOutputStream;
 
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
+using std::string;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -587,13 +592,13 @@
 }
 
 bool checkPermissionForIds(const char* permission, pid_t pid, uid_t uid) {
-    sp<IStatsCompanionService> scs = getStatsCompanionService();
+    shared_ptr<IStatsCompanionService> scs = getStatsCompanionService();
     if (scs == nullptr) {
         return false;
     }
 
     bool success;
-    binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success);
+    ::ndk::ScopedAStatus status = scs->checkPermission(string(permission), pid, uid, &success);
     if (!status.isOk()) {
         return false;
     }
diff --git a/cmds/statsd/src/statscompanion_util.cpp b/cmds/statsd/src/statscompanion_util.cpp
index d338827..ce07ec0 100644
--- a/cmds/statsd/src/statscompanion_util.cpp
+++ b/cmds/statsd/src/statscompanion_util.cpp
@@ -18,26 +18,16 @@
 #include "Log.h"
 
 #include "statscompanion_util.h"
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
 
 namespace android {
 namespace os {
 namespace statsd {
 
-sp <IStatsCompanionService> getStatsCompanionService() {
-    sp<IStatsCompanionService> statsCompanion = nullptr;
-    // Get statscompanion service from service manager
-    static const sp <IServiceManager> sm(defaultServiceManager());
-    if (statsCompanion == nullptr) {
-        if (sm != nullptr) {
-            const String16 name("statscompanion");
-            statsCompanion = interface_cast<IStatsCompanionService>(sm->checkService(name));
-            if (statsCompanion == nullptr) {
-                ALOGW("statscompanion service unavailable!");
-                return nullptr;
-            }
-        }
-    }
-    return statsCompanion;
+shared_ptr<IStatsCompanionService> getStatsCompanionService() {
+    ::ndk::SpAIBinder binder(AServiceManager_getService("statscompanion"));
+    return IStatsCompanionService::fromBinder(binder);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/statscompanion_util.h b/cmds/statsd/src/statscompanion_util.h
index dc4f283..e20c40b 100644
--- a/cmds/statsd/src/statscompanion_util.h
+++ b/cmds/statsd/src/statscompanion_util.h
@@ -16,14 +16,17 @@
 
 #pragma once
 
-#include "StatsLogProcessor.h"
+#include <aidl/android/os/IStatsCompanionService.h>
+
+using aidl::android::os::IStatsCompanionService;
+using std::shared_ptr;
 
 namespace android {
 namespace os {
 namespace statsd {
 
 /** Fetches and returns the StatsCompanionService. */
-sp<IStatsCompanionService> getStatsCompanionService();
+shared_ptr<IStatsCompanionService> getStatsCompanionService();
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.cpp b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
index 8fd6b46..93af5e9 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.cpp
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.cpp
@@ -27,38 +27,52 @@
 
 using std::vector;
 
-class BroadcastSubscriberDeathRecipient : public android::IBinder::DeathRecipient {
-    public:
-        BroadcastSubscriberDeathRecipient(const ConfigKey& configKey, int64_t subscriberId):
-            mConfigKey(configKey),
-            mSubscriberId(subscriberId) {}
-        ~BroadcastSubscriberDeathRecipient() override = default;
-    private:
-        ConfigKey mConfigKey;
-        int64_t mSubscriberId;
+struct BroadcastSubscriberDeathCookie {
+    BroadcastSubscriberDeathCookie(const ConfigKey& configKey, int64_t subscriberId,
+                                   const shared_ptr<IPendingIntentRef>& pir):
+        mConfigKey(configKey),
+        mSubscriberId(subscriberId),
+        mPir(pir) {}
 
-    void binderDied(const android::wp<android::IBinder>& who) override {
-        if (IInterface::asBinder(SubscriberReporter::getInstance().getBroadcastSubscriber(
-              mConfigKey, mSubscriberId)) == who.promote()) {
-            SubscriberReporter::getInstance().unsetBroadcastSubscriber(mConfigKey, mSubscriberId);
-        }
-    }
+    ConfigKey mConfigKey;
+    int64_t mSubscriberId;
+    shared_ptr<IPendingIntentRef> mPir;
 };
 
+static void broadcastSubscriberDied(void* cookie) {
+    BroadcastSubscriberDeathCookie* cookie_ = (BroadcastSubscriberDeathCookie*)cookie;
+    ConfigKey configKey = cookie_->mConfigKey;
+    int64_t subscriberId = cookie_->mSubscriberId;
+    shared_ptr<IPendingIntentRef> pir = cookie_->mPir;
+
+    // TODO(b/149254662): Fix threading. This currently fails if a new pir gets
+    // set between the get and the unset.
+    if (SubscriberReporter::getInstance().getBroadcastSubscriber(configKey, subscriberId) == pir) {
+        SubscriberReporter::getInstance().unsetBroadcastSubscriber(configKey, subscriberId);
+    }
+    // The death recipient corresponding to this specific pir can never be
+    // triggered again, so free up resources.
+    delete cookie_;
+}
+
+static ::ndk::ScopedAIBinder_DeathRecipient sBroadcastSubscriberDeathRecipient(
+        AIBinder_DeathRecipient_new(broadcastSubscriberDied));
+
 void SubscriberReporter::setBroadcastSubscriber(const ConfigKey& configKey,
                                                 int64_t subscriberId,
-                                                const sp<IPendingIntentRef>& pir) {
+                                                const shared_ptr<IPendingIntentRef>& pir) {
     VLOG("SubscriberReporter::setBroadcastSubscriber called.");
-    lock_guard<std::mutex> lock(mLock);
+    lock_guard<mutex> lock(mLock);
     mIntentMap[configKey][subscriberId] = pir;
-    IInterface::asBinder(pir)->linkToDeath(
-        new BroadcastSubscriberDeathRecipient(configKey, subscriberId));
+    // TODO(b/149254662): Is it ok to call linkToDeath while holding a lock?
+    AIBinder_linkToDeath(pir->asBinder().get(), sBroadcastSubscriberDeathRecipient.get(),
+                         new BroadcastSubscriberDeathCookie(configKey, subscriberId, pir));
 }
 
 void SubscriberReporter::unsetBroadcastSubscriber(const ConfigKey& configKey,
                                                   int64_t subscriberId) {
     VLOG("SubscriberReporter::unsetBroadcastSubscriber called.");
-    lock_guard<std::mutex> lock(mLock);
+    lock_guard<mutex> lock(mLock);
     auto subscriberMapIt = mIntentMap.find(configKey);
     if (subscriberMapIt != mIntentMap.end()) {
         subscriberMapIt->second.erase(subscriberId);
@@ -80,7 +94,7 @@
     //  config id - the name of this config (for this particular uid)
 
     VLOG("SubscriberReporter::alertBroadcastSubscriber called.");
-    lock_guard<std::mutex> lock(mLock);
+    lock_guard<mutex> lock(mLock);
 
     if (!subscription.has_broadcast_subscriber_details()
             || !subscription.broadcast_subscriber_details().has_subscriber_id()) {
@@ -89,10 +103,12 @@
     }
     int64_t subscriberId = subscription.broadcast_subscriber_details().subscriber_id();
 
-    vector<String16> cookies;
+    // TODO(b/149254662): Is there a way to convert a RepeatedPtrField into a
+    // vector without copying?
+    vector<string> cookies;
     cookies.reserve(subscription.broadcast_subscriber_details().cookie_size());
     for (auto& cookie : subscription.broadcast_subscriber_details().cookie()) {
-        cookies.push_back(String16(cookie.c_str()));
+        cookies.push_back(cookie);
     }
 
     auto it1 = mIntentMap.find(configKey);
@@ -109,10 +125,10 @@
     sendBroadcastLocked(it2->second, configKey, subscription, cookies, dimKey);
 }
 
-void SubscriberReporter::sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
+void SubscriberReporter::sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
                                              const ConfigKey& configKey,
                                              const Subscription& subscription,
-                                             const vector<String16>& cookies,
+                                             const vector<string>& cookies,
                                              const MetricDimensionKey& dimKey) const {
     VLOG("SubscriberReporter::sendBroadcastLocked called.");
     pir->sendSubscriberBroadcast(
@@ -124,9 +140,9 @@
             dimKey.getDimensionKeyInWhat().toStatsDimensionsValueParcel());
 }
 
-sp<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
-                                                                 int64_t subscriberId) {
-    lock_guard<std::mutex> lock(mLock);
+shared_ptr<IPendingIntentRef> SubscriberReporter::getBroadcastSubscriber(const ConfigKey& configKey,
+                                                                         int64_t subscriberId) {
+    lock_guard<mutex> lock(mLock);
     auto subscriberMapIt = mIntentMap.find(configKey);
     if (subscriberMapIt == mIntentMap.end()) {
         return nullptr;
diff --git a/cmds/statsd/src/subscriber/SubscriberReporter.h b/cmds/statsd/src/subscriber/SubscriberReporter.h
index 42599f5..0f97d39 100644
--- a/cmds/statsd/src/subscriber/SubscriberReporter.h
+++ b/cmds/statsd/src/subscriber/SubscriberReporter.h
@@ -16,9 +16,9 @@
 
 #pragma once
 
-#include <android/os/IPendingIntentRef.h>
-#include <android/os/IStatsCompanionService.h>
+#include <aidl/android/os/IPendingIntentRef.h>
 #include <utils/RefBase.h>
+#include <utils/String16.h>
 
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"  // subscription
@@ -28,6 +28,13 @@
 #include <unordered_map>
 #include <vector>
 
+using aidl::android::os::IPendingIntentRef;
+using std::mutex;
+using std::shared_ptr;
+using std::string;
+using std::unordered_map;
+using std::vector;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -51,7 +58,7 @@
      */
     void setBroadcastSubscriber(const ConfigKey& configKey,
                                 int64_t subscriberId,
-                                const sp<IPendingIntentRef>& pir);
+                                const shared_ptr<IPendingIntentRef>& pir);
 
     /**
      * Erases any intentSender information from the given (configKey, subscriberId) pair.
@@ -67,28 +74,25 @@
                                   const Subscription& subscription,
                                   const MetricDimensionKey& dimKey) const;
 
-    sp<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey, int64_t subscriberId);
+    shared_ptr<IPendingIntentRef> getBroadcastSubscriber(const ConfigKey& configKey,
+                                                         int64_t subscriberId);
 
 private:
     SubscriberReporter() {};
 
-    mutable std::mutex mLock;
-
-    /** Binder interface for communicating with StatsCompanionService. */
-    sp<IStatsCompanionService> mStatsCompanionService = nullptr;
+    mutable mutex mLock;
 
     /** Maps <ConfigKey, SubscriberId> -> IPendingIntentRef (which represents a PendingIntent). */
-    std::unordered_map<ConfigKey,
-            std::unordered_map<int64_t, sp<IPendingIntentRef>>> mIntentMap;
+    unordered_map<ConfigKey, unordered_map<int64_t, shared_ptr<IPendingIntentRef>>> mIntentMap;
 
     /**
      * Sends a broadcast via the given intentSender (using mStatsCompanionService), along
      * with the information in the other parameters.
      */
-    void sendBroadcastLocked(const sp<IPendingIntentRef>& pir,
+    void sendBroadcastLocked(const shared_ptr<IPendingIntentRef>& pir,
                              const ConfigKey& configKey,
                              const Subscription& subscription,
-                             const std::vector<String16>& cookies,
+                             const vector<string>& cookies,
                              const MetricDimensionKey& dimKey) const;
 };
 
diff --git a/cmds/statsd/tests/AlarmMonitor_test.cpp b/cmds/statsd/tests/AlarmMonitor_test.cpp
index 1fccb35..edbf8b5 100644
--- a/cmds/statsd/tests/AlarmMonitor_test.cpp
+++ b/cmds/statsd/tests/AlarmMonitor_test.cpp
@@ -17,14 +17,16 @@
 #include <gtest/gtest.h>
 
 using namespace android::os::statsd;
+using std::shared_ptr;
 
 #ifdef __ANDROID__
 TEST(AlarmMonitor, popSoonerThan) {
     std::string emptyMetricId;
     std::string emptyDimensionId;
     unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>> set;
-    AlarmMonitor am(2, [](const sp<IStatsCompanionService>&, int64_t){},
-                    [](const sp<IStatsCompanionService>&){});
+    AlarmMonitor am(2,
+                    [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                    [](const shared_ptr<IStatsCompanionService>&){});
 
     set = am.popSoonerThan(5);
     EXPECT_TRUE(set.empty());
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 69e11ed..3d7e5c9 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -32,6 +32,8 @@
 
 using namespace android;
 using namespace testing;
+using ::ndk::SharedRefBase;
+using std::shared_ptr;
 
 namespace android {
 namespace os {
@@ -49,10 +51,12 @@
     MockMetricsManager()
         : MetricsManager(ConfigKey(1, 12345), StatsdConfig(), 1000, 1000, new UidMap(),
                          new StatsPullerManager(),
-                         new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
-                                          [](const sp<IStatsCompanionService>&) {}),
-                         new AlarmMonitor(10, [](const sp<IStatsCompanionService>&, int64_t) {},
-                                          [](const sp<IStatsCompanionService>&) {})) {
+                         new AlarmMonitor(10,
+                                          [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+                                          [](const shared_ptr<IStatsCompanionService>&) {}),
+                         new AlarmMonitor(10,
+                                          [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+                                          [](const shared_ptr<IStatsCompanionService>&) {})) {
     }
 
     MOCK_METHOD0(byteSize, size_t());
@@ -1488,9 +1492,9 @@
     metric2ActivationTrigger2->set_activation_type(ACTIVATE_IMMEDIATELY);
 
     // Send the config.
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     string serialized = config1.SerializeAsString();
-    service.addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
+    service->addConfigurationChecked(uid, configId, {serialized.begin(), serialized.end()});
 
     // Make sure the config is stored on disk. Otherwise, we will not reset on system server death.
     StatsdConfig tmpConfig;
@@ -1501,7 +1505,7 @@
     // Metric 2 is not active.
     // Metric 3 is active.
     // {{{---------------------------------------------------------------------------
-    sp<StatsLogProcessor> processor = service.mProcessor;
+    sp<StatsLogProcessor> processor = service->mProcessor;
     EXPECT_EQ(1, processor->mMetricsManagers.size());
     auto it = processor->mMetricsManagers.find(cfgKey1);
     EXPECT_TRUE(it != processor->mMetricsManagers.end());
@@ -1592,7 +1596,7 @@
     EXPECT_TRUE(approximateSystemServerDeath < NS_PER_SEC + configAddedTimeNs);
 
     // System server dies.
-    service.binderDied(nullptr);
+    service->statsCompanionServiceDiedImpl();
 
     // We should have a new metrics manager. Lets get it and ensure activation status is restored.
     // {{{---------------------------------------------------------------------------
diff --git a/cmds/statsd/tests/StatsService_test.cpp b/cmds/statsd/tests/StatsService_test.cpp
index 7c00531..86f786e 100644
--- a/cmds/statsd/tests/StatsService_test.cpp
+++ b/cmds/statsd/tests/StatsService_test.cpp
@@ -16,6 +16,7 @@
 #include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 
+#include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
@@ -29,33 +30,34 @@
 namespace statsd {
 
 using android::util::ProtoOutputStream;
+using ::ndk::SharedRefBase;
 
 #ifdef __ANDROID__
 
 TEST(StatsServiceTest, TestAddConfig_simple) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     StatsdConfig config;
     config.set_id(12345);
     string serialized = config.SerializeAsString();
 
     EXPECT_TRUE(
-            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+            service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
 TEST(StatsServiceTest, TestAddConfig_empty) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     string serialized = "";
 
     EXPECT_TRUE(
-            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+            service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
 TEST(StatsServiceTest, TestAddConfig_invalid) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     string serialized = "Invalid config!";
 
     EXPECT_FALSE(
-            service.addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
+            service->addConfigurationChecked(123, 12345, {serialized.begin(), serialized.end()}));
 }
 
 TEST(StatsServiceTest, TestGetUidFromArgs) {
@@ -69,32 +71,32 @@
 
     int32_t uid;
 
-    StatsService service(nullptr, nullptr);
-    service.mEngBuild = true;
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    service->mEngBuild = true;
 
     // "-1"
-    EXPECT_FALSE(service.getUidFromArgs(args, 0, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 0, uid));
 
     // "0"
-    EXPECT_TRUE(service.getUidFromArgs(args, 1, uid));
+    EXPECT_TRUE(service->getUidFromArgs(args, 1, uid));
     EXPECT_EQ(0, uid);
 
     // "1"
-    EXPECT_TRUE(service.getUidFromArgs(args, 2, uid));
+    EXPECT_TRUE(service->getUidFromArgs(args, 2, uid));
     EXPECT_EQ(1, uid);
 
     // "999999999999999999"
-    EXPECT_FALSE(service.getUidFromArgs(args, 3, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 3, uid));
 
     // "a1"
-    EXPECT_FALSE(service.getUidFromArgs(args, 4, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 4, uid));
 
     // ""
-    EXPECT_FALSE(service.getUidFromArgs(args, 5, uid));
+    EXPECT_FALSE(service->getUidFromArgs(args, 5, uid));
 
     // For a non-userdebug, uid "1" cannot be impersonated.
-    service.mEngBuild = false;
-    EXPECT_FALSE(service.getUidFromArgs(args, 2, uid));
+    service->mEngBuild = false;
+    EXPECT_FALSE(service->getUidFromArgs(args, 2, uid));
 }
 
 #else
diff --git a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
index 5e6de1c..90ffcd0 100644
--- a/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AlarmTracker_test.cpp
@@ -15,12 +15,14 @@
 #include "src/anomaly/AlarmTracker.h"
 
 #include <gtest/gtest.h>
+#include <log/log_time.h>
 #include <stdio.h>
 #include <vector>
 
 using namespace testing;
 using android::sp;
 using std::set;
+using std::shared_ptr;
 using std::unordered_map;
 using std::vector;
 
@@ -34,8 +36,9 @@
 
 TEST(AlarmTrackerTest, TestTriggerTimestamp) {
     sp<AlarmMonitor> subscriberAlarmMonitor =
-        new AlarmMonitor(100, [](const sp<IStatsCompanionService>&, int64_t){},
-                         [](const sp<IStatsCompanionService>&){});
+        new AlarmMonitor(100,
+                         [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                         [](const shared_ptr<IStatsCompanionService>&){});
     Alarm alarm;
     alarm.set_offset_millis(15 * MS_PER_SEC);
     alarm.set_period_millis(60 * 60 * MS_PER_SEC);  // 1hr
diff --git a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
index 5e60aba..9127be8 100644
--- a/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
+++ b/cmds/statsd/tests/e2e/GaugeMetric_e2e_pull_test.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <android/binder_interface_utils.h>
 #include <gtest/gtest.h>
 
 #include <vector>
@@ -20,6 +21,8 @@
 #include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
 
+using ::ndk::SharedRefBase;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -71,7 +74,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -216,7 +220,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -317,7 +322,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback, ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -426,7 +432,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
@@ -544,7 +551,8 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(), ATOM_TAG);
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             ATOM_TAG);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
     processor->mPullerManager->ForceClearPullerCache();
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 9d58867..1eecbe5 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -12,9 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
-#include <binder/IPCThreadState.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_interface_utils.h>
 #include <gtest/gtest.h>
 
 #include <vector>
@@ -24,6 +23,9 @@
 #include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
 
+using::ndk::SharedRefBase;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -34,19 +36,17 @@
 const int kConfigKey = 789130123;  // Randomly chosen to avoid collisions with existing configs.
 const int kCallingUid = 0; // Randomly chosen
 
-void SendConfig(StatsService& service, const StatsdConfig& config) {
+void SendConfig(shared_ptr<StatsService>& service, const StatsdConfig& config) {
     string str;
     config.SerializeToString(&str);
-    std::vector<uint8_t> configAsVec(str.begin(), str.end());
-    bool success;
-    service.addConfiguration(kConfigKey, configAsVec, kCallingUid);
+    std::vector<int8_t> configAsVec(str.begin(), str.end());
+    service->addConfiguration(kConfigKey, configAsVec, kCallingUid);
 }
 
 ConfigMetricsReport GetReports(sp<StatsLogProcessor> processor, int64_t timestamp,
                                bool include_current = false) {
     vector<uint8_t> output;
-    IPCThreadState* ipc = IPCThreadState::self();
-    ConfigKey configKey(ipc->getCallingUid(), kConfigKey);
+    ConfigKey configKey(AIBinder_getCallingUid(), kConfigKey);
     processor->onDumpReport(configKey, timestamp, include_current /* include_current_bucket*/,
                             true /* erase_data */, ADB_DUMP, NO_TIME_CONSTRAINTS, &output);
     ConfigMetricsReportList reports;
@@ -114,56 +114,56 @@
 }  // anonymous namespace
 
 TEST(PartialBucketE2eTest, TestCountMetricWithoutSplit) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 2).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 3);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 3);
     // Expect no metrics since the bucket has not finished yet.
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricNoSplitOnNewApp) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     // Force the uidmap to update at timestamp 2.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
     // This is a new installation, so there shouldn't be a split (should be same as the without
     // split case).
-    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
                                String16(""));
     // Goes into the second bucket.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).count_metrics().data_size());
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricSplitOnUpgrade) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
-    service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
-                               {String16("")});
+    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                                {String16("")});
 
     // Force the uidmap to update at timestamp 2.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-    service.mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mUidMap->updateApp(start + 2, String16(kApp1.c_str()), 1, 2, String16("v2"),
                                String16(""));
     // Goes into the second bucket.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
     backfillStartEndTimestamp(&report);
 
     ASSERT_EQ(1, report.metrics_size());
@@ -177,20 +177,20 @@
 }
 
 TEST(PartialBucketE2eTest, TestCountMetricSplitOnRemoval) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     SendConfig(service, MakeConfig());
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
-    service.mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
-                               {String16("")});
+    service->mUidMap->updateMap(start, {1}, {1}, {String16("v1")}, {String16(kApp1.c_str())},
+                                {String16("")});
 
     // Force the uidmap to update at timestamp 2.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
-    service.mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 1).get());
+    service->mUidMap->removeApp(start + 2, String16(kApp1.c_str()), 1);
     // Goes into the second bucket.
-    service.mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
+    service->mProcessor->OnLogEvent(CreateAppCrashEvent(100, start + 3).get());
 
-    ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
+    ConfigMetricsReport report = GetReports(service->mProcessor, start + 4);
     backfillStartEndTimestamp(&report);
 
     ASSERT_EQ(1, report.metrics_size());
@@ -204,44 +204,44 @@
 }
 
 TEST(PartialBucketE2eTest, TestValueMetricWithoutMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
-    service.mPullerManager->RegisterPullAtomCallback(
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    service->mPullerManager->RegisterPullAtomCallback(
             /*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
-            new FakeSubsystemSleepCallback());
+            SharedRefBase::make<FakeSubsystemSleepCallback>());
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeValueMetricConfig(0));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
-                               String16("v2"), String16(""));
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+                                String16("v2"), String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).value_metrics().skipped_size());
 }
 
 TEST(PartialBucketE2eTest, TestValueMetricWithMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
-    service.mPullerManager->RegisterPullAtomCallback(
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
+    service->mPullerManager->RegisterPullAtomCallback(
             /*uid=*/0, android::util::SUBSYSTEM_SLEEP_STATE, NS_PER_SEC, NS_PER_SEC * 10, {},
-            new FakeSubsystemSleepCallback());
+            SharedRefBase::make<FakeSubsystemSleepCallback>());
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeValueMetricConfig(60 * NS_PER_SEC /* One minute */));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
                                String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
     backfillStartEndTimestamp(&report);
 
     ASSERT_EQ(1, report.metrics_size());
@@ -253,38 +253,38 @@
 }
 
 TEST(PartialBucketE2eTest, TestGaugeMetricWithoutMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeGaugeMetricConfig(0));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(5 * 60 * NS_PER_SEC + start + 2, String16(kApp1.c_str()), 1, 2,
                                String16("v2"), String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100, true);
     EXPECT_EQ(1, report.metrics_size());
     EXPECT_EQ(0, report.metrics(0).gauge_metrics().skipped_size());
 }
 
 TEST(PartialBucketE2eTest, TestGaugeMetricWithMinPartialBucket) {
-    StatsService service(nullptr, nullptr);
+    shared_ptr<StatsService> service = SharedRefBase::make<StatsService>(nullptr, nullptr);
     // Partial buckets don't occur when app is first installed.
-    service.mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
+    service->mUidMap->updateApp(1, String16(kApp1.c_str()), 1, 1, String16("v1"), String16(""));
     SendConfig(service, MakeGaugeMetricConfig(60 * NS_PER_SEC /* One minute */));
     int64_t start = getElapsedRealtimeNs();  // This is the start-time the metrics producers are
                                              // initialized with.
 
     const int64_t endSkipped = 5 * 60 * NS_PER_SEC + start + 2;
-    service.mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
-    service.mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
-                               String16(""));
+    service->mProcessor->informPullAlarmFired(5 * 60 * NS_PER_SEC + start);
+    service->mUidMap->updateApp(endSkipped, String16(kApp1.c_str()), 1, 2, String16("v2"),
+                                String16(""));
 
     ConfigMetricsReport report =
-            GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
+            GetReports(service->mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
     backfillStartEndTimestamp(&report);
     ASSERT_EQ(1, report.metrics_size());
     ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
diff --git a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
index a140af8..9d39f9c 100644
--- a/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/ValueMetric_pull_e2e_test.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <android/binder_interface_utils.h>
 #include <gtest/gtest.h>
 
 #include "src/StatsLogProcessor.h"
@@ -20,6 +21,8 @@
 
 #include <vector>
 
+using ::ndk::SharedRefBase;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -70,7 +73,7 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(),
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
                                              android::util::SUBSYSTEM_SLEEP_STATE);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -175,7 +178,7 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(),
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
                                              android::util::SUBSYSTEM_SLEEP_STATE);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
@@ -288,7 +291,7 @@
 
     ConfigKey cfgKey;
     auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
-                                             new FakeSubsystemSleepCallback(),
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
                                              android::util::SUBSYSTEM_SLEEP_STATE);
     EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
     EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index a011692e..e416b4c 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -14,8 +14,10 @@
 
 #include "src/external/StatsCallbackPuller.h"
 
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/util/StatsEventParcel.h>
+#include <android/binder_interface_utils.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <stdio.h>
@@ -35,6 +37,11 @@
 namespace statsd {
 
 using namespace testing;
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::os::BnPullAtomCallback;
+using aidl::android::os::IPullAtomResultReceiver;
+using aidl::android::util::StatsEventParcel;
+using ::ndk::SharedRefBase;
 using std::make_shared;
 using std::shared_ptr;
 using std::vector;
@@ -58,15 +65,15 @@
     return event;
 }
 
-void executePull(const sp<IPullAtomResultReceiver>& resultReceiver) {
+void executePull(const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
     // Convert stats_events into StatsEventParcels.
-    std::vector<android::util::StatsEventParcel> parcels;
+    vector<StatsEventParcel> parcels;
     for (int i = 0; i < values.size(); i++) {
         AStatsEvent* event = createSimpleEvent(values[i]);
         size_t size;
         uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
 
-        android::util::StatsEventParcel p;
+        StatsEventParcel p;
         // vector.assign() creates a copy, but this is inevitable unless
         // stats_event.h/c uses a vector as opposed to a buffer.
         p.buffer.assign(buffer, buffer + size);
@@ -80,11 +87,11 @@
 
 class FakePullAtomCallback : public BnPullAtomCallback {
 public:
-    binder::Status onPullAtom(int atomTag,
-                              const sp<IPullAtomResultReceiver>& resultReceiver) override {
+    Status onPullAtom(int atomTag,
+                      const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override {
         // Force pull to happen in separate thread to simulate binder.
         pullThread = std::thread(executePull, resultReceiver);
-        return binder::Status::ok();
+        return Status::ok();
     }
 };
 
@@ -111,7 +118,7 @@
 }  // Anonymous namespace.
 
 TEST_F(StatsCallbackPullerTest, PullSuccess) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     int64_t value = 43;
     pullSuccess = true;
     values.push_back(value);
@@ -132,20 +139,20 @@
 }
 
 TEST_F(StatsCallbackPullerTest, PullFail) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     pullSuccess = false;
     int64_t value = 1234;
     values.push_back(value);
 
     StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
 
-    vector<std::shared_ptr<LogEvent>> dataHolder;
+    vector<shared_ptr<LogEvent>> dataHolder;
     EXPECT_FALSE(puller.PullInternal(&dataHolder));
     EXPECT_EQ(0, dataHolder.size());
 }
 
 TEST_F(StatsCallbackPullerTest, PullTimeout) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     pullSuccess = true;
     pullDelayNs = 500000000;  // 500ms.
     pullTimeoutNs = 10000;    // 10 microseconds.
@@ -154,7 +161,7 @@
 
     StatsCallbackPuller puller(pullTagId, cb, pullCoolDownNs, pullTimeoutNs, {});
 
-    vector<std::shared_ptr<LogEvent>> dataHolder;
+    vector<shared_ptr<LogEvent>> dataHolder;
     int64_t startTimeNs = getElapsedRealtimeNs();
     // Returns true to let StatsPuller code evaluate the timeout.
     EXPECT_TRUE(puller.PullInternal(&dataHolder));
@@ -174,7 +181,7 @@
 
 // Register a puller and ensure that the timeout logic works.
 TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
-    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    shared_ptr<FakePullAtomCallback> cb = SharedRefBase::make<FakePullAtomCallback>();
     pullSuccess = true;
     pullDelayNs = 500000000;  // 500 ms.
     pullTimeoutNs = 10000;    // 10 microsseconds.
@@ -184,7 +191,7 @@
     StatsPullerManager pullerManager;
     pullerManager.RegisterPullAtomCallback(/*uid=*/-1, pullTagId, pullCoolDownNs, pullTimeoutNs,
                                            vector<int32_t>(), cb);
-    vector<std::shared_ptr<LogEvent>> dataHolder;
+    vector<shared_ptr<LogEvent>> dataHolder;
     int64_t startTimeNs = getElapsedRealtimeNs();
     // Returns false, since StatsPuller code will evaluate the timeout.
     EXPECT_FALSE(pullerManager.Pull(pullTagId, &dataHolder));
diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
index 73d1fd7..dac5f33 100644
--- a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -34,27 +34,6 @@
 
 #ifdef __ANDROID__
 
-class MyResultReceiver : public BnResultReceiver {
-public:
-    Mutex mMutex;
-    Condition mCondition;
-    bool mHaveResult = false;
-    int32_t mResult = 0;
-
-    virtual void send(int32_t resultCode) {
-        AutoMutex _l(mMutex);
-        mResult = resultCode;
-        mHaveResult = true;
-        mCondition.signal();
-    }
-
-    int32_t waitForResult() {
-        AutoMutex _l(mMutex);
-        mCondition.waitRelative(mMutex, 1000000000);
-        return mResult;
-    }
-};
-
 void runShellTest(ShellSubscription config, sp<MockUidMap> uidMap,
                   sp<MockStatsPullerManager> pullerManager,
                   const vector<std::shared_ptr<LogEvent>>& pushedEvents,
@@ -67,10 +46,7 @@
     ASSERT_EQ(0, pipe(fds_data));
 
     size_t bufferSize = config.ByteSize();
-
     // write the config to pipe, first write size of the config
-    vector<uint8_t> size_buffer(sizeof(bufferSize));
-    std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize));
     write(fds_config[1], &bufferSize, sizeof(bufferSize));
     // then write config itself
     vector<uint8_t> buffer(bufferSize);
@@ -79,11 +55,10 @@
     close(fds_config[1]);
 
     sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap, pullerManager);
-    sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
 
     // mimic a binder thread that a shell subscriber runs on. it would block.
-    std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] {
-        shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver, -1);
+    std::thread reader([&shellClient, &fds_config, &fds_data] {
+        shellClient->startNewSubscription(fds_config[0], fds_data[1], /*timeoutSec=*/-1);
     });
     reader.detach();
 
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index db09ee9..6958218 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -14,6 +14,11 @@
 
 #include "statsd_test_util.h"
 
+#include <aidl/android/util/StatsEventParcel.h>
+
+using aidl::android::util::StatsEventParcel;
+using std::shared_ptr;
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -581,7 +586,7 @@
 
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
-                                              const sp<IPullAtomCallback>& puller,
+                                              const shared_ptr<IPullAtomCallback>& puller,
                                               const int32_t atomTag) {
     sp<UidMap> uidMap = new UidMap();
     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
@@ -590,11 +595,13 @@
                                                 puller);
     }
     sp<AlarmMonitor> anomalyAlarmMonitor =
-        new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
-                [](const sp<IStatsCompanionService>&){});
+        new AlarmMonitor(1,
+                         [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                         [](const shared_ptr<IStatsCompanionService>&){});
     sp<AlarmMonitor> periodicAlarmMonitor =
-        new AlarmMonitor(1,  [](const sp<IStatsCompanionService>&, int64_t){},
-                [](const sp<IStatsCompanionService>&){});
+        new AlarmMonitor(1,
+                         [](const shared_ptr<IStatsCompanionService>&, int64_t){},
+                         [](const shared_ptr<IStatsCompanionService>&){});
     sp<StatsLogProcessor> processor =
             new StatsLogProcessor(uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
                                   timeBaseNs, [](const ConfigKey&) { return true; },
@@ -948,10 +955,10 @@
     }
 }
 
-binder::Status FakeSubsystemSleepCallback::onPullAtom(
-        int atomTag, const sp<IPullAtomResultReceiver>& resultReceiver) {
+Status FakeSubsystemSleepCallback::onPullAtom(int atomTag,
+        const shared_ptr<IPullAtomResultReceiver>& resultReceiver) {
     // Convert stats_events into StatsEventParcels.
-    std::vector<android::util::StatsEventParcel> parcels;
+    std::vector<StatsEventParcel> parcels;
     for (int i = 1; i < 3; i++) {
         AStatsEvent* event = AStatsEvent_obtain();
         AStatsEvent_setAtomId(event, atomTag);
@@ -965,7 +972,7 @@
         size_t size;
         uint8_t* buffer = AStatsEvent_getBuffer(event, &size);
 
-        android::util::StatsEventParcel p;
+        StatsEventParcel p;
         // vector.assign() creates a copy, but this is inevitable unless
         // stats_event.h/c uses a vector as opposed to a buffer.
         p.buffer.assign(buffer, buffer + size);
@@ -973,7 +980,7 @@
         AStatsEvent_write(event);
     }
     resultReceiver->pullFinished(atomTag, /*success=*/true, parcels);
-    return binder::Status::ok();
+    return Status::ok();
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 576a491..c8326ee 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -14,9 +14,9 @@
 
 #pragma once
 
-#include <android/os/BnPullAtomCallback.h>
-#include <android/os/IPullAtomCallback.h>
-#include <android/os/IPullAtomResultReceiver.h>
+#include <aidl/android/os/BnPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomCallback.h>
+#include <aidl/android/os/IPullAtomResultReceiver.h>
 #include <gtest/gtest.h>
 
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
@@ -31,8 +31,12 @@
 namespace os {
 namespace statsd {
 
+using ::aidl::android::os::BnPullAtomCallback;
+using ::aidl::android::os::IPullAtomCallback;
+using ::aidl::android::os::IPullAtomResultReceiver;
 using android::util::ProtoReader;
 using google::protobuf::RepeatedPtrField;
+using Status = ::ndk::ScopedAStatus;
 
 const int SCREEN_STATE_ATOM_ID = android::util::SCREEN_STATE_CHANGED;
 const int UID_PROCESS_STATE_ATOM_ID = android::util::UID_PROCESS_STATE_CHANGED;
@@ -230,7 +234,7 @@
 // Create a statsd log event processor upon the start time in seconds, config and key.
 sp<StatsLogProcessor> CreateStatsLogProcessor(const int64_t timeBaseNs, const int64_t currentTimeNs,
                                               const StatsdConfig& config, const ConfigKey& key,
-                                              const sp<IPullAtomCallback>& puller = nullptr,
+                                              const shared_ptr<IPullAtomCallback>& puller = nullptr,
                                               const int32_t atomTag = 0 /*for puller only*/);
 
 // Util function to sort the log events by timestamp.
@@ -285,8 +289,8 @@
 
 class FakeSubsystemSleepCallback : public BnPullAtomCallback {
 public:
-    binder::Status onPullAtom(int atomTag,
-                              const sp<IPullAtomResultReceiver>& resultReceiver) override;
+    Status onPullAtom(int atomTag,
+                      const shared_ptr<IPullAtomResultReceiver>& resultReceiver) override;
 };
 
 template <typename T>
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 82c7635..c1e2195 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -551,11 +551,6 @@
     private int mHtmlDescriptionRes;
 
     /**
-     * Non localized html description of the accessibility service.
-     */
-    private String mNonLocalizedHtmlDescription;
-
-    /**
      * Creates a new instance.
      */
     public AccessibilityServiceInfo() {
@@ -683,10 +678,6 @@
                     com.android.internal.R.styleable.AccessibilityService_htmlDescription);
             if (peekedValue != null) {
                 mHtmlDescriptionRes = peekedValue.resourceId;
-                final CharSequence nonLocalizedHtmlDescription = peekedValue.coerceToString();
-                if (nonLocalizedHtmlDescription != null) {
-                    mNonLocalizedHtmlDescription = nonLocalizedHtmlDescription.toString().trim();
-                }
             }
             asAttributes.recycle();
         } catch (NameNotFoundException e) {
@@ -919,7 +910,7 @@
     @Nullable
     public String loadHtmlDescription(@NonNull PackageManager packageManager) {
         if (mHtmlDescriptionRes == 0) {
-            return mNonLocalizedHtmlDescription;
+            return null;
         }
 
         final ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
@@ -1017,7 +1008,6 @@
         parcel.writeInt(mAnimatedImageRes);
         parcel.writeInt(mHtmlDescriptionRes);
         parcel.writeString(mNonLocalizedDescription);
-        parcel.writeString(mNonLocalizedHtmlDescription);
     }
 
     private void initFromParcel(Parcel parcel) {
@@ -1039,7 +1029,6 @@
         mAnimatedImageRes = parcel.readInt();
         mHtmlDescriptionRes = parcel.readInt();
         mNonLocalizedDescription = parcel.readString();
-        mNonLocalizedHtmlDescription = parcel.readString();
     }
 
     @Override
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2319dd2..5df67bc 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -126,6 +126,7 @@
 import android.view.autofill.AutofillManager.AutofillClient;
 import android.view.autofill.AutofillPopupWindow;
 import android.view.autofill.IAutofillWindowPresenter;
+import android.view.contentcapture.ContentCaptureContext;
 import android.view.contentcapture.ContentCaptureManager;
 import android.view.contentcapture.ContentCaptureManager.ContentCaptureClient;
 import android.widget.AdapterView;
@@ -1056,7 +1057,10 @@
         } catch (RemoteException re) {
             re.rethrowFromSystemServer();
         }
-        // TODO(b/147750355): Pass locusId and bundle to the Content Capture.
+        // If locusId is not null pass it to the Content Capture.
+        if (locusId != null) {
+            setLocusContextToContentCapture(locusId, bundle);
+        }
     }
 
     /** Return the application that owns this activity. */
@@ -1209,6 +1213,19 @@
         }
     }
 
+    private void setLocusContextToContentCapture(LocusId locusId, @Nullable Bundle bundle) {
+        final ContentCaptureManager cm = getContentCaptureManager();
+        if (cm == null) return;
+
+        ContentCaptureContext.Builder contentCaptureContextBuilder =
+                new ContentCaptureContext.Builder(locusId);
+        if (bundle != null) {
+            contentCaptureContextBuilder.setExtras(bundle);
+        }
+        cm.getMainContentCaptureSession().setContentCaptureContext(
+                contentCaptureContextBuilder.build());
+    }
+
     @Override
     protected void attachBaseContext(Context newBase) {
         super.attachBaseContext(newBase);
@@ -1890,6 +1907,7 @@
             if (!mAutoFillIgnoreFirstResumePause) {
                 View focus = getCurrentFocus();
                 if (focus != null && focus.canNotifyAutofillEnterExitEvent()) {
+                    // TODO(b/148815880): Bring up keyboard if resumed from inline authentication.
                     // TODO: in Activity killed/recreated case, i.e. SessionLifecycleTest#
                     // testDatasetVisibleWhileAutofilledAppIsLifecycled: the View's initial
                     // window visibility after recreation is INVISIBLE in onResume() and next frame
@@ -8450,7 +8468,7 @@
     /** @hide */
     @Override
     public final void autofillClientAuthenticate(int authenticationId, IntentSender intent,
-            Intent fillInIntent) {
+            Intent fillInIntent, boolean authenticateInline) {
         try {
             startIntentSenderForResultInner(intent, AUTO_FILL_AUTH_WHO_PREFIX,
                     authenticationId, fillInIntent, 0, 0, null);
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1de68ba..f6bbc68 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,6 +16,10 @@
 
 package android.app;
 
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
+
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
@@ -48,6 +52,8 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.os.UserManager;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -63,6 +69,7 @@
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.MessageSamplingConfig;
 import com.android.internal.os.RuntimeInit;
 import com.android.internal.os.ZygoteInit;
 import com.android.internal.util.ArrayUtils;
@@ -141,6 +148,13 @@
     @UnsupportedAppUsage
     final IAppOpsService mService;
 
+    /**
+     * Service for the application context, to be used by static methods via
+     * {@link #getService()}
+     */
+    @GuardedBy("sLock")
+    static IAppOpsService sService;
+
     @GuardedBy("mModeWatchers")
     private final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers =
             new ArrayMap<>();
@@ -159,6 +173,50 @@
     @GuardedBy("sLock")
     private static @Nullable AppOpsCollector sNotedAppOpsCollector;
 
+    /**
+     * Additional collector that collect accesses and forwards a few of them them via
+     * {@link IAppOpsService#reportRuntimeAppOpAccessMessageAndGetConfig}.
+     */
+    private static AppOpsCollector sMessageCollector =
+            new AppOpsCollector() {
+                @Override
+                public void onNoted(@NonNull SyncNotedAppOp op) {
+                    reportStackTraceIfNeeded(op);
+                }
+
+                @Override
+                public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp) {
+                    // collected directly in AppOpsService
+                }
+
+                @Override
+                public void onSelfNoted(@NonNull SyncNotedAppOp op) {
+                    reportStackTraceIfNeeded(op);
+                }
+
+                private void reportStackTraceIfNeeded(@NonNull SyncNotedAppOp op) {
+                    if (sConfig.getSampledOpCode() == OP_NONE
+                            && sConfig.getExpirationTimeSinceBootMillis()
+                            >= SystemClock.elapsedRealtime()) {
+                        return;
+                    }
+
+                    MessageSamplingConfig config = sConfig;
+                    if (leftCircularDistance(strOpToOp(op.getOp()), config.getSampledOpCode(),
+                            _NUM_OP) <= config.getAcceptableLeftDistance()
+                            || config.getExpirationTimeSinceBootMillis()
+                            < SystemClock.elapsedRealtime()) {
+                        String stackTrace = getFormattedStackTrace();
+                        try {
+                            sConfig = getService().reportRuntimeAppOpAccessMessageAndGetConfig(
+                                    ActivityThread.currentOpPackageName(), op, stackTrace);
+                        } catch (RemoteException e) {
+                            e.rethrowFromSystemServer();
+                        }
+                    }
+                }
+            };
+
     static IBinder sClientId;
 
     /**
@@ -550,7 +608,6 @@
     })
     public @interface OpFlags {}
 
-
     /** @hide */
     public static final String getFlagName(@OpFlags int flag) {
         switch (flag) {
@@ -569,6 +626,18 @@
         }
     }
 
+    /**
+     * Strategies used for message sampling
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"RUNTIME_APP_OPS_ACCESS__SAMPLING_STRATEGY__"}, value = {
+            RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__DEFAULT,
+            RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM,
+            RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED
+    })
+    public @interface SamplingStrategy {}
+
     private static final int UID_STATE_OFFSET = 31;
     private static final int FLAGS_MASK = 0xFFFFFFFF;
 
@@ -2225,6 +2294,10 @@
         }
     }
 
+    /** Config used to control app ops access messages sampling */
+    private static MessageSamplingConfig sConfig =
+            new MessageSamplingConfig(OP_NONE, 0, 0);
+
     /** @hide */
     public static final String KEY_HISTORICAL_OPS = "historical_ops";
 
@@ -7268,6 +7341,17 @@
         }
     }
 
+    /** @hide */
+    private static IAppOpsService getService() {
+        synchronized (sLock) {
+            if (sService == null) {
+                sService = IAppOpsService.Stub.asInterface(
+                        ServiceManager.getService(Context.APP_OPS_SERVICE));
+            }
+            return sService;
+        }
+    }
+
     /**
      * @deprecated use {@link #startOp(String, int, String, String, String)} instead
      */
@@ -7614,6 +7698,7 @@
                 sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
             }
         }
+        sMessageCollector.onSelfNoted(new SyncNotedAppOp(op, featureId));
     }
 
     /**
@@ -7764,6 +7849,10 @@
                         }
                     }
                 }
+                for (int code = notedAppOps.nextSetBit(0); code != -1;
+                        code = notedAppOps.nextSetBit(code + 1)) {
+                    sMessageCollector.onNoted(new SyncNotedAppOp(code, featureId));
+                }
             }
         }
     }
@@ -7958,10 +8047,13 @@
 
         StringBuilder sb = new StringBuilder();
         for (int i = firstInteresting; i <= lastInteresting; i++) {
-            sb.append(trace[i]);
-            if (i != lastInteresting) {
+            if (i != firstInteresting) {
                 sb.append('\n');
             }
+            if (sb.length() + trace[i].toString().length() > 600) {
+                break;
+            }
+            sb.append(trace[i]);
         }
 
         return sb.toString();
@@ -8089,6 +8181,22 @@
     }
 
     /**
+     * Pulls current AppOps access report and picks package and op to watch for next access report
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
+    public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
+        try {
+            return mService.collectRuntimeAppOpAccessMessage();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns all supported operation names.
      * @hide
      */
@@ -8297,4 +8405,12 @@
 
         return AppOpsManager.MODE_DEFAULT;
     }
+
+    /**
+     * Calculate left circular distance for two numbers modulo size.
+     * @hide
+     */
+    public static int leftCircularDistance(int from, int to, int size) {
+        return (to + size - from) % size;
+    }
 }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 180507c..3c475c1 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -315,11 +315,6 @@
     void positionTaskInStack(int taskId, int stackId, int position);
     void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
-    /**
-     * Dismisses split-screen multi-window mode.
-     * {@param toTop} If true the current primary split-screen stack will be placed or left on top.
-     */
-    void dismissSplitScreenMode(boolean toTop);
 
     /**
      * Dismisses PiP
diff --git a/core/java/android/app/ITaskStackListener.aidl b/core/java/android/app/ITaskStackListener.aidl
index 06288c0..37bdda0 100644
--- a/core/java/android/app/ITaskStackListener.aidl
+++ b/core/java/android/app/ITaskStackListener.aidl
@@ -204,4 +204,12 @@
      * @param frozen if true, Recents Tasks list is currently frozen, false otherwise
      */
     void onRecentTaskListFrozenChanged(boolean frozen);
+
+    /**
+     * Called when a task gets or loses focus.
+     *
+     * @param taskId id of the task.
+     * @param {@code true} if the task got focus, {@code false} if it lost it.
+     */
+    void onTaskFocusChanged(int taskId, boolean focused);
 }
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index f26e628..d16120d 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -21,6 +21,7 @@
 import android.graphics.drawable.Icon;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -51,6 +52,7 @@
         private String mTitle;
         private String mText;
         private Icon mIcon;
+        private String mConversationId;
 
         private HistoricalNotification() {}
 
@@ -94,6 +96,10 @@
             return mPackage + "|" + mUid + "|" + mPostedTimeMs;
         }
 
+        public String getConversationId() {
+            return mConversationId;
+        }
+
         @Override
         public String toString() {
             return "HistoricalNotification{" +
@@ -104,6 +110,7 @@
                     ", mTitle='" + mTitle + '\'' +
                     ", mText='" + mText + '\'' +
                     ", mIcon=" + mIcon +
+                    ", mConversationId=" + mConversationId +
                     '}';
         }
 
@@ -123,6 +130,7 @@
                     Objects.equals(getChannelId(), that.getChannelId()) &&
                     Objects.equals(getTitle(), that.getTitle()) &&
                     Objects.equals(getText(), that.getText()) &&
+                    Objects.equals(getConversationId(), that.getConversationId()) &&
                     iconsAreSame;
         }
 
@@ -130,7 +138,7 @@
         public int hashCode() {
             return Objects.hash(getPackage(), getChannelName(), getChannelId(), getUid(),
                     getUserId(),
-                    getPostedTimeMs(), getTitle(), getText(), getIcon());
+                    getPostedTimeMs(), getTitle(), getText(), getIcon(), getConversationId());
         }
 
         public static final class Builder {
@@ -143,6 +151,7 @@
             private String mTitle;
             private String mText;
             private Icon mIcon;
+            private String mConversationId;
 
             public Builder() {}
 
@@ -191,6 +200,11 @@
                 return this;
             }
 
+            public Builder setConversationId(String conversationId) {
+                mConversationId = conversationId;
+                return this;
+            }
+
             public HistoricalNotification build() {
                 HistoricalNotification n = new HistoricalNotification();
                 n.mPackage = mPackage;
@@ -202,6 +216,7 @@
                 n.mTitle = mTitle;
                 n.mText = mText;
                 n.mIcon = mIcon;
+                n.mConversationId = mConversationId;
                 return n;
             }
         }
@@ -299,6 +314,9 @@
             mStringsToWrite.add(notification.getPackage());
             mStringsToWrite.add(notification.getChannelName());
             mStringsToWrite.add(notification.getChannelId());
+            if (!TextUtils.isEmpty(notification.getConversationId())) {
+                mStringsToWrite.add(notification.getConversationId());
+            }
         }
     }
 
@@ -423,9 +441,17 @@
             channelIdIndex = -1;
         }
 
+        final int conversationIdIndex;
+        if (!TextUtils.isEmpty(notification.getConversationId())) {
+            conversationIdIndex = findStringIndex(notification.getConversationId());
+        } else {
+            conversationIdIndex = -1;
+        }
+
         p.writeInt(packageIndex);
         p.writeInt(channelNameIndex);
         p.writeInt(channelIdIndex);
+        p.writeInt(conversationIdIndex);
         p.writeInt(notification.getUid());
         p.writeInt(notification.getUserId());
         p.writeLong(notification.getPostedTimeMs());
@@ -461,6 +487,13 @@
             notificationOut.setChannelId(null);
         }
 
+        final int conversationIdIndex = p.readInt();
+        if (conversationIdIndex >= 0) {
+            notificationOut.setConversationId(mStringPool[conversationIdIndex]);
+        } else {
+            notificationOut.setConversationId(null);
+        }
+
         notificationOut.setUid(p.readInt());
         notificationOut.setUserId(p.readInt());
         notificationOut.setPostedTimeMs(p.readLong());
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/core/java/android/app/RuntimeAppOpAccessMessage.aidl
similarity index 74%
copy from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
copy to core/java/android/app/RuntimeAppOpAccessMessage.aidl
index fcacd52..68e8819 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
+++ b/core/java/android/app/RuntimeAppOpAccessMessage.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.view.animation;
+package android.app;
 
-public interface NativeInterpolatorFactory {
-    long createNativeInterpolator();
-}
+parcelable RuntimeAppOpAccessMessage;
diff --git a/core/java/android/app/RuntimeAppOpAccessMessage.java b/core/java/android/app/RuntimeAppOpAccessMessage.java
new file mode 100644
index 0000000..a81b8e7
--- /dev/null
+++ b/core/java/android/app/RuntimeAppOpAccessMessage.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2019 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.app;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.DataClass;
+
+/**
+ * Message for noted runtime permission access.
+ * @hide
+ */
+@Immutable
+@TestApi
+@SystemApi
+/*@DataClass(genConstructor = false)
+@DataClass.Suppress("getOpCode")*/
+public final class RuntimeAppOpAccessMessage implements Parcelable {
+    /** Uid of package for which runtime app op access message was collected */
+    private final @IntRange(from = 0L) int mUid;
+    /** Op code of operation access which was collected */
+    private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
+    /** Name of package for which runtime app op access message was collected */
+    private final @NonNull String mPackageName;
+    /** Feature of package for which runtime app op access message was collected */
+    private final @Nullable String mFeatureId;
+    /** Message collected (including stacktrace for synchronous ops) */
+    private final @NonNull String mMessage;
+    /** Sampling strategy used to collect this message. */
+    private final @AppOpsManager.SamplingStrategy int mSamplingStrategy;
+
+    public @NonNull String getOp() {
+        return AppOpsManager.opToPublicName(mOpCode);
+    }
+
+    /**
+     * Creates a new RuntimeAppOpAccessMessage.
+     *
+     * @param uid
+     *   Uid of package for which runtime app op access message was collected
+     * @param opCode
+     *   Op code of operation access which was collected
+     * @param packageName
+     *   Name of package for which runtime app op access message was collected
+     * @param featureId
+     *   Feature of package for which runtime app op access message was collected
+     * @param message
+     *   Message collected (including stacktrace for synchronous ops)
+     * @param samplingStrategy
+     *   Sampling strategy used to collect this message.
+     */
+    @DataClass.Generated.Member
+    public RuntimeAppOpAccessMessage(
+            @IntRange(from = 0L) int uid,
+            @IntRange(from = 0L) int opCode,
+            @NonNull String packageName,
+            @Nullable String featureId,
+            @NonNull String message,
+            @AppOpsManager.SamplingStrategy int samplingStrategy) {
+        this.mUid = uid;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mUid,
+                "from", 0L);
+        this.mOpCode = opCode;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mOpCode,
+                "from", 0L,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mFeatureId = featureId;
+        this.mMessage = message;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMessage);
+        this.mSamplingStrategy = samplingStrategy;
+        com.android.internal.util.AnnotationValidations.validate(
+                AppOpsManager.SamplingStrategy.class, null, mSamplingStrategy);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+
+
+
+    // Code below generated by codegen v1.0.14.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Uid of package for which runtime app op access message was collected
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0L) int getUid() {
+        return mUid;
+    }
+
+    /**
+     * Name of package for which runtime app op access message was collected
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Feature of package for which runtime app op access message was collected
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getFeatureId() {
+        return mFeatureId;
+    }
+
+    /**
+     * Message collected (including stacktrace for synchronous ops)
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getMessage() {
+        return mMessage;
+    }
+
+    /**
+     * Sampling strategy used to collect this message.
+     */
+    @DataClass.Generated.Member
+    public @AppOpsManager.SamplingStrategy int getSamplingStrategy() {
+        return mSamplingStrategy;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mFeatureId != null) flg |= 0x8;
+        dest.writeByte(flg);
+        dest.writeInt(mUid);
+        dest.writeInt(mOpCode);
+        dest.writeString(mPackageName);
+        if (mFeatureId != null) dest.writeString(mFeatureId);
+        dest.writeString(mMessage);
+        dest.writeInt(mSamplingStrategy);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ RuntimeAppOpAccessMessage(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        int uid = in.readInt();
+        int opCode = in.readInt();
+        String packageName = in.readString();
+        String featureId = (flg & 0x8) == 0 ? null : in.readString();
+        String message = in.readString();
+        int samplingStrategy = in.readInt();
+
+        this.mUid = uid;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mUid,
+                "from", 0L);
+        this.mOpCode = opCode;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mOpCode,
+                "from", 0L,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mPackageName = packageName;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mPackageName);
+        this.mFeatureId = featureId;
+        this.mMessage = message;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMessage);
+        this.mSamplingStrategy = samplingStrategy;
+        com.android.internal.util.AnnotationValidations.validate(
+                AppOpsManager.SamplingStrategy.class, null, mSamplingStrategy);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<RuntimeAppOpAccessMessage> CREATOR
+            = new Parcelable.Creator<RuntimeAppOpAccessMessage>() {
+        @Override
+        public RuntimeAppOpAccessMessage[] newArray(int size) {
+            return new RuntimeAppOpAccessMessage[size];
+        }
+
+        @Override
+        public RuntimeAppOpAccessMessage createFromParcel(@NonNull Parcel in) {
+            return new RuntimeAppOpAccessMessage(in);
+        }
+    };
+
+    /*@DataClass.Generated(
+            time = 1581517099127L,
+            codegenVersion = "1.0.14",
+            sourceFile = "frameworks/base/core/java/android/app/RuntimeAppOpAccessMessage.java",
+            inputSignatures = "private final @android.annotation.IntRange(from=0L) int mUid\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.NonNull java.lang.String mPackageName\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.app.AppOpsManager.SamplingStrategy int mSamplingStrategy\npublic @android.annotation.NonNull java.lang.String getOp()\nclass RuntimeAppOpAccessMessage extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false)")*/
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/core/java/android/app/SyncNotedAppOp.aidl
similarity index 74%
copy from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
copy to core/java/android/app/SyncNotedAppOp.aidl
index fcacd52..ab062d2 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
+++ b/core/java/android/app/SyncNotedAppOp.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.view.animation;
+package android.app;
 
-public interface NativeInterpolatorFactory {
-    long createNativeInterpolator();
-}
+parcelable SyncNotedAppOp;
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
index 065d5de..aa11b95 100644
--- a/core/java/android/app/SyncNotedAppOp.java
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -19,8 +19,10 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Parcelable;
 
 import com.android.internal.annotations.Immutable;
+import com.android.internal.util.DataClass;
 
 /**
  * Description of an app-op that was noted for the current process.
@@ -32,48 +34,154 @@
  * itself}.
  */
 @Immutable
-public final class SyncNotedAppOp {
-    private final int mOpCode;
+/*@DataClass(
+        genEqualsHashCode = true,
+        genConstructor = false
+)
+@DataClass.Suppress("getOpCode")*/
+public final class SyncNotedAppOp implements Parcelable {
+
+    /** op code of synchronous appop noted */
+    private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mOpCode;
+    /** featureId of synchronous appop noted */
     private final @Nullable String mFeatureId;
 
     /**
+     * Creates a new SyncNotedAppOp.
+     *
+     * @param opCode
+     *   op code of synchronous appop noted
+     * @param featureId
+     *   featureId of synchronous appop noted
+     */
+    public SyncNotedAppOp(@IntRange(from = 0L) int opCode, @Nullable String featureId) {
+        this.mOpCode = opCode;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mOpCode,
+                "from", 0,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mFeatureId = featureId;
+    }
+
+    /**
      * @return The op that was noted.
      */
     public @NonNull String getOp() {
         return AppOpsManager.opToPublicName(mOpCode);
     }
 
+    // Code below generated by codegen v1.0.14.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/SyncNotedAppOp.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
     /**
-     * @return The {@link android.content.Context#createFeatureContext Feature} in the app
+     * featureId of synchronous appop noted
      */
+    @DataClass.Generated.Member
     public @Nullable String getFeatureId() {
         return mFeatureId;
     }
 
-    /**
-     * Create a new sync op description
-     *
-     * @param opCode The op that was noted
-     *
-     * @hide
-     */
-    public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
-            @Nullable String featureId) {
-        mOpCode = opCode;
-        mFeatureId = featureId;
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(@Nullable Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(SyncNotedAppOp other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        SyncNotedAppOp that = (SyncNotedAppOp) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mOpCode == that.mOpCode
+                && java.util.Objects.equals(mFeatureId, that.mFeatureId);
     }
 
     @Override
-    public boolean equals(Object other) {
-        if (!(other instanceof SyncNotedAppOp)) {
-            return false;
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mOpCode;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureId);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mFeatureId != null) flg |= 0x2;
+        dest.writeByte(flg);
+        dest.writeInt(mOpCode);
+        if (mFeatureId != null) dest.writeString(mFeatureId);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ SyncNotedAppOp(@NonNull android.os.Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        byte flg = in.readByte();
+        int opCode = in.readInt();
+        String featureId = (flg & 0x2) == 0 ? null : in.readString();
+
+        this.mOpCode = opCode;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mOpCode,
+                "from", 0,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mFeatureId = featureId;
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<SyncNotedAppOp> CREATOR
+            = new Parcelable.Creator<SyncNotedAppOp>() {
+        @Override
+        public SyncNotedAppOp[] newArray(int size) {
+            return new SyncNotedAppOp[size];
         }
 
-        return mOpCode == ((SyncNotedAppOp) other).mOpCode;
-    }
+        @Override
+        public SyncNotedAppOp createFromParcel(@NonNull android.os.Parcel in) {
+            return new SyncNotedAppOp(in);
+        }
+    };
 
-    @Override
-    public int hashCode() {
-        return mOpCode;
-    }
+    /*@DataClass.Generated(
+            time = 1579188889960L,
+            codegenVersion = "1.0.14",
+            sourceFile = "frameworks/base/core/java/android/app/SyncNotedAppOp.java",
+            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mOpCode\nprivate final @android.annotation.Nullable java.lang.String mFeatureId\npublic @android.annotation.NonNull java.lang.String getOp()\npublic @android.annotation.SystemApi int getOpCode()\nclass SyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genConstructor=false)")*/
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
 }
diff --git a/core/java/android/app/TaskStackListener.java b/core/java/android/app/TaskStackListener.java
index 343b386..da0aadb 100644
--- a/core/java/android/app/TaskStackListener.java
+++ b/core/java/android/app/TaskStackListener.java
@@ -199,4 +199,8 @@
     @Override
     public void onRecentTaskListFrozenChanged(boolean frozen) {
     }
+
+    @Override
+    public void onTaskFocusChanged(int taskId, boolean focused) {
+    }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4a5a23a..40b81dd 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -11580,13 +11580,16 @@
     }
 
     /**
-     * Sets the set of package names that are allowed to request user consent for cross-profile
-     * communication.
+     * Sets the set of admin-whitelisted package names that are allowed to request user consent for
+     * cross-profile communication.
      *
      * <p>Assumes that the caller is a profile owner and is the given {@code admin}.
      *
      * <p>Previous calls are overridden by each subsequent call to this method.
      *
+     * <p>Note that other apps may be able to request user consent for cross-profile communication
+     * if they have been explicitly whitelisted by the OEM.
+     *
      * <p>When previously-set cross-profile packages are missing from {@code packageNames}, the
      * app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not
      * occur for packages that are whitelisted by the OEM.
@@ -11608,15 +11611,18 @@
 
     /**
      * Returns the set of package names that the admin has previously set as allowed to request user
-     * consent for cross-profile communication, via {@link
-     * #setCrossProfilePackages(ComponentName, Set)}.
+     * consent for cross-profile communication, via {@link #setCrossProfilePackages(ComponentName,
+     * Set)}.
      *
      * <p>Assumes that the caller is a profile owner and is the given {@code admin}.
      *
+     * <p>Note that other apps not included in the returned set may be able to request user consent
+     * for cross-profile communication if they have been explicitly whitelisted by the OEM.
+     *
      * @param admin the {@link DeviceAdminReceiver} this request is associated with
      * @return the set of package names the admin has previously set as allowed to request user
-     * consent for cross-profile communication, via {@link
-     * #setCrossProfilePackages(ComponentName, Set)}
+     * consent for cross-profile communication, via {@link #setCrossProfilePackages(ComponentName,
+     * Set)}
      */
     public @NonNull Set<String> getCrossProfilePackages(@NonNull ComponentName admin) {
         throwIfParentInstance("getCrossProfilePackages");
@@ -11634,18 +11640,17 @@
      * Returns the combined set of the following:
      * <ul>
      * <li>The package names that the admin has previously set as allowed to request user consent
-     * for cross-profile communication, via {@link
-     * #setCrossProfilePackages(ComponentName, Set)}.</li>
+     * for cross-profile communication, via {@link #setCrossProfilePackages(ComponentName,
+     * Set)}.</li>
      * <li>The default package names set by the OEM that are allowed to request user consent for
-     * cross-profile communication without being explicitly enabled by the admin, via
-     * {@link com.android.internal.R.array#cross_profile_apps} and
-     * {@link com.android.internal.R.array#vendor_cross_profile_apps}.</li>
+     * cross-profile communication without being explicitly enabled by the admin, via {@link
+     * com.android.internal.R.array#cross_profile_apps} and {@link com.android.internal.R.array
+     * #vendor_cross_profile_apps}.</li>
      * </ul>
      *
      * @return the combined set of whitelisted package names set via
-     * {@link #setCrossProfilePackages(ComponentName, Set)},
-     * {@link com.android.internal.R.array#cross_profile_apps},
-     * and {@link com.android.internal.R.array#vendor_cross_profile_apps}.
+     * {@link #setCrossProfilePackages(ComponentName, Set)}, {@link com.android.internal.R.array
+     * #cross_profile_apps}, and {@link com.android.internal.R.array#vendor_cross_profile_apps}.
      *
      * @hide
      */
@@ -11668,9 +11673,9 @@
 
     /**
      * Returns the default package names set by the OEM that are allowed to request user consent for
-     * cross-profile communication without being explicitly enabled by the admin, via
-     * {@link com.android.internal.R.array#cross_profile_apps} and
-     * {@link com.android.internal.R.array#vendor_cross_profile_apps}.
+     * cross-profile communication without being explicitly enabled by the admin, via {@link
+     * com.android.internal.R.array#cross_profile_apps} and {@link com.android.internal.R.array
+     * #vendor_cross_profile_apps}.
      *
      * @hide
      */
diff --git a/core/java/android/app/admin/FactoryResetProtectionPolicy.java b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
index ed74779..954db04 100644
--- a/core/java/android/app/admin/FactoryResetProtectionPolicy.java
+++ b/core/java/android/app/admin/FactoryResetProtectionPolicy.java
@@ -53,17 +53,17 @@
 
     private static final String KEY_FACTORY_RESET_PROTECTION_ACCOUNT =
             "factory_reset_protection_account";
-    private static final String KEY_FACTORY_RESET_PROTECTION_DISABLED =
-            "factory_reset_protection_disabled";
+    private static final String KEY_FACTORY_RESET_PROTECTION_ENABLED =
+            "factory_reset_protection_enabled";
     private static final String ATTR_VALUE = "value";
 
     private final List<String> mFactoryResetProtectionAccounts;
-    private final boolean mFactoryResetProtectionDisabled;
+    private final boolean mFactoryResetProtectionEnabled;
 
     private FactoryResetProtectionPolicy(List<String> factoryResetProtectionAccounts,
-            boolean factoryResetProtectionDisabled) {
+            boolean factoryResetProtectionEnabled) {
         mFactoryResetProtectionAccounts = factoryResetProtectionAccounts;
-        mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+        mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
     }
 
     /**
@@ -74,10 +74,10 @@
     }
 
     /**
-     * Return whether factory reset protection for the device is disabled or not.
+     * Return whether factory reset protection for the device is enabled or not.
      */
-    public boolean isFactoryResetProtectionDisabled() {
-        return mFactoryResetProtectionDisabled;
+    public boolean isFactoryResetProtectionEnabled() {
+        return mFactoryResetProtectionEnabled;
     }
 
     /**
@@ -85,12 +85,13 @@
      */
     public static class Builder {
         private List<String> mFactoryResetProtectionAccounts;
-        private boolean mFactoryResetProtectionDisabled;
+        private boolean mFactoryResetProtectionEnabled;
 
         /**
          * Initialize a new Builder to construct a {@link FactoryResetProtectionPolicy}.
          */
         public Builder() {
+            mFactoryResetProtectionEnabled = true;
         };
 
         /**
@@ -113,18 +114,19 @@
         }
 
         /**
-         * Sets whether factory reset protection is disabled or not.
+         * Sets whether factory reset protection is enabled or not.
          * <p>
          * Once disabled, factory reset protection will not kick in all together when the device
          * goes through untrusted factory reset. This applies to both the consumer unlock flow and
-         * the admin account overrides via {@link #setFactoryResetProtectionAccounts}
+         * the admin account overrides via {@link #setFactoryResetProtectionAccounts}. By default,
+         * factory reset protection is enabled.
          *
-         * @param factoryResetProtectionDisabled Whether the policy is disabled or not.
+         * @param factoryResetProtectionEnabled Whether the policy is enabled or not.
          * @return the same Builder instance.
          */
         @NonNull
-        public Builder setFactoryResetProtectionDisabled(boolean factoryResetProtectionDisabled) {
-            mFactoryResetProtectionDisabled = factoryResetProtectionDisabled;
+        public Builder setFactoryResetProtectionEnabled(boolean factoryResetProtectionEnabled) {
+            mFactoryResetProtectionEnabled = factoryResetProtectionEnabled;
             return this;
         }
 
@@ -136,7 +138,7 @@
         @NonNull
         public FactoryResetProtectionPolicy build() {
             return new FactoryResetProtectionPolicy(mFactoryResetProtectionAccounts,
-                    mFactoryResetProtectionDisabled);
+                    mFactoryResetProtectionEnabled);
         }
     }
 
@@ -144,7 +146,7 @@
     public String toString() {
         return "FactoryResetProtectionPolicy{"
                 + "mFactoryResetProtectionAccounts=" + mFactoryResetProtectionAccounts
-                + ", mFactoryResetProtectionDisabled=" + mFactoryResetProtectionDisabled
+                + ", mFactoryResetProtectionEnabled=" + mFactoryResetProtectionEnabled
                 + '}';
     }
 
@@ -155,7 +157,7 @@
         for (String account: mFactoryResetProtectionAccounts) {
             dest.writeString(account);
         }
-        dest.writeBoolean(mFactoryResetProtectionDisabled);
+        dest.writeBoolean(mFactoryResetProtectionEnabled);
     }
 
     @Override
@@ -173,10 +175,10 @@
                     for (int i = 0; i < accountsCount; i++) {
                         factoryResetProtectionAccounts.add(in.readString());
                     }
-                    boolean factoryResetProtectionDisabled = in.readBoolean();
+                    boolean factoryResetProtectionEnabled = in.readBoolean();
 
                     return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
-                            factoryResetProtectionDisabled);
+                            factoryResetProtectionEnabled);
                 }
 
                 @Override
@@ -195,8 +197,8 @@
     @Nullable
     public static FactoryResetProtectionPolicy readFromXml(@NonNull XmlPullParser parser) {
         try {
-            boolean factoryResetProtectionDisabled = Boolean.parseBoolean(
-                    parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_DISABLED));
+            boolean factoryResetProtectionEnabled = Boolean.parseBoolean(
+                    parser.getAttributeValue(null, KEY_FACTORY_RESET_PROTECTION_ENABLED));
 
             List<String> factoryResetProtectionAccounts = new ArrayList<>();
             int outerDepth = parser.getDepth();
@@ -214,7 +216,7 @@
             }
 
             return new FactoryResetProtectionPolicy(factoryResetProtectionAccounts,
-                    factoryResetProtectionDisabled);
+                    factoryResetProtectionEnabled);
         } catch (XmlPullParserException | IOException e) {
             Log.w(LOG_TAG, "Reading from xml failed", e);
         }
@@ -225,8 +227,8 @@
      * @hide
      */
     public void writeToXml(@NonNull XmlSerializer out) throws IOException {
-        out.attribute(null, KEY_FACTORY_RESET_PROTECTION_DISABLED,
-                Boolean.toString(mFactoryResetProtectionDisabled));
+        out.attribute(null, KEY_FACTORY_RESET_PROTECTION_ENABLED,
+                Boolean.toString(mFactoryResetProtectionEnabled));
         for (String account : mFactoryResetProtectionAccounts) {
             out.startTag(null, KEY_FACTORY_RESET_PROTECTION_ACCOUNT);
             out.attribute(null, ATTR_VALUE, account);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f88c90..2e591ca 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -6767,7 +6767,7 @@
                     this.mClipData = new ClipData(o.mClipData);
                 }
             } else {
-                if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
+                if (o.mExtras != null && !o.mExtras.isDefinitelyEmpty()) {
                     this.mExtras = Bundle.STRIPPED;
                 }
 
diff --git a/core/java/android/content/pm/CrossProfileApps.java b/core/java/android/content/pm/CrossProfileApps.java
index 50841c3..eb1da67 100644
--- a/core/java/android/content/pm/CrossProfileApps.java
+++ b/core/java/android/content/pm/CrossProfileApps.java
@@ -47,8 +47,15 @@
 public class CrossProfileApps {
 
     /**
-     * Broadcast signalling that the receiving app's ability to interact across profiles has
-     * changed, as defined by the return value of {@link #canInteractAcrossProfiles()}.
+     * Broadcast signalling that the receiving app's permission to interact across profiles has
+     * changed. This includes the user, admin, or OEM changing their consent such that the
+     * permission for the app to interact across profiles has changed.
+     *
+     * <p>This broadcast is not sent when other circumstances result in a change to being able to
+     * interact across profiles in practice, such as the profile being turned off or removed, apps
+     * being uninstalled, etc. The methods {@link #canInteractAcrossProfiles()} and {@link
+     * #canRequestInteractAcrossProfiles()} can be used by apps prior to attempting to interact
+     * across profiles or attempting to request user consent to interact across profiles.
      *
      * <p>Apps that have set the {@code android:crossProfile} manifest attribute to {@code true}
      * can receive this broadcast in manifest broadcast receivers. Otherwise, it can only be
@@ -99,8 +106,11 @@
     /**
      * Starts the specified activity of the caller package in the specified profile.
      *
-     * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
-     * permission and both the caller and target user profiles must be in the same profile group.
+     * <p>The caller must have the {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
+     * {@code android.Manifest.permission#INTERACT_ACROSS_USERS}, or {@code
+     * android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} permission. Both the caller and
+     * target user profiles must be in the same profile group. The target user must be a valid user
+     * returned from {@link #getTargetUserProfiles()}.
      *
      * @param intent The intent to launch. A component in the caller package must be specified.
      * @param targetUser The {@link UserHandle} of the profile; must be one of the users returned by
@@ -219,10 +229,11 @@
     }
 
     /**
-     * Returns whether the calling package can request to interact across profiles.
+     * Returns whether the calling package can request user consent to interact across profiles.
      *
-     * <p>The package's current ability to interact across profiles can be checked with
-     * {@link #canInteractAcrossProfiles()}.
+     * <p>If {@code true}, user consent can be obtained via {@link
+     * #createRequestInteractAcrossProfilesIntent()}. The package can then listen to {@link
+     * #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
      *
      * <p>Specifically, returns whether the following are all true:
      * <ul>
@@ -235,6 +246,10 @@
      * </li>
      * </ul>
      *
+     * <p>Note that user consent could already be granted if given a return value of {@code true}.
+     * The package's current ability to interact across profiles can be checked with {@link
+     * #canInteractAcrossProfiles()}.
+     *
      * @return true if the calling package can request to interact across profiles.
      */
     public boolean canRequestInteractAcrossProfiles() {
@@ -247,10 +262,7 @@
 
     /**
      * Returns whether the calling package can interact across profiles.
-     *
-     * <p>The package's current ability to request to interact across profiles can be checked with
-     * {@link #canRequestInteractAcrossProfiles()}.
-     *
+
      * <p>Specifically, returns whether the following are all true:
      * <ul>
      * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li>
@@ -262,6 +274,11 @@
      * </li>
      * </ul>
      *
+     * <p>If {@code false}, the package's current ability to request user consent to interact across
+     * profiles can be checked with {@link #canRequestInteractAcrossProfiles()}. If {@code true},
+     * user consent can be obtained via {@link #createRequestInteractAcrossProfilesIntent()}. The
+     * package can then listen to {@link #ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED} broadcasts.
+     *
      * @return true if the calling package can interact across profiles.
      * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the
      * calling UID.
@@ -276,11 +293,15 @@
 
     /**
      * Returns an {@link Intent} to open the settings page that allows the user to decide whether
-     * the calling app can interact across profiles. The current state is given by
-     * {@link #canInteractAcrossProfiles()}.
+     * the calling app can interact across profiles.
      *
      * <p>Returns {@code null} if {@link #canRequestInteractAcrossProfiles()} is {@code false}.
      *
+     * <p>Note that the user may already have given consent and the app may already be able to
+     * interact across profiles, even if {@link #canRequestInteractAcrossProfiles()} is {@code
+     * true}. The current ability to interact across profiles is given by {@link
+     * #canInteractAcrossProfiles()}.
+     *
      * @return an {@link Intent} to open the settings page that allows the user to decide whether
      * the app can interact across profiles
      *
diff --git a/core/java/android/content/pm/parsing/AndroidPackage.java b/core/java/android/content/pm/parsing/AndroidPackage.java
index da17ff3..b93c3a2 100644
--- a/core/java/android/content/pm/parsing/AndroidPackage.java
+++ b/core/java/android/content/pm/parsing/AndroidPackage.java
@@ -397,6 +397,8 @@
 
     boolean hasComponentClassName(String className);
 
+    boolean hasPreserveLegacyExternalStorage();
+
     // App Info
 
     boolean hasRequestedLegacyExternalStorage();
diff --git a/core/java/android/content/pm/parsing/ApkParseUtils.java b/core/java/android/content/pm/parsing/ApkParseUtils.java
index a267113..905794b 100644
--- a/core/java/android/content/pm/parsing/ApkParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkParseUtils.java
@@ -2101,6 +2101,10 @@
             parsingPackage.setAllowNativeHeapPointerTagging(sa.getBoolean(
                     R.styleable.AndroidManifestApplication_allowNativeHeapPointerTagging, true));
 
+            parsingPackage.setPreserveLegacyExternalStorage(sa.getBoolean(
+                            R.styleable.AndroidManifestApplication_preserveLegacyExternalStorage,
+                            false));
+
             parsingPackage
                     .setMaxAspectRatio(
                             sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0))
diff --git a/core/java/android/content/pm/parsing/PackageImpl.java b/core/java/android/content/pm/parsing/PackageImpl.java
index 778d7b8..180714a 100644
--- a/core/java/android/content/pm/parsing/PackageImpl.java
+++ b/core/java/android/content/pm/parsing/PackageImpl.java
@@ -289,6 +289,7 @@
     private String[] usesLibraryFiles;
     private List<SharedLibraryInfo> usesLibraryInfos;
     private String zygotePreloadName;
+    private boolean preserveLegacyExternalStorage;
 
     @VisibleForTesting
     public PackageImpl(
@@ -1519,6 +1520,12 @@
     }
 
     @Override
+    public PackageImpl setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage) {
+        this.preserveLegacyExternalStorage = preserveLegacyExternalStorage;
+        return this;
+    }
+
+    @Override
     public PackageImpl setUsesNonSdkApi(boolean usesNonSdkApi) {
         this.privateFlags = usesNonSdkApi
                 ? this.privateFlags | ApplicationInfo.PRIVATE_FLAG_USES_NON_SDK_API
@@ -2999,6 +3006,11 @@
         return queriesProviders;
     }
 
+    @Override
+    public boolean hasPreserveLegacyExternalStorage() {
+        return preserveLegacyExternalStorage;
+    }
+
     private static void internStringArrayList(List<String> list) {
         if (list != null) {
             final int N = list.size();
diff --git a/core/java/android/content/pm/parsing/ParsingPackage.java b/core/java/android/content/pm/parsing/ParsingPackage.java
index 954d65c..74a2640 100644
--- a/core/java/android/content/pm/parsing/ParsingPackage.java
+++ b/core/java/android/content/pm/parsing/ParsingPackage.java
@@ -192,6 +192,8 @@
     ParsingPackage setRequestLegacyExternalStorage(boolean requestLegacyExternalStorage);
 
     ParsingPackage setAllowNativeHeapPointerTagging(boolean allowNativeHeapPointerTagging);
+  
+    ParsingPackage setPreserveLegacyExternalStorage(boolean preserveLegacyExternalStorage);
 
     ParsingPackage setRestoreAnyVersion(boolean restoreAnyVersion);
 
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 25b84c5..65f45d8 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -80,7 +80,7 @@
      * Display category: Presentation displays.
      * <p>
      * This category can be used to identify secondary displays that are suitable for
-     * use as presentation displays such as HDMI or Wireless displays.  Applications
+     * use as presentation displays such as external or wireless displays.  Applications
      * may automatically project their content to presentation displays to provide
      * richer second screen experiences.
      * </p>
@@ -100,7 +100,7 @@
      * When this flag is set, the virtual display is public.
      * </p><p>
      * A public virtual display behaves just like most any other display that is connected
-     * to the system such as an HDMI or Wireless display.  Applications can open
+     * to the system such as an external or wireless display.  Applications can open
      * windows on the display and the system may mirror the contents of other displays
      * onto it.
      * </p><p>
@@ -364,7 +364,7 @@
                     addAllDisplaysLocked(mTempDisplays, displayIds);
                 } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) {
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI);
-                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI);
+                    addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_EXTERNAL);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY);
                     addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL);
                 }
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
new file mode 100644
index 0000000..edae06a
--- /dev/null
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.inputmethodservice;
+
+import static android.inputmethodservice.InputMethodService.DEBUG;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.inputmethod.InlineSuggestionsRequest;
+import android.view.inputmethod.InlineSuggestionsResponse;
+
+import com.android.internal.view.IInlineSuggestionsRequestCallback;
+import com.android.internal.view.IInlineSuggestionsResponseCallback;
+
+import java.lang.ref.WeakReference;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+/**
+ * Maintains an active inline suggestion session.
+ *
+ * <p>
+ * Each session corresponds to one inline suggestion request, but there may be multiple callbacks
+ * with the inline suggestions response.
+ */
+class InlineSuggestionSession {
+
+    private static final String TAG = InlineSuggestionSession.class.getSimpleName();
+
+    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+
+    @NonNull
+    private final ComponentName mComponentName;
+    @NonNull
+    private final IInlineSuggestionsRequestCallback mCallback;
+    @NonNull
+    private final InlineSuggestionsResponseCallbackImpl mResponseCallback;
+    @NonNull
+    private final Supplier<String> mClientPackageNameSupplier;
+    @NonNull
+    private final Supplier<InlineSuggestionsRequest> mRequestSupplier;
+    @NonNull
+    private final Supplier<IBinder> mHostInputTokenSupplier;
+    @NonNull
+    private final Consumer<InlineSuggestionsResponse> mResponseConsumer;
+
+    private volatile boolean mInvalidated = false;
+
+    InlineSuggestionSession(@NonNull ComponentName componentName,
+            @NonNull IInlineSuggestionsRequestCallback callback,
+            @NonNull Supplier<String> clientPackageNameSupplier,
+            @NonNull Supplier<InlineSuggestionsRequest> requestSupplier,
+            @NonNull Supplier<IBinder> hostInputTokenSupplier,
+            @NonNull Consumer<InlineSuggestionsResponse> responseConsumer) {
+        mComponentName = componentName;
+        mCallback = callback;
+        mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this);
+        mClientPackageNameSupplier = clientPackageNameSupplier;
+        mRequestSupplier = requestSupplier;
+        mHostInputTokenSupplier = hostInputTokenSupplier;
+        mResponseConsumer = responseConsumer;
+
+        makeInlineSuggestionsRequest();
+    }
+
+    /**
+     * This needs to be called before creating a new session, such that the later response callbacks
+     * will be discarded.
+     */
+    void invalidateSession() {
+        mInvalidated = true;
+    }
+
+    /**
+     * Sends an {@link InlineSuggestionsRequest} obtained from {@cocde supplier} to the current
+     * Autofill Session through
+     * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
+     */
+    private void makeInlineSuggestionsRequest() {
+        try {
+            final InlineSuggestionsRequest request = mRequestSupplier.get();
+            if (request == null) {
+                if (DEBUG) {
+                    Log.d(TAG, "onCreateInlineSuggestionsRequest() returned null request");
+                }
+                mCallback.onInlineSuggestionsUnsupported();
+            } else {
+                request.setHostInputToken(mHostInputTokenSupplier.get());
+                mCallback.onInlineSuggestionsRequest(request, mResponseCallback);
+            }
+        } catch (RemoteException e) {
+            Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
+        }
+    }
+
+    private void handleOnInlineSuggestionsResponse(@NonNull InlineSuggestionsResponse response) {
+        if (mInvalidated) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOnInlineSuggestionsResponse() called on invalid session");
+            }
+            return;
+        }
+        // TODO(b/149522488): checking the current focused input field to make sure we don't send
+        //  inline responses for previous input field
+        if (!mComponentName.getPackageName().equals(mClientPackageNameSupplier.get())) {
+            if (DEBUG) {
+                Log.d(TAG, "handleOnInlineSuggestionsResponse() called on the wrong package name");
+            }
+            return;
+        }
+        mResponseConsumer.accept(response);
+    }
+
+    /**
+     * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
+     */
+    static final class InlineSuggestionsResponseCallbackImpl
+            extends IInlineSuggestionsResponseCallback.Stub {
+        private final WeakReference<InlineSuggestionSession> mInlineSuggestionSession;
+
+        private InlineSuggestionsResponseCallbackImpl(
+                InlineSuggestionSession inlineSuggestionSession) {
+            mInlineSuggestionSession = new WeakReference<>(inlineSuggestionSession);
+        }
+
+        @Override
+        public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
+                throws RemoteException {
+            final InlineSuggestionSession session = mInlineSuggestionSession.get();
+            if (session != null) {
+                session.mHandler.sendMessage(obtainMessage(
+                        InlineSuggestionSession::handleOnInlineSuggestionsResponse, session,
+                        response));
+            }
+        }
+    }
+}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 49e1d5e..b1aa67e 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -22,8 +22,6 @@
 import static android.view.WindowInsets.Type.navigationBars;
 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
 
-import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
-
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 
 import android.annotation.AnyThread;
@@ -51,7 +49,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.SystemClock;
@@ -102,13 +99,11 @@
 import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
 import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
 import com.android.internal.view.IInlineSuggestionsRequestCallback;
-import com.android.internal.view.IInlineSuggestionsResponseCallback;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.lang.ref.WeakReference;
 import java.util.Collections;
 
 /**
@@ -450,7 +445,7 @@
     final int[] mTmpLocation = new int[2];
 
     @Nullable
-    private InlineSuggestionsRequestInfo mInlineSuggestionsRequestInfo = null;
+    private InlineSuggestionSession mInlineSuggestionSession;
 
     private boolean mAutomotiveHideNavBarForKeyboard;
     private boolean mIsAutomotive;
@@ -465,8 +460,6 @@
      */
     private IBinder mCurShowInputToken;
 
-    private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
-
     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer = info -> {
         onComputeInsets(mTmpInsets);
         if (isExtractViewShown()) {
@@ -538,7 +531,7 @@
             if (DEBUG) {
                 Log.d(TAG, "InputMethodService received onCreateInlineSuggestionsRequest()");
             }
-            handleOnCreateInlineSuggestionsRequest(componentName, autofillId, cb);
+            handleOnCreateInlineSuggestionsRequest(componentName, cb);
         }
 
         /**
@@ -770,39 +763,9 @@
         return false;
     }
 
-    /**
-     * Sends an {@link InlineSuggestionsRequest} obtained from
-     * {@link #onCreateInlineSuggestionsRequest()} to the current Autofill Session through
-     * {@link IInlineSuggestionsRequestCallback#onInlineSuggestionsRequest}.
-     */
-    private void makeInlineSuggestionsRequest() {
-        if (mInlineSuggestionsRequestInfo == null) {
-            Log.w(TAG, "makeInlineSuggestionsRequest() called with null requestInfo cache");
-            return;
-        }
-
-        final IInlineSuggestionsRequestCallback requestCallback =
-                mInlineSuggestionsRequestInfo.mCallback;
-        try {
-            final InlineSuggestionsRequest request = onCreateInlineSuggestionsRequest();
-            if (request == null) {
-                Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
-                requestCallback.onInlineSuggestionsUnsupported();
-            } else {
-                final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
-                        new InlineSuggestionsResponseCallbackImpl(this,
-                                mInlineSuggestionsRequestInfo.mComponentName,
-                                mInlineSuggestionsRequestInfo.mFocusedId);
-                requestCallback.onInlineSuggestionsRequest(request,
-                        inlineSuggestionsResponseCallback);
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, "makeInlinedSuggestionsRequest() remote exception:" + e);
-        }
-    }
-
+    @MainThread
     private void handleOnCreateInlineSuggestionsRequest(@NonNull ComponentName componentName,
-            @NonNull AutofillId autofillId, @NonNull IInlineSuggestionsRequestCallback callback) {
+            @NonNull IInlineSuggestionsRequestCallback callback) {
         if (!mInputStarted) {
             try {
                 Log.w(TAG, "onStartInput() not called yet");
@@ -813,24 +776,32 @@
             return;
         }
 
-        mInlineSuggestionsRequestInfo = new InlineSuggestionsRequestInfo(componentName, autofillId,
-                callback);
-
-        makeInlineSuggestionsRequest();
+        if (mInlineSuggestionSession != null) {
+            mInlineSuggestionSession.invalidateSession();
+        }
+        mInlineSuggestionSession = new InlineSuggestionSession(componentName, callback,
+                this::getEditorInfoPackageName, this::onCreateInlineSuggestionsRequest,
+                this::getHostInputToken, this::onInlineSuggestionsResponse);
     }
 
-    private void handleOnInlineSuggestionsResponse(@NonNull ComponentName componentName,
-            @NonNull AutofillId autofillId, @NonNull InlineSuggestionsResponse response) {
-        if (!mInlineSuggestionsRequestInfo.validate(componentName)) {
-            if (DEBUG) {
-                Log.d(TAG,
-                        "Response component=" + componentName + " differs from request component="
-                                + mInlineSuggestionsRequestInfo.mComponentName
-                                + ", ignoring response");
-            }
-            return;
+    @Nullable
+    private String getEditorInfoPackageName() {
+        if (mInputEditorInfo != null) {
+            return mInputEditorInfo.packageName;
         }
-        onInlineSuggestionsResponse(response);
+        return null;
+    }
+
+    /**
+     * Returns the {@link IBinder} input token from the host view root.
+     */
+    @Nullable
+    private IBinder getHostInputToken() {
+        ViewRootImpl viewRoot = null;
+        if (mRootView != null) {
+            viewRoot = mRootView.getViewRootImpl();
+        }
+        return viewRoot == null ? null : viewRoot.getInputToken();
     }
 
     private void notifyImeHidden() {
@@ -851,63 +822,6 @@
     }
 
     /**
-     * Internal implementation of {@link IInlineSuggestionsResponseCallback}.
-     */
-    private static final class InlineSuggestionsResponseCallbackImpl
-            extends IInlineSuggestionsResponseCallback.Stub {
-        private final WeakReference<InputMethodService> mInputMethodService;
-
-        private final ComponentName mRequestComponentName;
-        private final AutofillId mRequestAutofillId;
-
-        private InlineSuggestionsResponseCallbackImpl(InputMethodService inputMethodService,
-                ComponentName componentName, AutofillId autofillId) {
-            mInputMethodService = new WeakReference<>(inputMethodService);
-            mRequestComponentName = componentName;
-            mRequestAutofillId = autofillId;
-        }
-
-        @Override
-        public void onInlineSuggestionsResponse(InlineSuggestionsResponse response)
-                throws RemoteException {
-            final InputMethodService service = mInputMethodService.get();
-            if (service != null) {
-                service.mHandler.sendMessage(obtainMessage(
-                        InputMethodService::handleOnInlineSuggestionsResponse, service,
-                        mRequestComponentName, mRequestAutofillId, response));
-            }
-        }
-    }
-
-    /**
-     * Information about incoming requests from Autofill Frameworks for inline suggestions.
-     */
-    private static final class InlineSuggestionsRequestInfo {
-        final ComponentName mComponentName;
-        final AutofillId mFocusedId;
-        final IInlineSuggestionsRequestCallback mCallback;
-
-        InlineSuggestionsRequestInfo(ComponentName componentName, AutofillId focusedId,
-                IInlineSuggestionsRequestCallback callback) {
-            this.mComponentName = componentName;
-            this.mFocusedId = focusedId;
-            this.mCallback = callback;
-        }
-
-        /**
-         * Returns whether the cached {@link ComponentName} matches the passed in activity.
-         */
-        public boolean validate(ComponentName componentName) {
-            final boolean result = componentName.equals(mComponentName);
-            if (DEBUG && !result) {
-                Log.d(TAG, "Cached request info ComponentName=" + mComponentName
-                        + " differs from received ComponentName=" + componentName);
-            }
-            return result;
-        }
-    }
-
-    /**
      * Concrete implementation of
      * {@link AbstractInputMethodService.AbstractInputMethodSessionImpl} that provides
      * all of the standard behavior for an input method session.
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index 6453af8..81deba4 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -365,12 +365,16 @@
     }
 
     /**
+     * This method returns true when the parcel is 'definitely' empty.
+     * That is, it may return false for an empty parcel. But will never return true for a non-empty
+     * one.
+     *
      * @hide this should probably be the implementation of isEmpty().  To do that we
      * need to ensure we always use the special empty parcel form when the bundle is
      * empty.  (This may already be the case, but to be safe we'll do this later when
      * we aren't trying to stabilize.)
      */
-    public boolean maybeIsEmpty() {
+    public boolean isDefinitelyEmpty() {
         if (isParcelled()) {
             return isEmptyParcel();
         } else {
@@ -402,6 +406,9 @@
         if (other == null) {
             return false;
         }
+        if (isDefinitelyEmpty() && other.isDefinitelyEmpty()) {
+            return true;
+        }
         if (isParcelled() != other.isParcelled()) {
             // Big kind-of here!
             return false;
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index bb1dafc..8d04df0 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -215,6 +215,20 @@
     public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE";
 
     /**
+     * Activity Action: Allows the user to free up space by clearing app external cache directories.
+     * The intent doesn't automatically clear cache, but shows a dialog and lets the user decide.
+     * <p>
+     * This intent should be launched using
+     * {@link Activity#startActivityForResult(Intent, int)} so that the user
+     * knows which app is requesting to clear cache. The returned result will
+     * be {@link Activity#RESULT_OK} if the activity was launched and the user accepted to clear
+     * cache, or {@link Activity#RESULT_CANCELED} otherwise.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_EXTERNAL_STORAGE)
+    @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CLEAR_APP_CACHE = "android.os.storage.action.CLEAR_APP_CACHE";
+
+    /**
      * Extra {@link UUID} used to indicate the storage volume where an
      * application is interested in allocating or managing disk space.
      *
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 5cac5f5..e78aef5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -118,4 +118,10 @@
      * @param userId the userId for which to reset storage
      */
     public abstract void resetUser(int userId);
+
+    /**
+     * Returns {@code true} if the immediate last installed version of an app with {@code uid} had
+     * legacy storage, {@code false} otherwise.
+     */
+    public abstract boolean hasLegacyExternalStorage(int uid);
 }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 37efec3..f6072f9 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2055,6 +2055,19 @@
     public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS";
 
     /**
+     * Activity Action: Show screen that let user select enable (or disable) tethering.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS";
+
+    /**
      * Broadcast to trigger notification of asking user to enable MMS.
      * Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}.
      *
@@ -6449,6 +6462,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS =
                 "lock_screen_allow_private_notifications";
 
@@ -7867,6 +7881,7 @@
          * @hide
          */
         @UnsupportedAppUsage
+        @TestApi
         public static final String NFC_PAYMENT_DEFAULT_COMPONENT = "nfc_payment_default_component";
 
         /**
@@ -8069,6 +8084,7 @@
          * @hide
          */
         @SystemApi
+        @TestApi
         public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS =
                 "lock_screen_show_notifications";
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 7032825..e8e1223 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -75,6 +75,7 @@
     private final @Nullable SaveInfo mSaveInfo;
     private final @Nullable Bundle mClientState;
     private final @Nullable RemoteViews mPresentation;
+    private final @Nullable InlinePresentation mInlinePresentation;
     private final @Nullable RemoteViews mHeader;
     private final @Nullable RemoteViews mFooter;
     private final @Nullable IntentSender mAuthentication;
@@ -95,6 +96,7 @@
         mSaveInfo = builder.mSaveInfo;
         mClientState = builder.mClientState;
         mPresentation = builder.mPresentation;
+        mInlinePresentation = builder.mInlinePresentation;
         mHeader = builder.mHeader;
         mFooter = builder.mFooter;
         mAuthentication = builder.mAuthentication;
@@ -131,6 +133,11 @@
     }
 
     /** @hide */
+    public @Nullable InlinePresentation getInlinePresentation() {
+        return mInlinePresentation;
+    }
+
+    /** @hide */
     public @Nullable RemoteViews getHeader() {
         return mHeader;
     }
@@ -219,6 +226,7 @@
         private SaveInfo mSaveInfo;
         private Bundle mClientState;
         private RemoteViews mPresentation;
+        private InlinePresentation mInlinePresentation;
         private RemoteViews mHeader;
         private RemoteViews mFooter;
         private IntentSender mAuthentication;
@@ -318,6 +326,67 @@
         }
 
         /**
+         * Triggers a custom UI before before autofilling the screen with any data set in this
+         * response.
+         *
+         * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
+         * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
+         * for examples.
+         *
+         * <p>This method is similar to
+         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, but also accepts
+         * an {@link InlinePresentation} presentation which is required for authenticating through
+         * the inline autofill flow.
+         *
+         * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
+         * not work with {@link InlinePresentation}.</p>
+         *
+         * @param authentication Intent to an activity with your authentication flow.
+         * @param presentation The presentation to visualize the response.
+         * @param inlinePresentation The inlinePresentation to visualize the response inline.
+         * @param ids id of Views that when focused will display the authentication UI.
+         *
+         * @return This builder.
+         *
+         * @throws IllegalArgumentException if any of the following occurs:
+         * <ul>
+         *   <li>{@code ids} is {@code null}</li>
+         *   <li>{@code ids} is empty</li>
+         *   <li>{@code ids} contains a {@code null} element</li>
+         *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
+         *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
+         *   <li>both {@code authentication} and {@code inlinePresentation} are {@code null}</li>
+         *   <li>both {@code authentication} and {@code inlinePresentation} are
+         *   non-{@code null}</li>
+         * </ul>
+         *
+         * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
+         * {@link #setFooter(RemoteViews) footer} are already set for this builder.
+         *
+         * @see android.app.PendingIntent#getIntentSender()
+         */
+        @NonNull
+        public Builder setAuthentication(@NonNull AutofillId[] ids,
+                @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
+                @Nullable InlinePresentation inlinePresentation) {
+            throwIfDestroyed();
+            throwIfDisableAutofillCalled();
+            if (mHeader != null || mFooter != null) {
+                throw new IllegalStateException("Already called #setHeader() or #setFooter()");
+            }
+
+            if (authentication == null ^ (presentation == null && inlinePresentation == null)) {
+                throw new IllegalArgumentException("authentication and presentation "
+                        + "(dropdown or inline), must be both non-null or null");
+            }
+            mAuthentication = authentication;
+            mPresentation = presentation;
+            mInlinePresentation = inlinePresentation;
+            mAuthenticationIds = assertValid(ids);
+            return this;
+        }
+
+        /**
          * Specifies views that should not trigger new
          * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
          * FillCallback)} requests.
@@ -644,6 +713,8 @@
                         break;
                     }
                 }
+            } else if (mInlinePresentation != null) {
+                mSupportsInlineSuggestions = true;
             }
 
             mDestroyed = true;
@@ -691,6 +762,9 @@
         if (mPresentation != null) {
             builder.append(", hasPresentation");
         }
+        if (mInlinePresentation != null) {
+            builder.append(", hasInlinePresentation");
+        }
         if (mHeader != null) {
             builder.append(", hasHeader");
         }
@@ -740,6 +814,7 @@
         parcel.writeParcelableArray(mAuthenticationIds, flags);
         parcel.writeParcelable(mAuthentication, flags);
         parcel.writeParcelable(mPresentation, flags);
+        parcel.writeParcelable(mInlinePresentation, flags);
         parcel.writeParcelable(mHeader, flags);
         parcel.writeParcelable(mFooter, flags);
         parcel.writeParcelable(mUserData, flags);
@@ -774,8 +849,10 @@
                     AutofillId.class);
             final IntentSender authentication = parcel.readParcelable(null);
             final RemoteViews presentation = parcel.readParcelable(null);
+            final InlinePresentation inlinePresentation = parcel.readParcelable(null);
             if (authenticationIds != null) {
-                builder.setAuthentication(authenticationIds, authentication, presentation);
+                builder.setAuthentication(authenticationIds, authentication, presentation,
+                        inlinePresentation);
             }
             final RemoteViews header = parcel.readParcelable(null);
             if (header != null) {
diff --git a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
index decdcf5..c389b1a 100644
--- a/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionRenderService.aidl
@@ -16,6 +16,7 @@
 
 package android.service.autofill;
 
+import android.os.IBinder;
 import android.service.autofill.IInlineSuggestionUiCallback;
 import android.service.autofill.InlinePresentation;
 
@@ -25,6 +26,7 @@
  * @hide
  */
 oneway interface IInlineSuggestionRenderService {
-    void renderSuggestion(in IInlineSuggestionUiCallback callback, in InlinePresentation presentation,
-                     int width, int height);
+    void renderSuggestion(in IInlineSuggestionUiCallback callback,
+                          in InlinePresentation presentation, int width, int height,
+                          in IBinder hostInputToken);
 }
diff --git a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
index a55a2ce..210f95f 100644
--- a/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
+++ b/core/java/android/service/autofill/IInlineSuggestionUiCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.service.autofill;
 
+import android.os.IBinder;
 import android.view.SurfaceControl;
 
 /**
@@ -24,6 +25,8 @@
  * @hide
  */
 oneway interface IInlineSuggestionUiCallback {
-    void autofill();
+    void onAutofill();
     void onContent(in SurfaceControl surface);
+    void onError();
+    void onTransferTouchFocusToImeWindow(in IBinder sourceInputToken, int displayId);
 }
diff --git a/core/java/android/service/autofill/InlineSuggestionRenderService.java b/core/java/android/service/autofill/InlineSuggestionRenderService.java
index 2593aab..4aafb63 100644
--- a/core/java/android/service/autofill/InlineSuggestionRenderService.java
+++ b/core/java/android/service/autofill/InlineSuggestionRenderService.java
@@ -24,11 +24,16 @@
 import android.app.Service;
 import android.app.slice.Slice;
 import android.content.Intent;
+import android.graphics.PixelFormat;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.util.Log;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
 import android.view.View;
+import android.view.WindowManager;
 
 /**
  * A service that renders an inline presentation given the {@link InlinePresentation} containing
@@ -55,8 +60,40 @@
     private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
 
     private void handleRenderSuggestion(IInlineSuggestionUiCallback callback,
-            InlinePresentation presentation, int width, int height) {
-        //TODO(b/146453086): implementation in ExtService
+            InlinePresentation presentation, int width, int height, IBinder hostInputToken) {
+        if (hostInputToken == null) {
+            try {
+                callback.onError();
+            } catch (RemoteException e) {
+                Log.w(TAG, "RemoteException calling onError()");
+            }
+            return;
+        }
+        final SurfaceControlViewHost host = new SurfaceControlViewHost(this, this.getDisplay(),
+                hostInputToken);
+        final SurfaceControl surface = host.getSurfacePackage().getSurfaceControl();
+
+        final View suggestionView = onRenderSuggestion(presentation, width, height);
+
+        final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(this, callback);
+        suggestionRoot.addView(suggestionView);
+        suggestionRoot.setOnClickListener((v) -> {
+            try {
+                callback.onAutofill();
+            } catch (RemoteException e) {
+                Log.w(TAG, "RemoteException calling onAutofill()");
+            }
+        });
+
+        WindowManager.LayoutParams lp =
+                new WindowManager.LayoutParams(width, height,
+                        WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT);
+        host.addView(suggestionRoot, lp);
+        try {
+            callback.onContent(surface);
+        } catch (RemoteException e) {
+            Log.w(TAG, "RemoteException calling onContent(" + surface + ")");
+        }
     }
 
     @Override
@@ -66,11 +103,12 @@
             return new IInlineSuggestionRenderService.Stub() {
                 @Override
                 public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
-                        @NonNull InlinePresentation presentation, int width, int height) {
+                        @NonNull InlinePresentation presentation, int width, int height,
+                        @Nullable IBinder hostInputToken) {
                     mHandler.sendMessage(obtainMessage(
                             InlineSuggestionRenderService::handleRenderSuggestion,
                             InlineSuggestionRenderService.this, callback, presentation,
-                            width, height));
+                            width, height, hostInputToken));
                 }
             }.asBinder();
         }
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java b/core/java/android/service/autofill/InlineSuggestionRoot.java
similarity index 65%
rename from services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
rename to core/java/android/service/autofill/InlineSuggestionRoot.java
index e813dae..bdcc253 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionRoot.java
+++ b/core/java/android/service/autofill/InlineSuggestionRoot.java
@@ -14,39 +14,39 @@
  * limitations under the License.
  */
 
-package com.android.server.autofill.ui;
+package android.service.autofill;
 
 import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.content.Context;
+import android.os.RemoteException;
 import android.util.Log;
 import android.util.MathUtils;
 import android.view.MotionEvent;
 import android.view.ViewConfiguration;
 import android.widget.FrameLayout;
 
-import com.android.server.LocalServices;
-import com.android.server.wm.WindowManagerInternal;
-
 /**
  * This class is the root view for an inline suggestion. It is responsible for
  * detecting the click on the item and to also transfer input focus to the IME
  * window if we detect the user is scrolling.
+ *
+ * @hide
  */
- // TODO(b/146453086) Move to ExtServices and add @SystemApi to transfer touch focus
 @SuppressLint("ViewConstructor")
-class InlineSuggestionRoot extends FrameLayout {
-    private static final String LOG_TAG = InlineSuggestionRoot.class.getSimpleName();
+public class InlineSuggestionRoot extends FrameLayout {
+    private static final String TAG = "InlineSuggestionRoot";
 
-    private final @NonNull Runnable mOnErrorCallback;
+    private final @NonNull IInlineSuggestionUiCallback mCallback;
     private final int mTouchSlop;
 
     private float mDownX;
     private float mDownY;
 
-    InlineSuggestionRoot(@NonNull Context context, @NonNull Runnable onErrorCallback) {
+    public InlineSuggestionRoot(@NonNull Context context,
+            @NonNull IInlineSuggestionUiCallback callback) {
         super(context);
-        mOnErrorCallback = onErrorCallback;
+        mCallback = callback;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setFocusable(false);
     }
@@ -64,20 +64,15 @@
                 final float distance = MathUtils.dist(mDownX, mDownY,
                         event.getX(), event.getY());
                 if (distance > mTouchSlop) {
-                    transferTouchFocusToImeWindow();
+                    try {
+                        mCallback.onTransferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
+                                getContext().getDisplayId());
+                    } catch (RemoteException e) {
+                        Log.w(TAG, "RemoteException transferring touch focus to IME");
+                    }
                 }
             } break;
         }
         return super.onTouchEvent(event);
     }
-
-    private void transferTouchFocusToImeWindow() {
-        final WindowManagerInternal windowManagerInternal = LocalServices.getService(
-                WindowManagerInternal.class);
-        if (!windowManagerInternal.transferTouchFocusToImeWindow(getViewRootImpl().getInputToken(),
-                getContext().getDisplayId())) {
-            Log.e(LOG_TAG, "Cannot transfer touch focus from suggestion to IME");
-            mOnErrorCallback.run();
-        }
-    }
 }
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 79852d3..672b501 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -89,6 +89,8 @@
 
     private SparseArray<AutofillProxy> mAutofillProxies;
 
+    private AutofillProxy mAutofillProxyForLastRequest;
+
     // Used for metrics / debug only
     private ComponentName mServiceComponentName;
 
@@ -158,6 +160,38 @@
     }
 
     /**
+     * The child class of the service can call this method to initiate an Autofill flow.
+     *
+     * <p> The request would be respected only if the previous augmented autofill request was
+     * made for the same {@code activityComponent} and {@code autofillId}, and the field is
+     * currently on focus.
+     *
+     * <p> The request would start a new autofill flow. It doesn't guarantee that the
+     * {@link AutofillManager} will proceed with the request.
+     *
+     * @param activityComponent the client component for which the autofill is requested for
+     * @param autofillId        the client field id for which the autofill is requested for
+     * @return true if the request makes the {@link AutofillManager} start a new Autofill flow,
+     * false otherwise.
+     */
+    public final boolean requestAutofill(@NonNull ComponentName activityComponent,
+            @NonNull AutofillId autofillId) {
+        // TODO(b/149531989): revisit this. The request should start a new autofill session
+        //  rather than reusing the existing session.
+        final AutofillProxy proxy = mAutofillProxyForLastRequest;
+        if (proxy == null || !proxy.mComponentName.equals(activityComponent)
+                || !proxy.mFocusedId.equals(autofillId)) {
+            return false;
+        }
+        try {
+            return proxy.requestAutofill();
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+        return false;
+    }
+
+    /**
      * Asks the service to handle an "augmented" autofill request.
      *
      * <p>This method is called when the "stantard" autofill service cannot handle a request, which
@@ -241,6 +275,7 @@
         } catch (RemoteException e) {
             e.rethrowFromSystemServer();
         }
+        mAutofillProxyForLastRequest = proxy;
         onFillRequest(new FillRequest(proxy, inlineSuggestionsRequest), cancellationSignal,
                 new FillController(proxy), new FillCallback(proxy));
     }
@@ -268,6 +303,7 @@
                 proxy.destroy();
             }
             mAutofillProxies.clear();
+            mAutofillProxyForLastRequest = null;
         }
     }
 
@@ -287,6 +323,7 @@
             }
         }
         mAutofillProxies = null;
+        mAutofillProxyForLastRequest = null;
     }
 
     @Override
@@ -481,6 +518,11 @@
             mClient.requestHideFillUi(mSessionId, mFocusedId);
         }
 
+
+        private boolean requestAutofill() throws RemoteException {
+            return mClient.requestAutofill(mSessionId, mFocusedId);
+        }
+
         private void update(@NonNull AutofillId focusedId, @NonNull AutofillValue focusedValue,
                 @NonNull IFillCallback callback, @NonNull CancellationSignal cancellationSignal) {
             synchronized (mLock) {
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
index 9d210cd..f6a86d9 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallback.java
@@ -17,28 +17,11 @@
 package android.service.quickaccesswallet;
 
 import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.util.Log;
 
 /**
  * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
  */
-public final class GetWalletCardsCallback {
-
-    private static final String TAG = "QAWalletCallback";
-
-    private final IQuickAccessWalletServiceCallbacks mCallback;
-    private final Handler mHandler;
-    private boolean mCalled;
-
-    /**
-     * @hide
-     */
-    GetWalletCardsCallback(IQuickAccessWalletServiceCallbacks callback, Handler handler) {
-        mCallback = callback;
-        mHandler = handler;
-    }
+public interface GetWalletCardsCallback {
 
     /**
      * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
@@ -46,11 +29,10 @@
      *
      * @param response The response contains the list of {@link WalletCard walletCards} to be shown
      *                 to the user as well as the index of the card that should initially be
-     *                 presented as the selected card.
+     *                 presented as the selected card. The list should not contain more than the
+     *                 maximum number of cards requested.
      */
-    public void onSuccess(@NonNull GetWalletCardsResponse response) {
-        mHandler.post(() -> onSuccessInternal(response));
-    }
+    void onSuccess(@NonNull GetWalletCardsResponse response);
 
     /**
      * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
@@ -60,33 +42,5 @@
      *              (Personally Identifiable Information, such as username or email address).
      * @throws IllegalStateException if this method or {@link #onSuccess} was already called.
      */
-    public void onFailure(@NonNull GetWalletCardsError error) {
-        mHandler.post(() -> onFailureInternal(error));
-    }
-
-    private void onSuccessInternal(GetWalletCardsResponse response) {
-        if (mCalled) {
-            Log.w(TAG, "already called");
-            return;
-        }
-        mCalled = true;
-        try {
-            mCallback.onGetWalletCardsSuccess(response);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error returning wallet cards", e);
-        }
-    }
-
-    private void onFailureInternal(GetWalletCardsError error) {
-        if (mCalled) {
-            Log.w(TAG, "already called");
-            return;
-        }
-        mCalled = true;
-        try {
-            mCallback.onGetWalletCardsFailure(error);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error returning failure message", e);
-        }
-    }
+    void onFailure(@NonNull GetWalletCardsError error);
 }
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
new file mode 100644
index 0000000..d2494a5
--- /dev/null
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsCallbackImpl.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 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.service.quickaccesswallet;
+
+import android.annotation.NonNull;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * Handles response from the {@link QuickAccessWalletService} for {@link GetWalletCardsRequest}
+ *
+ * @hide
+ */
+final class GetWalletCardsCallbackImpl implements GetWalletCardsCallback {
+
+    private static final String TAG = "QAWalletCallback";
+
+    private final IQuickAccessWalletServiceCallbacks mCallback;
+    private final GetWalletCardsRequest mRequest;
+    private final Handler mHandler;
+    private boolean mCalled;
+
+    GetWalletCardsCallbackImpl(GetWalletCardsRequest request,
+            IQuickAccessWalletServiceCallbacks callback, Handler handler) {
+        mRequest = request;
+        mCallback = callback;
+        mHandler = handler;
+    }
+
+    /**
+     * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+     * was successfully handled by the service.
+     *
+     * @param response The response contains the list of {@link WalletCard walletCards} to be shown
+     *                 to the user as well as the index of the card that should initially be
+     *                 presented as the selected card.
+     */
+    public void onSuccess(@NonNull GetWalletCardsResponse response) {
+        Log.i(TAG, "onSuccess");
+        if (isValidResponse(response)) {
+            mHandler.post(() -> onSuccessInternal(response));
+        } else {
+            Log.w(TAG, "Invalid GetWalletCards response");
+            mHandler.post(() -> onFailureInternal(new GetWalletCardsError(null, null)));
+        }
+    }
+
+    /**
+     * Notifies the Android System that an {@link QuickAccessWalletService#onWalletCardsRequested}
+     * could not be handled by the service.
+     *
+     * @param error The error message. <b>Note: </b> this message should <b>not</b> contain PII
+     *              (Personally Identifiable Information, such as username or email address).
+     * @throws IllegalStateException if this method or {@link #onSuccess} was already called.
+     */
+    public void onFailure(@NonNull GetWalletCardsError error) {
+        mHandler.post(() -> onFailureInternal(error));
+    }
+
+    private void onSuccessInternal(GetWalletCardsResponse response) {
+        Log.i(TAG, "onSuccessInternal");
+        if (mCalled) {
+            Log.w(TAG, "already called");
+            return;
+        }
+        mCalled = true;
+        try {
+            mCallback.onGetWalletCardsSuccess(response);
+            Log.i(TAG, "onSuccessInternal: returned response");
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error returning wallet cards", e);
+        }
+    }
+
+    private void onFailureInternal(GetWalletCardsError error) {
+        if (mCalled) {
+            Log.w(TAG, "already called");
+            return;
+        }
+        mCalled = true;
+        try {
+            mCallback.onGetWalletCardsFailure(error);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error returning failure message", e);
+        }
+    }
+
+    private boolean isValidResponse(@NonNull GetWalletCardsResponse response) {
+        return response != null
+                && response.getWalletCards() != null
+                && response.getSelectedIndex() >= 0
+                && (response.getWalletCards().isEmpty() // selectedIndex may be 0 when list is empty
+                || response.getSelectedIndex() < response.getWalletCards().size())
+                && response.getWalletCards().size() < mRequest.getMaxCards()
+                && areValidCards(response.getWalletCards());
+    }
+
+    private boolean areValidCards(List<WalletCard> walletCards) {
+        for (WalletCard walletCard : walletCards) {
+            if (walletCard == null
+                    || walletCard.getCardId() == null
+                    || walletCard.getCardImage() == null
+                    || TextUtils.isEmpty(walletCard.getContentDescription())
+                    || walletCard.getPendingIntent() == null) {
+                return false;
+            }
+            Icon cardImage = walletCard.getCardImage();
+            if (cardImage.getType() == Icon.TYPE_BITMAP
+                    && walletCard.getCardImage().getBitmap().getConfig()
+                    != Bitmap.Config.HARDWARE) {
+                Log.w(TAG, "WalletCard bitmaps should be hardware bitmaps");
+            }
+        }
+        return true;
+    }
+}
diff --git a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
index 996622a..0551e27 100644
--- a/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
+++ b/core/java/android/service/quickaccesswallet/GetWalletCardsResponse.java
@@ -35,9 +35,13 @@
     /**
      * Construct a new response.
      *
-     * @param walletCards   The list of wallet cards.
+     * @param walletCards   The list of wallet cards. The list may be empty but must NOT be larger
+     *                      than {@link GetWalletCardsRequest#getMaxCards()}. The list may not
+     *                      contain null values.
      * @param selectedIndex The index of the card that should be presented as the initially
-     *                      'selected' card
+     *                      'selected' card. The index must be greater than or equal to zero and
+     *                      less than the size of the list of walletCards (unless the list is empty
+     *                      in which case the value may be 0).
      */
     public GetWalletCardsResponse(@NonNull List<WalletCard> walletCards, int selectedIndex) {
         this.mWalletCards = walletCards;
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
index cfc6d57..be9ab11 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClient.java
@@ -18,16 +18,16 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.content.Context;
 import android.content.Intent;
 
-import java.util.function.Consumer;
-
 /**
  * Facilitates accessing cards from the {@link QuickAccessWalletService}.
  *
  * @hide
  */
+@TestApi
 public interface QuickAccessWalletClient {
 
     /**
@@ -40,17 +40,65 @@
     }
 
     /**
-     * @return true if the {@link QuickAccessWalletService} is available.
+     * @return true if the {@link QuickAccessWalletService} is available. This means that the
+     * default NFC payment application has an exported service that can provide cards to the Quick
+     * Access Wallet. However, it does not mean that (1) the call will necessarily be successful,
+     * nor does it mean that cards may be displayed at this time. Addition checks are required:
+     * <ul>
+     *     <li>If {@link #isWalletFeatureAvailable()} is false, cards should not be displayed
+     *     <li>If the device is locked and {@link #isWalletFeatureAvailableWhenDeviceLocked} is
+     *     false, cards should not be displayed while the device remains locked. (A message
+     *     prompting the user to unlock to view cards may be appropriate).</li>
+     * </ul>
      */
     boolean isWalletServiceAvailable();
 
     /**
+     * Wallet cards should not be displayed if:
+     * <ul>
+     *     <li>The wallet service is unavailable</li>
+     *     <li>The device is not provisioned, ie user setup is incomplete</li>
+     *     <li>If the wallet feature has been disabled by the user</li>
+     *     <li>If the phone has been put into lockdown mode</li>
+     * </ul>
+     * <p>
+     * Quick Access Wallet implementers should call this method before calling
+     * {@link #getWalletCards} to ensure that cards may be displayed.
+     */
+    boolean isWalletFeatureAvailable();
+
+    /**
+     * Wallet cards may not be displayed on the lock screen if the user has opted to hide
+     * notifications or sensitive content on the lock screen.
+     * <ul>
+     *     <li>The device is not provisioned, ie user setup is incomplete</li>
+     *     <li>If the wallet feature has been disabled by the user</li>
+     *     <li>If the phone has been put into lockdown mode</li>
+     * </ul>
+     *
+     * <p>
+     * Quick Access Wallet implementers should call this method before calling
+     * {@link #getWalletCards} if the device is currently locked.
+     *
+     * @return true if cards may be displayed on the lock screen.
+     */
+    boolean isWalletFeatureAvailableWhenDeviceLocked();
+
+    /**
      * Get wallet cards from the {@link QuickAccessWalletService}.
      */
     void getWalletCards(
             @NonNull GetWalletCardsRequest request,
-            @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
-            @NonNull Consumer<GetWalletCardsError> onFailureListener);
+            @NonNull OnWalletCardsRetrievedCallback callback);
+
+    /**
+     * Callback for getWalletCards
+     */
+    interface OnWalletCardsRetrievedCallback {
+        void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response);
+
+        void onWalletCardRetrievalError(@NonNull GetWalletCardsError error);
+    }
 
     /**
      * Notify the {@link QuickAccessWalletService} service that a wallet card was selected.
@@ -65,12 +113,24 @@
     /**
      * Unregister event listener.
      */
-    void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+    void addWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
 
     /**
      * Unregister event listener
      */
-    void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener);
+    void removeWalletServiceEventListener(@NonNull WalletServiceEventListener listener);
+
+    /**
+     * A listener for {@link WalletServiceEvent walletServiceEvents}
+     */
+    interface WalletServiceEventListener {
+        void onWalletServiceEvent(@NonNull WalletServiceEvent event);
+    }
+
+    /**
+     * Unregister all event listeners and disconnect from the service.
+     */
+    void disconnect();
 
     /**
      * The manifest entry for the QuickAccessWalletService may also publish information about the
@@ -78,12 +138,12 @@
      * application.
      */
     @Nullable
-    Intent getWalletActivity();
+    Intent createWalletIntent();
 
     /**
      * The manifest entry for the {@link QuickAccessWalletService} may publish the activity that
      * hosts the settings
      */
     @Nullable
-    Intent getSettingsActivity();
+    Intent createWalletSettingsIntent();
 }
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
index 17c287f..37a8703 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletClientImpl.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -27,79 +28,225 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
-import android.os.Message;
 import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.internal.widget.LockPatternUtils;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.Map;
 import java.util.Queue;
 import java.util.UUID;
-import java.util.function.Consumer;
 
 /**
+ * Implements {@link QuickAccessWalletClient}. The client connects, performs requests, waits for
+ * responses, and disconnects automatically after a short period of time. The client may
  * @hide
  */
-@SuppressWarnings("AndroidJdkLibsChecker")
-class QuickAccessWalletClientImpl implements QuickAccessWalletClient, Handler.Callback,
-        ServiceConnection {
+public class QuickAccessWalletClientImpl implements QuickAccessWalletClient, ServiceConnection {
 
     private static final String TAG = "QAWalletSClient";
     private final Handler mHandler;
     private final Context mContext;
     private final Queue<ApiCaller> mRequestQueue;
-    private final Map<Consumer<WalletServiceEvent>, String> mEventListeners;
+    private final Map<WalletServiceEventListener, String> mEventListeners;
     private boolean mIsConnected;
+    /**
+     * Timeout for active service connections (1 minute)
+     */
+    private static final long SERVICE_CONNECTION_TIMEOUT_MS = 60 * 1000;
     @Nullable
     private IQuickAccessWalletService mService;
 
-
     @Nullable
     private final QuickAccessWalletServiceInfo mServiceInfo;
 
-    private static final int MSG_CONNECT = 1;
-    private static final int MSG_CONNECTED = 2;
-    private static final int MSG_EXECUTE = 3;
-    private static final int MSG_DISCONNECT = 4;
+    private static final int MSG_TIMEOUT_SERVICE = 5;
 
     QuickAccessWalletClientImpl(@NonNull Context context) {
         mContext = context.getApplicationContext();
         mServiceInfo = QuickAccessWalletServiceInfo.tryCreate(context);
-        mHandler = new Handler(Looper.getMainLooper(), this);
+        mHandler = new Handler(Looper.getMainLooper());
         mRequestQueue = new LinkedList<>();
         mEventListeners = new HashMap<>(1);
     }
 
     @Override
-    public boolean handleMessage(Message msg) {
-        switch (msg.what) {
-            case MSG_CONNECT:
-                connectInternal();
-                break;
-            case MSG_CONNECTED:
-                onConnectedInternal((IQuickAccessWalletService) msg.obj);
-                break;
-            case MSG_EXECUTE:
-                executeInternal((ApiCaller) msg.obj);
-                break;
-            case MSG_DISCONNECT:
-                disconnectInternal();
-                break;
-            default:
-                Log.w(TAG, "Unknown what: " + msg.what);
-                return false;
+    public boolean isWalletServiceAvailable() {
+        boolean available = mServiceInfo != null;
+        Log.i(TAG, "isWalletServiceAvailable: " + available);
+        return available;
+    }
+
+    @Override
+    public boolean isWalletFeatureAvailable() {
+        int currentUser = ActivityManager.getCurrentUser();
+        return checkUserSetupComplete()
+                && checkSecureSetting(Settings.Secure.GLOBAL_ACTIONS_PANEL_ENABLED)
+                && !new LockPatternUtils(mContext).isUserInLockdown(currentUser);
+    }
+
+    @Override
+    public boolean isWalletFeatureAvailableWhenDeviceLocked() {
+        return checkSecureSetting(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS)
+                && checkSecureSetting(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+    }
+
+    @Override
+    public void getWalletCards(
+            @NonNull GetWalletCardsRequest request,
+            @NonNull OnWalletCardsRetrievedCallback callback) {
+
+        Log.i(TAG, "getWalletCards");
+
+        if (!isWalletServiceAvailable()) {
+            callback.onWalletCardRetrievalError(new GetWalletCardsError(null, null));
+            return;
         }
-        return true;
+
+        BaseCallbacks serviceCallback = new BaseCallbacks() {
+            @Override
+            public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
+                mHandler.post(() -> callback.onWalletCardsRetrieved(response));
+            }
+
+            @Override
+            public void onGetWalletCardsFailure(GetWalletCardsError error) {
+                mHandler.post(() -> callback.onWalletCardRetrievalError(error));
+            }
+        };
+
+        executeApiCall(new ApiCaller("onWalletCardsRequested") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletCardsRequested(request, serviceCallback);
+            }
+
+            @Override
+            public void onApiError() {
+                serviceCallback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
+            }
+        });
+    }
+
+    @Override
+    public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
+        Log.i(TAG, "selectWalletCard");
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        executeApiCall(new ApiCaller("onWalletCardSelected") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletCardSelected(request);
+            }
+        });
+    }
+
+    @Override
+    public void notifyWalletDismissed() {
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        Log.i(TAG, "notifyWalletDismissed");
+        executeApiCall(new ApiCaller("onWalletDismissed") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                service.onWalletDismissed();
+            }
+        });
+    }
+
+    @Override
+    public void addWalletServiceEventListener(WalletServiceEventListener listener) {
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        Log.i(TAG, "registerWalletServiceEventListener");
+        BaseCallbacks callback = new BaseCallbacks() {
+            @Override
+            public void onWalletServiceEvent(WalletServiceEvent event) {
+                Log.i(TAG, "onWalletServiceEvent");
+                mHandler.post(() -> listener.onWalletServiceEvent(event));
+            }
+        };
+
+        executeApiCall(new ApiCaller("registerListener") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                String listenerId = UUID.randomUUID().toString();
+                WalletServiceEventListenerRequest request =
+                        new WalletServiceEventListenerRequest(listenerId);
+                mEventListeners.put(listener, listenerId);
+                service.registerWalletServiceEventListener(request, callback);
+            }
+        });
+    }
+
+    @Override
+    public void removeWalletServiceEventListener(WalletServiceEventListener listener) {
+        if (!isWalletServiceAvailable()) {
+            return;
+        }
+        Log.i(TAG, "unregisterWalletServiceEventListener");
+        executeApiCall(new ApiCaller("unregisterListener") {
+            @Override
+            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
+                String listenerId = mEventListeners.remove(listener);
+                if (listenerId == null) {
+                    return;
+                }
+                WalletServiceEventListenerRequest request =
+                        new WalletServiceEventListenerRequest(listenerId);
+                service.unregisterWalletServiceEventListener(request);
+            }
+        });
+    }
+
+    @Override
+    public void disconnect() {
+        Log.i(TAG, "disconnect");
+        mHandler.post(() -> disconnectInternal(true));
+    }
+
+    @Override
+    @Nullable
+    public Intent createWalletIntent() {
+        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
+            return null;
+        }
+        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
+                .setComponent(
+                        new ComponentName(
+                                mServiceInfo.getComponentName().getPackageName(),
+                                mServiceInfo.getWalletActivity()));
+    }
+
+    @Override
+    @Nullable
+    public Intent createWalletSettingsIntent() {
+        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
+            return null;
+        }
+        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
+                .setComponent(
+                        new ComponentName(
+                                mServiceInfo.getComponentName().getPackageName(),
+                                mServiceInfo.getSettingsActivity()));
     }
 
     private void connect() {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECT));
+        Log.i(TAG, "connect");
+        mHandler.post(this::connectInternal);
     }
 
     private void connectInternal() {
+        Log.i(TAG, "connectInternal");
         if (mServiceInfo == null) {
             Log.w(TAG, "Wallet service unavailable");
             return;
@@ -113,15 +260,18 @@
         intent.setComponent(mServiceInfo.getComponentName());
         int flags = Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY;
         mContext.bindService(intent, this, flags);
+        resetServiceConnectionTimeout();
     }
 
     private void onConnectedInternal(IQuickAccessWalletService service) {
+        Log.i(TAG, "onConnectedInternal");
         if (!mIsConnected) {
             Log.w(TAG, "onConnectInternal but connection closed");
             mService = null;
             return;
         }
         mService = service;
+        Log.i(TAG, "onConnectedInternal success: request queue size " + mRequestQueue.size());
         for (ApiCaller apiCaller : new ArrayList<>(mRequestQueue)) {
             try {
                 apiCaller.performApiCall(mService);
@@ -135,15 +285,33 @@
         }
     }
 
-    private void disconnect() {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
+    /**
+     * Resets the idle timeout for this connection by removing any pending timeout messages and
+     * posting a new delayed message.
+     */
+    private void resetServiceConnectionTimeout() {
+        Log.i(TAG, "resetServiceConnectionTimeout");
+        mHandler.removeMessages(MSG_TIMEOUT_SERVICE);
+        mHandler.postDelayed(
+                () -> disconnectInternal(true),
+                MSG_TIMEOUT_SERVICE,
+                SERVICE_CONNECTION_TIMEOUT_MS);
     }
 
-    private void disconnectInternal() {
+    private void disconnectInternal(boolean clearEventListeners) {
+        Log.i(TAG, "disconnectInternal: " + clearEventListeners);
         if (!mIsConnected) {
             Log.w(TAG, "already disconnected");
             return;
         }
+        if (clearEventListeners && !mEventListeners.isEmpty()) {
+            Log.i(TAG, "disconnectInternal: clear event listeners");
+            for (WalletServiceEventListener listener : mEventListeners.keySet()) {
+                removeWalletServiceEventListener(listener);
+            }
+            mHandler.post(() -> disconnectInternal(false));
+            return;
+        }
         mIsConnected = false;
         mContext.unbindService(/*conn=*/this);
         mService = null;
@@ -151,181 +319,81 @@
         mRequestQueue.clear();
     }
 
-    private void execute(ApiCaller apiCaller) {
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_EXECUTE, apiCaller));
+    private void executeApiCall(ApiCaller apiCaller) {
+        Log.i(TAG, "execute: " + apiCaller.mDesc);
+        mHandler.post(() -> executeInternal(apiCaller));
     }
 
     private void executeInternal(ApiCaller apiCall) {
+        Log.i(TAG, "executeInternal: " + apiCall.mDesc);
         if (mIsConnected && mService != null) {
             try {
                 apiCall.performApiCall(mService);
+                Log.i(TAG, "executeInternal success: " + apiCall.mDesc);
+                resetServiceConnectionTimeout();
             } catch (RemoteException e) {
-                Log.w(TAG, "executeInternal error", e);
+                Log.w(TAG, "executeInternal error: " + apiCall.mDesc, e);
                 apiCall.onApiError();
                 disconnect();
             }
         } else {
+            Log.i(TAG, "executeInternal: queued" + apiCall.mDesc);
             mRequestQueue.add(apiCall);
             connect();
         }
     }
 
-    public boolean isWalletServiceAvailable() {
-        return mServiceInfo != null;
-    }
-
     private abstract static class ApiCaller {
+        private final String mDesc;
+
+        private ApiCaller(String desc) {
+            this.mDesc = desc;
+        }
+
         abstract void performApiCall(IQuickAccessWalletService service) throws RemoteException;
 
         void onApiError() {
-            Log.w(TAG, "api error");
+            Log.w(TAG, "api error: " + mDesc);
         }
     }
 
-    public void getWalletCards(
-            @NonNull GetWalletCardsRequest request,
-            @NonNull Consumer<GetWalletCardsResponse> onSuccessListener,
-            @NonNull Consumer<GetWalletCardsError> onFailureListener) {
-
-        BaseCallbacks callback = new BaseCallbacks() {
-            @Override
-            public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
-                mHandler.post(() -> onSuccessListener.accept(response));
-            }
-
-            @Override
-            public void onGetWalletCardsFailure(GetWalletCardsError error) {
-                mHandler.post(() -> onFailureListener.accept(error));
-            }
-        };
-
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                service.onWalletCardsRequested(request, callback);
-            }
-
-            @Override
-            public void onApiError() {
-                callback.onGetWalletCardsFailure(new GetWalletCardsError(null, null));
-            }
-        });
-    }
-
-    public void selectWalletCard(@NonNull SelectWalletCardRequest request) {
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                service.onWalletCardSelected(request);
-            }
-        });
-    }
-
-    public void notifyWalletDismissed() {
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                service.onWalletDismissed();
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_DISCONNECT));
-            }
-        });
-    }
-
-    @Override
-    public void registerWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
-
-        BaseCallbacks callback = new BaseCallbacks() {
-            @Override
-            public void onWalletServiceEvent(WalletServiceEvent event) {
-                Log.i(TAG, "onWalletServiceEvent");
-                mHandler.post(() -> listener.accept(event));
-            }
-        };
-
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                String listenerId = UUID.randomUUID().toString();
-                WalletServiceEventListenerRequest request =
-                        new WalletServiceEventListenerRequest(listenerId);
-                mEventListeners.put(listener, listenerId);
-                service.registerWalletServiceEventListener(request, callback);
-            }
-        });
-    }
-
-    @Override
-    public void unregisterWalletServiceEventListener(Consumer<WalletServiceEvent> listener) {
-        execute(new ApiCaller() {
-            @Override
-            public void performApiCall(IQuickAccessWalletService service) throws RemoteException {
-                String listenerId = mEventListeners.get(listener);
-                if (listenerId == null) {
-                    return;
-                }
-                WalletServiceEventListenerRequest request =
-                        new WalletServiceEventListenerRequest(listenerId);
-                service.unregisterWalletServiceEventListener(request);
-            }
-        });
-    }
-
-    @Override
-    @Nullable
-    public Intent getWalletActivity() {
-        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getWalletActivity())) {
-            return null;
-        }
-        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
-                .setComponent(
-                        new ComponentName(
-                                mServiceInfo.getComponentName().getPackageName(),
-                                mServiceInfo.getWalletActivity()));
-    }
-
-    @Override
-    @Nullable
-    public Intent getSettingsActivity() {
-        if (mServiceInfo == null || TextUtils.isEmpty(mServiceInfo.getSettingsActivity())) {
-            return null;
-        }
-        return new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET_SETTINGS)
-                .setComponent(
-                        new ComponentName(
-                                mServiceInfo.getComponentName().getPackageName(),
-                                mServiceInfo.getSettingsActivity()));
-    }
-
-    /**
-     * Connection to the {@link QuickAccessWalletService}
-     */
-
-
-    @Override
+    @Override // ServiceConnection
     public void onServiceConnected(ComponentName name, IBinder binder) {
+        Log.i(TAG, "onServiceConnected: " + name);
         IQuickAccessWalletService service = IQuickAccessWalletService.Stub.asInterface(binder);
-        mHandler.sendMessage(mHandler.obtainMessage(MSG_CONNECTED, service));
+        mHandler.post(() -> onConnectedInternal(service));
     }
 
-    @Override
+    @Override // ServiceConnection
     public void onServiceDisconnected(ComponentName name) {
         // Do not disconnect, as we may later be re-connected
         Log.w(TAG, "onServiceDisconnected");
     }
 
-    @Override
+    @Override // ServiceConnection
     public void onBindingDied(ComponentName name) {
         // This is a recoverable error but the client will need to reconnect.
         Log.w(TAG, "onBindingDied");
         disconnect();
     }
 
-    @Override
+    @Override // ServiceConnection
     public void onNullBinding(ComponentName name) {
         Log.w(TAG, "onNullBinding");
         disconnect();
     }
 
+    private boolean checkSecureSetting(String name) {
+        return Settings.Secure.getInt(mContext.getContentResolver(), name, 0) == 1;
+    }
+
+    private boolean checkUserSetupComplete() {
+        return Settings.Secure.getIntForUser(
+                mContext.getContentResolver(),
+                Settings.Secure.USER_SETUP_COMPLETE, 0,
+                UserHandle.USER_CURRENT) == 1;
+    }
+
     private static class BaseCallbacks extends IQuickAccessWalletServiceCallbacks.Stub {
         public void onGetWalletCardsSuccess(GetWalletCardsResponse response) {
             throw new IllegalStateException();
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
index d968405..aba5611 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletService.java
@@ -45,8 +45,9 @@
  *   android.permission.BIND_NFC_SERVICE permission.
  *   <li>The user explicitly selected the application as the default payment application in
  *   the Tap &amp; pay settings screen.
- *   <li>The application requires the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
- *   permission in its manifest.
+ *   <li>The QuickAccessWalletService requires that the binding application hold the
+ *   {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE} permission, which only the System
+ *   Service can hold.
  *   <li>The user explicitly enables it using Android Settings (the
  *       {@link Settings#ACTION_QUICK_ACCESS_WALLET_SETTINGS} intent can be used to launch it).
  * </ol>
@@ -73,6 +74,11 @@
  *   a new card is selected, the Android System will notify the service through
  *   {@link #onWalletCardSelected} and will provide the {@link WalletCard#getCardId() cardId} of the
  *   card that is now selected.
+ *   <li>If the user commences an NFC payment, the service may send a {@link WalletServiceEvent}
+ *   to the System indicating that the wallet application now needs to show the activity associated
+ *   with making a payment. Sending a {@link WalletServiceEvent} of type
+ *   {@link WalletServiceEvent#TYPE_NFC_PAYMENT_STARTED} should cause the quick access wallet UI
+ *   to be dismissed.
  *   <li>When the wallet is dismissed, the Android System will notify the service through
  *   {@link #onWalletDismissed}.
  * </ol>
@@ -110,6 +116,7 @@
  *     android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE">
  *     <intent-filter>
  *         <action android:name="android.service.quickaccesswallet.QuickAccessWalletService" />
+ *         <category android:name="android.intent.category.DEFAULT"/>
  *     </intent-filter>
  *     <meta-data android:name="android.quickaccesswallet"
  *          android:resource="@xml/quickaccesswallet_configuration" />;
@@ -166,29 +173,6 @@
             "android.service.quickaccesswallet.action.VIEW_WALLET_SETTINGS";
 
     /**
-     * Broadcast Action: Sent by the wallet application to dismiss the Quick Access Wallet.
-     * <p>
-     * The Quick Access Wallet may be shown in a system window on top of other Activities. If the
-     * user selects a payment card from the Quick Access Wallet and then holds their phone to an NFC
-     * terminal, the wallet application will need to show a payment Activity. But if the Quick
-     * Access Wallet is still being shown, it may obscure the payment Activity. To avoid this, the
-     * wallet application can send a broadcast to the Android System with this action to request
-     * that the Quick Access Wallet be dismissed.
-     * <p>
-     * This broadcast must use the {@code android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE}
-     * permission to ensure that it is only delivered to System UI. Furthermore, your application
-     * must require the {@code android.permission.DISMISS_QUICK_ACCESS_WALLET}
-     * <p>
-     * <pre class="prettyprint">
-     * context.sendBroadcast(
-     *     new Intent(ACTION_DISMISS_WALLET), Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE);
-     * </pre>
-     */
-    @SdkConstant(SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_DISMISS_WALLET =
-            "android.service.quickaccesswallet.action.DISMISS_WALLET";
-
-    /**
      * Name under which a QuickAccessWalletService component publishes information about itself.
      * This meta-data should reference an XML resource containing a
      * <code>&lt;{@link
@@ -202,8 +186,20 @@
     public static final String SERVICE_META_DATA = "android.quickaccesswallet";
 
     private final Handler mHandler = new Handler(Looper.getMainLooper());
+
+    /**
+     * The service currently only supports one listener at a time. Multiple connections that
+     * register different listeners will clobber the listener. This field may only be accessed from
+     * the main thread.
+     */
     @Nullable
     private String mEventListenerId;
+
+    /**
+     * The service currently only supports one listener at a time. Multiple connections that
+     * register different listeners will clobber the listener. This field may only be accessed from
+     * the main thread.
+     */
     @Nullable
     private IQuickAccessWalletServiceCallbacks mEventListener;
 
@@ -240,7 +236,8 @@
     private void onWalletCardsRequestedInternal(
             GetWalletCardsRequest request,
             IQuickAccessWalletServiceCallbacks callback) {
-        onWalletCardsRequested(request, new GetWalletCardsCallback(callback, mHandler));
+        onWalletCardsRequested(request,
+                new GetWalletCardsCallbackImpl(request, callback, mHandler));
     }
 
     @Override
@@ -250,14 +247,14 @@
             // Binding to the QuickAccessWalletService is protected by the
             // android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE permission, which is defined in
             // R. Pre-R devices can have other side-loaded applications that claim this permission.
-            // This ensures that the service is only available when properly permission protected.
+            // Ensures that the service is only enabled when properly permission protected.
             Log.w(TAG, "Warning: binding on pre-R device");
         }
-        if (SERVICE_INTERFACE.equals(intent.getAction())) {
-            return mInterface.asBinder();
+        if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+            Log.w(TAG, "Wrong action");
+            return null;
         }
-        Log.w(TAG, "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
-        return null;
+        return mInterface.asBinder();
     }
 
     /**
diff --git a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
index 8793f28..23173a8 100644
--- a/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
+++ b/core/java/android/service/quickaccesswallet/QuickAccessWalletServiceInfo.java
@@ -77,8 +77,9 @@
         }
 
         if (!Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE.equals(serviceInfo.permission)) {
-            Log.w(TAG, String.format("QuickAccessWalletService from %s does not have permission %s",
-                    serviceInfo.packageName, Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
+            Log.w(TAG, String.format("%s.%s does not require permission %s",
+                    serviceInfo.packageName, serviceInfo.name,
+                    Manifest.permission.BIND_QUICK_ACCESS_WALLET_SERVICE));
             return null;
         }
 
diff --git a/core/java/android/service/quickaccesswallet/WalletCard.java b/core/java/android/service/quickaccesswallet/WalletCard.java
index c3b1a4b..e6ae0ab 100644
--- a/core/java/android/service/quickaccesswallet/WalletCard.java
+++ b/core/java/android/service/quickaccesswallet/WalletCard.java
@@ -174,22 +174,25 @@
          * @param cardId             The card id must be non-null and unique within the list of
          *                           cards returned. <b>Note:
          *                           </b> this card ID should <b>not</b> contain PII (Personally
-         *                           Identifiable Information, * such as username or email
-         *                           address).
+         *                           Identifiable Information, such as username or email address).
          * @param cardImage          The visual representation of the card. If the card image Icon
          *                           is a bitmap, it should have a width of {@link
          *                           GetWalletCardsRequest#getCardWidthPx()} and a height of {@link
          *                           GetWalletCardsRequest#getCardHeightPx()}. If the card image
          *                           does not have these dimensions, it may appear distorted when it
-         *                           is scaled to fit these dimensions on screen.
+         *                           is scaled to fit these dimensions on screen. Bitmaps should be
+         *                           of type {@link android.graphics.Bitmap.Config#HARDWARE} for
+         *                           performance reasons.
          * @param contentDescription The content description of the card image. This field is
-         *                           required.
+         *                           required and may not be null or empty.
          *                           <b>Note: </b> this message should <b>not</b> contain PII
          *                           (Personally Identifiable Information, such as username or email
          *                           address).
          * @param pendingIntent      If the user performs a click on the card, this PendingIntent
          *                           will be sent. If the device is locked, the wallet will first
-         *                           request device unlock before sending the pending intent.
+         *                           request device unlock before sending the pending intent. It is
+         *                           recommended that the pending intent be immutable (use {@link
+         *                           PendingIntent#FLAG_IMMUTABLE}).
          */
         public Builder(@NonNull String cardId,
                 @NonNull Icon cardImage,
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index f61217d..d1edf58 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -67,7 +67,7 @@
  * </ul>
  * </p><p>
  * A logical display does not necessarily represent a particular physical display device
- * such as the built-in screen or an external monitor.  The contents of a logical
+ * such as the internal display or an external display.  The contents of a logical
  * display may be presented on one or more physical displays according to the devices
  * that are currently attached and whether mirroring has been enabled.
  * </p>
@@ -104,8 +104,7 @@
     private int mCachedAppHeightCompat;
 
     /**
-     * The default Display id, which is the id of the built-in primary display
-     * assuming there is one.
+     * The default Display id, which is the id of the primary display assuming there is one.
      */
     public static final int DEFAULT_DISPLAY = 0;
 
@@ -188,7 +187,7 @@
      * Display flag: Indicates that the display is a presentation display.
      * <p>
      * This flag identifies secondary displays that are suitable for
-     * use as presentation displays such as HDMI or Wireless displays.  Applications
+     * use as presentation displays such as external or wireless displays.  Applications
      * may automatically project their content to presentation displays to provide
      * richer second screen experiences.
      * </p>
@@ -257,17 +256,17 @@
     public static final int TYPE_UNKNOWN = 0;
 
     /**
-     * Display type: Built-in display.
+     * Display type: Physical display connected through an internal port.
      * @hide
      */
-    public static final int TYPE_BUILT_IN = 1;
+    public static final int TYPE_INTERNAL = 1;
 
     /**
-     * Display type: HDMI display.
+     * Display type: Physical display connected through an external port.
      * @hide
      */
     @UnsupportedAppUsage
-    public static final int TYPE_HDMI = 2;
+    public static final int TYPE_EXTERNAL = 2;
 
     /**
      * Display type: WiFi display.
@@ -562,8 +561,8 @@
      * @return The display type.
      *
      * @see #TYPE_UNKNOWN
-     * @see #TYPE_BUILT_IN
-     * @see #TYPE_HDMI
+     * @see #TYPE_INTERNAL
+     * @see #TYPE_EXTERNAL
      * @see #TYPE_WIFI
      * @see #TYPE_OVERLAY
      * @see #TYPE_VIRTUAL
@@ -1251,10 +1250,10 @@
         switch (type) {
             case TYPE_UNKNOWN:
                 return "UNKNOWN";
-            case TYPE_BUILT_IN:
-                return "BUILT_IN";
-            case TYPE_HDMI:
-                return "HDMI";
+            case TYPE_INTERNAL:
+                return "INTERNAL";
+            case TYPE_EXTERNAL:
+                return "EXTERNAL";
             case TYPE_WIFI:
                 return "WIFI";
             case TYPE_OVERLAY:
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 37b9eb3..c62e9346 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -90,13 +90,11 @@
 
     /**
      * The user has started a gesture (e.g. on the soft keyboard).
-     * @hide
      */
     public static final int GESTURE_START = 12;
 
     /**
      * The user has finished a gesture (e.g. on the soft keyboard).
-     * @hide
      */
     public static final int GESTURE_END = 13;
 
diff --git a/core/java/android/view/RenderNodeAnimator.java b/core/java/android/view/RenderNodeAnimator.java
index 0c1edac..31fdfb7 100644
--- a/core/java/android/view/RenderNodeAnimator.java
+++ b/core/java/android/view/RenderNodeAnimator.java
@@ -16,518 +16,52 @@
 
 package android.view;
 
-import android.animation.Animator;
-import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.graphics.CanvasProperty;
 import android.graphics.Paint;
-import android.graphics.RecordingCanvas;
-import android.graphics.RenderNode;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.SparseIntArray;
-
-import com.android.internal.util.VirtualRefBasePtr;
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-
-import java.util.ArrayList;
 
 /**
  * @hide
  */
-public class RenderNodeAnimator extends Animator {
-    // Keep in sync with enum RenderProperty in Animator.h
-    public static final int TRANSLATION_X = 0;
-    public static final int TRANSLATION_Y = 1;
-    public static final int TRANSLATION_Z = 2;
-    public static final int SCALE_X = 3;
-    public static final int SCALE_Y = 4;
-    public static final int ROTATION = 5;
-    public static final int ROTATION_X = 6;
-    public static final int ROTATION_Y = 7;
-    public static final int X = 8;
-    public static final int Y = 9;
-    public static final int Z = 10;
-    public static final int ALPHA = 11;
-    // The last value in the enum, used for array size initialization
-    public static final int LAST_VALUE = ALPHA;
+public class RenderNodeAnimator extends android.graphics.animation.RenderNodeAnimator
+        implements android.graphics.animation.RenderNodeAnimator.ViewListener {
 
-    // Keep in sync with enum PaintFields in Animator.h
-    public static final int PAINT_STROKE_WIDTH = 0;
-
-    /**
-     * Field for the Paint alpha channel, which should be specified as a value
-     * between 0 and 255.
-     */
-    public static final int PAINT_ALPHA = 1;
-
-    // ViewPropertyAnimator uses a mask for its values, we need to remap them
-    // to the enum values here. RenderPropertyAnimator can't use the mask values
-    // directly as internally it uses a lookup table so it needs the values to
-    // be sequential starting from 0
-    private static final SparseIntArray sViewPropertyAnimatorMap = new SparseIntArray(15) {{
-        put(ViewPropertyAnimator.TRANSLATION_X, TRANSLATION_X);
-        put(ViewPropertyAnimator.TRANSLATION_Y, TRANSLATION_Y);
-        put(ViewPropertyAnimator.TRANSLATION_Z, TRANSLATION_Z);
-        put(ViewPropertyAnimator.SCALE_X, SCALE_X);
-        put(ViewPropertyAnimator.SCALE_Y, SCALE_Y);
-        put(ViewPropertyAnimator.ROTATION, ROTATION);
-        put(ViewPropertyAnimator.ROTATION_X, ROTATION_X);
-        put(ViewPropertyAnimator.ROTATION_Y, ROTATION_Y);
-        put(ViewPropertyAnimator.X, X);
-        put(ViewPropertyAnimator.Y, Y);
-        put(ViewPropertyAnimator.Z, Z);
-        put(ViewPropertyAnimator.ALPHA, ALPHA);
-    }};
-
-    private VirtualRefBasePtr mNativePtr;
-
-    private Handler mHandler;
-    private RenderNode mTarget;
     private View mViewTarget;
-    private int mRenderProperty = -1;
-    private float mFinalValue;
-    private TimeInterpolator mInterpolator;
-
-    private static final int STATE_PREPARE = 0;
-    private static final int STATE_DELAYED = 1;
-    private static final int STATE_RUNNING = 2;
-    private static final int STATE_FINISHED = 3;
-    private int mState = STATE_PREPARE;
-
-    private long mUnscaledDuration = 300;
-    private long mUnscaledStartDelay = 0;
-    // If this is true, we will run any start delays on the UI thread. This is
-    // the safe default, and is necessary to ensure start listeners fire at
-    // the correct time. Animators created by RippleDrawable (the
-    // CanvasProperty<> ones) do not have this expectation, and as such will
-    // set this to false so that the renderthread handles the startdelay instead
-    private final boolean mUiThreadHandlesDelay;
-    private long mStartDelay = 0;
-    private long mStartTime;
-
-    public static int mapViewPropertyToRenderProperty(int viewProperty) {
-        return sViewPropertyAnimatorMap.get(viewProperty);
-    }
 
     public RenderNodeAnimator(int property, float finalValue) {
-        mRenderProperty = property;
-        mFinalValue = finalValue;
-        mUiThreadHandlesDelay = true;
-        init(nCreateAnimator(property, finalValue));
+        super(property, finalValue);
     }
 
     public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
-        init(nCreateCanvasPropertyFloatAnimator(
-                property.getNativeContainer(), finalValue));
-        mUiThreadHandlesDelay = false;
+        super(property, finalValue);
     }
 
-    /**
-     * Creates a new render node animator for a field on a Paint property.
-     *
-     * @param property The paint property to target
-     * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or
-     *            {@link #PAINT_STROKE_WIDTH}
-     * @param finalValue The target value for the property
-     */
     public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
-        init(nCreateCanvasPropertyPaintAnimator(
-                property.getNativeContainer(), paintField, finalValue));
-        mUiThreadHandlesDelay = false;
+        super(property, paintField, finalValue);
     }
 
     public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
-        init(nCreateRevealAnimator(x, y, startRadius, endRadius));
-        mUiThreadHandlesDelay = true;
-    }
-
-    private void init(long ptr) {
-        mNativePtr = new VirtualRefBasePtr(ptr);
-    }
-
-    private void checkMutable() {
-        if (mState != STATE_PREPARE) {
-            throw new IllegalStateException("Animator has already started, cannot change it now!");
-        }
-        if (mNativePtr == null) {
-            throw new IllegalStateException("Animator's target has been destroyed "
-                    + "(trying to modify an animation after activity destroy?)");
-        }
-    }
-
-    static boolean isNativeInterpolator(TimeInterpolator interpolator) {
-        return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
-    }
-
-    private void applyInterpolator() {
-        if (mInterpolator == null || mNativePtr == null) return;
-
-        long ni;
-        if (isNativeInterpolator(mInterpolator)) {
-            ni = ((NativeInterpolatorFactory)mInterpolator).createNativeInterpolator();
-        } else {
-            long duration = nGetDuration(mNativePtr.get());
-            ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
-        }
-        nSetInterpolator(mNativePtr.get(), ni);
+        super(x, y, startRadius, endRadius);
     }
 
     @Override
-    public void start() {
-        if (mTarget == null) {
-            throw new IllegalStateException("Missing target!");
-        }
-
-        if (mState != STATE_PREPARE) {
-            throw new IllegalStateException("Already started!");
-        }
-
-        mState = STATE_DELAYED;
-        if (mHandler == null) {
-            mHandler = new Handler(true);
-        }
-        applyInterpolator();
-
-        if (mNativePtr == null) {
-            // It's dead, immediately cancel
-            cancel();
-        } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
-            nSetStartDelay(mNativePtr.get(), mStartDelay);
-            doStart();
-        } else {
-            getHelper().addDelayedAnimation(this);
-        }
-    }
-
-    private void doStart() {
+    public void onAlphaAnimationStart(float finalAlpha) {
         // Alpha is a special snowflake that has the canonical value stored
         // in mTransformationInfo instead of in RenderNode, so we need to update
         // it with the final value here.
-        if (mRenderProperty == RenderNodeAnimator.ALPHA) {
-            mViewTarget.ensureTransformationInfo();
-            mViewTarget.setAlphaInternal(mFinalValue);
-        }
-
-        moveToRunningState();
-
-        if (mViewTarget != null) {
-            // Kick off a frame to start the process
-            mViewTarget.invalidateViewProperty(true, false);
-        }
-    }
-
-    private void moveToRunningState() {
-        mState = STATE_RUNNING;
-        if (mNativePtr != null) {
-            nStart(mNativePtr.get());
-        }
-        notifyStartListeners();
-    }
-
-    private void notifyStartListeners() {
-        final ArrayList<AnimatorListener> listeners = cloneListeners();
-        final int numListeners = listeners == null ? 0 : listeners.size();
-        for (int i = 0; i < numListeners; i++) {
-            listeners.get(i).onAnimationStart(this);
-        }
+        mViewTarget.ensureTransformationInfo();
+        mViewTarget.setAlphaInternal(finalAlpha);
     }
 
     @Override
-    public void cancel() {
-        if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
-            if (mState == STATE_DELAYED) {
-                getHelper().removeDelayedAnimation(this);
-                moveToRunningState();
-            }
-
-            final ArrayList<AnimatorListener> listeners = cloneListeners();
-            final int numListeners = listeners == null ? 0 : listeners.size();
-            for (int i = 0; i < numListeners; i++) {
-                listeners.get(i).onAnimationCancel(this);
-            }
-
-            end();
-        }
-    }
-
-    @Override
-    public void end() {
-        if (mState != STATE_FINISHED) {
-            if (mState < STATE_RUNNING) {
-                getHelper().removeDelayedAnimation(this);
-                doStart();
-            }
-            if (mNativePtr != null) {
-                nEnd(mNativePtr.get());
-                if (mViewTarget != null) {
-                    // Kick off a frame to flush the state change
-                    mViewTarget.invalidateViewProperty(true, false);
-                }
-            } else {
-                // It's already dead, jump to onFinish
-                onFinished();
-            }
-        }
-    }
-
-    @Override
-    public void pause() {
-        throw new UnsupportedOperationException();
-    }
-
-    @Override
-    public void resume() {
-        throw new UnsupportedOperationException();
+    public void invalidateParent(boolean forceRedraw) {
+        mViewTarget.invalidateViewProperty(true, false);
     }
 
     /** @hide */
-    public void setTarget(View view) {
+    public void setTarget(@NonNull View view) {
         mViewTarget = view;
+        setViewListener(this);
         setTarget(mViewTarget.mRenderNode);
     }
-
-    /** Sets the animation target to the owning view of the RecordingCanvas */
-    public void setTarget(RecordingCanvas canvas) {
-        setTarget(canvas.mNode);
-    }
-
-    /** @hide */
-    public void setTarget(DisplayListCanvas canvas) {
-        setTarget((RecordingCanvas) canvas);
-    }
-
-    private void setTarget(RenderNode node) {
-        checkMutable();
-        if (mTarget != null) {
-            throw new IllegalStateException("Target already set!");
-        }
-        nSetListener(mNativePtr.get(), this);
-        mTarget = node;
-        mTarget.addAnimator(this);
-    }
-
-    public void setStartValue(float startValue) {
-        checkMutable();
-        nSetStartValue(mNativePtr.get(), startValue);
-    }
-
-    @Override
-    public void setStartDelay(long startDelay) {
-        checkMutable();
-        if (startDelay < 0) {
-            throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
-        }
-        mUnscaledStartDelay = startDelay;
-        mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
-    }
-
-    @Override
-    public long getStartDelay() {
-        return mUnscaledStartDelay;
-    }
-
-    @Override
-    public RenderNodeAnimator setDuration(long duration) {
-        checkMutable();
-        if (duration < 0) {
-            throw new IllegalArgumentException("duration must be positive; " + duration);
-        }
-        mUnscaledDuration = duration;
-        nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
-        return this;
-    }
-
-    @Override
-    public long getDuration() {
-        return mUnscaledDuration;
-    }
-
-    @Override
-    public long getTotalDuration() {
-        return mUnscaledDuration + mUnscaledStartDelay;
-    }
-
-    @Override
-    public boolean isRunning() {
-        return mState == STATE_DELAYED || mState == STATE_RUNNING;
-    }
-
-    @Override
-    public boolean isStarted() {
-        return mState != STATE_PREPARE;
-    }
-
-    @Override
-    public void setInterpolator(TimeInterpolator interpolator) {
-        checkMutable();
-        mInterpolator = interpolator;
-    }
-
-    @Override
-    public TimeInterpolator getInterpolator() {
-        return mInterpolator;
-    }
-
-    protected void onFinished() {
-        if (mState == STATE_PREPARE) {
-            // Unlikely but possible, the native side has been destroyed
-            // before we have started.
-            releaseNativePtr();
-            return;
-        }
-        if (mState == STATE_DELAYED) {
-            getHelper().removeDelayedAnimation(this);
-            notifyStartListeners();
-        }
-        mState = STATE_FINISHED;
-
-        final ArrayList<AnimatorListener> listeners = cloneListeners();
-        final int numListeners = listeners == null ? 0 : listeners.size();
-        for (int i = 0; i < numListeners; i++) {
-            listeners.get(i).onAnimationEnd(this);
-        }
-
-        // Release the native object, as it has a global reference to us. This
-        // breaks the cyclic reference chain, and allows this object to be
-        // GC'd
-        releaseNativePtr();
-    }
-
-    private void releaseNativePtr() {
-        if (mNativePtr != null) {
-            mNativePtr.release();
-            mNativePtr = null;
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private ArrayList<AnimatorListener> cloneListeners() {
-        ArrayList<AnimatorListener> listeners = getListeners();
-        if (listeners != null) {
-            listeners = (ArrayList<AnimatorListener>) listeners.clone();
-        }
-        return listeners;
-    }
-
-    public long getNativeAnimator() {
-        return mNativePtr.get();
-    }
-
-    /**
-     * @return true if the animator was started, false if still delayed
-     */
-    private boolean processDelayed(long frameTimeMs) {
-        if (mStartTime == 0) {
-            mStartTime = frameTimeMs;
-        } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
-            doStart();
-            return true;
-        }
-        return false;
-    }
-
-    private static DelayedAnimationHelper getHelper() {
-        DelayedAnimationHelper helper = sAnimationHelper.get();
-        if (helper == null) {
-            helper = new DelayedAnimationHelper();
-            sAnimationHelper.set(helper);
-        }
-        return helper;
-    }
-
-    private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
-            new ThreadLocal<DelayedAnimationHelper>();
-
-    private static class DelayedAnimationHelper implements Runnable {
-
-        private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
-        private final Choreographer mChoreographer;
-        private boolean mCallbackScheduled;
-
-        public DelayedAnimationHelper() {
-            mChoreographer = Choreographer.getInstance();
-        }
-
-        public void addDelayedAnimation(RenderNodeAnimator animator) {
-            mDelayedAnims.add(animator);
-            scheduleCallback();
-        }
-
-        public void removeDelayedAnimation(RenderNodeAnimator animator) {
-            mDelayedAnims.remove(animator);
-        }
-
-        private void scheduleCallback() {
-            if (!mCallbackScheduled) {
-                mCallbackScheduled = true;
-                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
-            }
-        }
-
-        @Override
-        public void run() {
-            long frameTimeMs = mChoreographer.getFrameTime();
-            mCallbackScheduled = false;
-
-            int end = 0;
-            for (int i = 0; i < mDelayedAnims.size(); i++) {
-                RenderNodeAnimator animator = mDelayedAnims.get(i);
-                if (!animator.processDelayed(frameTimeMs)) {
-                    if (end != i) {
-                        mDelayedAnims.set(end, animator);
-                    }
-                    end++;
-                }
-            }
-            while (mDelayedAnims.size() > end) {
-                mDelayedAnims.remove(mDelayedAnims.size() - 1);
-            }
-
-            if (mDelayedAnims.size() > 0) {
-                scheduleCallback();
-            }
-        }
-    }
-
-    // Called by native
-    private static void callOnFinished(RenderNodeAnimator animator) {
-        if (animator.mHandler != null) {
-            animator.mHandler.post(animator::onFinished);
-        } else {
-            new Handler(Looper.getMainLooper(), null, true).post(animator::onFinished);
-        }
-    }
-
-    @Override
-    public Animator clone() {
-        throw new IllegalStateException("Cannot clone this animator");
-    }
-
-    @Override
-    public void setAllowRunningAsynchronously(boolean mayRunAsync) {
-        checkMutable();
-        nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
-    }
-
-    private static native long nCreateAnimator(int property, float finalValue);
-    private static native long nCreateCanvasPropertyFloatAnimator(
-            long canvasProperty, float finalValue);
-    private static native long nCreateCanvasPropertyPaintAnimator(
-            long canvasProperty, int paintField, float finalValue);
-    private static native long nCreateRevealAnimator(
-            int x, int y, float startRadius, float endRadius);
-
-    private static native void nSetStartValue(long nativePtr, float startValue);
-    private static native void nSetDuration(long nativePtr, long duration);
-    private static native long nGetDuration(long nativePtr);
-    private static native void nSetStartDelay(long nativePtr, long startDelay);
-    private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
-    private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
-    private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
-
-    private static native void nStart(long animPtr);
-    private static native void nEnd(long animPtr);
 }
diff --git a/core/java/android/view/RenderNodeAnimatorSetHelper.java b/core/java/android/view/RenderNodeAnimatorSetHelper.java
deleted file mode 100644
index d222e07..0000000
--- a/core/java/android/view/RenderNodeAnimatorSetHelper.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.view;
-
-import android.animation.TimeInterpolator;
-import android.graphics.RecordingCanvas;
-import android.graphics.RenderNode;
-
-import com.android.internal.view.animation.FallbackLUTInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
-/**
- * This is a helper class to get access to methods and fields needed for RenderNodeAnimatorSet
- * that are internal or package private to android.view package.
- *
- * @hide
- */
-public class RenderNodeAnimatorSetHelper {
-
-    /** checkstyle @hide */
-    public static RenderNode getTarget(RecordingCanvas recordingCanvas) {
-        return recordingCanvas.mNode;
-    }
-
-    /** checkstyle @hide */
-    public static long createNativeInterpolator(TimeInterpolator interpolator, long
-            duration) {
-        if (interpolator == null) {
-            // create LinearInterpolator
-            return NativeInterpolatorFactoryHelper.createLinearInterpolator();
-        } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
-            return ((NativeInterpolatorFactory)interpolator).createNativeInterpolator();
-        } else {
-            return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
-        }
-    }
-
-}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index fe9e36e..cf48c52 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -321,14 +321,12 @@
     public static final int FX_SURFACE_NORMAL   = 0x00000000;
 
     /**
-     * Surface creation flag: Creates a Dim surface.
-     * Everything behind this surface is dimmed by the amount specified
-     * in {@link Transaction#setAlpha(SurfaceControl, float)}.  It is an error to lock a Dim
-     * surface, since it doesn't have a backing store.
+     * Surface creation flag: Creates a effect surface which
+     * represents a solid color and or shadows.
      *
      * @hide
      */
-    public static final int FX_SURFACE_DIM = 0x00020000;
+    public static final int FX_SURFACE_EFFECT = 0x00020000;
 
     /**
      * Surface creation flag: Creates a container surface.
@@ -739,11 +737,11 @@
          */
         public Builder setColorLayer() {
             unsetBufferSize();
-            return setFlags(FX_SURFACE_DIM, FX_SURFACE_MASK);
+            return setFlags(FX_SURFACE_EFFECT, FX_SURFACE_MASK);
         }
 
         private boolean isColorLayerSet() {
-            return  (mFlags & FX_SURFACE_DIM) == FX_SURFACE_DIM;
+            return  (mFlags & FX_SURFACE_EFFECT) == FX_SURFACE_EFFECT;
         }
 
         /**
@@ -1285,12 +1283,15 @@
      * @hide
      */
     public static final class DisplayInfo {
+        public boolean isInternal;
         public float density;
         public boolean secure;
 
         @Override
         public String toString() {
-            return "DisplayInfo{density=" + density + ", secure=" + secure + "}";
+            return "DisplayInfo{isInternal=" + isInternal
+                    + ", density=" + density
+                    + ", secure=" + secure + "}";
         }
     }
 
@@ -2075,6 +2076,7 @@
 
         private final ArrayMap<SurfaceControl, Point> mResizedSurfaces = new ArrayMap<>();
         Runnable mFreeNativeResources;
+        private static final float[] INVALID_COLOR = {-1, -1, -1};
 
         /**
          * @hide
@@ -2530,8 +2532,9 @@
         }
 
         /**
-         * Sets a color for the Surface.
-         * @param color A float array with three values to represent r, g, b in range [0..1]
+         * Fills the surface with the specified color.
+         * @param color A float array with three values to represent r, g, b in range [0..1]. An
+         * invalid color will remove the color fill.
          * @hide
          */
         @UnsupportedAppUsage
@@ -2542,6 +2545,16 @@
         }
 
         /**
+         * Removes color fill.
+        * @hide
+        */
+        public Transaction unsetColor(SurfaceControl sc) {
+            checkPreconditions(sc);
+            nativeSetColor(mNativeObject, sc.mNativeObject, INVALID_COLOR);
+            return this;
+        }
+
+        /**
          * Sets the security of the surface.  Setting the flag is equivalent to creating the
          * Surface with the {@link #SECURE} flag.
          * @hide
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index b971a20..688517d 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -1705,12 +1705,13 @@
     }
 
     Surface getOrCreateBLASTSurface(int width, int height) {
-        if (mSurfaceControl == null || !mSurfaceControl.isValid()) {
+        if (mSurfaceControl == null
+                || !mSurfaceControl.isValid()
+                || mBlastSurfaceControl == null
+                || !mBlastSurfaceControl.isValid()) {
             return null;
         }
-        if ((mBlastSurfaceControl != null)
-                && (mBlastBufferQueue == null)
-                && mBlastSurfaceControl.isValid()) {
+        if (mBlastBufferQueue == null) {
             mBlastBufferQueue = new BLASTBufferQueue(
                 mBlastSurfaceControl, width, height);
         }
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 3274449..56683dd 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -16,9 +16,6 @@
 
 package android.view;
 
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
-
 import android.annotation.NonNull;
 import android.app.ResourcesManager;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -70,6 +67,10 @@
 
     private IBinder mDefaultToken;
 
+    private boolean mIsViewAdded;
+    private View mLastView;
+    private WindowManager.LayoutParams mLastParams;
+
     public WindowManagerImpl(Context context) {
         this(context, null);
     }
@@ -101,6 +102,9 @@
     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
         applyDefaultToken(params);
         mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
+        mIsViewAdded = true;
+        mLastView = view;
+        mLastParams = (WindowManager.LayoutParams) params;
     }
 
     @Override
@@ -246,15 +250,18 @@
         // TODO(window-context): This can only be properly implemented
         //  once we flip the new insets mode flag.
         if (mParentWindow != null) {
+            if (mParentWindow.getDecorView().isAttachedToWindow()) {
+                return mParentWindow.getDecorView().getViewRootImpl()
+                        .getWindowInsets(true /* forceConstruct */);
+            }
             return getWindowInsetsFromServer(mParentWindow.getAttributes());
         }
-        return getWindowInsetsFromServer(getDefaultParams());
-    }
+        if (mIsViewAdded) {
+            return mLastView.getViewRootImpl().getWindowInsets(true /* forceConstruct */);
+        } else {
+            return getWindowInsetsFromServer(new WindowManager.LayoutParams());
+        }
 
-    private static WindowManager.LayoutParams getDefaultParams() {
-        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
-        params.flags = FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR;
-        return params;
     }
 
     private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs) {
diff --git a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
index 21d5a5b..a2bbc5c 100644
--- a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
@@ -17,19 +17,18 @@
 package android.view.animation;
 
 import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
 /**
  * An interpolator where the rate of change starts and ends slowly but
  * accelerates through the middle.
  */
 @HasNativeInterpolator
 public class AccelerateDecelerateInterpolator extends BaseInterpolator
-        implements NativeInterpolatorFactory {
+        implements NativeInterpolator {
     public AccelerateDecelerateInterpolator() {
     }
 
@@ -44,6 +43,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createAccelerateDecelerateInterpolator();
+        return NativeInterpolatorFactory.createAccelerateDecelerateInterpolator();
     }
 }
diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java
index 6c8d7b1..9d4cd32 100644
--- a/core/java/android/view/animation/AccelerateInterpolator.java
+++ b/core/java/android/view/animation/AccelerateInterpolator.java
@@ -20,12 +20,12 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
 import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
 
 /**
  * An interpolator where the rate of change starts out slowly and
@@ -33,7 +33,7 @@
  *
  */
 @HasNativeInterpolator
-public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class AccelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
     private final float mFactor;
     private final double mDoubleFactor;
 
@@ -85,6 +85,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createAccelerateInterpolator(mFactor);
+        return NativeInterpolatorFactory.createAccelerateInterpolator(mFactor);
     }
 }
diff --git a/core/java/android/view/animation/AnticipateInterpolator.java b/core/java/android/view/animation/AnticipateInterpolator.java
index 7a837c3..d146394 100644
--- a/core/java/android/view/animation/AnticipateInterpolator.java
+++ b/core/java/android/view/animation/AnticipateInterpolator.java
@@ -20,18 +20,18 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
 import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
 
 /**
  * An interpolator where the change starts backward then flings forward.
  */
 @HasNativeInterpolator
-public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class AnticipateInterpolator extends BaseInterpolator implements NativeInterpolator {
     private final float mTension;
 
     public AnticipateInterpolator() {
@@ -73,6 +73,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createAnticipateInterpolator(mTension);
+        return NativeInterpolatorFactory.createAnticipateInterpolator(mTension);
     }
 }
diff --git a/core/java/android/view/animation/AnticipateOvershootInterpolator.java b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
index 9a75134..4d6a390 100644
--- a/core/java/android/view/animation/AnticipateOvershootInterpolator.java
+++ b/core/java/android/view/animation/AnticipateOvershootInterpolator.java
@@ -24,11 +24,11 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
 
 /**
  * An interpolator where the change starts backward then flings forward and overshoots
@@ -36,7 +36,7 @@
  */
 @HasNativeInterpolator
 public class AnticipateOvershootInterpolator extends BaseInterpolator
-        implements NativeInterpolatorFactory {
+        implements NativeInterpolator {
     private final float mTension;
 
     public AnticipateOvershootInterpolator() {
@@ -103,6 +103,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createAnticipateOvershootInterpolator(mTension);
+        return NativeInterpolatorFactory.createAnticipateOvershootInterpolator(mTension);
     }
 }
diff --git a/core/java/android/view/animation/BounceInterpolator.java b/core/java/android/view/animation/BounceInterpolator.java
index 909eaa4..d3f6a3f 100644
--- a/core/java/android/view/animation/BounceInterpolator.java
+++ b/core/java/android/view/animation/BounceInterpolator.java
@@ -17,17 +17,16 @@
 package android.view.animation;
 
 import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
 /**
  * An interpolator where the change bounces at the end.
  */
 @HasNativeInterpolator
-public class BounceInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class BounceInterpolator extends BaseInterpolator implements NativeInterpolator {
     public BounceInterpolator() {
     }
 
@@ -56,6 +55,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createBounceInterpolator();
+        return NativeInterpolatorFactory.createBounceInterpolator();
     }
 }
\ No newline at end of file
diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java
index 72d64a1..6b1a80a 100644
--- a/core/java/android/view/animation/CycleInterpolator.java
+++ b/core/java/android/view/animation/CycleInterpolator.java
@@ -20,12 +20,12 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
 import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
 
 /**
  * Repeats the animation for a specified number of cycles. The
@@ -33,7 +33,7 @@
  *
  */
 @HasNativeInterpolator
-public class CycleInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class CycleInterpolator extends BaseInterpolator implements NativeInterpolator {
     public CycleInterpolator(float cycles) {
         mCycles = cycles;
     }
@@ -65,6 +65,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createCycleInterpolator(mCycles);
+        return NativeInterpolatorFactory.createCycleInterpolator(mCycles);
     }
 }
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
index 2d1249d..2d2f770 100644
--- a/core/java/android/view/animation/DecelerateInterpolator.java
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -20,12 +20,12 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
 import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
 
 /**
  * An interpolator where the rate of change starts out quickly and
@@ -33,7 +33,7 @@
  *
  */
 @HasNativeInterpolator
-public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class DecelerateInterpolator extends BaseInterpolator implements NativeInterpolator {
     public DecelerateInterpolator() {
     }
 
@@ -81,6 +81,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createDecelerateInterpolator(mFactor);
+        return NativeInterpolatorFactory.createDecelerateInterpolator(mFactor);
     }
 }
diff --git a/core/java/android/view/animation/LinearInterpolator.java b/core/java/android/view/animation/LinearInterpolator.java
index 2a047b4..f6a820c 100644
--- a/core/java/android/view/animation/LinearInterpolator.java
+++ b/core/java/android/view/animation/LinearInterpolator.java
@@ -17,17 +17,16 @@
 package android.view.animation;
 
 import android.content.Context;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
-
 /**
  * An interpolator where the rate of change is constant
  */
 @HasNativeInterpolator
-public class LinearInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class LinearInterpolator extends BaseInterpolator implements NativeInterpolator {
 
     public LinearInterpolator() {
     }
@@ -42,6 +41,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createLinearInterpolator();
+        return NativeInterpolatorFactory.createLinearInterpolator();
     }
 }
diff --git a/core/java/android/view/animation/OvershootInterpolator.java b/core/java/android/view/animation/OvershootInterpolator.java
index 306688a..e6445d7 100644
--- a/core/java/android/view/animation/OvershootInterpolator.java
+++ b/core/java/android/view/animation/OvershootInterpolator.java
@@ -20,19 +20,19 @@
 import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 
 import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
 
 /**
  * An interpolator where the change flings forward and overshoots the last value
  * then comes back.
  */
 @HasNativeInterpolator
-public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class OvershootInterpolator extends BaseInterpolator implements NativeInterpolator {
     private final float mTension;
 
     public OvershootInterpolator() {
@@ -76,6 +76,6 @@
     /** @hide */
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createOvershootInterpolator(mTension);
+        return NativeInterpolatorFactory.createOvershootInterpolator(mTension);
     }
 }
diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java
index 924437a..99d6b9c 100644
--- a/core/java/android/view/animation/PathInterpolator.java
+++ b/core/java/android/view/animation/PathInterpolator.java
@@ -20,14 +20,14 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Path;
+import android.graphics.animation.HasNativeInterpolator;
+import android.graphics.animation.NativeInterpolator;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.util.AttributeSet;
 import android.util.PathParser;
 import android.view.InflateException;
 
 import com.android.internal.R;
-import com.android.internal.view.animation.HasNativeInterpolator;
-import com.android.internal.view.animation.NativeInterpolatorFactory;
-import com.android.internal.view.animation.NativeInterpolatorFactoryHelper;
 
 /**
  * An interpolator that can traverse a Path that extends from <code>Point</code>
@@ -46,7 +46,7 @@
  * </pre></blockquote></p>
  */
 @HasNativeInterpolator
-public class PathInterpolator extends BaseInterpolator implements NativeInterpolatorFactory {
+public class PathInterpolator extends BaseInterpolator implements NativeInterpolator {
 
     // This governs how accurate the approximation of the Path is.
     private static final float PRECISION = 0.002f;
@@ -237,7 +237,7 @@
     /** @hide **/
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createPathInterpolator(mX, mY);
+        return NativeInterpolatorFactory.createPathInterpolator(mX, mY);
     }
 
 }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index c159f89..54bdb88 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -86,9 +86,10 @@
 import java.util.Objects;
 import java.util.Set;
 
-//TODO: use java.lang.ref.Cleaner once Android supports Java 9
 import sun.misc.Cleaner;
 
+//TODO: use java.lang.ref.Cleaner once Android supports Java 9
+
 /**
  * <p>The {@link AutofillManager} class provides ways for apps and custom views to
  * integrate with the Autofill Framework lifecycle.
@@ -547,7 +548,7 @@
          * @param fillInIntent The authentication fill-in intent.
          */
         void autofillClientAuthenticate(int authenticationId, IntentSender intent,
-                Intent fillInIntent);
+                Intent fillInIntent, boolean authenticateInline);
 
         /**
          * Tells the client this manager has state to be reset.
@@ -2070,7 +2071,7 @@
     }
 
     private void authenticate(int sessionId, int authenticationId, IntentSender intent,
-            Intent fillInIntent) {
+            Intent fillInIntent, boolean authenticateInline) {
         synchronized (mLock) {
             if (sessionId == mSessionId) {
                 final AutofillClient client = getClient();
@@ -2078,7 +2079,8 @@
                     // clear mOnInvisibleCalled and we will see if receive onInvisibleForAutofill()
                     // before onAuthenticationResult()
                     mOnInvisibleCalled = false;
-                    client.autofillClientAuthenticate(authenticationId, intent, fillInIntent);
+                    client.autofillClientAuthenticate(authenticationId, intent, fillInIntent,
+                            authenticateInline);
                 }
             }
         }
@@ -3250,10 +3252,11 @@
 
         @Override
         public void authenticate(int sessionId, int authenticationId, IntentSender intent,
-                Intent fillInIntent) {
+                Intent fillInIntent, boolean authenticateInline) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
-                afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent));
+                afm.post(() -> afm.authenticate(sessionId, authenticationId, intent, fillInIntent,
+                        authenticateInline));
             }
         }
 
@@ -3363,14 +3366,8 @@
             final AutofillManager afm = mAfm.get();
             if (afm == null) return null;
 
-            final AutofillClient client = afm.getClient();
-            if (client == null) {
-                Log.w(TAG, "getViewCoordinates(" + id + "): no autofill client");
-                return null;
-            }
-            final View view = client.autofillClientFindViewByAutofillIdTraversal(id);
+            final View view = getView(afm, id);
             if (view == null) {
-                Log.w(TAG, "getViewCoordinates(" + id + "): could not find view");
                 return null;
             }
             final Rect windowVisibleDisplayFrame = new Rect();
@@ -3411,5 +3408,42 @@
                 afm.post(() -> afm.requestHideFillUi(id, false));
             }
         }
+
+        @Override
+        public boolean requestAutofill(int sessionId, AutofillId id) {
+            final AutofillManager afm = mAfm.get();
+            if (afm == null || afm.mSessionId != sessionId) {
+                if (sDebug) {
+                    Slog.d(TAG, "Autofill not available or sessionId doesn't match");
+                }
+                return false;
+            }
+            final View view = getView(afm, id);
+            if (view == null || !view.isFocused()) {
+                if (sDebug) {
+                    Slog.d(TAG, "View not available or is not on focus");
+                }
+                return false;
+            }
+            if (sVerbose) {
+                Log.v(TAG, "requestAutofill() by AugmentedAutofillService.");
+            }
+            afm.post(() -> afm.requestAutofill(view));
+            return true;
+        }
+
+        @Nullable
+        private View getView(@NonNull AutofillManager afm, @NonNull AutofillId id) {
+            final AutofillClient client = afm.getClient();
+            if (client == null) {
+                Log.w(TAG, "getView(" + id + "): no autofill client");
+                return null;
+            }
+            View view = client.autofillClientFindViewByAutofillIdTraversal(id);
+            if (view == null) {
+                Log.w(TAG, "getView(" + id + "): could not find view");
+            }
+            return view;
+        }
     }
 }
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 140507c..03054df 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -50,4 +50,10 @@
       * Requests hiding the fill UI.
       */
     void requestHideFillUi(int sessionId, in AutofillId id);
+
+    /**
+      * Requests to start a new autofill flow. Returns true if the autofill request is made to
+      * {@link AutofillManager#requestAutofill(View)}.
+      */
+    boolean requestAutofill(int sessionId, in AutofillId id);
 }
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 84949c8..3903665 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -50,7 +50,7 @@
       * Authenticates a fill response or a data set.
       */
     void authenticate(int sessionId, int authenticationId, in IntentSender intent,
-            in Intent fillInIntent);
+            in Intent fillInIntent, boolean authenticateInline);
 
     /**
       * Sets the views to track. If saveOnAllViewsInvisible is set and all these view are invisible
diff --git a/core/java/android/view/inline/InlinePresentationSpec.java b/core/java/android/view/inline/InlinePresentationSpec.java
index a85b5f1..406e599 100644
--- a/core/java/android/view/inline/InlinePresentationSpec.java
+++ b/core/java/android/view/inline/InlinePresentationSpec.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.os.Parcelable;
 import android.util.Size;
 
@@ -55,6 +56,7 @@
     /**
      * @hide
      */
+    @SystemApi
     public @Nullable String getStyle() {
         return mStyle;
     }
@@ -281,10 +283,10 @@
     }
 
     @DataClass.Generated(
-            time = 1577145109444L,
+            time = 1581117017522L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inline/InlinePresentationSpec.java",
-            inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static  java.lang.String defaultStyle()\npublic @android.annotation.Nullable java.lang.String getStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "private final @android.annotation.NonNull android.util.Size mMinSize\nprivate final @android.annotation.NonNull android.util.Size mMaxSize\nprivate final @android.annotation.Nullable java.lang.String mStyle\nprivate static  java.lang.String defaultStyle()\npublic @android.annotation.SystemApi @android.annotation.Nullable java.lang.String getStyle()\nclass InlinePresentationSpec extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/inputmethod/InlineSuggestion.java b/core/java/android/view/inputmethod/InlineSuggestion.java
index a32ea4b..57269c4 100644
--- a/core/java/android/view/inputmethod/InlineSuggestion.java
+++ b/core/java/android/view/inputmethod/InlineSuggestion.java
@@ -286,7 +286,7 @@
     };
 
     @DataClass.Generated(
-            time = 1578972138081L,
+            time = 1581377984320L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestion.java",
             inputSignatures = "private static final  java.lang.String TAG\nprivate final @android.annotation.NonNull android.view.inputmethod.InlineSuggestionInfo mInfo\nprivate final @android.annotation.Nullable com.android.internal.view.inline.IInlineContentProvider mContentProvider\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestion newInlineSuggestion(android.view.inputmethod.InlineSuggestionInfo)\npublic  void inflate(android.content.Context,android.util.Size,java.util.concurrent.Executor,java.util.function.Consumer<android.view.View>)\nclass InlineSuggestion extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstDefs=true, genHiddenConstructor=true)")
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index 860ce90..be9370a 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityThread;
+import android.os.IBinder;
 import android.os.Parcelable;
 import android.view.inline.InlinePresentationSpec;
 
@@ -59,6 +60,14 @@
     private @NonNull String mHostPackageName;
 
     /**
+     * The host input token of the IME that made the request. This will be set by the system for
+     * safety reasons.
+     *
+     * @hide
+     */
+    private @Nullable IBinder mHostInputToken;
+
+    /**
      * @hide
      * @see {@link #mHostPackageName}.
      */
@@ -66,6 +75,14 @@
         mHostPackageName = hostPackageName;
     }
 
+    /**
+     * @hide
+     * @see {@link #mHostInputToken}.
+     */
+    public void setHostInputToken(IBinder hostInputToken) {
+        mHostInputToken = hostInputToken;
+    }
+
     private void onConstructed() {
         Preconditions.checkState(mMaxSuggestionCount >= mPresentationSpecs.size());
     }
@@ -78,11 +95,17 @@
         return ActivityThread.currentPackageName();
     }
 
+    private static IBinder defaultHostInputToken() {
+        return null;
+    }
+
     /** @hide */
     abstract static class BaseBuilder {
         abstract Builder setPresentationSpecs(@NonNull List<InlinePresentationSpec> value);
 
         abstract Builder setHostPackageName(@Nullable String value);
+
+        abstract Builder setHostInputToken(IBinder hostInputToken);
     }
 
 
@@ -104,7 +127,8 @@
     /* package-private */ InlineSuggestionsRequest(
             int maxSuggestionCount,
             @NonNull List<InlinePresentationSpec> presentationSpecs,
-            @NonNull String hostPackageName) {
+            @NonNull String hostPackageName,
+            @Nullable IBinder hostInputToken) {
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
         com.android.internal.util.AnnotationValidations.validate(
@@ -112,6 +136,7 @@
         this.mHostPackageName = hostPackageName;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mHostPackageName);
+        this.mHostInputToken = hostInputToken;
 
         onConstructed();
     }
@@ -145,6 +170,17 @@
         return mHostPackageName;
     }
 
+    /**
+     * The host input token of the IME that made the request. This will be set by the system for
+     * safety reasons.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable IBinder getHostInputToken() {
+        return mHostInputToken;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -154,7 +190,8 @@
         return "InlineSuggestionsRequest { " +
                 "maxSuggestionCount = " + mMaxSuggestionCount + ", " +
                 "presentationSpecs = " + mPresentationSpecs + ", " +
-                "hostPackageName = " + mHostPackageName +
+                "hostPackageName = " + mHostPackageName + ", " +
+                "hostInputToken = " + mHostInputToken +
         " }";
     }
 
@@ -173,7 +210,8 @@
         return true
                 && mMaxSuggestionCount == that.mMaxSuggestionCount
                 && java.util.Objects.equals(mPresentationSpecs, that.mPresentationSpecs)
-                && java.util.Objects.equals(mHostPackageName, that.mHostPackageName);
+                && java.util.Objects.equals(mHostPackageName, that.mHostPackageName)
+                && java.util.Objects.equals(mHostInputToken, that.mHostInputToken);
     }
 
     @Override
@@ -186,6 +224,7 @@
         _hash = 31 * _hash + mMaxSuggestionCount;
         _hash = 31 * _hash + java.util.Objects.hashCode(mPresentationSpecs);
         _hash = 31 * _hash + java.util.Objects.hashCode(mHostPackageName);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mHostInputToken);
         return _hash;
     }
 
@@ -195,9 +234,13 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        byte flg = 0;
+        if (mHostInputToken != null) flg |= 0x8;
+        dest.writeByte(flg);
         dest.writeInt(mMaxSuggestionCount);
         dest.writeParcelableList(mPresentationSpecs, flags);
         dest.writeString(mHostPackageName);
+        if (mHostInputToken != null) dest.writeStrongBinder(mHostInputToken);
     }
 
     @Override
@@ -211,10 +254,12 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        byte flg = in.readByte();
         int maxSuggestionCount = in.readInt();
         List<InlinePresentationSpec> presentationSpecs = new ArrayList<>();
         in.readParcelableList(presentationSpecs, InlinePresentationSpec.class.getClassLoader());
         String hostPackageName = in.readString();
+        IBinder hostInputToken = (flg & 0x8) == 0 ? null : in.readStrongBinder();
 
         this.mMaxSuggestionCount = maxSuggestionCount;
         this.mPresentationSpecs = presentationSpecs;
@@ -223,6 +268,7 @@
         this.mHostPackageName = hostPackageName;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mHostPackageName);
+        this.mHostInputToken = hostInputToken;
 
         onConstructed();
     }
@@ -251,6 +297,7 @@
         private int mMaxSuggestionCount;
         private @NonNull List<InlinePresentationSpec> mPresentationSpecs;
         private @NonNull String mHostPackageName;
+        private @Nullable IBinder mHostInputToken;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -320,10 +367,25 @@
             return this;
         }
 
+        /**
+         * The host input token of the IME that made the request. This will be set by the system for
+         * safety reasons.
+         *
+         * @hide
+         */
+        @DataClass.Generated.Member
+        @Override
+        @NonNull Builder setHostInputToken(@Nullable IBinder value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x8;
+            mHostInputToken = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull InlineSuggestionsRequest build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x8; // Mark builder used
+            mBuilderFieldsSet |= 0x10; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mMaxSuggestionCount = defaultMaxSuggestionCount();
@@ -331,15 +393,19 @@
             if ((mBuilderFieldsSet & 0x4) == 0) {
                 mHostPackageName = defaultHostPackageName();
             }
+            if ((mBuilderFieldsSet & 0x8) == 0) {
+                mHostInputToken = defaultHostInputToken();
+            }
             InlineSuggestionsRequest o = new InlineSuggestionsRequest(
                     mMaxSuggestionCount,
                     mPresentationSpecs,
-                    mHostPackageName);
+                    mHostPackageName,
+                    mHostInputToken);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x8) != 0) {
+            if ((mBuilderFieldsSet & 0x10) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -347,10 +413,10 @@
     }
 
     @DataClass.Generated(
-            time = 1578948035951L,
+            time = 1581555687721L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
-            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\npublic  void setHostPackageName(java.lang.String)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "public static final  int SUGGESTION_COUNT_UNLIMITED\nprivate final  int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.view.inline.InlinePresentationSpec> mPresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\npublic  void setHostPackageName(java.lang.String)\npublic  void setHostInputToken(android.os.IBinder)\nprivate  void onConstructed()\nprivate static  int defaultMaxSuggestionCount()\nprivate static  java.lang.String defaultHostPackageName()\nprivate static  android.os.IBinder defaultHostInputToken()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setPresentationSpecs(java.util.List<android.view.inline.InlinePresentationSpec>)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract  android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 469ab2e..0182975 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -8282,9 +8282,9 @@
         if (getKeyListener() != null && !mSingleLine && mEditor != null
                 && (mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS)
                         == EditorInfo.TYPE_CLASS_TEXT) {
-            int variation = mEditor.mInputType & EditorInfo.TYPE_MASK_VARIATION;
-            if (variation == EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE
-                    || variation == EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) {
+            int multilineFlags = EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE
+                    | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE;
+            if ((mEditor.mInputType & multilineFlags) != 0) {
                 return false;
             }
         }
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
index 78e8518..efcd54f 100644
--- a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -61,6 +61,7 @@
     private Set<Integer> mLoadedPages;
     private final UserHandle mPersonalProfileUserHandle;
     private final UserHandle mWorkProfileUserHandle;
+    private Injector mInjector;
 
     AbstractMultiProfilePagerAdapter(Context context, int currentPage,
             UserHandle personalProfileUserHandle,
@@ -70,6 +71,33 @@
         mLoadedPages = new HashSet<>();
         mPersonalProfileUserHandle = personalProfileUserHandle;
         mWorkProfileUserHandle = workProfileUserHandle;
+        UserManager userManager = context.getSystemService(UserManager.class);
+        mInjector = new Injector() {
+            @Override
+            public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+                    int targetUserId) {
+                return AbstractMultiProfilePagerAdapter.this
+                        .hasCrossProfileIntents(intents, sourceUserId, targetUserId);
+            }
+
+            @Override
+            public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+                return userManager.isQuietModeEnabled(workProfileUserHandle);
+            }
+
+            @Override
+            public void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle) {
+                userManager.requestQuietModeEnabled(enabled, workProfileUserHandle);
+            }
+        };
+    }
+
+    /**
+     * Overrides the default {@link Injector} for testing purposes.
+     */
+    @VisibleForTesting
+    public void setInjector(Injector injector) {
+        mInjector = injector;
     }
 
     void setOnProfileSelectedListener(OnProfileSelectedListener listener) {
@@ -252,19 +280,18 @@
 
     private boolean rebuildTab(ResolverListAdapter activeListAdapter, boolean doPostProcessing) {
         UserHandle listUserHandle = activeListAdapter.getUserHandle();
-        UserManager userManager = mContext.getSystemService(UserManager.class);
         if (listUserHandle == mWorkProfileUserHandle
-                && userManager.isQuietModeEnabled(mWorkProfileUserHandle)) {
+                && mInjector.isQuietModeEnabled(mWorkProfileUserHandle)) {
             showEmptyState(activeListAdapter,
                     R.drawable.ic_work_apps_off,
                     R.string.resolver_turn_on_work_apps,
                     R.string.resolver_turn_on_work_apps_explanation,
                     (View.OnClickListener) v ->
-                            userManager.requestQuietModeEnabled(false, mWorkProfileUserHandle));
+                            mInjector.requestQuietModeEnabled(false, mWorkProfileUserHandle));
             return false;
         }
         if (UserHandle.myUserId() != listUserHandle.getIdentifier()) {
-            if (!hasCrossProfileIntents(activeListAdapter.getIntents(),
+            if (!mInjector.hasCrossProfileIntents(activeListAdapter.getIntents(),
                     UserHandle.myUserId(), listUserHandle.getIdentifier())) {
                 if (listUserHandle == mPersonalProfileUserHandle) {
                     showEmptyState(activeListAdapter,
@@ -366,4 +393,26 @@
          */
         void onProfileSelected(int profileIndex);
     }
+
+    /**
+     * Describes an injector to be used for cross profile functionality. Overridable for testing.
+     */
+    @VisibleForTesting
+    public interface Injector {
+        /**
+         * Returns {@code true} if at least one of the provided {@code intents} can be forwarded
+         * from {@code sourceUserId} to {@code targetUserId}.
+         */
+        boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId, int targetUserId);
+
+        /**
+         * Returns whether the given profile is in quiet mode or not.
+         */
+        boolean isQuietModeEnabled(UserHandle workProfileUserHandle);
+
+        /**
+         * Enables or disables quiet mode for a managed profile.
+         */
+        void requestQuietModeEnabled(boolean enabled, UserHandle workProfileUserHandle);
+    }
 }
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index dabaf5a..1c1c254 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -18,6 +18,8 @@
 
 import android.app.AppOpsManager;
 import android.app.AsyncNotedAppOp;
+import android.app.SyncNotedAppOp;
+import android.app.RuntimeAppOpAccessMessage;
 import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
 import android.os.RemoteCallback;
@@ -25,6 +27,7 @@
 import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsAsyncNotedCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
+import com.android.internal.app.MessageSamplingConfig;
 
 interface IAppOpsService {
     // These methods are also called by native code, so must
@@ -54,6 +57,9 @@
 
     // Remaining methods are only used in Java.
     int checkPackage(int uid, String packageName);
+    RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage();
+    MessageSamplingConfig reportRuntimeAppOpAccessMessageAndGetConfig(String packageName,
+            in SyncNotedAppOp appOp, String message);
     @UnsupportedAppUsage
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
     @UnsupportedAppUsage
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/core/java/com/android/internal/app/MessageSamplingConfig.aidl
similarity index 75%
rename from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
rename to core/java/com/android/internal/app/MessageSamplingConfig.aidl
index fcacd52..ab89ca2 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
+++ b/core/java/com/android/internal/app/MessageSamplingConfig.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.view.animation;
+package com.android.internal.app;
 
-public interface NativeInterpolatorFactory {
-    long createNativeInterpolator();
-}
+parcelable MessageSamplingConfig;
diff --git a/core/java/com/android/internal/app/MessageSamplingConfig.java b/core/java/com/android/internal/app/MessageSamplingConfig.java
new file mode 100644
index 0000000..5300c1c
--- /dev/null
+++ b/core/java/com/android/internal/app/MessageSamplingConfig.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.DataClass;
+
+/**
+ * Configuration for AppOps access messages sampling.
+ */
+@Immutable
+/*@DataClass*/
+public final class MessageSamplingConfig implements Parcelable {
+    /** Op code targeted during current sampling session */
+    private final @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int mSampledOpCode;
+    /** Range of ops which should be reported during current sampling session */
+    private final @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int mAcceptableLeftDistance;
+    /** Expiration time for this sampling config */
+    private final @IntRange(from = 0L) long mExpirationTimeSinceBootMillis;
+
+
+
+
+
+
+
+    // Code below generated by codegen v1.0.14.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/com/android/internal/app/MessageSamplingConfig.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new MessageSamplingConfig.
+     *
+     * @param sampledOpCode
+     *   Op code targeted during current sampling session
+     * @param acceptableLeftDistance
+     *   Range of ops which should be reported during current sampling session
+     * @param expirationTimeSinceBootMillis
+     *   Expiration time for this sampling config
+     */
+    @DataClass.Generated.Member
+    public MessageSamplingConfig(
+            @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int sampledOpCode,
+            @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int acceptableLeftDistance,
+            @IntRange(from = 0L) long expirationTimeSinceBootMillis) {
+        this.mSampledOpCode = sampledOpCode;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mSampledOpCode,
+                "from", -1L,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mAcceptableLeftDistance = acceptableLeftDistance;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mAcceptableLeftDistance,
+                "from", 0L,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mExpirationTimeSinceBootMillis = expirationTimeSinceBootMillis;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mExpirationTimeSinceBootMillis,
+                "from", 0L);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Op code targeted during current sampling session
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = -1L, to = AppOpsManager._NUM_OP - 1) int getSampledOpCode() {
+        return mSampledOpCode;
+    }
+
+    /**
+     * Range of ops which should be reported during current sampling session
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0L, to = AppOpsManager._NUM_OP - 1) int getAcceptableLeftDistance() {
+        return mAcceptableLeftDistance;
+    }
+
+    /**
+     * Expiration time for this sampling config
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0L) long getExpirationTimeSinceBootMillis() {
+        return mExpirationTimeSinceBootMillis;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeInt(mSampledOpCode);
+        dest.writeInt(mAcceptableLeftDistance);
+        dest.writeLong(mExpirationTimeSinceBootMillis);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ MessageSamplingConfig(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        int sampledOpCode = in.readInt();
+        int acceptableLeftDistance = in.readInt();
+        long expirationTimeSinceBootMillis = in.readLong();
+
+        this.mSampledOpCode = sampledOpCode;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mSampledOpCode,
+                "from", -1L,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mAcceptableLeftDistance = acceptableLeftDistance;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mAcceptableLeftDistance,
+                "from", 0L,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mExpirationTimeSinceBootMillis = expirationTimeSinceBootMillis;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mExpirationTimeSinceBootMillis,
+                "from", 0L);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<MessageSamplingConfig> CREATOR
+            = new Parcelable.Creator<MessageSamplingConfig>() {
+        @Override
+        public MessageSamplingConfig[] newArray(int size) {
+            return new MessageSamplingConfig[size];
+        }
+
+        @Override
+        public MessageSamplingConfig createFromParcel(@NonNull Parcel in) {
+            return new MessageSamplingConfig(in);
+        }
+    };
+
+    /*@DataClass.Generated(
+            time = 1580691255495L,
+            codegenVersion = "1.0.14",
+            sourceFile = "frameworks/base/core/java/com/android/internal/app/MessageSamplingConfig.java",
+            inputSignatures = "private final @android.annotation.IntRange(from=-1L, to=AppOpsManager._NUM_OP - 1) int mSampledOpCode\nprivate final @android.annotation.IntRange(from=0L, to=AppOpsManager._NUM_OP - 1) int mAcceptableLeftDistance\nprivate final @android.annotation.IntRange(from=0L) long mExpirationTimeSinceBootMillis\nclass MessageSamplingConfig extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")*/
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 5565864..94924a5 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -24,7 +24,6 @@
 import android.net.Credentials;
 import android.net.LocalServerSocket;
 import android.net.LocalSocket;
-import android.os.Build;
 import android.os.FactoryTest;
 import android.os.IVold;
 import android.os.Process;
@@ -291,7 +290,7 @@
     static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
             int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
             int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
-            int targetSdkVersion, boolean isTopApp, String[] pkgDataInfoList) {
+            boolean isTopApp, String[] pkgDataInfoList) {
         ZygoteHooks.preFork();
 
         int pid = nativeForkAndSpecialize(
@@ -299,8 +298,6 @@
                 fdsToIgnore, startChildZygote, instructionSet, appDataDir, isTopApp,
                 pkgDataInfoList);
         if (pid == 0) {
-            Zygote.disableExecuteOnly(targetSdkVersion);
-
             // Note that this event ends at the end of handleChildProc,
             Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
         }
@@ -696,8 +693,6 @@
                                  args.mInstructionSet, args.mAppDataDir, args.mIsTopApp,
                                  args.mPkgDataInfoList);
 
-            disableExecuteOnly(args.mTargetSdkVersion);
-
             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
 
             return ZygoteInit.zygoteInit(args.mTargetSdkVersion,
@@ -777,17 +772,6 @@
     }
 
     /**
-     * Mark execute-only segments of libraries read+execute for apps with targetSdkVersion<Q.
-     */
-    private static void disableExecuteOnly(int targetSdkVersion) {
-        if ((targetSdkVersion < Build.VERSION_CODES.Q) && !nativeDisableExecuteOnly()) {
-            Log.e("Zygote", "Failed to set libraries to read+execute.");
-        }
-    }
-
-    private static native boolean nativeDisableExecuteOnly();
-
-    /**
      * @return  Raw file descriptors for the read-end of USAP reporting pipes.
      */
     static int[] getUsapPipeFDs() {
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index c91c661..4949811 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -257,8 +257,8 @@
         pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
                 parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
                 parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
-                parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion,
-                parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList);
+                parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
+                parsedArgs.mPkgDataInfoList);
 
         try {
             if (pid == 0) {
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index adb4036..36025e3 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1627,9 +1627,12 @@
 
         int opacity = PixelFormat.OPAQUE;
         final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
+        // TODO(b/149585281) remove when root task has the correct bounds for freeform
+        final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor
+                && winConfig.getWindowingMode() != WINDOWING_MODE_FREEFORM;
         // If we draw shadows in the compositor we don't need to force the surface to be
         // translucent.
-        if (winConfig.hasWindowShadow() && !mWindow.mRenderShadowsInCompositor) {
+        if (winConfig.hasWindowShadow() && !renderShadowsInCompositor) {
             // If the window has a shadow, it must be translucent.
             opacity = PixelFormat.TRANSLUCENT;
         } else{
@@ -2414,16 +2417,18 @@
     }
 
     private void updateElevation() {
+        final int windowingMode =
+                getResources().getConfiguration().windowConfiguration.getWindowingMode();
+        final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor
+                && windowingMode != WINDOWING_MODE_FREEFORM;
         // If rendering shadows in the compositor, don't set an elevation on the view
-        if (mWindow.mRenderShadowsInCompositor) {
+        if (renderShadowsInCompositor) {
             return;
         }
         float elevation = 0;
         final boolean wasAdjustedForStack = mElevationAdjustedForStack;
         // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
         // since the shadow is bound to the content size and not the target size.
-        final int windowingMode =
-                getResources().getConfiguration().windowConfiguration.getWindowingMode();
         if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
             elevation = hasWindowFocus() ?
                     DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
diff --git a/core/java/com/android/internal/policy/DockedDividerUtils.java b/core/java/com/android/internal/policy/DockedDividerUtils.java
index c68e506..b61b9de 100644
--- a/core/java/com/android/internal/policy/DockedDividerUtils.java
+++ b/core/java/com/android/internal/policy/DockedDividerUtils.java
@@ -16,14 +16,15 @@
 
 package com.android.internal.policy;
 
-import android.graphics.Rect;
-
 import static android.view.WindowManager.DOCKED_BOTTOM;
 import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 
+import android.content.res.Resources;
+import android.graphics.Rect;
+
 /**
  * Utility functions for docked stack divider used by both window manager and System UI.
  *
@@ -105,23 +106,6 @@
         return start + (end - start) / 2 - dividerSize / 2;
     }
 
-    public static int getDockSideFromCreatedMode(boolean dockOnTopOrLeft,
-            boolean isHorizontalDivision) {
-        if (dockOnTopOrLeft) {
-            if (isHorizontalDivision) {
-                return DOCKED_TOP;
-            } else {
-                return DOCKED_LEFT;
-            }
-        } else {
-            if (isHorizontalDivision) {
-                return DOCKED_BOTTOM;
-            } else {
-                return DOCKED_RIGHT;
-            }
-        }
-    }
-
     public static int invertDockSide(int dockSide) {
         switch (dockSide) {
             case DOCKED_LEFT:
@@ -136,4 +120,21 @@
                 return DOCKED_INVALID;
         }
     }
+
+    /** Returns the inset distance from the divider window edge to the dividerview. */
+    public static int getDividerInsets(Resources res) {
+        return res.getDimensionPixelSize(com.android.internal.R.dimen.docked_stack_divider_insets);
+    }
+
+    /** Returns the size of the divider */
+    public static int getDividerSize(Resources res, int dividerInsets) {
+        final int windowWidth = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.docked_stack_divider_thickness);
+        return windowWidth - 2 * dividerInsets;
+    }
+
+    /** Returns the docked-stack side */
+    public static int getDockSide(int displayWidth, int displayHeight) {
+        return displayWidth > displayHeight ? DOCKED_LEFT : DOCKED_TOP;
+    }
 }
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java b/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
deleted file mode 100644
index ebeec40..0000000
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactoryHelper.java
+++ /dev/null
@@ -1,37 +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.internal.view.animation;
-
-/**
- * Static utility class for constructing native interpolators to keep the
- * JNI simpler
- */
-public final class NativeInterpolatorFactoryHelper {
-    private NativeInterpolatorFactoryHelper() {}
-
-    public static native long createAccelerateDecelerateInterpolator();
-    public static native long createAccelerateInterpolator(float factor);
-    public static native long createAnticipateInterpolator(float tension);
-    public static native long createAnticipateOvershootInterpolator(float tension);
-    public static native long createBounceInterpolator();
-    public static native long createCycleInterpolator(float cycles);
-    public static native long createDecelerateInterpolator(float factor);
-    public static native long createLinearInterpolator();
-    public static native long createOvershootInterpolator(float tension);
-    public static native long createPathInterpolator(float[] x, float[] y);
-    public static native long createLutInterpolator(float[] values);
-}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 30d0d1d..653cbc9 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -42,9 +42,7 @@
         "android_util_StringBlock.cpp",
         "android_util_XmlBlock.cpp",
         "android_util_jar_StrictJarFile.cpp",
-        "android_view_RenderNodeAnimator.cpp",
         "com_android_internal_util_VirtualRefBasePtr.cpp",
-        "com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp",
     ],
 
     include_dirs: [
@@ -236,6 +234,7 @@
                 "libui",
                 "libgraphicsenv",
                 "libgui",
+                "libmediandk",
                 "libsensor",
                 "libinput",
                 "libcamera_client",
@@ -351,6 +350,8 @@
         "android/graphics/apex/android_paint.cpp",
         "android/graphics/apex/android_region.cpp",
 
+        "android_graphics_animation_NativeInterpolatorFactory.cpp",
+        "android_graphics_animation_RenderNodeAnimator.cpp",
         "android_graphics_Canvas.cpp",
         "android_graphics_ColorSpace.cpp",
         "android_graphics_drawable_AnimatedVectorDrawable.cpp",
@@ -449,9 +450,10 @@
             ],
             shared_libs: [
                 "libandroidfw",
+                "libEGL",
+                "libmediandk",
                 "libnativedisplay",
                 "libnativewindow",
-                "libgui",
                 "libpdfium",
             ],
             static_libs: [
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 19f6e27..bbc23fe 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -119,13 +119,11 @@
 extern int register_android_view_DisplayEventReceiver(JNIEnv* env);
 extern int register_android_view_InputApplicationHandle(JNIEnv* env);
 extern int register_android_view_InputWindowHandle(JNIEnv* env);
-extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_SurfaceControl(JNIEnv* env);
 extern int register_android_view_SurfaceSession(JNIEnv* env);
 extern int register_android_view_CompositionSamplingListener(JNIEnv* env);
 extern int register_android_view_TextureView(JNIEnv* env);
-extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
 extern int register_android_database_SQLiteConnection(JNIEnv* env);
 extern int register_android_database_SQLiteGlobal(JNIEnv* env);
@@ -1461,7 +1459,6 @@
         REG_JNI(register_android_os_VintfRuntimeInfo),
         REG_JNI(register_android_service_DataLoaderService),
         REG_JNI(register_android_view_DisplayEventReceiver),
-        REG_JNI(register_android_view_RenderNodeAnimator),
         REG_JNI(register_android_view_InputApplicationHandle),
         REG_JNI(register_android_view_InputWindowHandle),
         REG_JNI(register_android_view_Surface),
@@ -1469,7 +1466,6 @@
         REG_JNI(register_android_view_SurfaceSession),
         REG_JNI(register_android_view_CompositionSamplingListener),
         REG_JNI(register_android_view_TextureView),
-        REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),
         REG_JNI(register_com_google_android_gles_jni_EGLImpl),
         REG_JNI(register_com_google_android_gles_jni_GLImpl),
         REG_JNI(register_android_opengl_jni_EGL14),
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 7ceef98..7ee509b 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -65,6 +65,8 @@
 extern int register_android_graphics_PathMeasure(JNIEnv* env);
 extern int register_android_graphics_Picture(JNIEnv* env);
 extern int register_android_graphics_Region(JNIEnv* env);
+extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
+extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
 extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
 extern int register_android_graphics_fonts_Font(JNIEnv* env);
@@ -82,10 +84,8 @@
 extern int register_android_util_PathParser(JNIEnv* env);
 extern int register_android_util_jar_StrictJarFile(JNIEnv* env);
 extern int register_android_view_RenderNode(JNIEnv* env);
-extern int register_android_view_RenderNodeAnimator(JNIEnv* env);
 extern int register_android_view_DisplayListCanvas(JNIEnv* env);
 extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
-extern int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv *env);
 
 #define REG_JNI(name)      { name }
 struct RegJNIRec {
@@ -130,6 +130,10 @@
         {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
         {"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
         {"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+        {"android.graphics.animation.NativeInterpolatorFactory",
+         REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
+        {"android.graphics.animation.RenderNodeAnimator",
+         REG_JNI(register_android_graphics_animation_RenderNodeAnimator)},
         {"android.graphics.drawable.AnimatedVectorDrawable",
          REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable)},
         {"android.graphics.drawable.VectorDrawable",
@@ -151,11 +155,8 @@
         {"android.util.Log", REG_JNI(register_android_util_Log)},
         {"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
         {"android.util.jar.StrictJarFile", REG_JNI(register_android_util_jar_StrictJarFile)},
-        {"android.view.RenderNodeAnimator", REG_JNI(register_android_view_RenderNodeAnimator)},
         {"com.android.internal.util.VirtualRefBasePtr",
          REG_JNI(register_com_android_internal_util_VirtualRefBasePtr)},
-        {"com.android.internal.view.animation.NativeInterpolatorFactoryHelper",
-         REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper)},
 };
 // Vector to store the names of classes that need delegates of their native methods
 static vector<string> classesToDelegate;
diff --git a/core/jni/android/graphics/apex/jni_runtime.cpp b/core/jni/android/graphics/apex/jni_runtime.cpp
index 1f66153..35c997d 100644
--- a/core/jni/android/graphics/apex/jni_runtime.cpp
+++ b/core/jni/android/graphics/apex/jni_runtime.cpp
@@ -60,6 +60,8 @@
 extern int register_android_graphics_Picture(JNIEnv*);
 extern int register_android_graphics_Region(JNIEnv* env);
 extern int register_android_graphics_SurfaceTexture(JNIEnv* env);
+extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
+extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
 extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
 extern int register_android_graphics_drawable_VectorDrawable(JNIEnv* env);
 extern int register_android_graphics_fonts_Font(JNIEnv* env);
@@ -123,6 +125,8 @@
     REG_JNI(register_android_graphics_SurfaceTexture),
     REG_JNI(register_android_graphics_Typeface),
     REG_JNI(register_android_graphics_YuvImage),
+    REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory),
+    REG_JNI(register_android_graphics_animation_RenderNodeAnimator),
     REG_JNI(register_android_graphics_drawable_AnimatedVectorDrawable),
     REG_JNI(register_android_graphics_drawable_VectorDrawable),
     REG_JNI(register_android_graphics_fonts_Font),
diff --git a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp b/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
similarity index 95%
rename from core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
rename to core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
index f4d2e7b..2073ac2 100644
--- a/core/jni/com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp
+++ b/core/jni/android_graphics_animation_NativeInterpolatorFactory.cpp
@@ -90,7 +90,7 @@
 // JNI Glue
 // ----------------------------------------------------------------------------
 
-const char* const kClassPathName = "com/android/internal/view/animation/NativeInterpolatorFactoryHelper";
+const char* const kClassPathName = "android/graphics/animation/NativeInterpolatorFactory";
 
 static const JNINativeMethod gMethods[] = {
     { "createAccelerateDecelerateInterpolator", "()J", (void*) createAccelerateDecelerateInterpolator },
@@ -106,7 +106,7 @@
     { "createLutInterpolator", "([F)J", (void*) createLutInterpolator },
 };
 
-int register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper(JNIEnv* env) {
+int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env) {
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/android_view_RenderNodeAnimator.cpp b/core/jni/android_graphics_animation_RenderNodeAnimator.cpp
similarity index 96%
rename from core/jni/android_view_RenderNodeAnimator.cpp
rename to core/jni/android_graphics_animation_RenderNodeAnimator.cpp
index ca32b00..878d4fc 100644
--- a/core/jni/android_view_RenderNodeAnimator.cpp
+++ b/core/jni/android_graphics_animation_RenderNodeAnimator.cpp
@@ -190,7 +190,7 @@
 // JNI Glue
 // ----------------------------------------------------------------------------
 
-const char* const kClassPathName = "android/view/RenderNodeAnimator";
+const char* const kClassPathName = "android/graphics/animation/RenderNodeAnimator";
 
 static const JNINativeMethod gMethods[] = {
     { "nCreateAnimator", "(IF)J", (void*) createAnimator },
@@ -203,12 +203,12 @@
     { "nSetStartDelay", "(JJ)V", (void*) setStartDelay },
     { "nSetInterpolator", "(JJ)V", (void*) setInterpolator },
     { "nSetAllowRunningAsync", "(JZ)V", (void*) setAllowRunningAsync },
-    { "nSetListener", "(JLandroid/view/RenderNodeAnimator;)V", (void*) setListener},
+    { "nSetListener", "(JLandroid/graphics/animation/RenderNodeAnimator;)V", (void*) setListener},
     { "nStart", "(J)V", (void*) start},
     { "nEnd", "(J)V", (void*) end },
 };
 
-int register_android_view_RenderNodeAnimator(JNIEnv* env) {
+int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env) {
     sLifecycleChecker.incStrong(0);
     gRenderNodeAnimatorClassInfo.clazz = FindClassOrDie(env, kClassPathName);
     gRenderNodeAnimatorClassInfo.clazz = MakeGlobalRefOrDie(env,
@@ -216,7 +216,7 @@
 
     gRenderNodeAnimatorClassInfo.callOnFinished = GetStaticMethodIDOrDie(
             env, gRenderNodeAnimatorClassInfo.clazz, "callOnFinished",
-            "(Landroid/view/RenderNodeAnimator;)V");
+            "(Landroid/graphics/animation/RenderNodeAnimator;)V");
 
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 87be5f6..d8f30e94 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -566,10 +566,10 @@
     return (jint) status;
 }
 
-static jint
-android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state)
-{
-    return (jint) check_AudioSystem_Command(AudioSystem::setPhoneState((audio_mode_t) state));
+static jint android_media_AudioSystem_setPhoneState(JNIEnv *env, jobject thiz, jint state,
+                                                    jint uid) {
+    return (jint)check_AudioSystem_Command(
+            AudioSystem::setPhoneState((audio_mode_t)state, (uid_t)uid));
 }
 
 static jint
@@ -2434,7 +2434,7 @@
           (void *)android_media_AudioSystem_getDeviceConnectionState},
          {"handleDeviceConfigChange", "(ILjava/lang/String;Ljava/lang/String;I)I",
           (void *)android_media_AudioSystem_handleDeviceConfigChange},
-         {"setPhoneState", "(I)I", (void *)android_media_AudioSystem_setPhoneState},
+         {"setPhoneState", "(II)I", (void *)android_media_AudioSystem_setPhoneState},
          {"setForceUse", "(II)I", (void *)android_media_AudioSystem_setForceUse},
          {"getForceUse", "(I)I", (void *)android_media_AudioSystem_getForceUse},
          {"initStreamVolume", "(III)I", (void *)android_media_AudioSystem_initStreamVolume},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index f564d75..ce9a048 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -65,6 +65,7 @@
 static struct {
     jclass clazz;
     jmethodID ctor;
+    jfieldID isInternal;
     jfieldID density;
     jfieldID secure;
 } gDisplayInfoClassInfo;
@@ -781,6 +782,8 @@
     }
 
     jobject object = env->NewObject(gDisplayInfoClassInfo.clazz, gDisplayInfoClassInfo.ctor);
+    env->SetBooleanField(object, gDisplayInfoClassInfo.isInternal,
+                         info.connectionType == DisplayConnectionType::Internal);
     env->SetFloatField(object, gDisplayInfoClassInfo.density, info.density);
     env->SetBooleanField(object, gDisplayInfoClassInfo.secure, info.secure);
     return object;
@@ -1528,6 +1531,7 @@
     jclass infoClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayInfo");
     gDisplayInfoClassInfo.clazz = MakeGlobalRefOrDie(env, infoClazz);
     gDisplayInfoClassInfo.ctor = GetMethodIDOrDie(env, infoClazz, "<init>", "()V");
+    gDisplayInfoClassInfo.isInternal = GetFieldIDOrDie(env, infoClazz, "isInternal", "Z");
     gDisplayInfoClassInfo.density = GetFieldIDOrDie(env, infoClazz, "density", "F");
     gDisplayInfoClassInfo.secure = GetFieldIDOrDie(env, infoClazz, "secure", "Z");
 
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 5a8225c..27c5a73 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -17,40 +17,34 @@
 #define LOG_TAG "ThreadedRenderer"
 #define ATRACE_TAG ATRACE_TAG_VIEW
 
-#include <algorithm>
-#include <atomic>
-#include <inttypes.h>
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-#include <GraphicsJNI.h>
-
-#include <gui/BufferItemConsumer.h>
-#include <gui/BufferQueue.h>
-#include <gui/Surface.h>
-
-#include "android_graphics_HardwareRendererObserver.h"
-
-#include <private/EGL/cache.h>
-
-#include <utils/RefBase.h>
-#include <utils/StrongPointer.h>
-#include <utils/Timers.h>
-#include <utils/TraceUtils.h>
-#include <android_runtime/android_view_Surface.h>
-#include <system/window.h>
-
 #include <FrameInfo.h>
+#include <GraphicsJNI.h>
 #include <Picture.h>
 #include <Properties.h>
 #include <RootRenderNode.h>
+#include <dlfcn.h>
+#include <inttypes.h>
+#include <media/NdkImage.h>
+#include <media/NdkImageReader.h>
+#include <nativehelper/JNIHelp.h>
+#include <pipeline/skia/ShaderCache.h>
+#include <private/EGL/cache.h>
 #include <renderthread/CanvasContext.h>
 #include <renderthread/RenderProxy.h>
 #include <renderthread/RenderTask.h>
 #include <renderthread/RenderThread.h>
-#include <pipeline/skia/ShaderCache.h>
 #include <utils/Color.h>
+#include <utils/RefBase.h>
+#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
+#include <utils/TraceUtils.h>
+
+#include <algorithm>
+#include <atomic>
+
+#include "android_graphics_HardwareRendererObserver.h"
+#include "core_jni_helpers.h"
+#include "jni.h"
 
 namespace android {
 
@@ -78,6 +72,9 @@
     return env;
 }
 
+typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface);
+ANW_fromSurface fromSurface;
+
 class JvmErrorReporter : public ErrorHandler {
 public:
     JvmErrorReporter(JNIEnv* env) {
@@ -178,9 +175,9 @@
 static void android_view_ThreadedRenderer_setSurface(JNIEnv* env, jobject clazz,
         jlong proxyPtr, jobject jsurface, jboolean discardBuffer) {
     RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
-    sp<Surface> surface;
+    ANativeWindow* window = nullptr;
     if (jsurface) {
-        surface = android_view_Surface_getSurface(env, jsurface);
+        window = fromSurface(env, jsurface);
     }
     bool enableTimeout = true;
     if (discardBuffer) {
@@ -188,7 +185,7 @@
         enableTimeout = false;
         proxy->setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
     }
-    proxy->setSurface(surface, enableTimeout);
+    proxy->setSurface(window, enableTimeout);
 }
 
 static jboolean android_view_ThreadedRenderer_pause(JNIEnv* env, jobject clazz,
@@ -458,8 +455,10 @@
         jint right, jint bottom, jlong bitmapPtr) {
     SkBitmap bitmap;
     bitmap::toBitmap(bitmapPtr).getSkBitmap(&bitmap);
-    sp<Surface> surface = android_view_Surface_getSurface(env, jsurface);
-    return RenderProxy::copySurfaceInto(surface, left, top, right, bottom, &bitmap);
+    ANativeWindow* window = fromSurface(env, jsurface);
+    jint result = RenderProxy::copySurfaceInto(window, left, top, right, bottom, &bitmap);
+    ANativeWindow_release(window);
+    return result;
 }
 
 class ContextFactory : public IContextFactory {
@@ -480,23 +479,35 @@
     uint32_t width = jwidth;
     uint32_t height = jheight;
 
-    // Create a Surface wired up to a BufferItemConsumer
-    sp<IGraphicBufferProducer> producer;
-    sp<IGraphicBufferConsumer> rawConsumer;
-    BufferQueue::createBufferQueue(&producer, &rawConsumer);
-    // We only need 1 buffer but some drivers have bugs so workaround it by setting max count to 2
-    rawConsumer->setMaxBufferCount(2);
-    sp<BufferItemConsumer> consumer = new BufferItemConsumer(rawConsumer,
-            GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_NEVER);
-    consumer->setDefaultBufferSize(width, height);
-    sp<Surface> surface = new Surface(producer);
+    // Create an ImageReader wired up to a BufferItemConsumer
+    AImageReader* rawReader;
+    media_status_t result =
+            AImageReader_newWithUsage(width, height, AIMAGE_FORMAT_RGBA_8888,
+                                      AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE, 2, &rawReader);
+    std::unique_ptr<AImageReader, decltype(&AImageReader_delete)> reader(rawReader,
+                                                                         AImageReader_delete);
+
+    if (result != AMEDIA_OK) {
+        ALOGW("Error creating image reader!");
+        return nullptr;
+    }
+
+    // Note that ownership of this window is maintained by AImageReader, so we
+    // shouldn't need to wrap around a smart pointer.
+    ANativeWindow* window;
+    result = AImageReader_getWindow(rawReader, &window);
+
+    if (result != AMEDIA_OK) {
+        ALOGW("Error retrieving the native window!");
+        return nullptr;
+    }
 
     // Render into the surface
     {
         ContextFactory factory;
         RenderProxy proxy{true, renderNode, &factory};
         proxy.setSwapBehavior(SwapBehavior::kSwap_discardBuffer);
-        proxy.setSurface(surface);
+        proxy.setSurface(window);
         // Shadows can't be used via this interface, so just set the light source
         // to all 0s.
         proxy.setLightAlpha(0, 0);
@@ -508,33 +519,34 @@
         proxy.syncAndDrawFrame();
     }
 
-    // Yank out the GraphicBuffer
-    BufferItem bufferItem;
-    status_t err;
-    if ((err = consumer->acquireBuffer(&bufferItem, 0, true)) != OK) {
-        ALOGW("Failed to acquireBuffer, error %d (%s)", err, strerror(-err));
+    AImage* rawImage;
+    result = AImageReader_acquireNextImage(rawReader, &rawImage);
+    std::unique_ptr<AImage, decltype(&AImage_delete)> image(rawImage, AImage_delete);
+    if (result != AMEDIA_OK) {
+        ALOGW("Error reading image: %d!", result);
         return nullptr;
     }
-    sp<GraphicBuffer> buffer = bufferItem.mGraphicBuffer;
-    // We don't really care if this fails or not since we're just going to destroy this anyway
-    consumer->releaseBuffer(bufferItem);
-    if (!buffer.get()) {
-        ALOGW("GraphicBuffer is null?");
-        return nullptr;
-    }
-    if (buffer->getWidth() != width || buffer->getHeight() != height) {
-        ALOGW("GraphicBuffer size mismatch, got %dx%d expected %dx%d",
-                buffer->getWidth(), buffer->getHeight(), width, height);
+
+    AHardwareBuffer* buffer;
+    result = AImage_getHardwareBuffer(rawImage, &buffer);
+
+    AHardwareBuffer_Desc desc;
+    AHardwareBuffer_describe(buffer, &desc);
+
+    if (desc.width != width || desc.height != height) {
+        ALOGW("AHardwareBuffer size mismatch, got %dx%d expected %dx%d", desc.width, desc.height,
+              width, height);
         // Continue I guess?
     }
 
-    sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(bufferItem.mDataSpace);
+    sk_sp<SkColorSpace> cs = uirenderer::DataSpaceToColorSpace(
+            static_cast<android_dataspace>(ANativeWindow_getBuffersDataSpace(window)));
     if (cs == nullptr) {
         // nullptr is treated as SRGB in Skia, thus explicitly use SRGB in order to make sure
         // the returned bitmap has a color space.
         cs = SkColorSpace::MakeSRGB();
     }
-    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer->toAHardwareBuffer(), cs);
+    sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, cs);
     return bitmap::createBitmap(env, bitmap.release(),
             android::bitmap::kBitmapCreateFlag_Premultiplied);
 }
@@ -722,6 +734,11 @@
     gFrameCompleteCallback.onFrameComplete = GetMethodIDOrDie(env, frameCompleteClass,
             "onFrameComplete", "(J)V");
 
+    void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+    fromSurface = (ANW_fromSurface)dlsym(handle_, "ANativeWindow_fromSurface");
+    LOG_ALWAYS_FATAL_IF(fromSurface == nullptr,
+                        "Failed to find required symbol ANativeWindow_fromSurface!");
+
     return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods));
 }
 
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 9fbb8df..d91911c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -49,7 +49,6 @@
 #include <fcntl.h>
 #include <grp.h>
 #include <inttypes.h>
-#include <link.h>
 #include <malloc.h>
 #include <mntent.h>
 #include <paths.h>
@@ -59,7 +58,6 @@
 #include <sys/capability.h>
 #include <sys/cdefs.h>
 #include <sys/eventfd.h>
-#include <sys/mman.h>
 #include <sys/personality.h>
 #include <sys/prctl.h>
 #include <sys/resource.h>
@@ -72,25 +70,23 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/properties.h>
-#include <android-base/file.h>
 #include <android-base/stringprintf.h>
-#include <android-base/strings.h>
 #include <android-base/unique_fd.h>
 #include <bionic/malloc.h>
-#include <bionic/page.h>
 #include <cutils/fs.h>
 #include <cutils/multiuser.h>
 #include <cutils/sockets.h>
 #include <private/android_filesystem_config.h>
-#include <utils/String8.h>
-#include <utils/Trace.h>
-#include <selinux/android.h>
-#include <seccomp_policy.h>
-#include <stats_event_list.h>
 #include <processgroup/processgroup.h>
 #include <processgroup/sched_policy.h>
+#include <seccomp_policy.h>
+#include <selinux/android.h>
+#include <stats_socket.h>
+#include <utils/String8.h>
+#include <utils/Trace.h>
 
 #include "core_jni_helpers.h"
 #include <nativehelper/JNIHelp.h>
@@ -1080,7 +1076,7 @@
   // Close any logging related FDs before we start evaluating the list of
   // file descriptors.
   __android_log_close();
-  stats_log_close();
+  AStatsSocket_close();
 
   // If this is the first fork for this zygote, create the open FD table.  If
   // it isn't, we just need to check whether the list of open files has changed
@@ -1651,7 +1647,7 @@
   SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
 
   __android_log_close();
-  stats_log_close();
+  AStatsSocket_close();
 
   const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
   const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
@@ -1905,25 +1901,6 @@
   UnmountTree("/storage");
 }
 
-static int DisableExecuteOnly(struct dl_phdr_info* info,
-                              size_t size [[maybe_unused]],
-                              void* data [[maybe_unused]]) {
-  // Search for any execute-only segments and mark them read+execute.
-  for (int i = 0; i < info->dlpi_phnum; i++) {
-    const auto& phdr = info->dlpi_phdr[i];
-    if ((phdr.p_type == PT_LOAD) && (phdr.p_flags == PF_X)) {
-      auto addr = reinterpret_cast<void*>(info->dlpi_addr + PAGE_START(phdr.p_vaddr));
-      size_t len = PAGE_OFFSET(phdr.p_vaddr) + phdr.p_memsz;
-      if (mprotect(addr, len, PROT_READ | PROT_EXEC) == -1) {
-        ALOGE("mprotect(%p, %zu, PROT_READ | PROT_EXEC) failed: %m", addr, len);
-        return -1;
-      }
-    }
-  }
-  // Return non-zero to exit dl_iterate_phdr.
-  return 0;
-}
-
 }  // anonymous namespace
 
 namespace android {
@@ -2286,14 +2263,6 @@
   }
 }
 
-/**
- * @param env  Managed runtime environment
- * @return  True if disable was successful.
- */
-static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* env, jclass) {
-  return dl_iterate_phdr(DisableExecuteOnly, nullptr) == 0;
-}
-
 static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) {
   auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
   BlockSignal(SIGTERM, fail_fn);
@@ -2375,8 +2344,6 @@
         {"nativeGetUsapPoolCount", "()I",
          (void*)com_android_internal_os_Zygote_nativeGetUsapPoolCount},
         {"nativeEmptyUsapPool", "()V", (void*)com_android_internal_os_Zygote_nativeEmptyUsapPool},
-        {"nativeDisableExecuteOnly", "()Z",
-         (void*)com_android_internal_os_Zygote_nativeDisableExecuteOnly},
         {"nativeBlockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeBlockSigTerm},
         {"nativeUnblockSigTerm", "()V", (void*)com_android_internal_os_Zygote_nativeUnblockSigTerm},
         {"nativeBoostUsapPriority", "()V",
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 03676dd..829c252 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -34,6 +34,7 @@
 import "frameworks/base/core/proto/android/providers/settings.proto";
 import "frameworks/base/core/proto/android/server/activitymanagerservice.proto";
 import "frameworks/base/core/proto/android/server/alarmmanagerservice.proto";
+import "frameworks/base/core/proto/android/server/bluetooth_manager_service.proto";
 import "frameworks/base/core/proto/android/server/fingerprint.proto";
 import "frameworks/base/core/proto/android/server/jobscheduler.proto";
 import "frameworks/base/core/proto/android/server/location/context_hub.proto";
@@ -488,6 +489,11 @@
         (section).args = "connmetrics --proto"
     ];
 
+    optional com.android.server.BluetoothManagerServiceDumpProto bluetooth_manager = 3050 [
+        (section).type = SECTION_DUMPSYS,
+        (section).args = "bluetooth_manager --proto"
+    ];
+
     optional com.android.server.location.ContextHubServiceProto context_hub = 3051 [
         (section).type = SECTION_DUMPSYS,
         (section).args = "contexthub --proto"
diff --git a/core/proto/android/server/bluetooth_manager_service.proto b/core/proto/android/server/bluetooth_manager_service.proto
new file mode 100644
index 0000000..998413f
--- /dev/null
+++ b/core/proto/android/server/bluetooth_manager_service.proto
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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.server;
+
+import "frameworks/base/core/proto/android/bluetooth/enums.proto";
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+
+message BluetoothManagerServiceDumpProto {
+   option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+   message ActiveLog {
+      option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+      optional int64 timestamp_ms = 1;
+      optional bool enable = 2;
+      optional string package_name = 3;
+      optional .android.bluetooth.EnableDisableReasonEnum reason = 4;
+   }
+
+   optional bool enabled = 1;
+   optional int32 state = 2;
+   optional string state_name = 3;
+   optional string address = 4 [(.android.privacy).dest = DEST_EXPLICIT];
+   optional string name = 5 [(.android.privacy).dest = DEST_EXPLICIT];
+   optional int64 last_enabled_time_ms = 6;
+   optional int64 curr_timestamp_ms = 7;
+   repeated ActiveLog active_logs = 8;
+   optional int32 num_crashes = 9;
+   optional bool crash_log_maxed = 10;
+   repeated int64 crash_timestamps_ms = 11;
+   optional int32 num_ble_apps = 12;
+   repeated string ble_app_package_names = 13;
+}
\ No newline at end of file
diff --git a/core/proto/android/server/notificationhistory.proto b/core/proto/android/server/notificationhistory.proto
index 6749719..15f4abb 100644
--- a/core/proto/android/server/notificationhistory.proto
+++ b/core/proto/android/server/notificationhistory.proto
@@ -55,6 +55,11 @@
     // The small icon of the notification
     optional Icon icon = 12;
 
+    // The conversation id, if any, that this notification belongs to
+    optional string conversation_id = 13;
+    // conversation_id_index contains the index + 1 of the conversation id in the string pool
+    optional int32 conversation_id_index = 14;
+
     // Matches the constants of android.graphics.drawable.Icon
     enum ImageTypeEnum {
       TYPE_UNKNOWN = 0;
diff --git a/core/proto/android/server/peopleservice.proto b/core/proto/android/server/peopleservice.proto
index 294b6ef..e476c52 100644
--- a/core/proto/android/server/peopleservice.proto
+++ b/core/proto/android/server/peopleservice.proto
@@ -52,6 +52,11 @@
   optional int32 conversation_flags = 6;
 }
 
+// On disk data of events.
+message PeopleEventsProto {
+  repeated PeopleEventProto events = 1;
+}
+
 // Individual event (com.android.server.people.data.Event).
 message PeopleEventProto {
 
@@ -65,11 +70,23 @@
   optional int32 duration = 3;
 }
 
+// On disk data of event indexes.
+message PeopleEventIndexesProto {
+  repeated TypedPeopleEventIndexProto typed_indexes = 1;
+}
+
+// Mapping of event_type to event index.
+message TypedPeopleEventIndexProto {
+  optional int32 event_type = 1;
+  optional PeopleEventIndexProto index = 2;
+}
+
 // Index of events' time distributions (com.android.server.people.data.EventIndex).
 message PeopleEventIndexProto {
-    // Each long value in event_bitmaps represents a time slot, there should be 4 values. Further
-    // details can be found in class documentation.
-    repeated int64 event_bitmaps = 1;
+  // Each long value in event_bitmaps represents a time slot, there should be 4 values. Further
+  // details can be found in class documentation.
+  repeated int64 event_bitmaps = 1;
 
-    optional int64 last_updated_time = 2;
+  optional int64 last_updated_time = 2;
 }
+
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index b0b9ce6f..08db454 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -275,6 +275,7 @@
     optional float adjust_divider_amount = 25;
     optional bool animating_bounds = 26;
     optional float minimize_amount = 27;
+    optional bool created_by_organizer = 28;
 }
 
 /* represents ActivityRecordProto */
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index a648831..a4e2193 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -162,4 +162,18 @@
   SET_PERSONAL_APPS_SUSPENDED = 135;
   SET_MANAGED_PROFILE_MAXIMUM_TIME_OFF = 136;
   COMP_TO_ORG_OWNED_PO_MIGRATED = 137;
+  SET_CROSS_PROFILE_PACKAGES = 138;
+  SET_INTERACT_ACROSS_PROFILES_APP_OP = 139;
+  GET_CROSS_PROFILE_PACKAGES = 140;
+  CAN_REQUEST_INTERACT_ACROSS_PROFILES_TRUE = 141;
+  CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_NO_PROFILES = 142;
+  CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_WHITELIST = 143;
+  CAN_REQUEST_INTERACT_ACROSS_PROFILES_FALSE_PERMISSION = 144;
+  CAN_INTERACT_ACROSS_PROFILES_TRUE = 145;
+  CAN_INTERACT_ACROSS_PROFILES_FALSE_PERMISSION = 146;
+  CAN_INTERACT_ACROSS_PROFILES_FALSE_NO_PROFILES = 147;
+  CREATE_CROSS_PROFILE_INTENT = 148;
+  IS_MANAGED_PROFILE = 149;
+  START_ACTIVITY_BY_INTENT = 150;
+  BIND_CROSS_PROFILE_SERVICE = 151;
 }
diff --git a/core/res/Android.bp b/core/res/Android.bp
index 3dc74f8..b365de4 100644
--- a/core/res/Android.bp
+++ b/core/res/Android.bp
@@ -54,3 +54,22 @@
         "res/**/*",
     ],
 }
+
+// Generate a text file containing a list of permissions that non-system apps
+// are allowed to obtain.
+genrule {
+  name: "permission-list-normal",
+  out: ["permission-list-normal.txt"],
+  srcs: ["AndroidManifest.xml"],
+  cmd: "cat $(in) " +
+       // xmllint has trouble accessing attributes under the android namespace.
+       // Strip these prefixes prior to processing with xmllint.
+       " | sed -r 's/android:(name|protectionLevel)/\\1/g' " +
+       " | $(location xmllint) /dev/stdin --xpath " +
+       " '//permission[not(contains(@protectionLevel, \"signature\"))]/@name'" +
+       // The result of xmllint is name="value" pairs. Format these to just the
+       // permission name, one per-line.
+       " | sed -r 's/\\s*name=\\s*//g' | tr -d '\"'" +
+       " > $(out)",
+  tools: ["xmllint"]
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60b3a19..b2047ad 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4941,7 +4941,13 @@
          <p>Not for use by third-party applications.
          @hide -->
     <permission android:name="android.permission.ACCESS_TV_TUNER"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|privileged|vendorPrivileged" />
+
+    <!-- @SystemApi Allows an application to access descrambler of TV tuner HAL
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.ACCESS_TV_DESCRAMBLER"
+        android:protectionLevel="signature|privileged|vendorPrivileged" />
 
     <!-- @hide @SystemApi Allows an application to access locusId events in the usage stats. -->
     <permission android:name="android.permission.ACCESS_LOCUS_ID_USAGE_STATS"
@@ -5344,6 +5350,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.people.data.DataMaintenanceService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service
                 android:name="com.android.server.autofill.AutofillCompatAccessibilityService"
                 android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
diff --git a/core/res/res/layout/autofill_inline_suggestion.xml b/core/res/res/layout/autofill_inline_suggestion.xml
deleted file mode 100644
index 27faea4..0000000
--- a/core/res/res/layout/autofill_inline_suggestion.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<LinearLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    style="?android:attr/autofillInlineSuggestionChip"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="center"
-    android:paddingTop="4dp"
-    android:paddingBottom="4dp"
-    android:orientation="horizontal">
-
-    <ImageView
-        android:id="@+id/autofill_inline_suggestion_start_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:scaleType="fitCenter"
-        android:contentDescription="autofill_inline_suggestion_start_icon" />
-
-    <LinearLayout
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:layout_weight="1"
-        android:paddingStart="4dp"
-        android:paddingEnd="4dp"
-        android:orientation="vertical"
-        android:gravity="center">
-
-        <TextView
-            style="?android:attr/autofillInlineSuggestionTitle"
-            android:id="@+id/autofill_inline_suggestion_title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:maxLines="1"/>
-
-        <TextView
-            style="?android:attr/autofillInlineSuggestionSubtitle"
-            android:id="@+id/autofill_inline_suggestion_subtitle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:ellipsize="end"
-            android:maxLines="1"/>
-    </LinearLayout>
-
-    <ImageView
-        android:id="@+id/autofill_inline_suggestion_end_icon"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="center"
-        android:scaleType="fitCenter"
-        android:contentDescription="autofill_inline_suggestion_end_icon" />
-</LinearLayout>
diff --git a/core/res/res/layout/resolver_empty_states.xml b/core/res/res/layout/resolver_empty_states.xml
index 6959e9c..5ed4c53 100644
--- a/core/res/res/layout/resolver_empty_states.xml
+++ b/core/res/res/layout/resolver_empty_states.xml
@@ -34,7 +34,7 @@
         android:layout_marginTop="16dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:textAppearance="?attr/textAppearanceMedium"
+        android:fontFamily="@string/config_headlineFontFamilyMedium"
         android:textColor="@color/resolver_empty_state_text"
         android:textSize="18sp"/>
     <TextView
@@ -52,7 +52,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:background="@null"
-        android:textAppearance="?attr/textAppearanceMedium"
+        android:fontFamily="@string/config_headlineFontFamilyMedium"
         android:textSize="14sp"
         android:textColor="@color/resolver_tabs_active_color"/>
 </LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 7d8b8db..9cdd3ed 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3774,7 +3774,7 @@
         <attr name="animatedImageDrawable" format="reference"/>
         <!-- Html description of the accessibility service, to help users understand
              how the service can help them.-->
-        <attr name="htmlDescription" format="string"/>
+        <attr name="htmlDescription" format="reference"/>
 
         <!-- Short description of the accessibility service purpose or behavior.-->
         <attr name="description" />
@@ -3795,7 +3795,7 @@
         <attr name="animatedImageDrawable" format="reference"/>
         <!-- Html description of the target of accessibility shortcut purpose or behavior, to help
              users understand how the target of accessibility shortcut can help them. -->
-        <attr name="htmlDescription" format="string"/>
+        <attr name="htmlDescription" format="reference"/>
     </declare-styleable>
 
     <!-- Use <code>print-service</code> as the root tag of the XML resource that
@@ -9207,12 +9207,4 @@
     </declare-styleable>
 
     <attr name="autoSizePresetSizes" />
-
-    <declare-styleable name="AutofillInlineSuggestion">
-        <!-- @hide @SystemApi -->
-        <attr name="isAutofillInlineSuggestionTheme" format="boolean" />
-        <attr name="autofillInlineSuggestionChip" format="reference" />
-        <attr name="autofillInlineSuggestionTitle" format="reference" />
-        <attr name="autofillInlineSuggestionSubtitle" format="reference" />
-    </declare-styleable>
 </resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index e231c3a..16bed503 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -1770,6 +1770,21 @@
              -->
         <attr name="requestLegacyExternalStorage" format="boolean" />
 
+        <!-- If {@code true} this app would like to preserve the legacy storage
+             model from a previously installed version. Note that this may not always be
+             respected due to policy or backwards compatibility reasons.
+
+             <p>This has no effect on the first install of an app on a device.
+             For an updating app, setting this to {@code true} will preserve the legacy behaviour
+             configured by the {@code requestLegacyExternalStorage} flag. If on an update, this
+             flag is set to {@code false} then the legacy access is not preserved, such an app can
+             only have legacy access with the {@code requestLegacyExternalStorage} flag.
+             <p>
+
+             The default value is {@code false}.
+             -->
+        <attr name="preserveLegacyExternalStorage" format="boolean" />
+
         <!-- If {@code true} this app declares that it should be visible to all other apps on
              device, regardless of what they declare via the {@code queries} tags in their
              manifest.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9282925..bbe9a6d 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -59,6 +59,7 @@
         <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item>
         <item><xliff:g id="id">@string/status_bar_sensors_off</xliff:g></item>
+        <item><xliff:g id="id">@string/status_bar_screen_record</xliff:g></item>
     </string-array>
 
     <string translatable="false" name="status_bar_rotate">rotate</string>
@@ -94,6 +95,7 @@
     <string translatable="false" name="status_bar_camera">camera</string>
     <string translatable="false" name="status_bar_airplane">airplane</string>
     <string translatable="false" name="status_bar_sensors_off">sensors_off</string>
+    <string translatable="false" name="status_bar_screen_record">screen_record</string>
 
     <!-- Flag indicating whether the surface flinger has limited
          alpha compositing functionality in hardware.  If set, the window
@@ -2830,13 +2832,13 @@
          "logout" =  Logout the current user
          -->
     <string-array translatable="false" name="config_globalActionsList">
+        <item>emergency</item>
         <item>power</item>
         <item>restart</item>
         <item>lockdown</item>
         <item>logout</item>
         <item>bugreport</item>
         <item>screenshot</item>
-        <item>emergency</item>
     </string-array>
 
     <!-- Number of milliseconds to hold a wake lock to ensure that drawing is fully
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 0edad3b..80e5d1b 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3012,19 +3012,14 @@
       <public name="sdkVersion" />
       <!-- @hide @SystemApi -->
       <public name="minExtensionVersion" />
-      <public name="autofillInlineSuggestionChip" />
-      <public name="autofillInlineSuggestionTitle" />
-      <public name="autofillInlineSuggestionSubtitle" />
-      <!-- @hide @SystemApi -->
-      <public name="isAutofillInlineSuggestionTheme" />
       <public name="allowNativeHeapPointerTagging" />
+      <public name="preserveLegacyExternalStorage" />
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
     </public-group>
 
     <public-group type="style" first-id="0x010302e5">
-      <public name="Theme.AutofillInlineSuggestion" />
     </public-group>
 
     <public-group type="id" first-id="0x0102004a">
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 751eca0..bcce1f0 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1482,22 +1482,6 @@
         <item name="android:windowExitAnimation">@anim/slide_out_down</item>
     </style>
 
-    <!-- The style for the Autofill inline suggestion chip. -->
-    <!-- @hide -->
-    <style name="AutofillInlineSuggestionChip">
-        <item name="background">@drawable/autofill_dataset_picker_background</item>
-    </style>
-
-    <!-- @hide -->
-    <style name="AutofillInlineSuggestionTitle">
-        <item name="android:textAppearance">@style/TextAppearance</item>
-    </style>
-
-    <!-- @hide -->
-    <style name="AutofillInlineSuggestionSubtitle">
-        <item name="android:textAppearance">@style/TextAppearance.Small</item>
-    </style>
-
     <!-- The style for the container of media actions in a notification. -->
     <!-- @hide -->
     <style name="NotificationMediaActionContainer">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5aefe11..02d90a7 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2908,6 +2908,7 @@
   <java-symbol type="string" name="status_bar_microphone" />
   <java-symbol type="string" name="status_bar_camera" />
   <java-symbol type="string" name="status_bar_sensors_off" />
+  <java-symbol type="string" name="status_bar_screen_record" />
 
   <!-- Locale picker -->
   <java-symbol type="id" name="locale_search_menu" />
@@ -3260,7 +3261,6 @@
   <java-symbol type="layout" name="autofill_dataset_picker"/>
   <java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
   <java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
-  <java-symbol type="layout" name="autofill_inline_suggestion" />
   <java-symbol type="id" name="autofill" />
   <java-symbol type="id" name="autofill_dataset_footer"/>
   <java-symbol type="id" name="autofill_dataset_header"/>
@@ -3268,10 +3268,6 @@
   <java-symbol type="id" name="autofill_dataset_list"/>
   <java-symbol type="id" name="autofill_dataset_picker"/>
   <java-symbol type="id" name="autofill_dataset_title" />
-  <java-symbol type="id" name="autofill_inline_suggestion_end_icon" />
-  <java-symbol type="id" name="autofill_inline_suggestion_start_icon" />
-  <java-symbol type="id" name="autofill_inline_suggestion_subtitle" />
-  <java-symbol type="id" name="autofill_inline_suggestion_title" />
   <java-symbol type="id" name="autofill_save_custom_subtitle" />
   <java-symbol type="id" name="autofill_save_icon" />
   <java-symbol type="id" name="autofill_save_no" />
@@ -3902,4 +3898,6 @@
 
   <!-- Whether to expand the lock screen user switcher by default -->
   <java-symbol type="bool" name="config_expandLockScreenUserSwitcher" />
+
+  <java-symbol type="string" name="loading" />
 </resources>
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 5e6dd82..5d9cb48 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -892,12 +892,4 @@
         <item name="windowContentTransitions">false</item>
         <item name="windowActivityTransitions">false</item>
     </style>
-
-    <!-- Theme for the Autofill inline suggestion on IME -->
-    <style name="Theme.AutofillInlineSuggestion" parent="Theme.DeviceDefault">
-        <item name="isAutofillInlineSuggestionTheme">true</item>
-        <item name="autofillInlineSuggestionChip">@style/AutofillInlineSuggestionChip</item>
-        <item name="autofillInlineSuggestionTitle">@style/AutofillInlineSuggestionTitle</item>
-        <item name="autofillInlineSuggestionSubtitle">@style/AutofillInlineSuggestionSubtitle</item>
-    </style>
 </resources>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index be94f02..59335a5 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -138,9 +138,6 @@
     <!-- vr test permissions -->
     <uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
 
-    <!-- WindowMetricsTest permissions -->
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-
     <application android:theme="@style/Theme" android:supportsRtl="true">
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index d1608d0..8d8acb7 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -21,6 +21,7 @@
 import android.app.NotificationHistory.HistoricalNotification;
 import android.graphics.drawable.Icon;
 import android.os.Parcel;
+import android.util.Slog;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -48,6 +49,10 @@
         String expectedText = "text" + index;
         Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
                 index);
+        String conversationId = null;
+        if (index % 2 == 0) {
+            conversationId = "convo" + index;
+        }
 
         return new HistoricalNotification.Builder()
                 .setPackage(packageName)
@@ -59,6 +64,7 @@
                 .setTitle(expectedTitle)
                 .setText(expectedText)
                 .setIcon(expectedIcon)
+                .setConversationId(conversationId)
                 .build();
     }
 
@@ -74,6 +80,7 @@
         String expectedText = "text";
         Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
                 android.R.drawable.btn_star);
+        String expectedConversationId = "convo";
 
         HistoricalNotification n = new HistoricalNotification.Builder()
                 .setPackage(expectedPackage)
@@ -85,6 +92,7 @@
                 .setTitle(expectedTitle)
                 .setText(expectedText)
                 .setIcon(expectedIcon)
+                .setConversationId(expectedConversationId)
                 .build();
 
         assertThat(n.getPackage()).isEqualTo(expectedPackage);
@@ -96,6 +104,7 @@
         assertThat(n.getTitle()).isEqualTo(expectedTitle);
         assertThat(n.getText()).isEqualTo(expectedText);
         assertThat(expectedIcon.sameAs(n.getIcon())).isTrue();
+        assertThat(n.getConversationId()).isEqualTo(expectedConversationId);
     }
 
     @Test
@@ -153,6 +162,9 @@
             expectedStrings.add(n.getPackage());
             expectedStrings.add(n.getChannelName());
             expectedStrings.add(n.getChannelId());
+            if (n.getConversationId() != null) {
+                expectedStrings.add(n.getConversationId());
+            }
             history.addNotificationToWrite(n);
         }
 
@@ -180,6 +192,9 @@
             expectedStrings.add(n.getPackage());
             expectedStrings.add(n.getChannelName());
             expectedStrings.add(n.getChannelId());
+            if (n.getConversationId() != null) {
+                expectedStrings.add(n.getConversationId());
+            }
             history.addNotificationToWrite(n);
         }
 
@@ -212,6 +227,9 @@
                 postRemoveExpectedStrings.add(n.getPackage());
                 postRemoveExpectedStrings.add(n.getChannelName());
                 postRemoveExpectedStrings.add(n.getChannelId());
+                if (n.getConversationId() != null) {
+                    postRemoveExpectedStrings.add(n.getConversationId());
+                }
                 postRemoveExpectedEntries.add(n);
             }
 
@@ -221,14 +239,14 @@
         history.poolStringsFromNotifications();
 
         assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
-        // 2 package names and 10 * 2 unique channel names and ids
-        assertThat(history.getPooledStringsToWrite().length).isEqualTo(22);
+        // 2 package names and 10 * 2 unique channel names and ids and 5 conversation ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(27);
 
         history.removeNotificationsFromWrite("pkgOdd");
 
 
-        // 1 package names and 5 * 2 unique channel names and ids
-        assertThat(history.getPooledStringsToWrite().length).isEqualTo(11);
+        // 1 package names and 5 * 2 unique channel names and ids and 5 conversation ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(16);
         assertThat(history.getNotificationsToWrite())
                 .containsExactlyElementsIn(postRemoveExpectedEntries);
     }
@@ -246,6 +264,9 @@
                 postRemoveExpectedStrings.add(n.getPackage());
                 postRemoveExpectedStrings.add(n.getChannelName());
                 postRemoveExpectedStrings.add(n.getChannelId());
+                if (n.getConversationId() != null) {
+                    postRemoveExpectedStrings.add(n.getConversationId());
+                }
                 postRemoveExpectedEntries.add(n);
             }
 
@@ -255,14 +276,14 @@
         history.poolStringsFromNotifications();
 
         assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
-        // 1 package name and 10 unique channel names and ids
-        assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
+        // 1 package name and 20 unique channel names and ids and 5 conversation ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
 
         history.removeNotificationFromWrite("pkg", 987654323);
 
 
-        // 1 package names and 9 * 2 unique channel names and ids
-        assertThat(history.getPooledStringsToWrite().length).isEqualTo(19);
+        // 1 package names and 9 * 2 unique channel names and ids and 4 conversation ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(23);
         assertThat(history.getNotificationsToWrite())
                 .containsExactlyElementsIn(postRemoveExpectedEntries);
     }
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index e4dc993..4cc70ba 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -30,12 +30,23 @@
  * Unit tests for bundle that requires accessing hidden APS.  Tests that can be written only with
  * public APIs should go in the CTS counterpart.
  *
- * Run with:
- * bit FrameworksCoreTests:android.os.BundleTest
+ * Run with: atest FrameworksCoreTests:android.os.BundleTest
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class BundleTest {
+
+    /**
+     * Take a bundle, write it to a parcel and return the parcel.
+     */
+    private Parcel getParcelledBundle(Bundle bundle) {
+        final Parcel p = Parcel.obtain();
+        // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
+        bundle.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        return p;
+    }
+
     /**
      * Create a test bundle, parcel it and return the parcel.
      */
@@ -48,12 +59,7 @@
             pipe[1].close();
             source.putParcelable("fd", pipe[0]);
         }
-        final Parcel p = Parcel.obtain();
-        // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
-        source.writeToParcel(p, 0);
-        p.setDataPosition(0);
-
-        return p;
+        return getParcelledBundle(source);
     }
 
     /**
@@ -137,4 +143,78 @@
         checkBundle(b, withFd);
         p.recycle();
     }
+
+    @Test
+    public void kindofEquals_bothUnparcelled_same() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "S");
+        bundle2.putInt("IntKey", 2);
+
+        assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_bothUnparcelled_different() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "T");
+        bundle2.putLong("LongKey", 30L);
+
+        assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_bothParcelled_same() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+        bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "S");
+        bundle2.putInt("IntKey", 2);
+        bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+        assertTrue(bundle1.isParcelled());
+        assertTrue(bundle2.isParcelled());
+        assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_bothParcelled_different() {
+        Bundle bundle1 = new Bundle();
+        bundle1.putString("StringKey", "S");
+        bundle1.putInt("IntKey", 2);
+        bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+        Bundle bundle2 = new Bundle();
+        bundle2.putString("StringKey", "T");
+        bundle2.putLong("LongKey", 5);
+        bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+        assertTrue(bundle1.isParcelled());
+        assertTrue(bundle2.isParcelled());
+        assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
+
+    @Test
+    public void kindofEquals_ParcelledUnparcelled_empty() {
+        Bundle bundle1 = new Bundle();
+        bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+        Bundle bundle2 = new Bundle();
+
+        assertTrue(bundle1.isParcelled());
+        assertFalse(bundle2.isParcelled());
+        // Even though one is parcelled and the other is not, both are empty, so it should
+        // return true
+        assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+    }
 }
diff --git a/core/tests/coretests/src/android/view/WindowMetricsTest.java b/core/tests/coretests/src/android/view/WindowMetricsTest.java
deleted file mode 100644
index d2eb8f5..0000000
--- a/core/tests/coretests/src/android/view/WindowMetricsTest.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2020 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.view;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
-
-import android.content.Context;
-import android.hardware.display.DisplayManager;
-import android.os.Handler;
-import android.platform.test.annotations.Presubmit;
-
-import androidx.test.filters.FlakyTest;
-import androidx.test.filters.SmallTest;
-import androidx.test.platform.app.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-/**
- * Tests for {@link WindowManager#getCurrentWindowMetrics()} and
- * {@link WindowManager#getMaximumWindowMetrics()}.
- *
- * <p>Build/Install/Run:
- *  atest FrameworksCoreTests:WindowMetricsTest
- *
- * <p>This test class is a part of Window Manager Service tests and specified in
- * {@link com.android.server.wm.test.filters.FrameworksTestsFilter}.
- */
-@FlakyTest(bugId = 148789183, detail = "Remove after confirmed it's stable.")
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-@Presubmit
-public class WindowMetricsTest {
-    private Context mWindowContext;
-    private WindowManager mWm;
-
-    @Before
-    public void setUp() {
-        final Context insetContext = InstrumentationRegistry.getInstrumentation()
-                .getTargetContext();
-        final Display display = insetContext.getSystemService(DisplayManager.class)
-                .getDisplay(DEFAULT_DISPLAY);
-        mWindowContext = insetContext.createDisplayContext(display)
-                .createWindowContext(TYPE_APPLICATION_OVERLAY, null /* options */);
-        mWm = mWindowContext.getSystemService(WindowManager.class);
-    }
-
-    @Test
-    public void testAddViewANdRemoveView_GetMetrics_DoNotCrash() {
-        final View view = new View(mWindowContext);
-        final WindowManager.LayoutParams params =
-                new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
-        Handler.getMain().runWithScissors(() -> {
-            mWm.addView(view, params);
-            // Check get metrics do not crash.
-            mWm.getCurrentWindowMetrics();
-            mWm.getMaximumWindowMetrics();
-
-            mWm.removeViewImmediate(view);
-            // Check get metrics do not crash.
-            mWm.getCurrentWindowMetrics();
-            mWm.getMaximumWindowMetrics();
-        }, 0);
-    }
-}
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index ce71beb..6d0e58b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -18,6 +18,7 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
 import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
@@ -355,18 +356,14 @@
     public void hasOtherProfileOneOption() throws Exception {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
-
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         markWorkProfileUserAvailable();
+
+        ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
         Intent sendIntent = createSendTextIntent();
-        List<ResolvedComponentInfo> resolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(2);
-        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
-        when(ChooserWrapperActivity.sOverrides.resolverListController.getResolversForIntent(
-                Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
-
         final ChooserWrapperActivity activity = mActivityRule
                 .launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
@@ -382,9 +379,11 @@
 
         // Make a stable copy of the components as the original list may be modified
         List<ResolvedComponentInfo> stableCopy =
-                createResolvedComponentsForTestWithOtherProfile(2);
+                createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
         waitForIdle();
-        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);
+
+        onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
                 .perform(click());
         waitForIdle();
         assertThat(chosen[0], is(toChoose));
@@ -1218,17 +1217,7 @@
         int workProfileTargets = 4;
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(
                 workProfileTargets);
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendTextIntent();
         sendIntent.setType("TestType");
         markWorkProfileUserAvailable();
@@ -1245,7 +1234,7 @@
     }
 
     @Test
-    public void testWorkTab_workProfileHasExpectedNumberOfTargets() {
+    public void testWorkTab_workProfileHasExpectedNumberOfTargets() throws InterruptedException {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
@@ -1254,18 +1243,7 @@
                 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets);
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendTextIntent();
         sendIntent.setType("TestType");
 
@@ -1284,12 +1262,12 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         int workProfileTargets = 4;
         List<ResolvedComponentInfo> workResolvedComponentInfos =
                 createResolvedComponentsForTest(workProfileTargets);
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendTextIntent();
         sendIntent.setType("TestType");
         ResolveInfo[] chosen = new ResolveInfo[1];
@@ -1312,6 +1290,85 @@
         assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
     }
 
+    @Test
+    public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        int workProfileTargets = 4;
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(workProfileTargets);
+        sOverrides.hasCrossProfileIntents = false;
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+
+        final ChooserWrapperActivity activity =
+                mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+
+        onView(withText(R.string.resolver_cant_share_with_work_apps))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testWorkTab_workProfileDisabled_emptyStateShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        int workProfileTargets = 4;
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(workProfileTargets);
+        sOverrides.isQuietModeEnabled = true;
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+
+        final ChooserWrapperActivity activity =
+                mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        onView(withText(R.string.resolver_turn_on_work_apps))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testWorkTab_noWorkTargets_emptyStateShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTest(3);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(0);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendTextIntent();
+        sendIntent.setType("TestType");
+
+        final ChooserWrapperActivity activity =
+                mActivityRule.launchActivity(Intent.createChooser(sendIntent, "work tab test"));
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        onView(withText(R.string.resolver_no_apps_available))
+                .check(matches(isDisplayed()));
+    }
+
     private Intent createSendTextIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
@@ -1486,4 +1543,21 @@
     private void markWorkProfileUserAvailable() {
         sOverrides.workProfileUserHandle = UserHandle.of(10);
     }
+
+    private void setupResolverControllers(
+            List<ResolvedComponentInfo> personalResolvedComponentInfos,
+            List<ResolvedComponentInfo> workResolvedComponentInfos) {
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
+        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index a68b5908..363551b 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -16,15 +16,10 @@
 
 package com.android.internal.app;
 
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.annotation.Nullable;
-import android.app.prediction.AppPredictionContext;
-import android.app.prediction.AppPredictionManager;
-import android.app.prediction.AppPredictor;
 import android.app.usage.UsageStatsManager;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -35,7 +30,6 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
-import android.os.Bundle;
 import android.os.UserHandle;
 import android.util.Size;
 
@@ -45,8 +39,7 @@
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 
-import org.mockito.Mockito;
-
+import java.util.List;
 import java.util.function.Function;
 
 public class ChooserWrapperActivity extends ChooserActivity {
@@ -56,6 +49,15 @@
     static final OverrideData sOverrides = new OverrideData();
     private UsageStatsManager mUsm;
 
+    @Override
+    protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+            Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
+        AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
+                super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
+        multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
+        return multiProfilePagerAdapter;
+    }
+
     ChooserListAdapter getAdapter() {
         return mChooserMultiProfilePagerAdapter.getActiveListAdapter();
     }
@@ -206,6 +208,9 @@
         public int alternateProfileSetting;
         public Resources resources;
         public UserHandle workProfileUserHandle;
+        public boolean hasCrossProfileIntents;
+        public boolean isQuietModeEnabled;
+        public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
 
         public void reset() {
             onSafelyStartCallback = null;
@@ -221,6 +226,26 @@
             alternateProfileSetting = 0;
             resources = null;
             workProfileUserHandle = null;
+            hasCrossProfileIntents = true;
+            isQuietModeEnabled = false;
+            multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
+                @Override
+                public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+                        int targetUserId) {
+                    return hasCrossProfileIntents;
+                }
+
+                @Override
+                public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+                    return isQuietModeEnabled;
+                }
+
+                @Override
+                public void requestQuietModeEnabled(boolean enabled,
+                        UserHandle workProfileUserHandle) {
+                    isQuietModeEnabled = enabled;
+                }
+            };
         }
     }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 5f4194a..a7bf488 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -18,16 +18,16 @@
 
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.swipeUp;
 import static androidx.test.espresso.assertion.ViewAssertions.matches;
-import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isCompletelyDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
 import static androidx.test.espresso.matcher.ViewMatchers.isEnabled;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.matcher.ViewMatchers.withText;
 
 import static com.android.internal.app.MatcherUtils.first;
 import static com.android.internal.app.ResolverDataProvider.createPackageManagerMockedInfo;
-import static com.android.internal.app.ResolverDataProvider.createResolvedComponentInfoWithOtherId;
 import static com.android.internal.app.ResolverWrapperActivity.sOverrides;
 
 import static org.hamcrest.CoreMatchers.allOf;
@@ -56,9 +56,6 @@
 import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
 import com.android.internal.widget.ResolverDrawerLayout;
 
-import org.hamcrest.BaseMatcher;
-import org.hamcrest.Description;
-import org.hamcrest.Matcher;
 import org.junit.Before;
 import org.junit.Ignore;
 import org.junit.Rule;
@@ -224,17 +221,14 @@
     public void hasOtherProfileOneOption() throws Exception {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
-
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(2, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         markWorkProfileUserAvailable();
+
+        ResolveInfo toChoose = personalResolvedComponentInfos.get(1).getResolveInfoAt(0);
         Intent sendIntent = createSendImageIntent();
-        List<ResolvedComponentInfo> resolvedComponentInfos =
-                createResolvedComponentsForTestWithOtherProfile(2);
-        ResolveInfo toChoose = resolvedComponentInfos.get(1).getResolveInfoAt(0);
-
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(resolvedComponentInfos);
-
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
         Espresso.registerIdlingResources(activity.getAdapter().getLabelIdlingResource());
         waitForIdle();
@@ -249,8 +243,9 @@
         };
         // Make a stable copy of the components as the original list may be modified
         List<ResolvedComponentInfo> stableCopy =
-                createResolvedComponentsForTestWithOtherProfile(2);
-        onView(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name))
+                createResolvedComponentsForTestWithOtherProfile(2, /* userId= */ 10);
+        // We pick the first one as there is another one in the work profile side
+        onView(first(withText(stableCopy.get(1).getResolveInfoAt(0).activityInfo.name)))
                 .perform(click());
         onView(withId(R.id.button_once))
                 .perform(click());
@@ -415,18 +410,14 @@
     }
 
     @Test
-    public void testWorkTab_workTabListEmptyBeforeGoingToTab() {
+    public void testWorkTab_workTabListPopulatedBeforeGoingToTab() throws InterruptedException {
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
-                createResolvedComponentsForTest(3);
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId = */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        setupResolverControllers(personalResolvedComponentInfos,
+                new ArrayList<>(workResolvedComponentInfos));
         Intent sendIntent = createSendImageIntent();
         markWorkProfileUserAvailable();
 
@@ -434,8 +425,8 @@
         waitForIdle();
 
         assertThat(activity.getCurrentUserHandle().getIdentifier(), is(0));
-        // The work list adapter must only be filled when we open the work tab
-        assertThat(activity.getWorkListAdapter().getCount(), is(0));
+        // The work list adapter must be populated in advance before tapping the other tab
+        assertThat(activity.getWorkListAdapter().getCount(), is(4));
     }
 
     @Test
@@ -445,17 +436,7 @@
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
                 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(personalResolvedComponentInfos);
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendImageIntent();
         markWorkProfileUserAvailable();
 
@@ -474,34 +455,7 @@
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
                 createResolvedComponentsForTestWithOtherProfile(3);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-        when(sOverrides.resolverListController.getResolversForIntentAsUser(
-                Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.resolverListController.getResolversForIntentAsUser(
-                Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle)))
-                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle)))
-                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendImageIntent();
         markWorkProfileUserAvailable();
 
@@ -521,18 +475,7 @@
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
                 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendImageIntent();
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -552,18 +495,7 @@
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
                 createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendImageIntent();
         ResolveInfo[] chosen = new ResolveInfo[1];
         sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -597,35 +529,7 @@
         List<ResolvedComponentInfo> personalResolvedComponentInfos =
                 createResolvedComponentsForTestWithOtherProfile(1);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-
-        when(sOverrides.resolverListController.getResolversForIntentAsUser(
-                Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.resolverListController.getResolversForIntentAsUser(
-                Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle)))
-                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(sOverrides.workProfileUserHandle)))
-                .thenReturn(new ArrayList<>(workResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(new ArrayList<>(workResolvedComponentInfos));
-        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class)))
-                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class),
-                eq(UserHandle.SYSTEM))).thenReturn(new ArrayList<>(personalResolvedComponentInfos));
-
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendImageIntent();
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
@@ -644,10 +548,10 @@
         // enable the work tab feature flag
         ResolverActivity.ENABLE_TABBED_VIEW = true;
         markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId= */ 10);
         List<ResolvedComponentInfo> workResolvedComponentInfos = createResolvedComponentsForTest(4);
-        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
-                Mockito.anyBoolean(),
-                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
         Intent sendIntent = createSendImageIntent();
         ResolveInfo[] chosen = new ResolveInfo[1];
         sOverrides.onSafelyStartCallback = targetInfo -> {
@@ -672,6 +576,82 @@
         assertThat(chosen[0], is(workResolvedComponentInfos.get(0).getResolveInfoAt(0)));
     }
 
+    @Test
+    public void testWorkTab_crossProfileIntentsDisabled_personalToWork_emptyStateShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        int workProfileTargets = 4;
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(workProfileTargets);
+        sOverrides.hasCrossProfileIntents = false;
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendImageIntent();
+        sendIntent.setType("TestType");
+
+        mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+
+        onView(withText(R.string.resolver_cant_share_with_work_apps))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testWorkTab_workProfileDisabled_emptyStateShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        int workProfileTargets = 4;
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTestWithOtherProfile(3, /* userId */ 10);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(workProfileTargets);
+        sOverrides.isQuietModeEnabled = true;
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendImageIntent();
+        sendIntent.setType("TestType");
+
+        mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        onView(withText(R.string.resolver_turn_on_work_apps))
+                .check(matches(isDisplayed()));
+    }
+
+    @Test
+    public void testWorkTab_noWorkTargets_emptyStateShown() {
+        // enable the work tab feature flag
+        ResolverActivity.ENABLE_TABBED_VIEW = true;
+        markWorkProfileUserAvailable();
+        List<ResolvedComponentInfo> personalResolvedComponentInfos =
+                createResolvedComponentsForTest(3);
+        List<ResolvedComponentInfo> workResolvedComponentInfos =
+                createResolvedComponentsForTest(0);
+        setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+        Intent sendIntent = createSendImageIntent();
+        sendIntent.setType("TestType");
+
+        mActivityRule.launchActivity(sendIntent);
+        waitForIdle();
+        onView(withId(R.id.contentPanel))
+                .perform(swipeUp());
+        onView(withText(R.string.resolver_work_tab)).perform(click());
+        waitForIdle();
+
+        onView(withText(R.string.resolver_no_apps_available))
+                .check(matches(isDisplayed()));
+    }
+
     private Intent createSendImageIntent() {
         Intent sendIntent = new Intent();
         sendIntent.setAction(Intent.ACTION_SEND);
@@ -722,4 +702,21 @@
     private void markWorkProfileUserAvailable() {
         ResolverWrapperActivity.sOverrides.workProfileUserHandle = UserHandle.of(10);
     }
+
+    private void setupResolverControllers(
+            List<ResolvedComponentInfo> personalResolvedComponentInfos,
+            List<ResolvedComponentInfo> workResolvedComponentInfos) {
+        when(sOverrides.resolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+        when(sOverrides.workResolverListController.getResolversForIntent(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class))).thenReturn(workResolvedComponentInfos);
+        when(sOverrides.workResolverListController.getResolversForIntentAsUser(Mockito.anyBoolean(),
+                Mockito.anyBoolean(),
+                Mockito.isA(List.class),
+                eq(UserHandle.SYSTEM)))
+                .thenReturn(new ArrayList<>(personalResolvedComponentInfos));
+    }
 }
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 36c8724..2087104 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -47,6 +47,15 @@
                 filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this);
     }
 
+    @Override
+    protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+            Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed) {
+        AbstractMultiProfilePagerAdapter multiProfilePagerAdapter =
+                super.createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
+        multiProfilePagerAdapter.setInjector(sOverrides.multiPagerAdapterInjector);
+        return multiProfilePagerAdapter;
+    }
+
     ResolverWrapperAdapter getAdapter() {
         return (ResolverWrapperAdapter) mMultiProfilePagerAdapter.getActiveListAdapter();
     }
@@ -124,6 +133,9 @@
         public ResolverListController workResolverListController;
         public Boolean isVoiceInteraction;
         public UserHandle workProfileUserHandle;
+        public boolean hasCrossProfileIntents;
+        public boolean isQuietModeEnabled;
+        public AbstractMultiProfilePagerAdapter.Injector multiPagerAdapterInjector;
 
         public void reset() {
             onSafelyStartCallback = null;
@@ -132,6 +144,26 @@
             resolverListController = mock(ResolverListController.class);
             workResolverListController = mock(ResolverListController.class);
             workProfileUserHandle = null;
+            hasCrossProfileIntents = true;
+            isQuietModeEnabled = false;
+            multiPagerAdapterInjector = new AbstractMultiProfilePagerAdapter.Injector() {
+                @Override
+                public boolean hasCrossProfileIntents(List<Intent> intents, int sourceUserId,
+                        int targetUserId) {
+                    return hasCrossProfileIntents;
+                }
+
+                @Override
+                public boolean isQuietModeEnabled(UserHandle workProfileUserHandle) {
+                    return isQuietModeEnabled;
+                }
+
+                @Override
+                public void requestQuietModeEnabled(boolean enabled,
+                        UserHandle workProfileUserHandle) {
+                    isQuietModeEnabled = enabled;
+                }
+            };
         }
     }
 }
\ No newline at end of file
diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java
index 3835b2d..752695f 100644
--- a/graphics/java/android/graphics/RenderNode.java
+++ b/graphics/java/android/graphics/RenderNode.java
@@ -22,8 +22,8 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.graphics.animation.RenderNodeAnimator;
 import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimator;
 import android.view.Surface;
 import android.view.View;
 
diff --git a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
similarity index 89%
rename from core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
rename to graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
index d28ab07..36062c1 100644
--- a/core/java/com/android/internal/view/animation/FallbackLUTInterpolator.java
+++ b/graphics/java/android/graphics/animation/FallbackLUTInterpolator.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.view.animation;
+package android.graphics.animation;
 
 import android.animation.TimeInterpolator;
 import android.util.TimeUtils;
@@ -26,14 +26,15 @@
  * with {@link HasNativeInterpolator}
  *
  * This implements TimeInterpolator to allow for easier interop with Animators
+ * @hide
  */
 @HasNativeInterpolator
-public class FallbackLUTInterpolator implements NativeInterpolatorFactory, TimeInterpolator {
+public class FallbackLUTInterpolator implements NativeInterpolator, TimeInterpolator {
 
     // If the duration of an animation is more than 300 frames, we cap the sample size to 300.
     private static final int MAX_SAMPLE_POINTS = 300;
     private TimeInterpolator mSourceInterpolator;
-    private final float mLut[];
+    private final float[] mLut;
 
     /**
      * Used to cache the float[] LUT for use across multiple native
@@ -50,7 +51,7 @@
         // We need 2 frame values as the minimal.
         int numAnimFrames = Math.max(2, (int) Math.ceil(((double) duration) / animIntervalMs));
         numAnimFrames = Math.min(numAnimFrames, MAX_SAMPLE_POINTS);
-        float values[] = new float[numAnimFrames];
+        float[] values = new float[numAnimFrames];
         float lastFrame = numAnimFrames - 1;
         for (int i = 0; i < numAnimFrames; i++) {
             float inValue = i / lastFrame;
@@ -61,7 +62,7 @@
 
     @Override
     public long createNativeInterpolator() {
-        return NativeInterpolatorFactoryHelper.createLutInterpolator(mLut);
+        return NativeInterpolatorFactory.createLutInterpolator(mLut);
     }
 
     /**
@@ -69,7 +70,7 @@
      */
     public static long createNativeInterpolator(TimeInterpolator interpolator, long duration) {
         float[] lut = createLUT(interpolator, duration);
-        return NativeInterpolatorFactoryHelper.createLutInterpolator(lut);
+        return NativeInterpolatorFactory.createLutInterpolator(lut);
     }
 
     @Override
diff --git a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
similarity index 89%
rename from core/java/com/android/internal/view/animation/HasNativeInterpolator.java
rename to graphics/java/android/graphics/animation/HasNativeInterpolator.java
index 48ea4da..c53d5a4 100644
--- a/core/java/com/android/internal/view/animation/HasNativeInterpolator.java
+++ b/graphics/java/android/graphics/animation/HasNativeInterpolator.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.view.animation;
+package android.graphics.animation;
 
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
@@ -23,10 +23,10 @@
 
 /**
  * This is a class annotation that signals that it is safe to create
- * a native interpolator counterpart via {@link NativeInterpolatorFactory}
+ * a native interpolator counterpart via {@link NativeInterpolator}
  *
  * The idea here is to prevent subclasses of interpolators from being treated as a
- * NativeInterpolatorFactory, and instead have them fall back to the LUT & LERP
+ * NativeInterpolator, and instead have them fall back to the LUT & LERP
  * method like a custom interpolator.
  *
  * @hide
diff --git a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java b/graphics/java/android/graphics/animation/NativeInterpolator.java
similarity index 71%
copy from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
copy to graphics/java/android/graphics/animation/NativeInterpolator.java
index fcacd52..1e6fea8 100644
--- a/core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
+++ b/graphics/java/android/graphics/animation/NativeInterpolator.java
@@ -14,8 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.internal.view.animation;
+package android.graphics.animation;
 
-public interface NativeInterpolatorFactory {
+/**
+ * @hide
+ */
+public interface NativeInterpolator {
+    /**
+     * Generates a native interpolator object that can be used by HardwareRenderer to draw
+     * RenderNodes.
+     * @return ptr to native object
+     */
     long createNativeInterpolator();
 }
diff --git a/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
new file mode 100644
index 0000000..3886614
--- /dev/null
+++ b/graphics/java/android/graphics/animation/NativeInterpolatorFactory.java
@@ -0,0 +1,67 @@
+/*
+ * 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 android.graphics.animation;
+
+import android.animation.TimeInterpolator;
+
+/**
+ * Static utility class for constructing native interpolators to keep the
+ * JNI simpler
+ *
+ * @hide
+ */
+public final class NativeInterpolatorFactory {
+    private NativeInterpolatorFactory() {}
+
+    /**
+     * Create a native interpolator from the provided param generating a LUT variant if a native
+     * implementation does not exist.
+     */
+    public static long createNativeInterpolator(TimeInterpolator interpolator, long
+            duration) {
+        if (interpolator == null) {
+            return createLinearInterpolator();
+        } else if (RenderNodeAnimator.isNativeInterpolator(interpolator)) {
+            return ((NativeInterpolator) interpolator).createNativeInterpolator();
+        } else {
+            return FallbackLUTInterpolator.createNativeInterpolator(interpolator, duration);
+        }
+    }
+
+    /** Creates a specialized native interpolator for Accelerate/Decelerate */
+    public static native long createAccelerateDecelerateInterpolator();
+    /** Creates a specialized native interpolator for Accelerate */
+    public static native long createAccelerateInterpolator(float factor);
+    /** Creates a specialized native interpolator for Anticipate */
+    public static native long createAnticipateInterpolator(float tension);
+    /** Creates a specialized native interpolator for Anticipate with Overshoot */
+    public static native long createAnticipateOvershootInterpolator(float tension);
+    /** Creates a specialized native interpolator for Bounce */
+    public static native long createBounceInterpolator();
+    /** Creates a specialized native interpolator for Cycle */
+    public static native long createCycleInterpolator(float cycles);
+    /** Creates a specialized native interpolator for Decelerate */
+    public static native long createDecelerateInterpolator(float factor);
+    /** Creates a specialized native interpolator for Linear interpolation */
+    public static native long createLinearInterpolator();
+    /** Creates a specialized native interpolator for Overshoot */
+    public static native long createOvershootInterpolator(float tension);
+    /** Creates a specialized native interpolator for along traveling along a Path */
+    public static native long createPathInterpolator(float[] x, float[] y);
+    /** Creates a specialized native interpolator for LUT */
+    public static native long createLutInterpolator(float[] values);
+}
diff --git a/graphics/java/android/graphics/animation/RenderNodeAnimator.java b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
new file mode 100644
index 0000000..282b2f9
--- /dev/null
+++ b/graphics/java/android/graphics/animation/RenderNodeAnimator.java
@@ -0,0 +1,513 @@
+/*
+ * 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 android.graphics.animation;
+
+import android.animation.Animator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.graphics.CanvasProperty;
+import android.graphics.Paint;
+import android.graphics.RecordingCanvas;
+import android.graphics.RenderNode;
+import android.os.Handler;
+import android.os.Looper;
+import android.view.Choreographer;
+
+import com.android.internal.util.VirtualRefBasePtr;
+
+import java.util.ArrayList;
+
+/**
+ * @hide
+ */
+public class RenderNodeAnimator extends Animator {
+    // Keep in sync with enum RenderProperty in Animator.h
+    public static final int TRANSLATION_X = 0;
+    public static final int TRANSLATION_Y = 1;
+    public static final int TRANSLATION_Z = 2;
+    public static final int SCALE_X = 3;
+    public static final int SCALE_Y = 4;
+    public static final int ROTATION = 5;
+    public static final int ROTATION_X = 6;
+    public static final int ROTATION_Y = 7;
+    public static final int X = 8;
+    public static final int Y = 9;
+    public static final int Z = 10;
+    public static final int ALPHA = 11;
+    // The last value in the enum, used for array size initialization
+    public static final int LAST_VALUE = ALPHA;
+
+    // Keep in sync with enum PaintFields in Animator.h
+    public static final int PAINT_STROKE_WIDTH = 0;
+
+    /**
+     * Field for the Paint alpha channel, which should be specified as a value
+     * between 0 and 255.
+     */
+    public static final int PAINT_ALPHA = 1;
+
+    private VirtualRefBasePtr mNativePtr;
+
+    private Handler mHandler;
+    private RenderNode mTarget;
+    private ViewListener mViewListener;
+    private int mRenderProperty = -1;
+    private float mFinalValue;
+    private TimeInterpolator mInterpolator;
+
+    private static final int STATE_PREPARE = 0;
+    private static final int STATE_DELAYED = 1;
+    private static final int STATE_RUNNING = 2;
+    private static final int STATE_FINISHED = 3;
+    private int mState = STATE_PREPARE;
+
+    private long mUnscaledDuration = 300;
+    private long mUnscaledStartDelay = 0;
+    // If this is true, we will run any start delays on the UI thread. This is
+    // the safe default, and is necessary to ensure start listeners fire at
+    // the correct time. Animators created by RippleDrawable (the
+    // CanvasProperty<> ones) do not have this expectation, and as such will
+    // set this to false so that the renderthread handles the startdelay instead
+    private final boolean mUiThreadHandlesDelay;
+    private long mStartDelay = 0;
+    private long mStartTime;
+
+    /**
+     * Interface used by the view system to update the view hierarchy in conjunction
+     * with this animator.
+     */
+    public interface ViewListener {
+        /** notify the listener that an alpha animation has begun. */
+        void onAlphaAnimationStart(float finalAlpha);
+        /** notify the listener that the animator has mutated a value that requires invalidation */
+        void invalidateParent(boolean forceRedraw);
+    }
+
+    public RenderNodeAnimator(int property, float finalValue) {
+        mRenderProperty = property;
+        mFinalValue = finalValue;
+        mUiThreadHandlesDelay = true;
+        init(nCreateAnimator(property, finalValue));
+    }
+
+    public RenderNodeAnimator(CanvasProperty<Float> property, float finalValue) {
+        init(nCreateCanvasPropertyFloatAnimator(
+                property.getNativeContainer(), finalValue));
+        mUiThreadHandlesDelay = false;
+    }
+
+    /**
+     * Creates a new render node animator for a field on a Paint property.
+     *
+     * @param property The paint property to target
+     * @param paintField Paint field to animate, one of {@link #PAINT_ALPHA} or
+     *            {@link #PAINT_STROKE_WIDTH}
+     * @param finalValue The target value for the property
+     */
+    public RenderNodeAnimator(CanvasProperty<Paint> property, int paintField, float finalValue) {
+        init(nCreateCanvasPropertyPaintAnimator(
+                property.getNativeContainer(), paintField, finalValue));
+        mUiThreadHandlesDelay = false;
+    }
+
+    public RenderNodeAnimator(int x, int y, float startRadius, float endRadius) {
+        init(nCreateRevealAnimator(x, y, startRadius, endRadius));
+        mUiThreadHandlesDelay = true;
+    }
+
+    private void init(long ptr) {
+        mNativePtr = new VirtualRefBasePtr(ptr);
+    }
+
+    private void checkMutable() {
+        if (mState != STATE_PREPARE) {
+            throw new IllegalStateException("Animator has already started, cannot change it now!");
+        }
+        if (mNativePtr == null) {
+            throw new IllegalStateException("Animator's target has been destroyed "
+                    + "(trying to modify an animation after activity destroy?)");
+        }
+    }
+
+    static boolean isNativeInterpolator(TimeInterpolator interpolator) {
+        return interpolator.getClass().isAnnotationPresent(HasNativeInterpolator.class);
+    }
+
+    private void applyInterpolator() {
+        if (mInterpolator == null || mNativePtr == null) return;
+
+        long ni;
+        if (isNativeInterpolator(mInterpolator)) {
+            ni = ((NativeInterpolator) mInterpolator).createNativeInterpolator();
+        } else {
+            long duration = nGetDuration(mNativePtr.get());
+            ni = FallbackLUTInterpolator.createNativeInterpolator(mInterpolator, duration);
+        }
+        nSetInterpolator(mNativePtr.get(), ni);
+    }
+
+    @Override
+    public void start() {
+        if (mTarget == null) {
+            throw new IllegalStateException("Missing target!");
+        }
+
+        if (mState != STATE_PREPARE) {
+            throw new IllegalStateException("Already started!");
+        }
+
+        mState = STATE_DELAYED;
+        if (mHandler == null) {
+            mHandler = new Handler(true);
+        }
+        applyInterpolator();
+
+        if (mNativePtr == null) {
+            // It's dead, immediately cancel
+            cancel();
+        } else if (mStartDelay <= 0 || !mUiThreadHandlesDelay) {
+            nSetStartDelay(mNativePtr.get(), mStartDelay);
+            doStart();
+        } else {
+            getHelper().addDelayedAnimation(this);
+        }
+    }
+
+    private void doStart() {
+        // Alpha is a special snowflake that has the canonical value stored
+        // in mTransformationInfo instead of in RenderNode, so we need to update
+        // it with the final value here.
+        if (mRenderProperty == RenderNodeAnimator.ALPHA && mViewListener != null) {
+            mViewListener.onAlphaAnimationStart(mFinalValue);
+        }
+
+        moveToRunningState();
+
+        if (mViewListener != null) {
+            // Kick off a frame to start the process
+            mViewListener.invalidateParent(false);
+        }
+    }
+
+    private void moveToRunningState() {
+        mState = STATE_RUNNING;
+        if (mNativePtr != null) {
+            nStart(mNativePtr.get());
+        }
+        notifyStartListeners();
+    }
+
+    private void notifyStartListeners() {
+        final ArrayList<AnimatorListener> listeners = cloneListeners();
+        final int numListeners = listeners == null ? 0 : listeners.size();
+        for (int i = 0; i < numListeners; i++) {
+            listeners.get(i).onAnimationStart(this);
+        }
+    }
+
+    @Override
+    public void cancel() {
+        if (mState != STATE_PREPARE && mState != STATE_FINISHED) {
+            if (mState == STATE_DELAYED) {
+                getHelper().removeDelayedAnimation(this);
+                moveToRunningState();
+            }
+
+            final ArrayList<AnimatorListener> listeners = cloneListeners();
+            final int numListeners = listeners == null ? 0 : listeners.size();
+            for (int i = 0; i < numListeners; i++) {
+                listeners.get(i).onAnimationCancel(this);
+            }
+
+            end();
+        }
+    }
+
+    @Override
+    public void end() {
+        if (mState != STATE_FINISHED) {
+            if (mState < STATE_RUNNING) {
+                getHelper().removeDelayedAnimation(this);
+                doStart();
+            }
+            if (mNativePtr != null) {
+                nEnd(mNativePtr.get());
+                if (mViewListener != null) {
+                    // Kick off a frame to flush the state change
+                    mViewListener.invalidateParent(false);
+                }
+            } else {
+                // It's already dead, jump to onFinish
+                onFinished();
+            }
+        }
+    }
+
+    @Override
+    public void pause() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void resume() {
+        throw new UnsupportedOperationException();
+    }
+
+    /** @hide */
+    public void setViewListener(ViewListener listener) {
+        mViewListener = listener;
+    }
+
+    /** Sets the animation target to the owning view of the RecordingCanvas */
+    public final void setTarget(RecordingCanvas canvas) {
+        setTarget(canvas.mNode);
+    }
+
+    /** Sets the node that is to be the target of this animation */
+    protected void setTarget(RenderNode node) {
+        checkMutable();
+        if (mTarget != null) {
+            throw new IllegalStateException("Target already set!");
+        }
+        nSetListener(mNativePtr.get(), this);
+        mTarget = node;
+        mTarget.addAnimator(this);
+    }
+
+    /** Set the start value for the animation */
+    public void setStartValue(float startValue) {
+        checkMutable();
+        nSetStartValue(mNativePtr.get(), startValue);
+    }
+
+    @Override
+    public void setStartDelay(long startDelay) {
+        checkMutable();
+        if (startDelay < 0) {
+            throw new IllegalArgumentException("startDelay must be positive; " + startDelay);
+        }
+        mUnscaledStartDelay = startDelay;
+        mStartDelay = (long) (ValueAnimator.getDurationScale() * startDelay);
+    }
+
+    @Override
+    public long getStartDelay() {
+        return mUnscaledStartDelay;
+    }
+
+    @Override
+    public RenderNodeAnimator setDuration(long duration) {
+        checkMutable();
+        if (duration < 0) {
+            throw new IllegalArgumentException("duration must be positive; " + duration);
+        }
+        mUnscaledDuration = duration;
+        nSetDuration(mNativePtr.get(), (long) (duration * ValueAnimator.getDurationScale()));
+        return this;
+    }
+
+    @Override
+    public long getDuration() {
+        return mUnscaledDuration;
+    }
+
+    @Override
+    public long getTotalDuration() {
+        return mUnscaledDuration + mUnscaledStartDelay;
+    }
+
+    @Override
+    public boolean isRunning() {
+        return mState == STATE_DELAYED || mState == STATE_RUNNING;
+    }
+
+    @Override
+    public boolean isStarted() {
+        return mState != STATE_PREPARE;
+    }
+
+    @Override
+    public void setInterpolator(TimeInterpolator interpolator) {
+        checkMutable();
+        mInterpolator = interpolator;
+    }
+
+    @Override
+    public TimeInterpolator getInterpolator() {
+        return mInterpolator;
+    }
+
+    protected void onFinished() {
+        if (mState == STATE_PREPARE) {
+            // Unlikely but possible, the native side has been destroyed
+            // before we have started.
+            releaseNativePtr();
+            return;
+        }
+        if (mState == STATE_DELAYED) {
+            getHelper().removeDelayedAnimation(this);
+            notifyStartListeners();
+        }
+        mState = STATE_FINISHED;
+
+        final ArrayList<AnimatorListener> listeners = cloneListeners();
+        final int numListeners = listeners == null ? 0 : listeners.size();
+        for (int i = 0; i < numListeners; i++) {
+            listeners.get(i).onAnimationEnd(this);
+        }
+
+        // Release the native object, as it has a global reference to us. This
+        // breaks the cyclic reference chain, and allows this object to be
+        // GC'd
+        releaseNativePtr();
+    }
+
+    private void releaseNativePtr() {
+        if (mNativePtr != null) {
+            mNativePtr.release();
+            mNativePtr = null;
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private ArrayList<AnimatorListener> cloneListeners() {
+        ArrayList<AnimatorListener> listeners = getListeners();
+        if (listeners != null) {
+            listeners = (ArrayList<AnimatorListener>) listeners.clone();
+        }
+        return listeners;
+    }
+
+    public long getNativeAnimator() {
+        return mNativePtr.get();
+    }
+
+    /**
+     * @return true if the animator was started, false if still delayed
+     */
+    private boolean processDelayed(long frameTimeMs) {
+        if (mStartTime == 0) {
+            mStartTime = frameTimeMs;
+        } else if ((frameTimeMs - mStartTime) >= mStartDelay) {
+            doStart();
+            return true;
+        }
+        return false;
+    }
+
+    private static DelayedAnimationHelper getHelper() {
+        DelayedAnimationHelper helper = sAnimationHelper.get();
+        if (helper == null) {
+            helper = new DelayedAnimationHelper();
+            sAnimationHelper.set(helper);
+        }
+        return helper;
+    }
+
+    private static ThreadLocal<DelayedAnimationHelper> sAnimationHelper =
+            new ThreadLocal<DelayedAnimationHelper>();
+
+    private static class DelayedAnimationHelper implements Runnable {
+
+        private ArrayList<RenderNodeAnimator> mDelayedAnims = new ArrayList<RenderNodeAnimator>();
+        private final Choreographer mChoreographer;
+        private boolean mCallbackScheduled;
+
+        DelayedAnimationHelper() {
+            mChoreographer = Choreographer.getInstance();
+        }
+
+        public void addDelayedAnimation(RenderNodeAnimator animator) {
+            mDelayedAnims.add(animator);
+            scheduleCallback();
+        }
+
+        public void removeDelayedAnimation(RenderNodeAnimator animator) {
+            mDelayedAnims.remove(animator);
+        }
+
+        private void scheduleCallback() {
+            if (!mCallbackScheduled) {
+                mCallbackScheduled = true;
+                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
+            }
+        }
+
+        @Override
+        public void run() {
+            long frameTimeMs = mChoreographer.getFrameTime();
+            mCallbackScheduled = false;
+
+            int end = 0;
+            for (int i = 0; i < mDelayedAnims.size(); i++) {
+                RenderNodeAnimator animator = mDelayedAnims.get(i);
+                if (!animator.processDelayed(frameTimeMs)) {
+                    if (end != i) {
+                        mDelayedAnims.set(end, animator);
+                    }
+                    end++;
+                }
+            }
+            while (mDelayedAnims.size() > end) {
+                mDelayedAnims.remove(mDelayedAnims.size() - 1);
+            }
+
+            if (mDelayedAnims.size() > 0) {
+                scheduleCallback();
+            }
+        }
+    }
+
+    // Called by native
+    private static void callOnFinished(RenderNodeAnimator animator) {
+        if (animator.mHandler != null) {
+            animator.mHandler.post(animator::onFinished);
+        } else {
+            new Handler(Looper.getMainLooper(), null, true).post(animator::onFinished);
+        }
+    }
+
+    @Override
+    public Animator clone() {
+        throw new IllegalStateException("Cannot clone this animator");
+    }
+
+    @Override
+    public void setAllowRunningAsynchronously(boolean mayRunAsync) {
+        checkMutable();
+        nSetAllowRunningAsync(mNativePtr.get(), mayRunAsync);
+    }
+
+    private static native long nCreateAnimator(int property, float finalValue);
+    private static native long nCreateCanvasPropertyFloatAnimator(
+            long canvasProperty, float finalValue);
+    private static native long nCreateCanvasPropertyPaintAnimator(
+            long canvasProperty, int paintField, float finalValue);
+    private static native long nCreateRevealAnimator(
+            int x, int y, float startRadius, float endRadius);
+
+    private static native void nSetStartValue(long nativePtr, float startValue);
+    private static native void nSetDuration(long nativePtr, long duration);
+    private static native long nGetDuration(long nativePtr);
+    private static native void nSetStartDelay(long nativePtr, long startDelay);
+    private static native void nSetInterpolator(long animPtr, long interpolatorPtr);
+    private static native void nSetAllowRunningAsync(long animPtr, boolean mayRunAsync);
+    private static native void nSetListener(long animPtr, RenderNodeAnimator listener);
+
+    private static native void nStart(long animPtr);
+    private static native void nEnd(long animPtr);
+}
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 1acf6c5..9fb72cf 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -42,6 +42,7 @@
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
 import android.graphics.RenderNode;
+import android.graphics.animation.NativeInterpolatorFactory;
 import android.os.Build;
 import android.os.Handler;
 import android.util.ArrayMap;
@@ -54,7 +55,6 @@
 import android.util.TimeUtils;
 import android.view.Choreographer;
 import android.view.NativeVectorDrawableAnimator;
-import android.view.RenderNodeAnimatorSetHelper;
 import android.view.View;
 
 import com.android.internal.R;
@@ -1532,7 +1532,7 @@
             long startDelay = extraDelay + animator.getStartDelay();
             TimeInterpolator interpolator = animator.getInterpolator();
             long nativeInterpolator =
-                    RenderNodeAnimatorSetHelper.createNativeInterpolator(interpolator, duration);
+                    NativeInterpolatorFactory.createNativeInterpolator(interpolator, duration);
 
             startDelay *= ValueAnimator.getDurationScale();
             duration *= ValueAnimator.getDurationScale();
@@ -1548,7 +1548,7 @@
          * to the last seen RenderNode target and start right away.
          */
         protected void recordLastSeenTarget(RecordingCanvas canvas) {
-            final RenderNode node = RenderNodeAnimatorSetHelper.getTarget(canvas);
+            final RenderNode node = canvas.mNode;
             mLastSeenTarget = new WeakReference<RenderNode>(node);
             // Add the animator to the list of animators on every draw
             if (mInitialized || mPendingAnimationActions.size() > 0) {
diff --git a/graphics/java/android/graphics/drawable/RippleForeground.java b/graphics/java/android/graphics/drawable/RippleForeground.java
index cce9ba3..0f37695 100644
--- a/graphics/java/android/graphics/drawable/RippleForeground.java
+++ b/graphics/java/android/graphics/drawable/RippleForeground.java
@@ -25,9 +25,9 @@
 import android.graphics.Paint;
 import android.graphics.RecordingCanvas;
 import android.graphics.Rect;
+import android.graphics.animation.RenderNodeAnimator;
 import android.util.FloatProperty;
 import android.util.MathUtils;
-import android.view.RenderNodeAnimator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.LinearInterpolator;
 import android.view.animation.PathInterpolator;
diff --git a/identity/java/android/security/identity/CredstoreIdentityCredential.java b/identity/java/android/security/identity/CredstoreIdentityCredential.java
index c520331..7c0af6d 100644
--- a/identity/java/android/security/identity/CredstoreIdentityCredential.java
+++ b/identity/java/android/security/identity/CredstoreIdentityCredential.java
@@ -152,8 +152,8 @@
             derivedKey = Util.computeHkdf("HmacSha256", sharedSecret, salt, info, 32);
             mReaderSecretKey = new SecretKeySpec(derivedKey, "AES");
 
-            mEphemeralCounter = 0;
-            mReadersExpectedEphemeralCounter = 0;
+            mEphemeralCounter = 1;
+            mReadersExpectedEphemeralCounter = 1;
 
         } catch (NoSuchAlgorithmException e) {
             throw new RuntimeException("Error performing key agreement", e);
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index debb38b2..81dedda 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -84,7 +84,6 @@
                 "libGLESv3",
                 "libvulkan",
                 "libui",
-                "libgui",
                 "libnativedisplay",
                 "libnativewindow",
                 "libprotobuf-cpp-lite",
@@ -289,6 +288,13 @@
     name: "hwui_test_defaults",
     defaults: ["hwui_defaults"],
     test_suites: ["device-tests"],
+    target: {
+        android: {
+            shared_libs: [
+	        "libgui",
+	    ],
+	}
+    },
     srcs: [
         "tests/common/scenes/*.cpp",
         "tests/common/LeakChecker.cpp",
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 58cc08b..914c046 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -32,7 +32,6 @@
 
 #ifndef _WIN32
 #include <binder/IServiceManager.h>
-#include <private/gui/ComposerService.h>
 #endif
 #include <ui/PixelFormat.h>
 
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
index e482cad..fc6e114 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h
@@ -16,8 +16,10 @@
 
 #pragma once
 
-#include "SkiaPipeline.h"
+#include <EGL/egl.h>
+#include <system/window.h>
 
+#include "SkiaPipeline.h"
 #include "renderstate/RenderState.h"
 
 namespace android {
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 29b4dd7..41aa1ff 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -24,18 +24,19 @@
 #include <SkOverdrawColorFilter.h>
 #include <SkPicture.h>
 #include <SkPictureRecorder.h>
-#include <SkTypeface.h>
 #include <SkSerialProcs.h>
+#include <SkTypeface.h>
+#include <android-base/properties.h>
+#include <unistd.h>
+
+#include <sstream>
+
 #include "LightingInfo.h"
 #include "VectorDrawable.h"
 #include "thread/CommonPool.h"
 #include "tools/SkSharingProc.h"
-#include "utils/TraceUtils.h"
 #include "utils/String8.h"
-
-#include <unistd.h>
-
-#include <android-base/properties.h>
+#include "utils/TraceUtils.h"
 
 using namespace android::uirenderer::renderthread;
 
@@ -600,27 +601,24 @@
 // Overdraw debugging
 
 // These colors should be kept in sync with Caches::getOverdrawColor() with a few differences.
-// This implementation:
-// (1) Requires transparent entries for "no overdraw" and "single draws".
-// (2) Requires premul colors (instead of unpremul).
-// (3) Requires RGBA colors (instead of BGRA).
-static const uint32_t kOverdrawColors[2][6] = {
-        {
-                0x00000000,
-                0x00000000,
-                0x2f2f0000,
-                0x2f002f00,
-                0x3f00003f,
-                0x7f00007f,
-        },
-        {
-                0x00000000,
-                0x00000000,
-                0x2f2f0000,
-                0x4f004f4f,
-                0x5f50335f,
-                0x7f00007f,
-        },
+// This implementation requires transparent entries for "no overdraw" and "single draws".
+static const SkColor kOverdrawColors[2][6] = {
+    {
+        0x00000000,
+        0x00000000,
+        0x2f0000ff,
+        0x2f00ff00,
+        0x3fff0000,
+        0x7fff0000,
+    },
+    {
+        0x00000000,
+        0x00000000,
+        0x2f0000ff,
+        0x4fffff00,
+        0x5fff89d7,
+        0x7fff0000,
+    },
 };
 
 void SkiaPipeline::renderOverdraw(const SkRect& clip,
@@ -642,8 +640,8 @@
 
     // Draw overdraw colors to the canvas.  The color filter will convert counts to colors.
     SkPaint paint;
-    const SkPMColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
-    paint.setColorFilter(SkOverdrawColorFilter::Make(colors));
+    const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
+    paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors));
     surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 91f9447..4299dd3 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -139,15 +139,15 @@
     mAnimationContext->destroy();
 }
 
-void CanvasContext::setSurface(sp<Surface>&& surface, bool enableTimeout) {
+void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) {
     ATRACE_CALL();
 
-    if (surface) {
-        mNativeSurface = std::make_unique<ReliableSurface>(std::move(surface));
+    if (window) {
+        mNativeSurface = std::make_unique<ReliableSurface>(window);
         mNativeSurface->init();
         if (enableTimeout) {
             // TODO: Fix error handling & re-shorten timeout
-            ANativeWindow_setDequeueTimeout(mNativeSurface->getNativeWindow(), 4000_ms);
+            ANativeWindow_setDequeueTimeout(window, 4000_ms);
         }
     } else {
         mNativeSurface = nullptr;
@@ -167,7 +167,7 @@
 
     mFrameNumber = -1;
 
-    if (hasSurface) {
+    if (window != nullptr && hasSurface) {
         mHaveNewSurface = true;
         mSwapHistory.clear();
         // Enable frame stats after the surface has been bound to the appropriate graphics API.
@@ -204,8 +204,7 @@
 
 void CanvasContext::allocateBuffers() {
     if (mNativeSurface) {
-        ANativeWindow* anw = mNativeSurface->getNativeWindow();
-        ANativeWindow_allocateBuffers(anw);
+        ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
     }
 }
 
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 629c741..0f1b8ae 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -35,7 +35,6 @@
 #include <SkRect.h>
 #include <SkSize.h>
 #include <cutils/compiler.h>
-#include <gui/Surface.h>
 #include <utils/Functor.h>
 
 #include <functional>
@@ -111,7 +110,7 @@
     // Won't take effect until next EGLSurface creation
     void setSwapBehavior(SwapBehavior swapBehavior);
 
-    void setSurface(sp<Surface>&& surface, bool enableTimeout = true);
+    void setSurface(ANativeWindow* window, bool enableTimeout = true);
     bool pauseSurface();
     void setStopped(bool stopped);
     bool hasSurface() const { return mNativeSurface.get(); }
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index c1fed26..5e0471c 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -20,7 +20,6 @@
 #include <GLES/gl.h>
 #include <cutils/properties.h>
 #include <log/log.h>
-#include <private/gui/SyncFeatures.h>
 #include <sync/sync.h>
 #include <utils/Trace.h>
 
@@ -79,6 +78,9 @@
     bool displayP3 = false;
     bool contextPriority = false;
     bool surfacelessContext = false;
+    bool nativeFenceSync = false;
+    bool fenceSync = false;
+    bool waitSync = false;
 } EglExtensions;
 
 EglManager::EglManager()
@@ -226,6 +228,9 @@
     EglExtensions.displayP3 = extensions.has("EGL_EXT_gl_colorspace_display_p3_passthrough");
     EglExtensions.contextPriority = extensions.has("EGL_IMG_context_priority");
     EglExtensions.surfacelessContext = extensions.has("EGL_KHR_surfaceless_context");
+    EglExtensions.nativeFenceSync = extensions.has("EGL_ANDROID_native_fence_sync");
+    EglExtensions.fenceSync = extensions.has("EGL_KHR_fence_sync");
+    EglExtensions.waitSync = extensions.has("EGL_KHR_wait_sync");
 }
 
 bool EglManager::hasEglContext() {
@@ -527,8 +532,7 @@
         return INVALID_OPERATION;
     }
 
-    if (SyncFeatures::getInstance().useWaitSync() &&
-        SyncFeatures::getInstance().useNativeFenceSync()) {
+    if (EglExtensions.waitSync && EglExtensions.nativeFenceSync) {
         // Block GPU on the fence.
         // Create an EGLSyncKHR from the current fence.
         int fenceFd = ::dup(fence);
@@ -572,7 +576,7 @@
         return INVALID_OPERATION;
     }
 
-    if (SyncFeatures::getInstance().useNativeFenceSync()) {
+    if (EglExtensions.nativeFenceSync) {
         EGLSyncKHR sync = eglCreateSyncKHR(mEglDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
         if (sync == EGL_NO_SYNC_KHR) {
             ALOGE("EglManager::createReleaseFence: error creating EGL fence: %#x", eglGetError());
@@ -589,7 +593,7 @@
         }
         *nativeFence = fenceFd;
         *eglFence = EGL_NO_SYNC_KHR;
-    } else if (useFenceSync && SyncFeatures::getInstance().useFenceSync()) {
+    } else if (useFenceSync && EglExtensions.fenceSync) {
         if (*eglFence != EGL_NO_SYNC_KHR) {
             // There is already a fence for the current slot.  We need to
             // wait on that before replacing it with another fence to
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index e92500f..8a0b4e8 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -16,7 +16,10 @@
 
 #include "ReliableSurface.h"
 
+#include <log/log_main.h>
 #include <private/android/AHardwareBufferHelpers.h>
+// TODO: this should be including apex instead.
+#include <vndk/window.h>
 
 namespace android::uirenderer::renderthread {
 
@@ -26,8 +29,9 @@
 // to propagate this error back to the caller
 constexpr bool DISABLE_BUFFER_PREFETCH = true;
 
-ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
-    LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
+ReliableSurface::ReliableSurface(ANativeWindow* window) : mWindow(window) {
+    LOG_ALWAYS_FATAL_IF(!mWindow, "Error, unable to wrap a nullptr");
+    ANativeWindow_acquire(mWindow);
 }
 
 ReliableSurface::~ReliableSurface() {
@@ -36,26 +40,27 @@
     // As a concrete example, if the underlying ANativeWindow is associated with
     // an EGLSurface that is still in use, then if we don't clear out the
     // interceptors then we walk into undefined behavior.
-    ANativeWindow_setCancelBufferInterceptor(mSurface.get(), nullptr, nullptr);
-    ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), nullptr, nullptr);
-    ANativeWindow_setQueueBufferInterceptor(mSurface.get(), nullptr, nullptr);
-    ANativeWindow_setPerformInterceptor(mSurface.get(), nullptr, nullptr);
+    ANativeWindow_setCancelBufferInterceptor(mWindow, nullptr, nullptr);
+    ANativeWindow_setDequeueBufferInterceptor(mWindow, nullptr, nullptr);
+    ANativeWindow_setQueueBufferInterceptor(mWindow, nullptr, nullptr);
+    ANativeWindow_setPerformInterceptor(mWindow, nullptr, nullptr);
+    ANativeWindow_release(mWindow);
 }
 
 void ReliableSurface::init() {
-    int result = ANativeWindow_setCancelBufferInterceptor(mSurface.get(), hook_cancelBuffer, this);
+    int result = ANativeWindow_setCancelBufferInterceptor(mWindow, hook_cancelBuffer, this);
     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d",
                         result);
 
-    result = ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), hook_dequeueBuffer, this);
+    result = ANativeWindow_setDequeueBufferInterceptor(mWindow, hook_dequeueBuffer, this);
     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d",
                         result);
 
-    result = ANativeWindow_setQueueBufferInterceptor(mSurface.get(), hook_queueBuffer, this);
+    result = ANativeWindow_setQueueBufferInterceptor(mWindow, hook_queueBuffer, this);
     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d",
                         result);
 
-    result = ANativeWindow_setPerformInterceptor(mSurface.get(), hook_perform, this);
+    result = ANativeWindow_setPerformInterceptor(mWindow, hook_perform, this);
     LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
                         result);
 }
@@ -87,7 +92,7 @@
     ANativeWindowBuffer* buffer = nullptr;
 
     // Note that this calls back into our own hooked method.
-    int result = ANativeWindow_dequeueBuffer(mSurface.get(), &buffer, &fenceFd);
+    int result = ANativeWindow_dequeueBuffer(mWindow, &buffer, &fenceFd);
 
     {
         std::lock_guard _lock{mMutex};
@@ -117,7 +122,7 @@
         // Note that clearReservedBuffer may be reentrant here, so
         // mReservedBuffer must be cleared once we reach here to avoid recursing
         // forever.
-        ANativeWindow_cancelBuffer(mSurface.get(), buffer, releaseFd);
+        ANativeWindow_cancelBuffer(mWindow, buffer, releaseFd);
     }
 }
 
@@ -239,10 +244,10 @@
             case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY:
                 /* width */ va_arg(args, uint32_t);
                 /* height */ va_arg(args, uint32_t);
-                rs->mFormat = va_arg(args, PixelFormat);
+                rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
                 break;
             case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
-                rs->mFormat = va_arg(args, PixelFormat);
+                rs->mFormat = static_cast<AHardwareBuffer_Format>(va_arg(args, int32_t));
                 break;
         }
     }
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index e3cd8c0..58cd067 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -16,12 +16,14 @@
 
 #pragma once
 
+#include <android-base/unique_fd.h>
 #include <apex/window.h>
-#include <gui/Surface.h>
+#include <utils/Errors.h>
 #include <utils/Macros.h>
 #include <utils/StrongPointer.h>
 
 #include <memory>
+#include <mutex>
 
 namespace android::uirenderer::renderthread {
 
@@ -29,7 +31,7 @@
     PREVENT_COPY_AND_ASSIGN(ReliableSurface);
 
 public:
-    ReliableSurface(sp<Surface>&& surface);
+    ReliableSurface(ANativeWindow* window);
     ~ReliableSurface();
 
     // Performs initialization that is not safe to do in the constructor.
@@ -37,12 +39,10 @@
     // passed as the data pointer is not safe.
     void init();
 
-    ANativeWindow* getNativeWindow() { return mSurface.get(); }
+    ANativeWindow* getNativeWindow() { return mWindow; }
 
     int reserveNext();
 
-    int query(int what, int* value) const { return mSurface->query(what, value); }
-
     int getAndClearError() {
         int ret = mBufferQueueState;
         mBufferQueueState = OK;
@@ -50,12 +50,12 @@
     }
 
 private:
-    sp<Surface> mSurface;
+    ANativeWindow* mWindow;
 
     mutable std::mutex mMutex;
 
     uint64_t mUsage = AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
-    PixelFormat mFormat = PIXEL_FORMAT_RGBA_8888;
+    AHardwareBuffer_Format mFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
     std::unique_ptr<AHardwareBuffer, void (*)(AHardwareBuffer*)> mScratchBuffer{
             nullptr, AHardwareBuffer_release};
     ANativeWindowBuffer* mReservedBuffer = nullptr;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 1e7fc71..b66a13d 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -16,8 +16,6 @@
 
 #include "RenderProxy.h"
 
-#include <gui/Surface.h>
-
 #include "DeferredLayerUpdater.h"
 #include "DisplayList.h"
 #include "Properties.h"
@@ -78,9 +76,11 @@
     mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); });
 }
 
-void RenderProxy::setSurface(const sp<Surface>& surface, bool enableTimeout) {
-    mRenderThread.queue().post([this, surf = surface, enableTimeout]() mutable {
-        mContext->setSurface(std::move(surf), enableTimeout);
+void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) {
+    ANativeWindow_acquire(window);
+    mRenderThread.queue().post([this, win = window, enableTimeout]() mutable {
+        mContext->setSurface(win, enableTimeout);
+        ANativeWindow_release(win);
     });
 }
 
@@ -314,10 +314,9 @@
             [context = mContext, renderAhead] { context->setRenderAheadDepth(renderAhead); });
 }
 
-int RenderProxy::copySurfaceInto(sp<Surface>& surface, int left, int top, int right, int bottom,
+int RenderProxy::copySurfaceInto(ANativeWindow* window, int left, int top, int right, int bottom,
                                  SkBitmap* bitmap) {
     auto& thread = RenderThread::getInstance();
-    ANativeWindow* window = surface.get();
     return static_cast<int>(thread.queue().runSync([&]() -> auto {
         return thread.readback().copySurfaceInto(window, Rect(left, top, right, bottom), bitmap);
     }));
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index ab0dd2b..3baeb2f 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -18,6 +18,7 @@
 #define RENDERPROXY_H_
 
 #include <SkBitmap.h>
+#include <android/native_window.h>
 #include <cutils/compiler.h>
 #include <utils/Functor.h>
 
@@ -69,7 +70,7 @@
     ANDROID_API bool loadSystemProperties();
     ANDROID_API void setName(const char* name);
 
-    ANDROID_API void setSurface(const sp<Surface>& surface, bool enableTimeout = true);
+    ANDROID_API void setSurface(ANativeWindow* window, bool enableTimeout = true);
     ANDROID_API void allocateBuffers();
     ANDROID_API bool pause();
     ANDROID_API void setStopped(bool stopped);
@@ -140,11 +141,7 @@
      */
     ANDROID_API void setRenderAheadDepth(int renderAhead);
 
-    // TODO: This api will need to take in an ANativeWindow instead, but the
-    // caller, ThreadedRenderer, doesn't have access to libandroid due to a
-    // circular dependency, so it can't use the JNI ANativeWindow methods. Once
-    // that is resolved then replace the surface type here.
-    ANDROID_API static int copySurfaceInto(sp<Surface>& surface, int left, int top, int right,
+    ANDROID_API static int copySurfaceInto(ANativeWindow* window, int left, int top, int right,
                                            int bottom, SkBitmap* bitmap);
     ANDROID_API static void prepareToDraw(Bitmap& bitmap);
 
diff --git a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
index f6cff1c..f4fce27 100644
--- a/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
+++ b/libs/hwui/tests/common/scenes/MagnifierAnimation.cpp
@@ -70,7 +70,7 @@
             magnifier->getSkBitmap(&temp);
             constexpr int x = 90;
             constexpr int y = 325;
-            RenderProxy::copySurfaceInto(renderTarget, x, y, x + magnifier->width(),
+            RenderProxy::copySurfaceInto(renderTarget.get(), x, y, x + magnifier->width(),
                                          y + magnifier->height(), &temp);
         }
     }
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 3b6baa7..801cb7d 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -131,7 +131,7 @@
     ContextFactory factory;
     std::unique_ptr<RenderProxy> proxy(new RenderProxy(false, rootNode.get(), &factory));
     proxy->loadSystemProperties();
-    proxy->setSurface(surface);
+    proxy->setSurface(surface.get());
     float lightX = width / 2.0;
     proxy->setLightAlpha(255 * 0.075, 255 * 0.15);
     proxy->setLightGeometry((Vector3){lightX, dp(-200.0f), dp(800.0f)}, dp(800.0f));
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 7d999c4..d08aea6 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -210,7 +210,7 @@
     auto surface = testContext.surface();
     int width = ANativeWindow_getWidth(surface.get());
     int height = ANativeWindow_getHeight(surface.get());
-    canvasContext->setSurface(std::move(surface));
+    canvasContext->setSurface(surface.get());
 
     TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
     DamageAccumulator damageAccumulator;
diff --git a/libs/protoutil/Android.bp b/libs/protoutil/Android.bp
index b0af997..d2b7d5c 100644
--- a/libs/protoutil/Android.bp
+++ b/libs/protoutil/Android.bp
@@ -45,6 +45,12 @@
     defaults: ["libprotoutil_defaults"],
 
     export_include_dirs: ["include"],
+
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.os.statsd",
+        "test_com.android.os.statsd",
+    ],
 }
 
 cc_test {
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index f075a53..3dc7cfc 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -16,6 +16,8 @@
 
 package android.location;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Binder;
@@ -62,20 +64,24 @@
         }
 
         private void execute(Consumer<TListener> operation) {
-            mExecutor.execute(() -> {
-                TListener listener = mListener;
-                if (listener == null) {
-                    return;
-                }
+            mExecutor.execute(
+                    obtainRunnable(Registration<TRequest, TListener>::accept, this, operation)
+                            .recycleOnUse());
+        }
 
-                // we may be under the binder identity if a direct executor is used
-                long identity = Binder.clearCallingIdentity();
-                try {
-                    operation.accept(listener);
-                } finally {
-                    Binder.restoreCallingIdentity(identity);
-                }
-            });
+        private void accept(Consumer<TListener> operation) {
+            TListener listener = mListener;
+            if (listener == null) {
+                return;
+            }
+
+            // we may be under the binder identity if a direct executor is used
+            long identity = Binder.clearCallingIdentity();
+            try {
+                operation.accept(listener);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
diff --git a/location/java/android/location/GnssMeasurementCorrections.java b/location/java/android/location/GnssMeasurementCorrections.java
index 19c3992..ffbe11a 100644
--- a/location/java/android/location/GnssMeasurementCorrections.java
+++ b/location/java/android/location/GnssMeasurementCorrections.java
@@ -179,6 +179,10 @@
      * Gets the environment bearing in degrees clockwise from true north, in the direction of user
      * motion. Environment bearing is provided when it is known with high probability that
      * velocity is aligned with an environment feature (such as edge of a building, or road).
+     *
+     * {@link #hasEnvironmentBearing} should be called to check the environment bearing is available
+     * before calling this method. The value is undefined if {@link #hasEnvironmentBearing} returns
+     * false.
      */
     @FloatRange(from = 0.0f, to = 360.0f)
     public float getEnvironmentBearingDegrees() {
@@ -186,7 +190,15 @@
     }
 
     /**
-     * Gets the environment bearing uncertainty in degrees.
+     * Gets the environment bearing uncertainty in degrees. It represents the standard deviation of
+     * the physical structure in the circle of position uncertainty. The uncertainty can take values
+     * between 0 and 180 degrees. The {@link #hasEnvironmentBearing} becomes false as the
+     * uncertainty value passes a predefined threshold depending on the physical structure around
+     * the user.
+     *
+     * {@link #hasEnvironmentBearing} should be called to check the environment bearing is available
+     * before calling this method. The value is undefined if {@link #hasEnvironmentBearing} returns
+     * false.
      */
     @FloatRange(from = 0.0f, to = 180.0f)
     public float getEnvironmentBearingUncertaintyDegrees() {
@@ -358,6 +370,8 @@
          * user motion. Environment bearing is provided when it is known with high probability
          * that velocity is aligned with an environment feature (such as edge of a building, or
          * road).
+         *
+         * Both the bearing and uncertainty must be set for the environment bearing to be valid.
          */
         @NonNull public Builder setEnvironmentBearingDegrees(
                 @FloatRange(from = 0.0f, to = 360.0f)
@@ -369,6 +383,8 @@
 
         /**
          * Sets the environment bearing uncertainty in degrees.
+         *
+         * Both the bearing and uncertainty must be set for the environment bearing to be valid.
          */
         @NonNull public Builder setEnvironmentBearingUncertaintyDegrees(
                 @FloatRange(from = 0.0f, to = 180.0f)
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3d0765b..03e1c75 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -22,6 +22,8 @@
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static android.app.AlarmManager.ELAPSED_REALTIME;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
 import android.Manifest;
 import android.annotation.CallbackExecutor;
 import android.annotation.NonNull;
@@ -57,6 +59,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledRunnable;
 
 import java.util.Collections;
 import java.util.List;
@@ -81,8 +84,6 @@
 @RequiresFeature(PackageManager.FEATURE_LOCATION)
 public class LocationManager {
 
-    private static final String TAG = "LocationManager";
-
     /**
      * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a
      * specific package.
@@ -91,7 +92,7 @@
      */
     @ChangeId
     @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
-    public static final long TARGETED_PENDING_INTENT = 148963590L;
+    private static final long TARGETED_PENDING_INTENT = 148963590L;
 
     /**
      * For apps targeting Android K and above, incomplete locations may not be passed to
@@ -2604,18 +2605,28 @@
                 return;
             }
 
-            mExecutor.execute(() -> {
-                Consumer<Location> consumer;
-                synchronized (GetCurrentLocationTransport.this) {
-                    if (mConsumer == null) {
-                        return;
-                    }
-                    consumer = mConsumer;
-                    cancel();
-                }
+            PooledRunnable runnable =
+                    obtainRunnable(GetCurrentLocationTransport::acceptResult, this, location)
+                            .recycleOnUse();
+            try {
+                mExecutor.execute(runnable);
+            } catch (RejectedExecutionException e) {
+                runnable.recycle();
+                throw e;
+            }
+        }
 
-                consumer.accept(location);
-            });
+        private void acceptResult(Location location) {
+            Consumer<Location> consumer;
+            synchronized (this) {
+                if (mConsumer == null) {
+                    return;
+                }
+                consumer = mConsumer;
+                cancel();
+            }
+
+            consumer.accept(location);
         }
     }
 
@@ -2649,30 +2660,36 @@
                 return;
             }
 
+            PooledRunnable runnable =
+                    obtainRunnable(LocationListenerTransport::acceptLocation, this, currentExecutor,
+                            location).recycleOnUse();
             try {
-                currentExecutor.execute(() -> {
-                    try {
-                        if (currentExecutor != mExecutor) {
-                            return;
-                        }
-
-                        // we may be under the binder identity if a direct executor is used
-                        long identity = Binder.clearCallingIdentity();
-                        try {
-                            mListener.onLocationChanged(location);
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    } finally {
-                        locationCallbackFinished();
-                    }
-                });
+                currentExecutor.execute(runnable);
             } catch (RejectedExecutionException e) {
+                runnable.recycle();
                 locationCallbackFinished();
                 throw e;
             }
         }
 
+        private void acceptLocation(Executor currentExecutor, Location location) {
+            try {
+                if (currentExecutor != mExecutor) {
+                    return;
+                }
+
+                // we may be under the binder identity if a direct executor is used
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    mListener.onLocationChanged(location);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            } finally {
+                locationCallbackFinished();
+            }
+        }
+
         @Override
         public void onProviderEnabled(String provider) {
             Executor currentExecutor = mExecutor;
@@ -2680,25 +2697,13 @@
                 return;
             }
 
+            PooledRunnable runnable =
+                    obtainRunnable(LocationListenerTransport::acceptProviderChange, this,
+                            currentExecutor, provider, true).recycleOnUse();
             try {
-                currentExecutor.execute(() -> {
-                    try {
-                        if (currentExecutor != mExecutor) {
-                            return;
-                        }
-
-                        // we may be under the binder identity if a direct executor is used
-                        long identity = Binder.clearCallingIdentity();
-                        try {
-                            mListener.onProviderEnabled(provider);
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    } finally {
-                        locationCallbackFinished();
-                    }
-                });
+                currentExecutor.execute(runnable);
             } catch (RejectedExecutionException e) {
+                runnable.recycle();
                 locationCallbackFinished();
                 throw e;
             }
@@ -2711,30 +2716,41 @@
                 return;
             }
 
+            PooledRunnable runnable =
+                    obtainRunnable(LocationListenerTransport::acceptProviderChange, this,
+                            currentExecutor, provider, false).recycleOnUse();
             try {
-                currentExecutor.execute(() -> {
-                    try {
-                        if (currentExecutor != mExecutor) {
-                            return;
-                        }
-
-                        // we may be under the binder identity if a direct executor is used
-                        long identity = Binder.clearCallingIdentity();
-                        try {
-                            mListener.onProviderDisabled(provider);
-                        } finally {
-                            Binder.restoreCallingIdentity(identity);
-                        }
-                    } finally {
-                        locationCallbackFinished();
-                    }
-                });
+                currentExecutor.execute(runnable);
             } catch (RejectedExecutionException e) {
+                runnable.recycle();
                 locationCallbackFinished();
                 throw e;
             }
         }
 
+        private void acceptProviderChange(Executor currentExecutor, String provider,
+                boolean enabled) {
+            try {
+                if (currentExecutor != mExecutor) {
+                    return;
+                }
+
+                // we may be under the binder identity if a direct executor is used
+                long identity = Binder.clearCallingIdentity();
+                try {
+                    if (enabled) {
+                        mListener.onProviderEnabled(provider);
+                    } else {
+                        mListener.onProviderDisabled(provider);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            } finally {
+                locationCallbackFinished();
+            }
+        }
+
         @Override
         public void onRemoved() {
             // TODO: onRemoved is necessary to GC hanging listeners, but introduces some interesting
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 0a0f7f6..53379b87 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1024,7 +1024,18 @@
                                                       String device_name,
                                                       int codecFormat);
     @UnsupportedAppUsage
-    public static native int setPhoneState(int state);
+    public static int setPhoneState(int state) {
+        Log.w(TAG, "Do not use this method! Use AudioManager.setMode() instead.");
+        return 0;
+    }
+    /**
+     * @hide
+     * Send the current audio mode to audio policy manager and audio HAL.
+     * @param state the audio mode
+     * @param uid the UID of the app owning the audio mode
+     * @return command completion status.
+     */
+    public static native int setPhoneState(int state, int uid);
     @UnsupportedAppUsage
     public static native int setForceUse(int usage, int config);
     @UnsupportedAppUsage
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 0e6ade5..2178393 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -875,12 +875,12 @@
         }
 
         /**
-         * @return the unmodifiable list of transferrable routes for the session.
+         * @return the unmodifiable list of transferable routes for the session.
          */
         @NonNull
-        public List<MediaRoute2Info> getTransferrableRoutes() {
+        public List<MediaRoute2Info> getTransferableRoutes() {
             synchronized (mControllerLock) {
-                return getRoutesWithIdsLocked(mSessionInfo.getTransferrableRoutes());
+                return getRoutesWithIdsLocked(mSessionInfo.getTransferableRoutes());
             }
         }
 
@@ -1033,12 +1033,12 @@
          * all of the following conditions:
          * <ul>
          * <li>ID should not be included in {@link #getSelectedRoutes()}</li>
-         * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+         * <li>ID should be included in {@link #getTransferableRoutes()}</li>
          * </ul>
          * If the route doesn't meet any of above conditions, it will be ignored.
          *
          * @see #getSelectedRoutes()
-         * @see #getTransferrableRoutes()
+         * @see #getTransferableRoutes()
          * @see RoutingControllerCallback#onControllerUpdated
          */
         public void transferToRoute(@NonNull MediaRoute2Info route) {
@@ -1057,9 +1057,9 @@
                 return;
             }
 
-            List<MediaRoute2Info> transferrableRoutes = getTransferrableRoutes();
-            if (!checkRouteListContainsRouteId(transferrableRoutes, route.getId())) {
-                Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+            List<MediaRoute2Info> transferableRoutes = getTransferableRoutes();
+            if (!checkRouteListContainsRouteId(transferableRoutes, route.getId())) {
+                Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
                 return;
             }
 
@@ -1156,7 +1156,7 @@
                     .map(MediaRoute2Info::getId).collect(Collectors.toList());
             List<String> deselectableRoutes = getDeselectableRoutes().stream()
                     .map(MediaRoute2Info::getId).collect(Collectors.toList());
-            List<String> transferrableRoutes = getTransferrableRoutes().stream()
+            List<String> transferableRoutes = getTransferableRoutes().stream()
                     .map(MediaRoute2Info::getId).collect(Collectors.toList());
 
             StringBuilder result = new StringBuilder()
@@ -1171,8 +1171,8 @@
                     .append(", deselectableRoutes={")
                     .append(deselectableRoutes)
                     .append("}")
-                    .append(", transferrableRoutes={")
-                    .append(transferrableRoutes)
+                    .append(", transferableRoutes={")
+                    .append(transferableRoutes)
                     .append("}")
                     .append(" }");
             return result.toString();
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 5ce291c..4801d44 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -259,7 +259,7 @@
     /**
      * Selects media route for the specified package name.
      *
-     * If the given route is {@link RoutingController#getTransferrableRoutes() a transferrable
+     * If the given route is {@link RoutingController#getTransferableRoutes() a transferable
      * route} of a routing session of the application, the session will be transferred to
      * the route. If not, a new routing session will be created.
      *
@@ -274,7 +274,7 @@
         //TODO: instead of release all controllers, add an API to specify controllers that
         // should be released (or is the system controller).
         for (RoutingSessionInfo sessionInfo : getRoutingSessions(packageName)) {
-            if (!transferred && sessionInfo.getTransferrableRoutes().contains(route.getId())) {
+            if (!transferred && sessionInfo.getTransferableRoutes().contains(route.getId())) {
                 new RoutingController(sessionInfo).transferToRoute(route);
                 transferred = true;
             } else if (!sessionInfo.isSystemSession()) {
@@ -543,13 +543,13 @@
         }
 
         /**
-         * @return the unmodifiable list of transferrable routes for the session.
+         * @return the unmodifiable list of transferable routes for the session.
          */
         @NonNull
-        public List<MediaRoute2Info> getTransferrableRoutes() {
+        public List<MediaRoute2Info> getTransferableRoutes() {
             List<String> routeIds;
             synchronized (mControllerLock) {
-                routeIds = mSessionInfo.getTransferrableRoutes();
+                routeIds = mSessionInfo.getTransferableRoutes();
             }
             return getRoutesWithIds(routeIds);
         }
@@ -643,12 +643,12 @@
          * all of the following conditions:
          * <ul>
          * <li>ID should not be included in {@link #getSelectedRoutes()}</li>
-         * <li>ID should be included in {@link #getTransferrableRoutes()}</li>
+         * <li>ID should be included in {@link #getTransferableRoutes()}</li>
          * </ul>
          * If the route doesn't meet any of above conditions, it will be ignored.
          *
          * @see #getSelectedRoutes()
-         * @see #getTransferrableRoutes()
+         * @see #getTransferableRoutes()
          */
         public void transferToRoute(@NonNull MediaRoute2Info route) {
             Objects.requireNonNull(route, "route must not be null");
@@ -663,8 +663,8 @@
                 return;
             }
 
-            if (!sessionInfo.getTransferrableRoutes().contains(route.getId())) {
-                Log.w(TAG, "Ignoring transferring to a non-transferrable route=" + route);
+            if (!sessionInfo.getTransferableRoutes().contains(route.getId())) {
+                Log.w(TAG, "Ignoring transferring to a non-transferable route=" + route);
                 return;
             }
 
diff --git a/media/java/android/media/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index ebcb9ed..2e038e6 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -31,8 +31,16 @@
 import java.util.Set;
 
 /**
- * A media route discovery preference describing the kinds of routes that media router
+ * A media route discovery preference describing the features of routes that media router
  * would like to discover and whether to perform active scanning.
+ * <p>
+ * When {@link MediaRouter2} instances set discovery preferences by calling
+ * {@link MediaRouter2#registerRouteCallback}, they are merged into a single discovery preference
+ * and it is delivered to call {@link MediaRoute2ProviderService#onDiscoveryPreferenceChanged}.
+ * </p><p>
+ * According to the given discovery preference, {@link MediaRoute2ProviderService} discovers
+ * routes and publishes them.
+ * </p>
  *
  * @see MediaRouter2#registerRouteCallback
  */
@@ -53,12 +61,12 @@
 
     @NonNull
     private final List<String> mPreferredFeatures;
-    private final boolean mActiveScan;
+    private final boolean mShouldPerformActiveScan;
     @Nullable
     private final Bundle mExtras;
 
     /**
-     * An empty discovery preference.
+     * An empty discovery preference
      * @hide
      */
     public static final RouteDiscoveryPreference EMPTY =
@@ -66,23 +74,39 @@
 
     RouteDiscoveryPreference(@NonNull Builder builder) {
         mPreferredFeatures = builder.mPreferredFeatures;
-        mActiveScan = builder.mActiveScan;
+        mShouldPerformActiveScan = builder.mActiveScan;
         mExtras = builder.mExtras;
     }
 
     RouteDiscoveryPreference(@NonNull Parcel in) {
         mPreferredFeatures = in.createStringArrayList();
-        mActiveScan = in.readBoolean();
+        mShouldPerformActiveScan = in.readBoolean();
         mExtras = in.readBundle();
     }
 
+    /**
+     * Gets the features of routes that media router would like to discover.
+     * <p>
+     * Routes that have at least one of the features will be discovered.
+     * They may include predefined features such as
+     * {@link MediaRoute2Info#FEATURE_LIVE_AUDIO}, {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
+     * or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK} or custom features defined by a provider.
+     * </p>
+     */
     @NonNull
     public List<String> getPreferredFeatures() {
         return mPreferredFeatures;
     }
 
-    public boolean isActiveScan() {
-        return mActiveScan;
+    /**
+     * Gets whether active scanning should be performed.
+     * <p>
+     * If any of discovery preferences sets this as {@code true}, active scanning will
+     * be performed regardless of other discovery preferences.
+     * </p>
+     */
+    public boolean shouldPerformActiveScan() {
+        return mShouldPerformActiveScan;
     }
 
     /**
@@ -100,7 +124,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeStringList(mPreferredFeatures);
-        dest.writeBoolean(mActiveScan);
+        dest.writeBoolean(mShouldPerformActiveScan);
         dest.writeBundle(mExtras);
     }
 
@@ -112,7 +136,7 @@
                 .append(String.join(", ", mPreferredFeatures))
                 .append("}")
                 .append(", activeScan=")
-                .append(mActiveScan)
+                .append(mShouldPerformActiveScan)
                 .append(" }");
 
         return result.toString();
@@ -128,12 +152,12 @@
         }
         RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
         return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
-                && mActiveScan == other.mActiveScan;
+                && mShouldPerformActiveScan == other.mShouldPerformActiveScan;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPreferredFeatures, mActiveScan);
+        return Objects.hash(mPreferredFeatures, mShouldPerformActiveScan);
     }
 
     /**
@@ -154,12 +178,12 @@
             Objects.requireNonNull(preference, "preference must not be null");
 
             mPreferredFeatures = preference.getPreferredFeatures();
-            mActiveScan = preference.isActiveScan();
+            mActiveScan = preference.shouldPerformActiveScan();
             mExtras = preference.getExtras();
         }
 
         /**
-         * A constructor to combine all of the preferences into a single preference .
+         * A constructor to combine all of the preferences into a single preference.
          * It ignores extras of preferences.
          *
          * @hide
@@ -171,13 +195,19 @@
             mActiveScan = false;
             for (RouteDiscoveryPreference preference : preferences) {
                 routeFeatureSet.addAll(preference.mPreferredFeatures);
-                mActiveScan |= preference.mActiveScan;
+                mActiveScan |= preference.mShouldPerformActiveScan;
             }
             mPreferredFeatures = new ArrayList<>(routeFeatureSet);
         }
 
         /**
          * Sets preferred route features to discover.
+         * @param preferredFeatures features of routes that media router would like to discover.
+         *                          May include predefined features
+         *                          such as {@link MediaRoute2Info#FEATURE_LIVE_AUDIO},
+         *                          {@link MediaRoute2Info#FEATURE_LIVE_VIDEO},
+         *                          or {@link MediaRoute2Info#FEATURE_REMOTE_PLAYBACK}
+         *                          or custom features defined by a provider.
          */
         @NonNull
         public Builder setPreferredFeatures(@NonNull List<String> preferredFeatures) {
@@ -188,9 +218,13 @@
 
         /**
          * Sets if active scanning should be performed.
+         * <p>
+         * Since active scanning uses more system resources, set this as {@code true} only
+         * when it's necessary.
+         * </p>
          */
         @NonNull
-        public Builder setActiveScan(boolean activeScan) {
+        public Builder setShouldPerformActiveScan(boolean activeScan) {
             mActiveScan = activeScan;
             return this;
         }
diff --git a/media/java/android/media/RoutingSessionInfo.java b/media/java/android/media/RoutingSessionInfo.java
index 0d4e666..19a46ce 100644
--- a/media/java/android/media/RoutingSessionInfo.java
+++ b/media/java/android/media/RoutingSessionInfo.java
@@ -55,7 +55,7 @@
     final List<String> mSelectedRoutes;
     final List<String> mSelectableRoutes;
     final List<String> mDeselectableRoutes;
-    final List<String> mTransferrableRoutes;
+    final List<String> mTransferableRoutes;
 
     final int mVolumeHandling;
     final int mVolumeMax;
@@ -79,8 +79,8 @@
                 convertToUniqueRouteIds(builder.mSelectableRoutes));
         mDeselectableRoutes = Collections.unmodifiableList(
                 convertToUniqueRouteIds(builder.mDeselectableRoutes));
-        mTransferrableRoutes = Collections.unmodifiableList(
-                convertToUniqueRouteIds(builder.mTransferrableRoutes));
+        mTransferableRoutes = Collections.unmodifiableList(
+                convertToUniqueRouteIds(builder.mTransferableRoutes));
 
         mVolumeHandling = builder.mVolumeHandling;
         mVolumeMax = builder.mVolumeMax;
@@ -100,7 +100,7 @@
         mSelectedRoutes = ensureList(src.createStringArrayList());
         mSelectableRoutes = ensureList(src.createStringArrayList());
         mDeselectableRoutes = ensureList(src.createStringArrayList());
-        mTransferrableRoutes = ensureList(src.createStringArrayList());
+        mTransferableRoutes = ensureList(src.createStringArrayList());
 
         mVolumeHandling = src.readInt();
         mVolumeMax = src.readInt();
@@ -193,11 +193,11 @@
     }
 
     /**
-     * Gets the list of ids of transferrable routes for the session.
+     * Gets the list of ids of transferable routes for the session.
      */
     @NonNull
-    public List<String> getTransferrableRoutes() {
-        return mTransferrableRoutes;
+    public List<String> getTransferableRoutes() {
+        return mTransferableRoutes;
     }
 
     /**
@@ -260,7 +260,7 @@
         dest.writeStringList(mSelectedRoutes);
         dest.writeStringList(mSelectableRoutes);
         dest.writeStringList(mDeselectableRoutes);
-        dest.writeStringList(mTransferrableRoutes);
+        dest.writeStringList(mTransferableRoutes);
         dest.writeInt(mVolumeHandling);
         dest.writeInt(mVolumeMax);
         dest.writeInt(mVolume);
@@ -284,7 +284,7 @@
                 && Objects.equals(mSelectedRoutes, other.mSelectedRoutes)
                 && Objects.equals(mSelectableRoutes, other.mSelectableRoutes)
                 && Objects.equals(mDeselectableRoutes, other.mDeselectableRoutes)
-                && Objects.equals(mTransferrableRoutes, other.mTransferrableRoutes)
+                && Objects.equals(mTransferableRoutes, other.mTransferableRoutes)
                 && (mVolumeHandling == other.mVolumeHandling)
                 && (mVolumeMax == other.mVolumeMax)
                 && (mVolume == other.mVolume);
@@ -293,7 +293,7 @@
     @Override
     public int hashCode() {
         return Objects.hash(mId, mClientPackageName, mProviderId,
-                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferrableRoutes,
+                mSelectedRoutes, mSelectableRoutes, mDeselectableRoutes, mTransferableRoutes,
                 mVolumeMax, mVolumeHandling, mVolume);
     }
 
@@ -311,8 +311,8 @@
                 .append(", deselectableRoutes={")
                 .append(String.join(",", mDeselectableRoutes))
                 .append("}")
-                .append(", transferrableRoutes={")
-                .append(String.join(",", mTransferrableRoutes))
+                .append(", transferableRoutes={")
+                .append(String.join(",", mTransferableRoutes))
                 .append("}")
                 .append(", volumeHandling=").append(getVolumeHandling())
                 .append(", volumeMax=").append(getVolumeMax())
@@ -350,7 +350,7 @@
         final List<String> mSelectedRoutes;
         final List<String> mSelectableRoutes;
         final List<String> mDeselectableRoutes;
-        final List<String> mTransferrableRoutes;
+        final List<String> mTransferableRoutes;
         int mVolumeHandling = MediaRoute2Info.PLAYBACK_VOLUME_FIXED;
         int mVolumeMax;
         int mVolume;
@@ -381,7 +381,7 @@
             mSelectedRoutes = new ArrayList<>();
             mSelectableRoutes = new ArrayList<>();
             mDeselectableRoutes = new ArrayList<>();
-            mTransferrableRoutes = new ArrayList<>();
+            mTransferableRoutes = new ArrayList<>();
         }
 
         /**
@@ -400,7 +400,7 @@
             mSelectedRoutes = new ArrayList<>(sessionInfo.mSelectedRoutes);
             mSelectableRoutes = new ArrayList<>(sessionInfo.mSelectableRoutes);
             mDeselectableRoutes = new ArrayList<>(sessionInfo.mDeselectableRoutes);
-            mTransferrableRoutes = new ArrayList<>(sessionInfo.mTransferrableRoutes);
+            mTransferableRoutes = new ArrayList<>(sessionInfo.mTransferableRoutes);
 
             mVolumeHandling = sessionInfo.mVolumeHandling;
             mVolumeMax = sessionInfo.mVolumeMax;
@@ -524,35 +524,35 @@
         }
 
         /**
-         * Clears the transferrable routes.
+         * Clears the transferable routes.
          */
         @NonNull
-        public Builder clearTransferrableRoutes() {
-            mTransferrableRoutes.clear();
+        public Builder clearTransferableRoutes() {
+            mTransferableRoutes.clear();
             return this;
         }
 
         /**
-         * Adds a route to the transferrable routes. The {@code routeId} must not be empty.
+         * Adds a route to the transferable routes. The {@code routeId} must not be empty.
          */
         @NonNull
-        public Builder addTransferrableRoute(@NonNull String routeId) {
+        public Builder addTransferableRoute(@NonNull String routeId) {
             if (TextUtils.isEmpty(routeId)) {
                 throw new IllegalArgumentException("routeId must not be empty");
             }
-            mTransferrableRoutes.add(routeId);
+            mTransferableRoutes.add(routeId);
             return this;
         }
 
         /**
-         * Removes a route from the transferrable routes. The {@code routeId} must not be empty.
+         * Removes a route from the transferable routes. The {@code routeId} must not be empty.
          */
         @NonNull
-        public Builder removeTransferrableRoute(@NonNull String routeId) {
+        public Builder removeTransferableRoute(@NonNull String routeId) {
             if (TextUtils.isEmpty(routeId)) {
                 throw new IllegalArgumentException("routeId must not be empty");
             }
-            mTransferrableRoutes.remove(routeId);
+            mTransferableRoutes.remove(routeId);
             return this;
         }
 
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 44142e3..4db1109 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -612,10 +612,10 @@
      *
      * @return  a {@link Descrambler} object.
      */
-    @RequiresPermission(android.Manifest.permission.ACCESS_TV_TUNER)
+    @RequiresPermission(android.Manifest.permission.ACCESS_TV_DESCRAMBLER)
     @Nullable
     public Descrambler openDescrambler() {
-        TunerUtils.checkTunerPermission(mContext);
+        TunerUtils.checkDescramblerPermission(mContext);
         return nativeOpenDescrambler();
     }
 
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index f54808d..82af658 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -38,6 +38,14 @@
      * Invalid stream ID.
      */
     public static final int INVALID_STREAM_ID = Constants.Constant.INVALID_STREAM_ID;
+    /**
+     * Invalid filter ID.
+     */
+    public static final int INVALID_FILTER_ID = Constants.Constant.INVALID_FILTER_ID;
+    /**
+     * Invalid AV Sync ID.
+     */
+    public static final int INVALID_AV_SYNC_ID = Constants.Constant.INVALID_AV_SYNC_ID;
 
     /** @hide */
     @IntDef(prefix = "SCAN_TYPE_", value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_AUTO, SCAN_TYPE_BLIND})
diff --git a/media/java/android/media/tv/tuner/TunerUtils.java b/media/java/android/media/tv/tuner/TunerUtils.java
index 30aaa02..5ecb8f0 100644
--- a/media/java/android/media/tv/tuner/TunerUtils.java
+++ b/media/java/android/media/tv/tuner/TunerUtils.java
@@ -27,7 +27,9 @@
  * @hide
  */
 public final class TunerUtils {
-    private static final String PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER;
+    private static final String TUNER_PERMISSION = android.Manifest.permission.ACCESS_TV_TUNER;
+    private static final String DESCRAMBLER_PERMISSION =
+            android.Manifest.permission.ACCESS_TV_DESCRAMBLER;
 
     /**
      * Checks whether the caller has permission to access tuner.
@@ -36,9 +38,30 @@
      * @throws SecurityException if the caller doesn't have the permission.
      */
     public static void checkTunerPermission(Context context) {
-        if (context.checkCallingOrSelfPermission(PERMISSION)
+        checkPermission(context, TUNER_PERMISSION);
+    }
+
+    /**
+     * Checks whether the caller has permission to access the descrambler.
+     *
+     * @param context context of the caller.
+     * @throws SecurityException if the caller doesn't have the permission.
+     */
+    public static void checkDescramblerPermission(Context context) {
+        checkPermission(context, DESCRAMBLER_PERMISSION);
+    }
+
+    /**
+     * Checks whether the caller has the given permission.
+     *
+     * @param context context of the caller.
+     * @param permission the given permission.
+     * @throws SecurityException if the caller doesn't have the permission.
+     */
+    public static void checkPermission(Context context, String permission) {
+        if (context.checkCallingOrSelfPermission(permission)
                 != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Caller must have " + PERMISSION + " permission.");
+            throw new SecurityException("Caller must have " + permission + " permission.");
         }
     }
 
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
index 267927f..f1dcf3d 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/SampleMediaRoute2ProviderService.java
@@ -191,7 +191,7 @@
         RoutingSessionInfo sessionInfo = new RoutingSessionInfo.Builder(sessionId, packageName)
                 .addSelectedRoute(routeId)
                 .addSelectableRoute(ROUTE_ID4_TO_SELECT_AND_DESELECT)
-                .addTransferrableRoute(ROUTE_ID5_TO_TRANSFER_TO)
+                .addTransferableRoute(ROUTE_ID5_TO_TRANSFER_TO)
                 .setVolumeHandling(PLAYBACK_VOLUME_VARIABLE)
                 .setVolumeMax(SESSION_VOLUME_MAX)
                 .setVolume(SESSION_VOLUME_INITIAL)
@@ -300,7 +300,7 @@
                 .clearSelectedRoutes()
                 .addSelectedRoute(routeId)
                 .removeDeselectableRoute(routeId)
-                .removeTransferrableRoute(routeId)
+                .removeTransferableRoute(routeId)
                 .build();
         notifySessionUpdated(newSessionInfo);
         publishRoutes();
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
index 07b7b22..8eeaefd 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIBinder.java
@@ -17,8 +17,10 @@
 package com.android.systemui;
 
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bubbles.dagger.BubbleModule;
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.navigationbar.car.CarNavigationBar;
 import com.android.systemui.pip.PipUI;
 import com.android.systemui.power.PowerUI;
@@ -29,6 +31,7 @@
 import com.android.systemui.statusbar.car.CarStatusBar;
 import com.android.systemui.statusbar.car.CarStatusBarModule;
 import com.android.systemui.statusbar.notification.InstantAppNotifier;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.tv.TvStatusBar;
 import com.android.systemui.theme.ThemeOverlayController;
@@ -42,7 +45,8 @@
 import dagger.multibindings.IntoMap;
 
 /** Binder for car specific {@link SystemUI} modules. */
-@Module(includes = {RecentsModule.class, CarStatusBarModule.class})
+@Module(includes = {RecentsModule.class, CarStatusBarModule.class, NotificationsModule.class,
+        BubbleModule.class, KeyguardModule.class})
 public abstract class CarSystemUIBinder {
     /** Inject into AuthController. */
     @Binds
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 498bd87..7f64990 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -54,10 +54,10 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarDependenciesModule;
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
+import com.android.systemui.statusbar.dagger.StatusBarDependenciesModule;
 import com.android.systemui.statusbar.notification.BypassHeadsUpNotifier;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationAlertingManager;
@@ -67,6 +67,7 @@
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.NotificationRowModule;
 import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
@@ -87,6 +88,7 @@
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneDependenciesModule;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -112,7 +114,8 @@
 /**
  * Dagger Module providing {@link CarStatusBar}.
  */
-@Module(includes = {StatusBarDependenciesModule.class})
+@Module(includes = {StatusBarDependenciesModule.class, StatusBarPhoneDependenciesModule.class,
+        NotificationRowModule.class})
 public class CarStatusBarModule {
     /**
      * Provides our instance of StatusBar which is considered optional.
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
index e6f8c01..dc6dddb 100644
--- a/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
+++ b/packages/SettingsLib/LayoutPreference/res/layout/cross_profiles_settings_entity_header.xml
@@ -18,75 +18,39 @@
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/entity_header"
-    style="@style/EntityHeader"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:paddingStart="?android:attr/listPreferredItemPaddingStart"
-    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-    android:orientation="horizontal">
+    style="@style/EntityHeader">
 
     <LinearLayout
         android:id="@+id/entity_header_content"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
+        android:paddingTop="32dp"
+        android:paddingBottom="32dp"
         android:layout_centerHorizontal="true"
-        android:gravity="center_horizontal"
-        android:orientation="horizontal">
+        android:orientation="vertical">
 
         <LinearLayout
             android:id="@+id/entity_header_content"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_centerHorizontal="true"
-            android:gravity="center_horizontal"
-            android:orientation="vertical">
+            android:layout_gravity="center"
+            android:orientation="horizontal">
 
             <ImageView
                 android:id="@+id/entity_header_icon_personal"
-                android:layout_width="48dp"
-                android:layout_height="48dp"
-                android:scaleType="fitCenter"
-                android:antialias="true"/>
+                style="@style/CrossProfileEntityHeaderIcon" />
 
-            <TextView
-                android:id="@+id/install_type"
-                style="@style/TextAppearance.EntityHeaderSummary"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="2dp"
-                android:text="Personal"/>
-        </LinearLayout>
-
-        <ImageView
-            android:id="@+id/entity_header_swap_horiz"
-            android:layout_width="24dp"
-            android:layout_height="24dp"
-            android:scaleType="fitCenter"
-            android:antialias="true"
-            android:src="@drawable/ic_swap_horiz_grey"/>
-
-        <LinearLayout
-            android:id="@+id/entity_header_content"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_centerHorizontal="true"
-            android:gravity="center_horizontal"
-            android:orientation="vertical">
+            <ImageView
+                android:id="@+id/entity_header_swap_horiz"
+                style="@style/CrossProfileSwapHorizIcon "/>
 
             <ImageView
                 android:id="@+id/entity_header_icon_work"
-                android:layout_width="48dp"
-                android:layout_height="48dp"
-                android:scaleType="fitCenter"
-                android:antialias="true"/>
-            <TextView
-                android:id="@+id/install_type"
-                style="@style/TextAppearance.EntityHeaderSummary"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_marginTop="2dp"
-                android:text="Work"/>
+                style="@style/CrossProfileEntityHeaderIcon" />
         </LinearLayout>
-    </LinearLayout>
 
+        <TextView
+            android:id="@+id/entity_header_title"
+            style="@style/CrossProfileEntityHeaderTitle" />
+    </LinearLayout>
 </RelativeLayout>
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
index 6a2b729..4a99e84 100644
--- a/packages/SettingsLib/LayoutPreference/res/values/styles.xml
+++ b/packages/SettingsLib/LayoutPreference/res/values/styles.xml
@@ -37,4 +37,35 @@
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">marquee</item>
     </style>
+
+    <style name="CrossProfileEntityHeaderIcon">
+        <item name="android:layout_width">48dp</item>
+        <item name="android:layout_height">48dp</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:gravity">center</item>
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:antialias">true</item>
+    </style>
+
+    <style name="CrossProfileSwapHorizIcon">
+        <item name="android:layout_width">24dp</item>
+        <item name="android:layout_height">24dp</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:gravity">center</item>
+        <item name="android:scaleType">fitCenter</item>
+        <item name="android:layout_marginStart">10dp</item>
+        <item name="android:layout_marginEnd">10dp</item>
+        <item name="android:src">@drawable/ic_swap_horiz_grey</item>
+        <item name="android:antialias">true</item>
+    </style>
+
+    <style name="CrossProfileEntityHeaderTitle">
+        <item name="android:layout_width">wrap_content</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_gravity">center</item>
+        <item name="android:textSize">18sp</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:fontFamily">google-sans-medium</item>
+        <item name="android:layout_marginTop">8dp</item>
+    </style>
 </resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 8e4a982..5ff88ac 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -33,6 +33,7 @@
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
@@ -83,10 +84,179 @@
      * Get current device that played media.
      * @return MediaDevice
      */
-    public MediaDevice getCurrentConnectedDevice() {
+    MediaDevice getCurrentConnectedDevice() {
         return mCurrentConnectedDevice;
     }
 
+    /**
+     * Transfer MediaDevice for media without package name.
+     */
+    boolean connectDeviceWithoutPackageName(MediaDevice device) {
+        boolean isConnected = false;
+        final List<RoutingSessionInfo> infos = mRouterManager.getActiveSessions();
+        if (infos.size() > 0) {
+            final RoutingSessionInfo info = infos.get(0);
+            final MediaRouter2Manager.RoutingController controller =
+                    mRouterManager.getControllerForSession(info);
+
+            controller.transferToRoute(device.mRouteInfo);
+            isConnected = true;
+        }
+        return isConnected;
+    }
+
+    /**
+     * Add a MediaDevice to let it play current media.
+     *
+     * @param device MediaDevice
+     * @return If add device successful return {@code true}, otherwise return {@code false}
+     */
+    boolean addDeviceToPlayMedia(MediaDevice device) {
+        if (TextUtils.isEmpty(mPackageName)) {
+            Log.w(TAG, "addDeviceToPlayMedia() package name is null or empty!");
+            return false;
+        }
+
+        final RoutingSessionInfo info = getRoutingSessionInfo();
+        if (info != null && info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
+            mRouterManager.getControllerForSession(info).selectRoute(device.mRouteInfo);
+            return true;
+        }
+
+        Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : "
+                + device.getName());
+
+        return false;
+    }
+
+    private RoutingSessionInfo getRoutingSessionInfo() {
+        for (RoutingSessionInfo info : mRouterManager.getRoutingSessions(mPackageName)) {
+            if (TextUtils.equals(info.getClientPackageName(), mPackageName)) {
+                return info;
+            }
+        }
+
+        Log.w(TAG, "RoutingSessionInfo() cannot found match packagename : " + mPackageName);
+        return null;
+    }
+
+    /**
+     * Remove a {@code device} from current media.
+     *
+     * @param device MediaDevice
+     * @return If device stop successful return {@code true}, otherwise return {@code false}
+     */
+    boolean removeDeviceFromPlayMedia(MediaDevice device) {
+        if (TextUtils.isEmpty(mPackageName)) {
+            Log.w(TAG, "removeDeviceFromMedia() package name is null or empty!");
+            return false;
+        }
+
+        final RoutingSessionInfo info = getRoutingSessionInfo();
+        if (info != null && info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
+            mRouterManager.getControllerForSession(info).deselectRoute(device.mRouteInfo);
+            return true;
+        }
+
+        Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : "
+                + device.getName());
+
+        return false;
+    }
+
+    /**
+     * Get the MediaDevice list that can be added to current media.
+     *
+     * @return list of MediaDevice
+     */
+    List<MediaDevice> getSelectableMediaDevice() {
+        final List<MediaDevice> deviceList = new ArrayList<>();
+        if (TextUtils.isEmpty(mPackageName)) {
+            Log.w(TAG, "getSelectableMediaDevice() package name is null or empty!");
+            return deviceList;
+        }
+
+        final RoutingSessionInfo info = getRoutingSessionInfo();
+        if (info != null) {
+            for (MediaRoute2Info route : mRouterManager.getControllerForSession(info)
+                    .getSelectableRoutes()) {
+                deviceList.add(new InfoMediaDevice(mContext, mRouterManager,
+                        route, mPackageName));
+            }
+            return deviceList;
+        }
+
+        Log.w(TAG, "getSelectableMediaDevice() cannot found selectable MediaDevice from : "
+                + mPackageName);
+
+        return deviceList;
+    }
+
+    /**
+     * Adjust the volume of {@link android.media.RoutingSessionInfo}.
+     *
+     * @param volume the value of volume
+     */
+    void adjustSessionVolume(int volume) {
+        if (TextUtils.isEmpty(mPackageName)) {
+            Log.w(TAG, "adjustSessionVolume() package name is null or empty!");
+            return;
+        }
+
+        final RoutingSessionInfo info = getRoutingSessionInfo();
+        if (info != null) {
+            Log.d(TAG, "adjustSessionVolume() adjust volume : " + volume + ", with : "
+                    + mPackageName);
+            mRouterManager.setSessionVolume(info, volume);
+            return;
+        }
+
+        Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : "
+                + mPackageName);
+    }
+
+    /**
+     * Gets the maximum volume of the {@link android.media.RoutingSessionInfo}.
+     *
+     * @return  maximum volume of the session, and return -1 if not found.
+     */
+    public int getSessionVolumeMax() {
+        if (TextUtils.isEmpty(mPackageName)) {
+            Log.w(TAG, "getSessionVolumeMax() package name is null or empty!");
+            return -1;
+        }
+
+        final RoutingSessionInfo info = getRoutingSessionInfo();
+        if (info != null) {
+            return info.getVolumeMax();
+        }
+
+        Log.w(TAG, "getSessionVolumeMax() can't found corresponding RoutingSession with : "
+                + mPackageName);
+        return -1;
+    }
+
+    /**
+     * Gets the current volume of the {@link android.media.RoutingSessionInfo}.
+     *
+     * @return current volume of the session, and return -1 if not found.
+     */
+    public int getSessionVolume() {
+        if (TextUtils.isEmpty(mPackageName)) {
+            Log.w(TAG, "getSessionVolume() package name is null or empty!");
+            return -1;
+        }
+
+        final RoutingSessionInfo info = getRoutingSessionInfo();
+        if (info != null) {
+            return info.getVolume();
+        }
+
+        Log.w(TAG, "getSessionVolume() can't found corresponding RoutingSession with : "
+                + mPackageName);
+        return -1;
+    }
+
     private void refreshDevices() {
         mMediaDevices.clear();
         mCurrentConnectedDevice = null;
@@ -150,23 +320,6 @@
         }
     }
 
-    /**
-     * Transfer MediaDevice for media without package name.
-     */
-    public boolean connectDeviceWithoutPackageName(MediaDevice device) {
-        boolean isConnected = false;
-        final List<RoutingSessionInfo> infos = mRouterManager.getActiveSessions();
-        if (infos.size() > 0) {
-            final RoutingSessionInfo info = infos.get(0);
-            final MediaRouter2Manager.RoutingController controller =
-                    mRouterManager.getControllerForSession(info);
-
-            controller.transferToRoute(device.mRouteInfo);
-            isConnected = true;
-        }
-        return isConnected;
-    }
-
     class RouterManagerCallback extends MediaRouter2Manager.Callback {
 
         @Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index f8db70a..fc373a5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -252,6 +252,62 @@
         return devices;
     }
 
+    /**
+     * Add a MediaDevice to let it play current media.
+     *
+     * @param device MediaDevice
+     * @return If add device successful return {@code true}, otherwise return {@code false}
+     */
+    public boolean addDeviceToPlayMedia(MediaDevice device) {
+        return mInfoMediaManager.addDeviceToPlayMedia(device);
+    }
+
+    /**
+     * Remove a {@code device} from current media.
+     *
+     * @param device MediaDevice
+     * @return If device stop successful return {@code true}, otherwise return {@code false}
+     */
+    public boolean removeDeviceFromPlayMedia(MediaDevice device) {
+        return mInfoMediaManager.removeDeviceFromPlayMedia(device);
+    }
+
+    /**
+     * Get the MediaDevice list that can be added to current media.
+     *
+     * @return list of MediaDevice
+     */
+    public List<MediaDevice> getSelectableMediaDevice() {
+        return mInfoMediaManager.getSelectableMediaDevice();
+    }
+
+    /**
+     * Adjust the volume of session.
+     *
+     * @param volume the value of volume
+     */
+    public void adjustSessionVolume(int volume) {
+        mInfoMediaManager.adjustSessionVolume(volume);
+    }
+
+    /**
+     * Gets the maximum volume of the {@link android.media.RoutingSessionInfo}.
+     *
+     * @return  maximum volume of the session, and return -1 if not found.
+     */
+    public int getSessionVolumeMax() {
+        return mInfoMediaManager.getSessionVolumeMax();
+    }
+
+    /**
+     * Gets the current volume of the {@link android.media.RoutingSessionInfo}.
+     *
+     * @return current volume of the session, and return -1 if not found.
+     */
+    public int getSessionVolume() {
+        return mInfoMediaManager.getSessionVolume();
+    }
+
     private MediaDevice updateCurrentConnectedDevice() {
         MediaDevice phoneMediaDevice = null;
         for (MediaDevice device : mMediaDevices) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
new file mode 100644
index 0000000..885b7d3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/ConversationIconFactory.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2020 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.settingslib.notification;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherApps;
+import android.content.pm.PackageManager;
+import android.content.pm.ShortcutInfo;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.util.IconDrawableFactory;
+
+import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ShadowGenerator;
+
+/**
+ * Factory for creating normalized conversation icons.
+ * We are not using Launcher's IconFactory because conversation rendering only runs on the UI
+ * thread, so there is no need to manage a pool across multiple threads.
+ */
+public class ConversationIconFactory extends BaseIconFactory {
+
+    final LauncherApps mLauncherApps;
+    final PackageManager mPackageManager;
+    final IconDrawableFactory mIconDrawableFactory;
+
+    public ConversationIconFactory(Context context, LauncherApps la, PackageManager pm,
+            IconDrawableFactory iconDrawableFactory, int iconSizePx) {
+        super(context, context.getResources().getConfiguration().densityDpi,
+                iconSizePx);
+        mLauncherApps = la;
+        mPackageManager = pm;
+        mIconDrawableFactory = iconDrawableFactory;
+    }
+
+    private int getBadgeSize() {
+        return mContext.getResources().getDimensionPixelSize(
+                com.android.launcher3.icons.R.dimen.profile_badge_size);
+    }
+    /**
+     * Returns the conversation info drawable
+     */
+    private Drawable getConversationDrawable(ShortcutInfo shortcutInfo) {
+        return mLauncherApps.getShortcutIconDrawable(shortcutInfo, mFillResIconDpi);
+    }
+
+    /**
+     * Get the {@link Drawable} that represents the app icon
+     */
+    private Drawable getBadgedIcon(String packageName, int userId) {
+        try {
+            final ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
+                    packageName, PackageManager.GET_META_DATA, userId);
+            return mIconDrawableFactory.getBadgedIcon(appInfo, userId);
+        } catch (PackageManager.NameNotFoundException e) {
+            return mPackageManager.getDefaultActivityIcon();
+        }
+    }
+
+    /**
+     * Turns a Drawable into a Bitmap
+     */
+    BitmapInfo toBitmap(Drawable userBadgedAppIcon) {
+        Bitmap bitmap = createIconBitmap(
+                userBadgedAppIcon, 1f, getBadgeSize());
+
+        Canvas c = new Canvas();
+        ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
+        c.setBitmap(bitmap);
+        shadowGenerator.recreateIcon(Bitmap.createBitmap(bitmap), c);
+        return createIconBitmap(bitmap);
+    }
+
+    /**
+     * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+     */
+    public Bitmap getConversationBitmap(ShortcutInfo info, String packageName, int uid) {
+        return getConversationBitmap(getConversationDrawable(info), packageName, uid);
+    }
+
+    /**
+     * Returns a {@link BitmapInfo} for the entire conversation icon including the badge.
+     */
+    public Bitmap getConversationBitmap(Drawable baseIcon, String packageName, int uid) {
+        int userId = UserHandle.getUserId(uid);
+        Drawable badge = getBadgedIcon(packageName, userId);
+        BitmapInfo iconInfo = createBadgedIconBitmap(baseIcon,
+                UserHandle.of(userId),
+                true /* shrinkNonAdaptiveIcons */);
+
+        badgeWithDrawable(iconInfo.icon,
+                new BitmapDrawable(mContext.getResources(), toBitmap(badge).icon));
+        return iconInfo.icon;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 954eb9b..b47aa98 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1366,7 +1366,7 @@
             mConfig.allowedKeyManagement.set(KeyMgmt.NONE);
         } else {
             mConfig.allowedKeyManagement.set(KeyMgmt.OWE);
-            mConfig.requirePMF = true;
+            mConfig.requirePmf = true;
         }
     }
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 8b815bf..9668629 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -28,6 +28,7 @@
 import android.media.RoutingSessionInfo;
 
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
+import com.android.settingslib.testutils.shadow.ShadowRouter2Manager;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -36,15 +37,18 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
 
 import java.util.ArrayList;
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowRouter2Manager.class})
 public class InfoMediaManagerTest {
 
     private static final String TEST_PACKAGE_NAME = "com.test.packagename";
     private static final String TEST_ID = "test_id";
+    private static final String TEST_NAME = "test_name";
 
     @Mock
     private MediaRouter2Manager mRouterManager;
@@ -53,6 +57,7 @@
 
     private InfoMediaManager mInfoMediaManager;
     private Context mContext;
+    private ShadowRouter2Manager mShadowRouter2Manager;
 
     @Before
     public void setUp() {
@@ -61,22 +66,8 @@
 
         mInfoMediaManager =
                 new InfoMediaManager(mContext, TEST_PACKAGE_NAME, null, mLocalBluetoothManager);
-        mInfoMediaManager.mRouterManager = mRouterManager;
-    }
-
-    @Test
-    public void stopScan_shouldRemoveCallback() {
-        mInfoMediaManager.stopScan();
-
-        verify(mRouterManager).unregisterCallback(mInfoMediaManager.mMediaRouterCallback);
-    }
-
-    @Test
-    public void startScan_shouldAddCallback() {
-        mInfoMediaManager.startScan();
-
-        verify(mRouterManager).registerCallback(mInfoMediaManager.mExecutor,
-                mInfoMediaManager.mMediaRouterCallback);
+        mShadowRouter2Manager = ShadowRouter2Manager.getShadow();
+        mInfoMediaManager.mRouterManager = MediaRouter2Manager.getInstance(mContext);
     }
 
     @Test
@@ -87,7 +78,7 @@
 
         final List<MediaRoute2Info> routes = new ArrayList<>();
         routes.add(info);
-        when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+        mShadowRouter2Manager.setAvailableRoutes(routes);
 
         final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
         assertThat(mediaDevice).isNull();
@@ -108,7 +99,7 @@
 
         final List<MediaRoute2Info> routes = new ArrayList<>();
         routes.add(info);
-        when(mRouterManager.getAllRoutes()).thenReturn(routes);
+        mShadowRouter2Manager.setAllRoutes(routes);
 
         final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
         assertThat(mediaDevice).isNull();
@@ -129,7 +120,7 @@
 
         final List<MediaRoute2Info> routes = new ArrayList<>();
         routes.add(info);
-        when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+        mShadowRouter2Manager.setAvailableRoutes(routes);
 
         final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
         assertThat(mediaDevice).isNull();
@@ -157,7 +148,7 @@
 
         final List<MediaRoute2Info> routes = new ArrayList<>();
         routes.add(info);
-        when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+        mShadowRouter2Manager.setAvailableRoutes(routes);
 
         final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
         assertThat(mediaDevice).isNull();
@@ -178,7 +169,7 @@
 
         final List<MediaRoute2Info> routes = new ArrayList<>();
         routes.add(info);
-        when(mRouterManager.getAllRoutes()).thenReturn(routes);
+        mShadowRouter2Manager.setAllRoutes(routes);
 
         final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
         assertThat(mediaDevice).isNull();
@@ -194,12 +185,12 @@
     @Test
     public void connectDeviceWithoutPackageName_noSession_returnFalse() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
-        final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, info,
-                TEST_PACKAGE_NAME);
+        final MediaDevice device = new InfoMediaDevice(mContext, mInfoMediaManager.mRouterManager,
+                info, TEST_PACKAGE_NAME);
 
         final List<RoutingSessionInfo> infos = new ArrayList<>();
 
-        when(mRouterManager.getActiveSessions()).thenReturn(infos);
+        mShadowRouter2Manager.setActiveSessions(infos);
 
         assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
     }
@@ -239,10 +230,212 @@
         assertThat(mediaDevice).isNull();
 
         mInfoMediaManager.mPackageName = "";
-        mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+        mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes);
 
         final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
         assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
         assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
     }
+
+    @Test
+    public void addDeviceToPlayMedia_packageNameIsNull_returnFalse() {
+        mInfoMediaManager.mPackageName = null;
+        final MediaDevice device = mock(MediaDevice.class);
+
+        assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isFalse();
+    }
+
+    @Test
+    public void addDeviceToPlayMedia_containSelectableRoutes_returnTrue() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
+        final MediaDevice device =
+                new InfoMediaDevice(mContext, mInfoMediaManager.mRouterManager, route2Info,
+                        TEST_PACKAGE_NAME);
+
+        final List<String> list = new ArrayList<>();
+        list.add(TEST_ID);
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(info.getSelectableRoutes()).thenReturn(list);
+        when(route2Info.getId()).thenReturn(TEST_ID);
+        when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isTrue();
+    }
+
+    @Test
+    public void addDeviceToPlayMedia_notContainSelectableRoutes_returnFalse() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
+        final MediaDevice device =
+                new InfoMediaDevice(mContext, mInfoMediaManager.mRouterManager, route2Info,
+                        TEST_PACKAGE_NAME);
+
+        final List<String> list = new ArrayList<>();
+        list.add("fake_id");
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(info.getSelectableRoutes()).thenReturn(list);
+        when(route2Info.getId()).thenReturn(TEST_ID);
+        when(route2Info.getName()).thenReturn(TEST_NAME);
+        when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isFalse();
+    }
+
+    @Test
+    public void removeDeviceFromMedia_packageNameIsNull_returnFalse() {
+        mInfoMediaManager.mPackageName = null;
+        final MediaDevice device = mock(MediaDevice.class);
+
+        assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isFalse();
+    }
+
+    @Test
+    public void removeDeviceFromMedia_containSelectedRoutes_returnTrue() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
+        final MediaDevice device =
+                new InfoMediaDevice(mContext, mInfoMediaManager.mRouterManager, route2Info,
+                        TEST_PACKAGE_NAME);
+
+        final List<String> list = new ArrayList<>();
+        list.add(TEST_ID);
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(info.getSelectedRoutes()).thenReturn(list);
+        when(route2Info.getId()).thenReturn(TEST_ID);
+        when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isTrue();
+    }
+
+    @Test
+    public void removeDeviceFromMedia_notContainSelectedRoutes_returnFalse() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        final MediaRoute2Info route2Info = mock(MediaRoute2Info.class);
+        final MediaDevice device =
+                new InfoMediaDevice(mContext, mInfoMediaManager.mRouterManager, route2Info,
+                        TEST_PACKAGE_NAME);
+
+        final List<String> list = new ArrayList<>();
+        list.add("fake_id");
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+        when(info.getSelectedRoutes()).thenReturn(list);
+        when(route2Info.getId()).thenReturn(TEST_ID);
+        when(route2Info.getName()).thenReturn(TEST_NAME);
+        when(route2Info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isFalse();
+    }
+
+    @Test
+    public void getSelectableMediaDevice_packageNameIsNull_returnFalse() {
+        mInfoMediaManager.mPackageName = null;
+
+        assertThat(mInfoMediaManager.getSelectableMediaDevice()).isEmpty();
+    }
+
+    @Test
+    public void getSelectableMediaDevice_notContainPackageName_returnEmpty() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn("com.fake.packagename");
+
+        assertThat(mInfoMediaManager.getSelectableMediaDevice()).isEmpty();
+    }
+
+    @Test
+    public void adjustSessionVolume_packageNameIsNull_noCrash() {
+        mInfoMediaManager.mPackageName = null;
+
+        mInfoMediaManager.adjustSessionVolume(10);
+    }
+
+    @Test
+    public void getSessionVolumeMax_packageNameIsNull_returnNotFound() {
+        mInfoMediaManager.mPackageName = null;
+
+        assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
+    }
+
+    @Test
+    public void getSessionVolumeMax_containPackageName_returnMaxVolume() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        mInfoMediaManager.getSessionVolumeMax();
+
+        verify(info).getVolumeMax();
+    }
+
+    @Test
+    public void getSessionVolumeMax_notContainPackageName_returnNotFound() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn("com.fake.packagename");
+
+        assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
+    }
+
+    @Test
+    public void getSessionVolume_packageNameIsNull_returnNotFound() {
+        mInfoMediaManager.mPackageName = null;
+
+        assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
+    }
+
+    @Test
+    public void getSessionVolume_containPackageName_returnMaxVolume() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        mInfoMediaManager.getSessionVolume();
+
+        verify(info).getVolume();
+    }
+
+    @Test
+    public void getSessionVolume_notContainPackageName_returnNotFound() {
+        final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
+        final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
+        routingSessionInfos.add(info);
+
+        mShadowRouter2Manager.setRoutingSessions(routingSessionInfos);
+        when(info.getClientPackageName()).thenReturn("com.fake.packagename");
+
+        assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
new file mode 100644
index 0000000..db0cb06
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 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.settingslib.testutils.shadow;
+
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+import android.media.RoutingSessionInfo;
+
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.shadow.api.Shadow;
+
+import java.util.List;
+
+@Implements(MediaRouter2Manager.class)
+public class ShadowRouter2Manager {
+
+    private List<MediaRoute2Info> mAvailableRoutes;
+    private List<MediaRoute2Info> mAllRoutes;
+    private List<RoutingSessionInfo> mActiveSessions;
+    private List<RoutingSessionInfo> mRoutingSessions;
+
+    @Implementation
+    protected List<MediaRoute2Info> getAvailableRoutes(String packageName) {
+        return mAvailableRoutes;
+    }
+
+    public void setAvailableRoutes(List<MediaRoute2Info> infos) {
+        mAvailableRoutes = infos;
+    }
+
+    @Implementation
+    protected List<MediaRoute2Info> getAllRoutes() {
+        return mAllRoutes;
+    }
+
+    public void setAllRoutes(List<MediaRoute2Info> infos) {
+        mAllRoutes = infos;
+    }
+
+    @Implementation
+    protected List<RoutingSessionInfo> getActiveSessions() {
+        return mActiveSessions;
+    }
+
+    public void setActiveSessions(List<RoutingSessionInfo> infos) {
+        mActiveSessions = infos;
+    }
+
+    @Implementation
+    protected List<RoutingSessionInfo> getRoutingSessions(String packageName) {
+        return mRoutingSessions;
+    }
+
+    public void setRoutingSessions(List<RoutingSessionInfo> infos) {
+        mRoutingSessions = infos;
+    }
+
+    public static ShadowRouter2Manager getShadow() {
+        return (ShadowRouter2Manager) Shadow.extract(
+                MediaRouter2Manager.getInstance(RuntimeEnvironment.application));
+    }
+}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 8037266..c5d4fa9 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -126,6 +126,7 @@
         VALIDATORS.put(System.SCREEN_OFF_TIMEOUT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_FOR_VR, new InclusiveIntegerRangeValidator(0, 255));
         VALIDATORS.put(System.SCREEN_BRIGHTNESS_MODE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.ADAPTIVE_SLEEP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.MODE_RINGER_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.MUTE_STREAMS_AFFECTED, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(System.VIBRATE_ON, BOOLEAN_VALIDATOR);
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index 1814593..530823a 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -606,26 +606,21 @@
 
         BugreportInfo info = new BugreportInfo(mContext, baseName, name,
                 shareTitle, shareDescription, bugreportType, mBugreportsDir);
-        ParcelFileDescriptor bugreportFd;
-        ParcelFileDescriptor screenshotFd;
-
-        try {
-            bugreportFd = info.createAndGetBugreportFd();
-            if (bugreportFd == null) {
-                Log.e(TAG, "Bugreport parcel file descriptor is null.");
-                return;
-            }
-            screenshotFd = info.createAndGetDefaultScreenshotFd();
-            if (screenshotFd == null) {
-                Log.e(TAG, "Screenshot parcel file descriptor is null. Deleting bugreport file");
-                FileUtils.closeQuietly(bugreportFd);
-                info.bugreportFile.delete();
-                return;
-            }
-        } catch (IOException e) {
-            Log.e(TAG, "Error in generating bugreport files: ", e);
+        ParcelFileDescriptor bugreportFd = info.getBugreportFd();
+        if (bugreportFd == null) {
+            Log.e(TAG, "Failed to start bugreport generation as "
+                    + " bugreport parcel file descriptor is null.");
             return;
         }
+        ParcelFileDescriptor screenshotFd = info.getDefaultScreenshotFd();
+        if (screenshotFd == null) {
+            Log.e(TAG, "Failed to start bugreport generation as"
+                    + " screenshot parcel file descriptor is null. Deleting bugreport file");
+            FileUtils.closeQuietly(bugreportFd);
+            info.bugreportFile.delete();
+            return;
+        }
+
         mBugreportManager = (BugreportManager) mContext.getSystemService(
                 Context.BUGREPORT_SERVICE);
         final Executor executor = ActivityThread.currentActivityThread().getExecutor();
@@ -639,7 +634,7 @@
             mBugreportManager.startBugreport(bugreportFd, screenshotFd,
                     new BugreportParams(bugreportType), executor, bugreportCallback);
         } catch (RuntimeException e) {
-            Log.i(TAG, "error in generating bugreports: ", e);
+            Log.i(TAG, "Error in generating bugreports: ", e);
             // The binder call didn't go through successfully, so need to close the fds.
             // If the calls went through API takes ownership.
             FileUtils.closeQuietly(bugreportFd);
@@ -657,11 +652,15 @@
         return null;
     }
 
-    private static void createReadWriteFile(File file) throws IOException {
-        if (!file.exists()) {
-            file.createNewFile();
-            file.setReadable(true, true);
-            file.setWritable(true, true);
+    private static void createReadWriteFile(File file) {
+        try {
+            if (!file.exists()) {
+                file.createNewFile();
+                file.setReadable(true, true);
+                file.setWritable(true, true);
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Error in creating bugreport file: ", e);
         }
     }
 
@@ -1836,23 +1835,23 @@
 
         void createBugreportFile(File bugreportsDir) {
             bugreportFile = new File(bugreportsDir, getFileName(this, ".zip"));
+            createReadWriteFile(bugreportFile);
         }
 
         void createScreenshotFile(File bugreportsDir) {
             File screenshotFile = new File(bugreportsDir, getScreenshotName("default"));
             addScreenshot(screenshotFile);
+            createReadWriteFile(screenshotFile);
         }
 
-        ParcelFileDescriptor createAndGetBugreportFd() throws IOException {
-            createReadWriteFile(bugreportFile);
+        ParcelFileDescriptor getBugreportFd() {
             return getFd(bugreportFile);
         }
 
-        ParcelFileDescriptor createAndGetDefaultScreenshotFd() throws IOException {
+        ParcelFileDescriptor getDefaultScreenshotFd() {
             if (screenshotFiles.isEmpty()) {
                 return null;
             }
-            createReadWriteFile(screenshotFiles.get(0));
             return getFd(screenshotFiles.get(0));
         }
 
diff --git a/packages/SystemUI/res/color/control_background.xml b/packages/SystemUI/res/color/control_background.xml
index 646fe5d..977310c 100644
--- a/packages/SystemUI/res/color/control_background.xml
+++ b/packages/SystemUI/res/color/control_background.xml
@@ -2,5 +2,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_enabled="false"
         android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_blue_50" />
+  <item android:color="@color/GM2_blue_200"
+        android:alpha="0.2" />
 </selector>
diff --git a/packages/SystemUI/res/color/control_foreground.xml b/packages/SystemUI/res/color/control_foreground.xml
index bf028f1..339f1e2 100644
--- a/packages/SystemUI/res/color/control_foreground.xml
+++ b/packages/SystemUI/res/color/control_foreground.xml
@@ -2,5 +2,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false"
          android:color="@color/control_default_foreground" />
-   <item android:color="@color/GM2_blue_700" />
+   <item android:color="@color/GM2_blue_200" />
  </selector>
diff --git a/packages/SystemUI/res/color/light_background.xml b/packages/SystemUI/res/color/light_background.xml
index 2effd99..1299464 100644
--- a/packages/SystemUI/res/color/light_background.xml
+++ b/packages/SystemUI/res/color/light_background.xml
@@ -2,5 +2,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_enabled="false"
         android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_yellow_50" />
+  <item android:color="@color/GM2_yellow_200"
+        android:alpha="0.2" />
 </selector>
diff --git a/packages/SystemUI/res/color/light_foreground.xml b/packages/SystemUI/res/color/light_foreground.xml
index 8143028..7c02075 100644
--- a/packages/SystemUI/res/color/light_foreground.xml
+++ b/packages/SystemUI/res/color/light_foreground.xml
@@ -2,5 +2,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_enabled="false"
         android:color="@color/control_default_foreground" />
-  <item android:color="@color/GM2_orange_900" />
+  <item android:color="@color/GM2_yellow_200" />
 </selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_background.xml b/packages/SystemUI/res/color/thermo_cool_background.xml
index 646fe5d..977310c 100644
--- a/packages/SystemUI/res/color/thermo_cool_background.xml
+++ b/packages/SystemUI/res/color/thermo_cool_background.xml
@@ -2,5 +2,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_enabled="false"
         android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_blue_50" />
+  <item android:color="@color/GM2_blue_200"
+        android:alpha="0.2" />
 </selector>
diff --git a/packages/SystemUI/res/color/thermo_cool_foreground.xml b/packages/SystemUI/res/color/thermo_cool_foreground.xml
index bf028f1..339f1e2 100644
--- a/packages/SystemUI/res/color/thermo_cool_foreground.xml
+++ b/packages/SystemUI/res/color/thermo_cool_foreground.xml
@@ -2,5 +2,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false"
          android:color="@color/control_default_foreground" />
-   <item android:color="@color/GM2_blue_700" />
+   <item android:color="@color/GM2_blue_200" />
  </selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_background.xml b/packages/SystemUI/res/color/thermo_heat_background.xml
index 6f29ed5..2709ebe 100644
--- a/packages/SystemUI/res/color/thermo_heat_background.xml
+++ b/packages/SystemUI/res/color/thermo_heat_background.xml
@@ -2,5 +2,6 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_enabled="false"
         android:color="@color/control_default_background" />
-  <item android:color="@color/GM2_red_50" />
+  <item android:color="@color/GM2_red_200"
+        android:alpha="0.2" />
 </selector>
diff --git a/packages/SystemUI/res/color/thermo_heat_foreground.xml b/packages/SystemUI/res/color/thermo_heat_foreground.xml
index 72f4b8d..ffcf550 100644
--- a/packages/SystemUI/res/color/thermo_heat_foreground.xml
+++ b/packages/SystemUI/res/color/thermo_heat_foreground.xml
@@ -2,5 +2,5 @@
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_enabled="false"
         android:color="@color/control_default_foreground" />
-  <item android:color="@color/GM2_red_700" />
+  <item android:color="@color/GM2_red_200" />
 </selector>
diff --git a/packages/SystemUI/res/drawable/control_background.xml b/packages/SystemUI/res/drawable/control_background.xml
index b246ea0..29b4efa 100644
--- a/packages/SystemUI/res/drawable/control_background.xml
+++ b/packages/SystemUI/res/drawable/control_background.xml
@@ -19,7 +19,7 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
   <item>
     <shape>
-      <solid android:color="?android:attr/colorBackgroundFloating"/>
+      <solid android:color="@color/control_default_background" />
       <corners android:radius="@dimen/control_corner_radius" />
     </shape>
   </item>
diff --git a/packages/SystemUI/res/drawable/ic_screen_record_background.xml b/packages/SystemUI/res/drawable/ic_screen_record_background.xml
new file mode 100644
index 0000000..9195305
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_screen_record_background.xml
@@ -0,0 +1,25 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:tint="?android:attr/colorError"
+    android:viewportWidth="24.0"
+    android:viewportHeight="24.0">
+    <path
+        android:pathData="M10,0L14,0A10,10 0,0 1,24 10L24,10A10,10 0,0 1,14 20L10,20A10,10 0,0 1,0 10L0,10A10,10 0,0 1,10 0z"
+        android:fillColor="@android:color/white"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record.xml
new file mode 100644
index 0000000..486af9e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record.xml
@@ -0,0 +1,16 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable />
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml
new file mode 100644
index 0000000..ab2314e
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_1.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable
+    level="1"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml
new file mode 100644
index 0000000..8764ff9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_2.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable
+    level="2"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml b/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml
new file mode 100644
index 0000000..0ff4d9a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_screen_record_3.xml
@@ -0,0 +1,18 @@
+<!--
+Copyright (C) 2020 The Android Open Source Project
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+<com.android.systemui.statusbar.ScreenRecordDrawable
+    level="3"
+/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index f90012d..4fe21e5 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -2,15 +2,17 @@
 <ScrollView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/global_actions_container"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
   <androidx.constraintlayout.widget.ConstraintLayout
       android:id="@+id/global_actions_grid_root"
       android:layout_width="match_parent"
-      android:layout_height="wrap_content"
+      android:layout_height="match_parent"
       android:clipChildren="false"
       android:clipToPadding="false"
+      android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
       android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset">
 
     <com.android.systemui.globalactions.GlobalActionsFlatLayout
@@ -25,65 +27,20 @@
         android:gravity="top | center_horizontal"
         android:clipChildren="false"
         android:clipToPadding="false"
-        android:paddingBottom="@dimen/global_actions_grid_container_shadow_offset"
-        android:layout_marginTop="@dimen/global_actions_top_margin"
-        android:layout_marginBottom="@dimen/global_actions_grid_container_negative_shadow_offset">
+        android:layout_marginTop="@dimen/global_actions_top_margin">
       <LinearLayout
-          android:layout_height="wrap_content"
+          android:id="@android:id/list"
           android:layout_width="wrap_content"
-          android:layoutDirection="ltr"
-          android:clipChildren="false"
-          android:clipToPadding="false"
-          android:layout_marginBottom="@dimen/global_actions_grid_container_bottom_margin">
-        <!-- For separated items-->
-        <LinearLayout
-            android:id="@+id/separated_button"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
-            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
-            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
-            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
-            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
-            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
-            android:orientation="vertical"
-            android:gravity="center"
-            android:translationZ="@dimen/global_actions_translate"
-            />
-        <!-- Grid of action items -->
-        <com.android.systemui.globalactions.ListGridLayout
-            android:id="@android:id/list"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:orientation="vertical"
-            android:gravity="right"
-            android:layout_marginRight="@dimen/global_actions_grid_side_margin"
-            android:translationZ="@dimen/global_actions_translate"
-            android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
-            android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
-            android:paddingTop="@dimen/global_actions_grid_vertical_padding"
-            android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
-            >
-          <LinearLayout
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:visibility="gone"
-              android:layoutDirection="locale"
-              />
-          <LinearLayout
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:visibility="gone"
-              android:layoutDirection="locale"
-              />
-          <LinearLayout
-              android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:visibility="gone"
-              android:layoutDirection="locale"
-              />
-        </com.android.systemui.globalactions.ListGridLayout>
-      </LinearLayout>
+          android:layout_height="wrap_content"
+          android:layout_marginLeft="@dimen/global_actions_grid_side_margin"
+          android:layout_marginRight="@dimen/global_actions_grid_side_margin"
+          android:paddingLeft="@dimen/global_actions_grid_horizontal_padding"
+          android:paddingRight="@dimen/global_actions_grid_horizontal_padding"
+          android:paddingTop="@dimen/global_actions_grid_vertical_padding"
+          android:paddingBottom="@dimen/global_actions_grid_vertical_padding"
+          android:orientation="horizontal"
+          android:gravity="left"
+          android:translationZ="@dimen/global_actions_translate" />
     </com.android.systemui.globalactions.GlobalActionsFlatLayout>
 
     <LinearLayout
@@ -110,8 +67,7 @@
         android:layout_marginLeft="@dimen/global_actions_grid_horizontal_padding"
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
-        app:layout_constraintTop_toBottomOf="@id/global_actions_panel">
-
-    </LinearLayout>
+        app:layout_constraintTop_toBottomOf="@id/global_actions_panel"
+        app:layout_constraintBottom_toBottomOf="parent" />
   </androidx.constraintlayout.widget.ConstraintLayout>
 </ScrollView>
diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml
index d0151ff..0b74a11a 100644
--- a/packages/SystemUI/res/layout/global_screenshot.xml
+++ b/packages/SystemUI/res/layout/global_screenshot.xml
@@ -76,6 +76,7 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:src="@android:color/white"
+        android:elevation="8dp"
         android:visibility="gone"/>
     <com.android.systemui.screenshot.ScreenshotSelectorView
         android:id="@+id/global_screenshot_selector"
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 09058f2..43ceb4e 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -201,6 +201,7 @@
     <color name="GM2_grey_900">#202124</color>
 
     <color name="GM2_red_50">#FCE8E6</color>
+    <color name="GM2_red_200">#F6AEA9</color>
     <color name="GM2_red_300">#F28B82</color>
     <color name="GM2_red_500">#B71C1C</color>
     <color name="GM2_red_700">#C5221F</color>
@@ -213,6 +214,7 @@
     <color name="GM2_blue_700">#1967D2</color>
 
     <color name="GM2_yellow_50">#FEF7E0</color>
+    <color name="GM2_yellow_200">#FDE293</color>
     <color name="GM2_yellow_500">#FFFBBC04</color>
 
     <color name="GM2_green_500">#FF34A853</color>
@@ -222,6 +224,8 @@
     <color name="magnification_border_color">#FF9900</color>
 
     <!-- controls -->
-    <color name="control_default_foreground">?android:attr/textColorPrimary</color>
-    <color name="control_default_background">?android:attr/colorBackgroundFloating</color>
+    <color name="control_primary_text">@*android:color/foreground_material_dark</color>
+    <color name="control_secondary_text">@*android:color/dim_foreground_dark</color>
+    <color name="control_default_foreground">@*android:color/foreground_material_dark</color>
+    <color name="control_default_background">@*android:color/background_floating_material_dark</color>
 </resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index ec56c1f..15575a4 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1228,6 +1228,8 @@
     <!-- Screen Record -->
     <dimen name="screenrecord_dialog_padding">18dp</dimen>
     <dimen name="screenrecord_logo_size">24dp</dimen>
+    <dimen name="screenrecord_status_text_size">14sp</dimen>
+    <dimen name="screenrecord_status_icon_radius">5dp</dimen>
 
     <dimen name="kg_user_switcher_text_size">16sp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 6fedac0..fecb75c 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -635,16 +635,16 @@
 
     <style name="TextAppearance.Control.Status">
         <item name="android:textSize">12sp</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">@color/control_primary_text</item>
     </style>
 
     <style name="TextAppearance.Control.Title">
         <item name="android:textSize">14sp</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textColor">@color/control_primary_text</item>
     </style>
     <style name="TextAppearance.Control.Subtitle">
         <item name="android:textSize">12sp</item>
-        <item name="android:textColor">?android:attr/textColorSecondary</item>
+        <item name="android:textColor">@color/control_secondary_text</item>
     </style>
 
 </resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 2d288ff..b1d39f5 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -123,4 +123,9 @@
      */
      void handleImageAsScreenshot(in Bitmap screenImage, in Rect locationInScreen,
               in Insets visibleInsets, int taskId) = 21;
+
+    /**
+     * Sets the split-screen divider minimized state
+     */
+    void setSplitScreenMinimized(boolean minimized) = 22;
 }
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 27fe37e..dc5cb1f 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -61,6 +61,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
 import com.android.systemui.shared.system.PackageManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.NotificationListener;
@@ -329,6 +330,7 @@
     @Inject Lazy<DisplayImeController> mDisplayImeController;
     @Inject Lazy<RecordingController> mRecordingController;
     @Inject Lazy<ProtoTracer> mProtoTracer;
+    @Inject Lazy<Divider> mDivider;
 
     @Inject
     public Dependency() {
@@ -530,6 +532,7 @@
         mProviders.put(AutoHideController.class, mAutoHideController::get);
 
         mProviders.put(RecordingController.class, mRecordingController::get);
+        mProviders.put(Divider.class, mDivider::get);
 
         sDependency = this;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java b/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
deleted file mode 100644
index 5c0df17..0000000
--- a/packages/SystemUI/src/com/android/systemui/DockedStackExistsListener.java
+++ /dev/null
@@ -1,92 +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.
- */
-
-package com.android.systemui;
-
-import android.os.RemoteException;
-import android.util.Log;
-import android.view.IDockedStackListener;
-import android.view.WindowManagerGlobal;
-
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Utility wrapper to listen for whether or not a docked stack exists, to be
- * used for things like the different overview icon in that mode.
- */
-public class DockedStackExistsListener {
-
-    private static final String TAG = "DockedStackExistsListener";
-
-    private static ArrayList<WeakReference<Consumer<Boolean>>> sCallbacks = new ArrayList<>();
-    private static boolean mLastExists;
-
-    static {
-        try {
-            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
-                    new IDockedStackListener.Stub() {
-                        @Override
-                        public void onDividerVisibilityChanged(boolean b) throws RemoteException {
-
-                        }
-
-                        @Override
-                        public void onDockedStackExistsChanged(boolean exists)
-                                throws RemoteException {
-                            DockedStackExistsListener.onDockedStackExistsChanged(exists);
-                        }
-
-                        @Override
-                        public void onDockedStackMinimizedChanged(boolean b, long l, boolean b1)
-                                throws RemoteException {
-
-                        }
-
-                        @Override
-                        public void onAdjustedForImeChanged(boolean b, long l)
-                                throws RemoteException {
-
-                        }
-
-                        @Override
-                        public void onDockSideChanged(int i) throws RemoteException {
-
-                        }
-                    });
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed registering docked stack exists listener", e);
-        }
-    }
-
-
-    private static void onDockedStackExistsChanged(boolean exists) {
-        mLastExists = exists;
-        synchronized (sCallbacks) {
-            sCallbacks.removeIf(wf -> {
-                Consumer<Boolean> l = wf.get();
-                if (l != null) l.accept(exists);
-                return l == null;
-            });
-        }
-    }
-
-    public static void register(Consumer<Boolean> callback) {
-        callback.accept(mLastExists);
-        synchronized (sCallbacks) {
-            sCallbacks.add(new WeakReference<>(callback));
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
index d153fb0..611da0d 100644
--- a/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/MultiListLayout.java
@@ -57,7 +57,10 @@
     }
 
     protected void setSeparatedViewVisibility(boolean visible) {
-        getSeparatedView().setVisibility(visible ? View.VISIBLE : View.GONE);
+        ViewGroup separatedView = getSeparatedView();
+        if (separatedView != null) {
+            separatedView.setVisibility(visible ? View.VISIBLE : View.GONE);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index e66b9f2..a2ae59e 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -22,12 +22,8 @@
 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 
-import static com.android.systemui.tuner.TunablePadding.FLAG_END;
-import static com.android.systemui.tuner.TunablePadding.FLAG_START;
-
 import android.annotation.Dimension;
 import android.app.ActivityManager;
-import android.app.Fragment;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
@@ -69,13 +65,7 @@
 import com.android.systemui.RegionInterceptingFrameLayout.RegionInterceptableView;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
-import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.SecureSetting;
-import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.tuner.TunablePadding;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.leak.RotationUtils;
@@ -86,8 +76,6 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-import dagger.Lazy;
-
 /**
  * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
  * for antialiasing and emulation purposes.
@@ -102,7 +90,6 @@
     private static final boolean DEBUG_SCREENSHOT_ROUNDED_CORNERS =
             SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
     private static final boolean VERBOSE = false;
-    private final Lazy<StatusBar> mStatusBarLazy;
 
     private DisplayManager mDisplayManager;
     private final BroadcastDispatcher mBroadcastDispatcher;
@@ -146,12 +133,10 @@
 
     @Inject
     public ScreenDecorations(Context context,
-            Lazy<StatusBar> statusBarLazy,
             @Main Handler handler,
             BroadcastDispatcher broadcastDispatcher,
             TunerService tunerService) {
         super(context);
-        mStatusBarLazy = statusBarLazy;
         mMainHandler = handler;
         mBroadcastDispatcher = broadcastDispatcher;
         mTunerService = tunerService;
@@ -161,7 +146,6 @@
     public void start() {
         mHandler = startHandlerThread();
         mHandler.post(this::startOnScreenDecorationsThread);
-        setupStatusBarPaddingIfNeeded();
     }
 
     @VisibleForTesting
@@ -440,42 +424,6 @@
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
     }
 
-
-    private void setupStatusBarPaddingIfNeeded() {
-        // TODO: This should be moved to a more appropriate place, as it is not related to the
-        // screen decorations overlay.
-        int padding = mContext.getResources().getDimensionPixelSize(
-                R.dimen.rounded_corner_content_padding);
-        if (padding != 0) {
-            setupStatusBarPadding(padding);
-        }
-
-    }
-
-    private void setupStatusBarPadding(int padding) {
-        // Add some padding to all the content near the edge of the screen.
-        StatusBar statusBar = mStatusBarLazy.get();
-        final View notificationShadeWindowView = statusBar.getNotificationShadeWindowView();
-        if (notificationShadeWindowView != null) {
-            TunablePadding.addTunablePadding(
-                    notificationShadeWindowView.findViewById(R.id.keyguard_header),
-                    PADDING, padding, FLAG_END);
-
-            final FragmentHostManager fragmentHostManager =
-                    FragmentHostManager.get(notificationShadeWindowView);
-            fragmentHostManager.addTagListener(QS.TAG,
-                    new TunablePaddingTagListener(padding, R.id.header));
-        }
-
-        final View statusBarWindow = statusBar.getStatusBarWindow();
-        if (statusBarWindow != null) {
-            final FragmentHostManager fragmentHostManager =
-                    FragmentHostManager.get(statusBarWindow);
-            fragmentHostManager.addTagListener(CollapsedStatusBarFragment.TAG,
-                    new TunablePaddingTagListener(padding, R.id.status_bar));
-        }
-    }
-
     @VisibleForTesting
     WindowManager.LayoutParams getWindowLayoutParams() {
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
@@ -566,32 +514,6 @@
         view.setLayoutParams(params);
     }
 
-    @VisibleForTesting
-    static class TunablePaddingTagListener implements FragmentListener {
-
-        private final int mPadding;
-        private final int mId;
-        private TunablePadding mTunablePadding;
-
-        public TunablePaddingTagListener(int padding, int id) {
-            mPadding = padding;
-            mId = id;
-        }
-
-        @Override
-        public void onFragmentViewCreated(String tag, Fragment fragment) {
-            if (mTunablePadding != null) {
-                mTunablePadding.destroy();
-            }
-            View view = fragment.getView();
-            if (mId != 0) {
-                view = view.findViewById(mId);
-            }
-            mTunablePadding = TunablePadding.addTunablePadding(view, PADDING, mPadding,
-                    FLAG_START | FLAG_END);
-        }
-    }
-
     public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener,
             RegionInterceptableView {
 
@@ -763,11 +685,11 @@
                 return false;
             }
             if (mStart) {
-                return displayCutout.getSafeInsetLeft() > 0
-                        || displayCutout.getSafeInsetTop() > 0;
+                return !displayCutout.getBoundingRectLeft().isEmpty()
+                        || !displayCutout.getBoundingRectTop().isEmpty();
             } else {
-                return displayCutout.getSafeInsetRight() > 0
-                        || displayCutout.getSafeInsetBottom() > 0;
+                return !displayCutout.getBoundingRectRight().isEmpty()
+                        || !displayCutout.getBoundingRectBottom().isEmpty();
             }
         }
 
@@ -809,15 +731,15 @@
 
         private int getGravity(DisplayCutout displayCutout) {
             if (mStart) {
-                if (displayCutout.getSafeInsetLeft() > 0) {
+                if (!displayCutout.getBoundingRectLeft().isEmpty()) {
                     return Gravity.LEFT;
-                } else if (displayCutout.getSafeInsetTop() > 0) {
+                } else if (!displayCutout.getBoundingRectTop().isEmpty()) {
                     return Gravity.TOP;
                 }
             } else {
-                if (displayCutout.getSafeInsetRight() > 0) {
+                if (!displayCutout.getBoundingRectRight().isEmpty()) {
                     return Gravity.RIGHT;
-                } else if (displayCutout.getSafeInsetBottom() > 0) {
+                } else if (!displayCutout.getBoundingRectBottom().isEmpty()) {
                     return Gravity.BOTTOM;
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 1e39954..cdeb5c3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -38,9 +38,11 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.shared.system.SysUiStatsLog;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import java.io.FileDescriptor;
@@ -50,7 +52,7 @@
 /**
  * Encapsulates the data and UI elements of a bubble.
  */
-class Bubble {
+class Bubble implements BubbleViewProvider {
     private static final String TAG = "Bubble";
 
     private NotificationEntry mEntry;
@@ -148,12 +150,12 @@
     }
 
     @Nullable
-    BadgedImageView getIconView() {
+    public BadgedImageView getIconView() {
         return mIconView;
     }
 
     @Nullable
-    BubbleExpandedView getExpandedView() {
+    public BubbleExpandedView getExpandedView() {
         return mExpandedView;
     }
 
@@ -238,7 +240,7 @@
      * Note that this contents visibility doesn't affect visibility at {@link android.view.View},
      * and setting {@code false} actually means rendering the expanded view in transparent.
      */
-    void setContentVisibility(boolean visibility) {
+    public void setContentVisibility(boolean visibility) {
         if (mExpandedView != null) {
             mExpandedView.setContentVisibility(visibility);
         }
@@ -481,4 +483,36 @@
     public int hashCode() {
         return Objects.hash(mKey);
     }
+
+    public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) {
+        if (this.getEntry() == null
+                || this.getEntry().getSbn() == null) {
+            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+                    null /* package name */,
+                    null /* notification channel */,
+                    0 /* notification ID */,
+                    0 /* bubble position */,
+                    bubbleCount,
+                    action,
+                    normalX,
+                    normalY,
+                    false /* unread bubble */,
+                    false /* on-going bubble */,
+                    false /* isAppForeground (unused) */);
+        } else {
+            StatusBarNotification notification = this.getEntry().getSbn();
+            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
+                    notification.getPackageName(),
+                    notification.getNotification().getChannelId(),
+                    notification.getId(),
+                    index,
+                    bubbleCount,
+                    action,
+                    normalX,
+                    normalY,
+                    this.showInShade(),
+                    this.isOngoing(),
+                    false /* isAppForeground (unused) */);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 762e5f2..1f94dbd 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -72,9 +72,7 @@
 import com.android.systemui.DumpController;
 import com.android.systemui.Dumpable;
 import com.android.systemui.R;
-import com.android.systemui.bubbles.BubbleController.BubbleExpandListener;
-import com.android.systemui.bubbles.BubbleController.BubbleStateChangeListener;
-import com.android.systemui.bubbles.BubbleController.NotifCallback;
+import com.android.systemui.bubbles.dagger.BubbleModule;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.PinnedStackListenerForwarder;
@@ -105,16 +103,12 @@
 import java.util.HashSet;
 import java.util.List;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /**
  * Bubbles are a special type of content that can "float" on top of other apps or System UI.
  * Bubbles can be expanded to show more content.
  *
  * The controller manages addition, removal, and visible state of bubbles on screen.
  */
-@Singleton
 public class BubbleController implements ConfigurationController.ConfigurationListener, Dumpable {
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleController" : TAG_BUBBLES;
@@ -277,7 +271,6 @@
         }
     }
 
-    @Inject
     public BubbleController(Context context,
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarStateController statusBarStateController,
@@ -298,6 +291,9 @@
                 notifPipeline, featureFlags, dumpController);
     }
 
+    /**
+     * Injected constructor. See {@link BubbleModule}.
+     */
     public BubbleController(Context context,
             NotificationShadeWindowController notificationShadeWindowController,
             StatusBarStateController statusBarStateController,
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
new file mode 100644
index 0000000..6cf1086
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2020 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.bubbles;
+
+import static android.view.View.GONE;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.InsetDrawable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+/**
+ * Class for showing aged out bubbles.
+ */
+public class BubbleOverflow implements BubbleViewProvider {
+
+    private ImageView mOverflowBtn;
+    private BubbleExpandedView mOverflowExpandedView;
+    private LayoutInflater mInflater;
+    private Context mContext;
+
+    public BubbleOverflow(Context context) {
+        mContext = context;
+        mInflater = LayoutInflater.from(context);
+    }
+
+    public void setUpOverflow(ViewGroup parentViewGroup) {
+        mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
+                R.layout.bubble_expanded_view, parentViewGroup /* root */,
+                false /* attachToRoot */);
+        mOverflowExpandedView.setOverflow(true);
+
+        mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
+                parentViewGroup /* root */,
+                false /* attachToRoot */);
+
+        setOverflowBtnTheme();
+        mOverflowBtn.setVisibility(GONE);
+    }
+
+    ImageView getBtn() {
+        return mOverflowBtn;
+    }
+
+    void setBtnVisible(int visible) {
+        mOverflowBtn.setVisibility(visible);
+    }
+
+    // TODO(b/149146374) Propagate theme change to bubbles in overflow.
+    void setOverflowBtnTheme() {
+        TypedArray ta = mContext.obtainStyledAttributes(
+                new int[]{android.R.attr.colorBackgroundFloating});
+        int bgColor = ta.getColor(0, Color.WHITE /* default */);
+        ta.recycle();
+
+        InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
+        ColorDrawable bg = new ColorDrawable(bgColor);
+        AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
+        mOverflowBtn.setImageDrawable(adaptiveIcon);
+    }
+
+
+    public BubbleExpandedView getExpandedView() {
+        return mOverflowExpandedView;
+    }
+
+    public void setContentVisibility(boolean visible) {
+        mOverflowExpandedView.setContentVisibility(visible);
+    }
+
+    public void logUIEvent(int bubbleCount, int action, float normalX, float normalY,
+            int index) {
+        // TODO(b/149133814) Log overflow UI events.
+    }
+
+    public View getIconView() {
+        return mOverflowBtn;
+    }
+
+    public String getKey() {
+        return BubbleOverflowActivity.KEY;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
index f3cfa83..756c598 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflowActivity.java
@@ -47,6 +47,7 @@
  * Must be public to be accessible to androidx...AppComponentFactory
  */
 public class BubbleOverflowActivity extends Activity {
+    public static final String KEY = "Overflow";
     private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleOverflowActivity" : TAG_BUBBLES;
 
     private LinearLayout mEmptyState;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index cff78cf..9a6295a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -33,8 +33,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.Color;
 import android.graphics.ColorMatrix;
 import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
@@ -42,13 +40,9 @@
 import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
 import android.os.Bundle;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
-import android.service.notification.StatusBarNotification;
 import android.util.Log;
 import android.view.Choreographer;
 import android.view.DisplayCutout;
@@ -63,7 +57,6 @@
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.widget.FrameLayout;
-import android.widget.ImageView;
 
 import androidx.annotation.MainThread;
 import androidx.annotation.Nullable;
@@ -199,7 +192,7 @@
     private int mPointerHeight;
     private int mStatusBarHeight;
     private int mImeOffset;
-    private Bubble mExpandedBubble;
+    private BubbleViewProvider mExpandedBubble;
     private boolean mIsExpanded;
 
     /** Whether the stack is currently on the left side of the screen, or animating there. */
@@ -322,8 +315,8 @@
     private Runnable mAfterMagnet;
 
     private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
-    private BubbleExpandedView mOverflowExpandedView;
-    private ImageView mOverflowBtn;
+
+    private BubbleOverflow mBubbleOverflow;
 
     public BubbleStackView(Context context, BubbleData data,
             @Nullable SurfaceSynchronizer synchronizer) {
@@ -333,7 +326,6 @@
         mInflater = LayoutInflater.from(context);
         mTouchHandler = new BubbleTouchHandler(this, data, context);
         setOnTouchListener(mTouchHandler);
-        mInflater = LayoutInflater.from(context);
 
         Resources res = getResources();
         mMaxBubbles = res.getInteger(R.integer.bubbles_max_rendered);
@@ -407,12 +399,8 @@
                         .setStiffness(SpringForce.STIFFNESS_LOW)
                         .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY));
         mExpandedViewYAnim.addEndListener((anim, cancelled, value, velocity) -> {
-            if (mIsExpanded) {
-                if (mExpandedBubble == null) {
-                    mOverflowExpandedView.updateView();
-                } else {
-                    mExpandedBubble.getExpandedView().updateView();
-                }
+            if (mIsExpanded && mExpandedBubble != null) {
+                mExpandedBubble.getExpandedView().updateView();
             }
         });
 
@@ -420,8 +408,12 @@
         setFocusable(true);
         mBubbleContainer.bringToFront();
 
+        mBubbleOverflow = new BubbleOverflow(mContext);
         if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
-            setUpOverflow();
+            mBubbleOverflow.setUpOverflow(this);
+            mBubbleContainer.addView(mBubbleOverflow.getBtn(), 0,
+                    new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+
         }
 
         setOnApplyWindowInsetsListener((View view, WindowInsets insets) -> {
@@ -432,9 +424,7 @@
                     // Update the insets after we're done translating otherwise position
                     // calculation for them won't be correct.
                     () -> {
-                        if (mExpandedBubble == null) {
-                            mOverflowExpandedView.updateInsets(insets);
-                        } else {
+                        if (mExpandedBubble != null) {
                             mExpandedBubble.getExpandedView().updateInsets(insets);
                         }
                     });
@@ -449,9 +439,7 @@
                     // Reposition & adjust the height for new orientation
                     if (mIsExpanded) {
                         mExpandedViewContainer.setTranslationY(getExpandedViewY());
-                        if (mExpandedBubble == null) {
-                            mOverflowExpandedView.updateView();
-                        } else {
+                        if (mExpandedBubble != null) {
                             mExpandedBubble.getExpandedView().updateView();
                         }
                     }
@@ -516,40 +504,8 @@
         });
     }
 
-    private void setUpOverflow() {
-        mOverflowExpandedView = (BubbleExpandedView) mInflater.inflate(
-                R.layout.bubble_expanded_view, this /* root */, false /* attachToRoot */);
-        mOverflowExpandedView.setOverflow(true);
-
-        mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
-                this /* root */,
-                false /* attachToRoot */);
-
-        mBubbleContainer.addView(mOverflowBtn, 0,
-                new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-
-        setOverflowBtnTheme();
-        mOverflowBtn.setVisibility(GONE);
-    }
-
-    // TODO(b/149146374) Propagate theme change to bubbles in overflow.
-    private void setOverflowBtnTheme() {
-        TypedArray ta = mContext.obtainStyledAttributes(
-                new int[]{android.R.attr.colorBackgroundFloating});
-        int bgColor = ta.getColor(0, Color.WHITE /* default */);
-        ta.recycle();
-
-        InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(), 28);
-        ColorDrawable bg = new ColorDrawable(bgColor);
-        AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
-        mOverflowBtn.setImageDrawable(adaptiveIcon);
-    }
-
     void showExpandedViewContents(int displayId) {
-        if (mOverflowExpandedView != null
-                && mOverflowExpandedView.getVirtualDisplayId() == displayId) {
-            mOverflowExpandedView.setContentVisibility(true);
-        } else if (mExpandedBubble != null
+        if (mExpandedBubble != null
                 && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
             mExpandedBubble.setContentVisibility(true);
         }
@@ -573,7 +529,7 @@
     public void onThemeChanged() {
         setUpFlyout();
         if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
-            setOverflowBtnTheme();
+            mBubbleOverflow.setOverflowBtnTheme();
         }
     }
 
@@ -756,15 +712,22 @@
     /**
      * The {@link BadgedImageView} that is expanded, null if one does not exist.
      */
-    BadgedImageView getExpandedBubbleView() {
+    View getExpandedBubbleView() {
         return mExpandedBubble != null ? mExpandedBubble.getIconView() : null;
     }
 
     /**
      * The {@link Bubble} that is expanded, null if one does not exist.
      */
+    @Nullable
     Bubble getExpandedBubble() {
-        return mExpandedBubble;
+        if (mExpandedBubble == null
+                || (BubbleExperimentConfig.allowBubbleOverflow(mContext)
+                    && mExpandedBubble.getIconView() == mBubbleOverflow.getBtn()
+                    && mExpandedBubble.getKey() == BubbleOverflowActivity.KEY)) {
+            return null;
+        }
+        return (Bubble) mExpandedBubble;
     }
 
     // via BubbleData.Listener
@@ -818,7 +781,7 @@
             if (DEBUG_BUBBLE_STACK_VIEW) {
                 Log.d(TAG, "Show overflow button.");
             }
-            mOverflowBtn.setVisibility(VISIBLE);
+            mBubbleOverflow.setBtnVisible(VISIBLE);
             if (apply) {
                 mExpandedAnimationController.expandFromStack(() -> {
                     updatePointerPosition();
@@ -828,7 +791,7 @@
             if (DEBUG_BUBBLE_STACK_VIEW) {
                 Log.d(TAG, "Collapsed. Hide overflow button.");
             }
-            mOverflowBtn.setVisibility(GONE);
+            mBubbleOverflow.setBtnVisible(GONE);
         }
     }
 
@@ -849,7 +812,7 @@
     }
 
     void showOverflow() {
-        setSelectedBubble(null);
+        setSelectedBubble(mBubbleOverflow);
     }
 
     /**
@@ -858,14 +821,14 @@
      * position of any bubble.
      */
     // via BubbleData.Listener
-    public void setSelectedBubble(@Nullable Bubble bubbleToSelect) {
+    public void setSelectedBubble(@Nullable BubbleViewProvider bubbleToSelect) {
         if (DEBUG_BUBBLE_STACK_VIEW) {
             Log.d(TAG, "setSelectedBubble: " + bubbleToSelect);
         }
         if (mExpandedBubble != null && mExpandedBubble.equals(bubbleToSelect)) {
             return;
         }
-        final Bubble previouslySelected = mExpandedBubble;
+        final BubbleViewProvider previouslySelected = mExpandedBubble;
         mExpandedBubble = bubbleToSelect;
 
         if (mIsExpanded) {
@@ -874,14 +837,11 @@
             // expanded view becomes visible on the screen. See b/126856255
             mExpandedViewContainer.setAlpha(0.0f);
             mSurfaceSynchronizer.syncSurfaceAndRun(() -> {
-                if (previouslySelected == null) {
-                    mOverflowExpandedView.setContentVisibility(false);
-                } else {
-                    previouslySelected.setContentVisibility(false);
-                }
+                previouslySelected.setContentVisibility(false);
                 updateExpandedBubble();
                 updatePointerPosition();
                 requestUpdate();
+
                 logBubbleEvent(previouslySelected,
                         SysUiStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
                 logBubbleEvent(bubbleToSelect, SysUiStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
@@ -941,8 +901,8 @@
         if (mIsExpanded) {
             if (isIntersecting(mBubbleContainer, x, y)) {
                 if (BubbleExperimentConfig.allowBubbleOverflow(mContext)
-                        && isIntersecting(mOverflowBtn, x, y)) {
-                    return mOverflowBtn;
+                        && isIntersecting(mBubbleOverflow.getBtn(), x, y)) {
+                    return mBubbleOverflow.getBtn();
                 }
                 // Could be tapping or dragging a bubble while expanded
                 for (int i = 0; i < getBubbleCount(); i++) {
@@ -1030,7 +990,7 @@
 
     private void animateCollapse() {
         mIsExpanded = false;
-        final Bubble previouslySelected = mExpandedBubble;
+        final BubbleViewProvider previouslySelected = mExpandedBubble;
         beforeExpandedViewAnimation();
 
         if (DEBUG_BUBBLE_STACK_VIEW) {
@@ -1046,11 +1006,7 @@
                 () -> {
                     mBubbleContainer.setActiveController(mStackAnimationController);
                     afterExpandedViewAnimation();
-                    if (previouslySelected == null) {
-                        mOverflowExpandedView.setContentVisibility(false);
-                    } else {
-                        previouslySelected.setContentVisibility(false);
-                    }
+                    previouslySelected.setContentVisibility(false);
                 });
 
         mExpandedViewXAnim.animateToFinalPosition(getCollapsedX());
@@ -1093,7 +1049,7 @@
                 mExpandedAnimateYDistance);
     }
 
-    private void notifyExpansionChanged(Bubble bubble, boolean expanded) {
+    private void notifyExpansionChanged(BubbleViewProvider bubble, boolean expanded) {
         if (mExpandListener != null && bubble != null) {
             mExpandListener.onBubbleExpandChanged(expanded, bubble.getKey());
         }
@@ -1613,11 +1569,8 @@
             Log.d(TAG, "updateExpandedBubble()");
         }
         mExpandedViewContainer.removeAllViews();
-        if (mIsExpanded) {
-            BubbleExpandedView bev = mOverflowExpandedView;
-            if (mExpandedBubble != null) {
-                bev = mExpandedBubble.getExpandedView();
-            }
+        if (mIsExpanded && mExpandedBubble != null) {
+            BubbleExpandedView bev = mExpandedBubble.getExpandedView();
             mExpandedViewContainer.addView(bev);
             bev.populateExpandedView();
             mExpandedViewContainer.setVisibility(VISIBLE);
@@ -1636,9 +1589,7 @@
             if (!mExpandedViewYAnim.isRunning()) {
                 // We're not animating so set the value
                 mExpandedViewContainer.setTranslationY(y);
-                if (mExpandedBubble == null) {
-                    mOverflowExpandedView.updateView();
-                } else {
+                if (mExpandedBubble != null) {
                     mExpandedBubble.getExpandedView().updateView();
                 }
             } else {
@@ -1693,15 +1644,16 @@
     /**
      * Finds the bubble index within the stack.
      *
-     * @param bubble the bubble to look up.
+     * @param provider the bubble view provider with the bubble to look up.
      * @return the index of the bubble view within the bubble stack. The range of the position
      * is between 0 and the bubble count minus 1.
      */
-    int getBubbleIndex(@Nullable Bubble bubble) {
-        if (bubble == null) {
+    int getBubbleIndex(@Nullable BubbleViewProvider provider) {
+        if (provider == null || provider.getKey() == BubbleOverflowActivity.KEY) {
             return 0;
         }
-        return mBubbleContainer.indexOfChild(bubble.getIconView());
+        Bubble b = (Bubble) provider;
+        return mBubbleContainer.indexOfChild(b.getIconView());
     }
 
     /**
@@ -1733,36 +1685,12 @@
      *               the user interaction is not specific to one bubble.
      * @param action the user interaction enum.
      */
-    private void logBubbleEvent(@Nullable Bubble bubble, int action) {
-        if (bubble == null || bubble.getEntry() == null
-                || bubble.getEntry().getSbn() == null) {
-            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                    null /* package name */,
-                    null /* notification channel */,
-                    0 /* notification ID */,
-                    0 /* bubble position */,
-                    getBubbleCount(),
-                    action,
-                    getNormalizedXPosition(),
-                    getNormalizedYPosition(),
-                    false /* unread bubble */,
-                    false /* on-going bubble */,
-                    false /* isAppForeground (unused) */);
-        } else {
-            StatusBarNotification notification = bubble.getEntry().getSbn();
-            SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED,
-                    notification.getPackageName(),
-                    notification.getNotification().getChannelId(),
-                    notification.getId(),
-                    getBubbleIndex(bubble),
-                    getBubbleCount(),
-                    action,
-                    getNormalizedXPosition(),
-                    getNormalizedYPosition(),
-                    bubble.showInShade(),
-                    bubble.isOngoing(),
-                    false /* isAppForeground (unused) */);
+    private void logBubbleEvent(@Nullable BubbleViewProvider bubble, int action) {
+        if (bubble == null) {
+            return;
         }
+        bubble.logUIEvent(getBubbleCount(), action, getNormalizedXPosition(),
+                getNormalizedYPosition(), getBubbleIndex(bubble));
     }
 
     /**
@@ -1770,14 +1698,10 @@
      * a back key down/up event pair is forwarded to the bubble Activity.
      */
     boolean performBackPressIfNeeded() {
-        if (!isExpanded()) {
+        if (!isExpanded() || mExpandedBubble == null) {
             return false;
         }
-        if (mExpandedBubble == null) {
-            return mOverflowExpandedView.performBackPressIfNeeded();
-        } else {
-            return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
-        }
+        return mExpandedBubble.getExpandedView().performBackPressIfNeeded();
     }
 
     /** For debugging only */
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
new file mode 100644
index 0000000..59fc435
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.bubbles;
+
+import android.view.View;
+
+/**
+ * Interface to represent actual Bubbles and UI elements that act like bubbles, like BubbleOverflow.
+ */
+interface BubbleViewProvider {
+    BubbleExpandedView getExpandedView();
+    void setContentVisibility(boolean visible);
+    View getIconView();
+    void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index);
+    String getKey();
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
new file mode 100644
index 0000000..0337ee3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/dagger/BubbleModule.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 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.bubbles.dagger;
+
+import android.content.Context;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.bubbles.BubbleData;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/** */
+@Module
+public interface BubbleModule {
+
+    /**
+     */
+    @Singleton
+    @Provides
+    static BubbleController newBubbleController(
+            Context context,
+            NotificationShadeWindowController notificationShadeWindowController,
+            StatusBarStateController statusBarStateController,
+            ShadeController shadeController,
+            BubbleData data,
+            ConfigurationController configurationController,
+            NotificationInterruptionStateProvider interruptionStateProvider,
+            ZenModeController zenModeController,
+            NotificationLockscreenUserManager notifUserManager,
+            NotificationGroupManager groupManager,
+            NotificationEntryManager entryManager,
+            NotifPipeline notifPipeline,
+            FeatureFlags featureFlags,
+            DumpController dumpController) {
+        return new BubbleController(
+                context,
+                notificationShadeWindowController,
+                statusBarStateController,
+                shadeController,
+                data,
+                /* synchronizer */null,
+                configurationController,
+                interruptionStateProvider,
+                zenModeController,
+                notifUserManager,
+                groupManager,
+                entryManager,
+                notifPipeline,
+                featureFlags,
+                dumpController);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
index e09d20b..9ec71c7 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsProviderLifecycleManager.kt
@@ -86,7 +86,7 @@
         private const val MSG_ACTION = 2
         private const val MSG_UNBIND = 3
         private const val BIND_RETRY_DELAY = 1000L // ms
-        private const val LOAD_TIMEOUT = 5000L // ms
+        private const val LOAD_TIMEOUT_SECONDS = 30L // seconds
         private const val MAX_BIND_RETRIES = 5
         private const val MAX_CONTROLS_REQUEST = 100000L
         private const val DEBUG = true
@@ -231,7 +231,7 @@
             // Don't accept load callbacks after this
             lastLoadCallback = null
             unbindService()
-        }, LOAD_TIMEOUT, TimeUnit.MILLISECONDS)
+        }, LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
 
         invokeOrQueue(::load, Message.Load)
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
index 44f3fbc..275c778 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/Behavior.kt
@@ -16,6 +16,20 @@
 
 package com.android.systemui.controls.ui
 
+/**
+ * All controls need to respond to changes in state and handle user-generated events.
+ * Implementations of this interface provide these different means by adding their own
+ * event handlers, and will update the control ui as they see fit.
+ */
 interface Behavior {
-    fun apply(cvh: ControlViewHolder, cws: ControlWithState)
+
+    /**
+     * Only called once per instance
+     */
+    fun initialize(cvh: ControlViewHolder)
+
+    /**
+     * Will be invoked on every update provided to the Control
+     */
+    fun bind(cws: ControlWithState)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 55c1b6a..feaea7c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
@@ -17,14 +17,15 @@
 package com.android.systemui.controls.ui
 
 import android.content.Context
+import android.graphics.BlendMode
 import android.graphics.drawable.ClipDrawable
-import android.graphics.drawable.GradientDrawable
 import android.graphics.drawable.Icon
 import android.graphics.drawable.LayerDrawable
 import android.service.controls.Control
 import android.service.controls.actions.ControlAction
 import android.service.controls.templates.ControlTemplate
 import android.service.controls.templates.TemperatureControlTemplate
+import android.service.controls.templates.ThumbnailTemplate
 import android.service.controls.templates.ToggleRangeTemplate
 import android.service.controls.templates.ToggleTemplate
 import android.view.View
@@ -36,12 +37,15 @@
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.R
 
+import kotlin.reflect.KClass
+
 private const val UPDATE_DELAY_IN_MILLIS = 3000L
 
 class ControlViewHolder(
     val layout: ViewGroup,
     val controlsController: ControlsController,
-    val uiExecutor: DelayableExecutor
+    val uiExecutor: DelayableExecutor,
+    val bgExecutor: DelayableExecutor
 ) {
     val icon: ImageView = layout.requireViewById(R.id.icon)
     val status: TextView = layout.requireViewById(R.id.status)
@@ -50,15 +54,14 @@
     val subtitle: TextView = layout.requireViewById(R.id.subtitle)
     val context: Context = layout.getContext()
     val clipLayer: ClipDrawable
-    val gd: GradientDrawable
     lateinit var cws: ControlWithState
     var cancelUpdate: Runnable? = null
+    var behavior: Behavior? = null
 
     init {
         val ld = layout.getBackground() as LayerDrawable
         ld.mutate()
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
-        gd = clipLayer.getDrawable() as GradientDrawable
     }
 
     fun bindData(cws: ControlWithState) {
@@ -83,7 +86,14 @@
             })
         }
 
-        findBehavior(status, template).apply(this, cws)
+        val clazz = findBehavior(status, template)
+        if (behavior == null || behavior!!::class != clazz) {
+            // Behavior changes can signal a change in template from the app or
+            // first time setup
+            behavior = clazz.java.newInstance()
+            behavior?.initialize(this)
+        }
+        behavior?.bind(cws)
     }
 
     fun actionResponse(@ControlAction.ResponseResult response: Int) {
@@ -115,20 +125,14 @@
         controlsController.action(cws.ci, action)
     }
 
-    private fun findBehavior(status: Int, template: ControlTemplate): Behavior {
+    private fun findBehavior(status: Int, template: ControlTemplate): KClass<out Behavior> {
         return when {
-            status == Control.STATUS_UNKNOWN -> UnknownBehavior()
-            template is ToggleTemplate -> ToggleBehavior()
-            template is ToggleRangeTemplate -> ToggleRangeBehavior()
-            template is TemperatureControlTemplate -> TemperatureControlBehavior()
-            else -> {
-                object : Behavior {
-                    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-                        cvh.status.setText(cws.control?.getStatusText())
-                        cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
-                    }
-                }
-            }
+            status == Control.STATUS_UNKNOWN -> UnknownBehavior::class
+            template is ToggleTemplate -> ToggleBehavior::class
+            template is ToggleRangeTemplate -> ToggleRangeBehavior::class
+            template is TemperatureControlTemplate -> TemperatureControlBehavior::class
+            template is ThumbnailTemplate -> StaticBehavior::class
+            else -> DefaultBehavior::class
         }
     }
 
@@ -141,7 +145,10 @@
         icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
         icon.setImageTintList(fg)
 
-        gd.setColor(bg)
+        clipLayer.getDrawable().apply {
+            setTintBlendMode(BlendMode.HUE)
+            setTintList(bg)
+        }
     }
 
     fun setEnabled(enabled: Boolean) {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 9e6636f..98cdf28 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -223,7 +223,7 @@
             val item = inflater.inflate(
                 R.layout.controls_base_item, lastRow, false) as ViewGroup
             lastRow.addView(item)
-            val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor)
+            val cvh = ControlViewHolder(item, controlsController.get(), uiExecutor, bgExecutor)
             val key = ControlKey(it.component, it.controlId)
             cvh.bindData(controlsById.getValue(key))
             controlViewsById.put(key, cvh)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
new file mode 100644
index 0000000..af581e1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/CornerDrawable.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.graphics.Canvas
+import android.graphics.Path
+import android.graphics.Rect
+import android.graphics.RectF
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+
+/**
+ * Use a path to add mask for corners around the drawable, to match the radius
+ * of the underlying shape.
+ */
+class CornerDrawable(val wrapped: Drawable, val cornerRadius: Float) : DrawableWrapper(wrapped) {
+    val path: Path = Path()
+
+    init {
+        val b = getBounds()
+        updatePath(RectF(b))
+    }
+
+    override fun draw(canvas: Canvas) {
+        canvas.clipPath(path)
+        super.draw(canvas)
+    }
+
+    override fun setBounds(l: Int, t: Int, r: Int, b: Int) {
+        updatePath(RectF(l.toFloat(), t.toFloat(), r.toFloat(), b.toFloat()))
+        super.setBounds(l, t, r, b)
+    }
+
+    override fun setBounds(r: Rect) {
+        updatePath(RectF(r))
+        super.setBounds(r)
+    }
+
+    private fun updatePath(r: RectF) {
+        path.reset()
+        path.addRoundRect(r, cornerRadius, cornerRadius, Path.Direction.CW)
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
new file mode 100644
index 0000000..1747929
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DefaultBehavior.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+class DefaultBehavior : Behavior {
+    lateinit var cvh: ControlViewHolder
+
+    override fun initialize(cvh: ControlViewHolder) {
+        this.cvh = cvh
+    }
+
+    override fun bind(cws: ControlWithState) {
+        cvh.status.setText(cws.control?.getStatusText() ?: "")
+        cvh.setEnabled(false)
+        cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
new file mode 100644
index 0000000..c006d6f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 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.controls.ui
+
+import android.graphics.drawable.ClipDrawable
+import android.graphics.drawable.LayerDrawable
+import android.service.controls.Control
+import android.service.controls.templates.ThumbnailTemplate
+
+import com.android.systemui.R
+import com.android.systemui.controls.ui.ControlActionCoordinator.MAX_LEVEL
+
+/**
+ * Used for controls that cannot be interacted with. Information is presented to the user
+ * but no actions can be taken. If using a ThumbnailTemplate, the background image will
+ * be changed.
+ */
+class StaticBehavior() : Behavior {
+    lateinit var control: Control
+    lateinit var cvh: ControlViewHolder
+
+    override fun initialize(cvh: ControlViewHolder) {
+        this.cvh = cvh
+    }
+
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+
+        cvh.status.setText(control.getStatusText())
+
+        val ld = cvh.layout.getBackground() as LayerDrawable
+        val clipLayer = ld.findDrawableByLayerId(R.id.clip_layer) as ClipDrawable
+
+        clipLayer.setLevel(MAX_LEVEL)
+        cvh.setEnabled(true)
+        cvh.applyRenderInfo(RenderInfo.lookup(control.getDeviceType(), true))
+
+        val template = control.getControlTemplate()
+        if (template is ThumbnailTemplate) {
+            cvh.bgExecutor.execute {
+                // clear the default tinting in favor of only using alpha
+                val drawable = template.getThumbnail().loadDrawable(cvh.context)
+                drawable.setTintList(null)
+                drawable.setAlpha((0.45 * 255).toInt())
+                cvh.uiExecutor.execute {
+                    val radius = cvh.context.getResources()
+                            .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
+                    clipLayer.setDrawable(CornerDrawable(drawable, radius))
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
index 7661c6f..239d2e5 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt
@@ -16,12 +16,10 @@
 
 package com.android.systemui.controls.ui
 
-import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
 import android.service.controls.Control
 import android.service.controls.templates.TemperatureControlTemplate
-import android.widget.TextView
 
 import com.android.systemui.R
 import com.android.systemui.controls.ui.ControlActionCoordinator.MIN_LEVEL
@@ -32,15 +30,15 @@
     lateinit var control: Control
     lateinit var cvh: ControlViewHolder
     lateinit var template: TemperatureControlTemplate
-    lateinit var status: TextView
-    lateinit var context: Context
 
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        this.control = cws.control!!
+    override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
-        status = cvh.status
+    }
 
-        status.setText(control.getStatusText())
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+
+        cvh.status.setText(control.getStatusText())
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
index 4c35d26..d306d7c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleBehavior.kt
@@ -16,11 +16,9 @@
 
 package com.android.systemui.controls.ui
 
-import android.content.Context
 import android.graphics.drawable.Drawable
 import android.graphics.drawable.LayerDrawable
 import android.view.View
-import android.widget.TextView
 import android.service.controls.Control
 import android.service.controls.templates.ToggleTemplate
 
@@ -33,21 +31,21 @@
     lateinit var template: ToggleTemplate
     lateinit var control: Control
     lateinit var cvh: ControlViewHolder
-    lateinit var context: Context
-    lateinit var status: TextView
 
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        this.control = cws.control!!
+    override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
-        status = cvh.status
-
-        status.setText(control.getStatusText())
-
-        template = control.getControlTemplate() as ToggleTemplate
+        cvh.setEnabled(false)
 
         cvh.layout.setOnClickListener(View.OnClickListener() {
             ControlActionCoordinator.toggle(cvh, template.getTemplateId(), template.isChecked())
         })
+    }
+
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+
+        cvh.status.setText(control.getStatusText())
+        template = control.getControlTemplate() as ToggleTemplate
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
index 8d65ca3..45d7397 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ToggleRangeBehavior.kt
@@ -51,17 +51,13 @@
         private const val DEFAULT_FORMAT = "%.1f"
     }
 
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        this.control = cws.control!!
+    override fun initialize(cvh: ControlViewHolder) {
         this.cvh = cvh
-
-        statusExtra = cvh.statusExtra
         status = cvh.status
-
-        status.setText(control.getStatusText())
-
         context = status.getContext()
 
+        cvh.setEnabled(false)
+
         val gestureListener = ToggleRangeGestureListener(cvh.layout)
         val gestureDetector = GestureDetector(context, gestureListener)
         cvh.layout.setOnTouchListener({ v: View, e: MotionEvent ->
@@ -77,6 +73,13 @@
 
             return@setOnTouchListener false
         })
+    }
+
+    override fun bind(cws: ControlWithState) {
+        this.control = cws.control!!
+
+        statusExtra = cvh.statusExtra
+        status.setText(control.getStatusText())
 
         val ld = cvh.layout.getBackground() as LayerDrawable
         clipLayer = ld.findDrawableByLayerId(R.id.clip_layer)
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
index 5a6e5b4..1f33b85 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -17,8 +17,14 @@
 package com.android.systemui.controls.ui
 
 class UnknownBehavior : Behavior {
-    override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        cvh.status.setText("Loading...")
+    lateinit var cvh: ControlViewHolder
+
+    override fun initialize(cvh: ControlViewHolder) {
+        this.cvh = cvh
+    }
+
+    override fun bind(cws: ControlWithState) {
+        cvh.status.setText(cvh.context.getString(com.android.internal.R.string.loading))
         cvh.setEnabled(false)
         cvh.applyRenderInfo(RenderInfo.lookup(cws.ci.deviceType, false))
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
index bf501ce..7c0033c 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java
@@ -27,7 +27,6 @@
 import android.os.ServiceManager;
 import android.util.DisplayMetrics;
 import android.view.Choreographer;
-import android.view.IWindowManager;
 import android.view.LayoutInflater;
 import android.view.WindowManager;
 
@@ -46,8 +45,6 @@
 import com.android.systemui.shared.system.DevicePolicyManagerWrapper;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NavigationBarController;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.phone.AutoHideController;
 import com.android.systemui.statusbar.phone.ConfigurationControllerImpl;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DataSaverController;
@@ -162,16 +159,6 @@
 
     @Singleton
     @Provides
-    public AutoHideController provideAutoHideController(Context context,
-            @Main Handler mainHandler,
-            NotificationRemoteInputManager notificationRemoteInputManager,
-            IWindowManager iWindowManager) {
-        return new AutoHideController(context, mainHandler, notificationRemoteInputManager,
-                iWindowManager);
-    }
-
-    @Singleton
-    @Provides
     public ActivityManagerWrapper provideActivityManagerWrapper() {
         return ActivityManagerWrapper.getInstance();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 5de88e1..413a522 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -24,17 +24,19 @@
 import com.android.systemui.accessibility.SystemActions;
 import com.android.systemui.accessibility.WindowMagnification;
 import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bubbles.dagger.BubbleModule;
 import com.android.systemui.globalactions.GlobalActionsComponent;
 import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.pip.PipUI;
 import com.android.systemui.power.PowerUI;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsModule;
 import com.android.systemui.shortcut.ShortcutKeyDispatcher;
 import com.android.systemui.stackdivider.Divider;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.InstantAppNotifier;
 import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarModule;
 import com.android.systemui.statusbar.tv.TvStatusBar;
 import com.android.systemui.theme.ThemeOverlayController;
 import com.android.systemui.toast.ToastUI;
@@ -49,7 +51,8 @@
 /**
  * SystemUI objects that are injectable should go here.
  */
-@Module(includes = {RecentsModule.class, StatusBarModule.class})
+@Module(includes = {RecentsModule.class, StatusBarModule.class, BubbleModule.class,
+        KeyguardModule.class})
 public abstract class SystemUIBinder {
     /** Inject into AuthController. */
     @Binds
@@ -142,7 +145,7 @@
     @ClassKey(StatusBar.class)
     public abstract SystemUI bindsStatusBar(StatusBar sysui);
 
-   /** Inject into SystemActions. */
+    /** Inject into SystemActions. */
     @Binds
     @IntoMap
     @ClassKey(SystemActions.class)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 91aeb22..030b504 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -35,10 +35,8 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowBlurController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
-import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
 import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
@@ -69,7 +67,6 @@
             AssistModule.class,
             ConcurrencyModule.class,
             LogModule.class,
-            NotificationsModule.class,
             PeopleHubModule.class,
         },
         subcomponents = {StatusBarComponent.class,
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index d298b99..9203e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -64,12 +64,12 @@
 import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
+import android.view.IWindowManager;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.WindowManager;
-import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -88,13 +88,13 @@
 import com.android.internal.util.ScreenshotHelper;
 import com.android.internal.view.RotationPolicy;
 import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.MultiListLayout;
 import com.android.systemui.MultiListLayout.MultiListAdapter;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.controls.ui.ControlsUiController;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
@@ -110,6 +110,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -155,6 +156,7 @@
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final ContentResolver mContentResolver;
     private final Resources mResources;
+    private final ConfigurationController mConfigurationController;
     private final UserManager mUserManager;
     private final TrustManager mTrustManager;
     private final IActivityManager mIActivityManager;
@@ -186,6 +188,8 @@
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private GlobalActionsPanelPlugin mPanelPlugin;
     private ControlsUiController mControlsUiController;
+    private final IWindowManager mIWindowManager;
+    private final Executor mBackgroundExecutor;
 
     /**
      * @param context everything needs a context :(
@@ -204,7 +208,8 @@
             BlurUtils blurUtils, SysuiColorExtractor colorExtractor,
             IStatusBarService statusBarService,
             NotificationShadeWindowController notificationShadeWindowController,
-            ControlsUiController controlsUiController) {
+            ControlsUiController controlsUiController, IWindowManager iWindowManager,
+            @Background Executor backgroundExecutor) {
         mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme);
         mWindowManagerFuncs = windowManagerFuncs;
         mAudioManager = audioManager;
@@ -215,6 +220,7 @@
         mBroadcastDispatcher = broadcastDispatcher;
         mContentResolver = contentResolver;
         mResources = resources;
+        mConfigurationController = configurationController;
         mUserManager = userManager;
         mTrustManager = trustManager;
         mIActivityManager = iActivityManager;
@@ -225,6 +231,8 @@
         mStatusBarService = statusBarService;
         mNotificationShadeWindowController = notificationShadeWindowController;
         mControlsUiController = controlsUiController;
+        mIWindowManager = iWindowManager;
+        mBackgroundExecutor = backgroundExecutor;
 
         // receive broadcasts
         IntentFilter filter = new IntentFilter();
@@ -249,7 +257,7 @@
         mScreenshotHelper = new ScreenshotHelper(context);
         mScreenRecordHelper = new ScreenRecordHelper(context);
 
-        configurationController.addCallback(this);
+        mConfigurationController.addCallback(this);
 
         mActivityStarter = activityStarter;
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@@ -335,45 +343,7 @@
         } else {
             mSilentModeAction = new SilentModeTriStateAction(mAudioManager, mHandler);
         }
-        mAirplaneModeOn = new ToggleAction(
-                R.drawable.ic_lock_airplane_mode,
-                R.drawable.ic_lock_airplane_mode_off,
-                R.string.global_actions_toggle_airplane_mode,
-                R.string.global_actions_airplane_mode_on_status,
-                R.string.global_actions_airplane_mode_off_status) {
-
-            void onToggle(boolean on) {
-                if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
-                    mIsWaitingForEcmExit = true;
-                    // Launch ECM exit dialog
-                    Intent ecmDialogIntent =
-                            new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
-                    ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-                    mContext.startActivity(ecmDialogIntent);
-                } else {
-                    changeAirplaneModeSystemSetting(on);
-                }
-            }
-
-            @Override
-            protected void changeStateFromPress(boolean buttonOn) {
-                if (!mHasTelephony) return;
-
-                // In ECM mode airplane state cannot be changed
-                if (!TelephonyProperties.in_ecm_mode().orElse(false)) {
-                    mState = buttonOn ? State.TurningOn : State.TurningOff;
-                    mAirplaneState = mState;
-                }
-            }
-
-            public boolean showDuringKeyguard() {
-                return true;
-            }
-
-            public boolean showBeforeProvisioning() {
-                return false;
-            }
-        };
+        mAirplaneModeOn = new AirplaneModeAction();
         onAirplaneModeChanged();
 
         mItems = new ArrayList<Action>();
@@ -495,7 +465,7 @@
     }
 
     public void destroy() {
-        Dependency.get(ConfigurationController.class).removeCallback(this);
+        mConfigurationController.removeCallback(this);
     }
 
     private final class PowerAction extends SinglePressAction implements LongPressAction {
@@ -537,7 +507,7 @@
 
         @Override
         public boolean shouldBeSeparated() {
-            return !shouldShowControls();
+            return true;
         }
 
         @Override
@@ -836,14 +806,12 @@
 
             @Override
             public void onPress() {
-                new LockPatternUtils(mContext)
-                        .requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
-                                UserHandle.USER_ALL);
+                mLockPatternUtils.requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
+                        UserHandle.USER_ALL);
                 try {
-                    WindowManagerGlobal.getWindowManagerService().lockNow(null);
+                    mIWindowManager.lockNow(null);
                     // Lock profiles (if any) on the background thread.
-                    final Handler bgHandler = new Handler(Dependency.get(Dependency.BG_LOOPER));
-                    bgHandler.post(() -> lockProfiles());
+                    mBackgroundExecutor.execute(() -> lockProfiles());
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error while trying to lock device.", e);
                 }
@@ -1333,6 +1301,48 @@
         }
     }
 
+    private class AirplaneModeAction extends ToggleAction {
+        AirplaneModeAction() {
+            super(
+                R.drawable.ic_lock_airplane_mode,
+                R.drawable.ic_lock_airplane_mode_off,
+                R.string.global_actions_toggle_airplane_mode,
+                R.string.global_actions_airplane_mode_on_status,
+                R.string.global_actions_airplane_mode_off_status);
+        }
+        void onToggle(boolean on) {
+            if (mHasTelephony && TelephonyProperties.in_ecm_mode().orElse(false)) {
+                mIsWaitingForEcmExit = true;
+                // Launch ECM exit dialog
+                Intent ecmDialogIntent =
+                        new Intent(TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS, null);
+                ecmDialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                mContext.startActivity(ecmDialogIntent);
+            } else {
+                changeAirplaneModeSystemSetting(on);
+            }
+        }
+
+        @Override
+        protected void changeStateFromPress(boolean buttonOn) {
+            if (!mHasTelephony) return;
+
+            // In ECM mode airplane state cannot be changed
+            if (!TelephonyProperties.in_ecm_mode().orElse(false)) {
+                mState = buttonOn ? State.TurningOn : State.TurningOff;
+                mAirplaneState = mState;
+            }
+        }
+
+        public boolean showDuringKeyguard() {
+            return true;
+        }
+
+        public boolean showBeforeProvisioning() {
+            return false;
+        }
+    }
+
     private class SilentModeToggleAction extends ToggleAction {
         public SilentModeToggleAction() {
             super(R.drawable.ic_audio_vol_mute,
@@ -1555,6 +1565,7 @@
 
         private ControlsUiController mControlsUiController;
         private ViewGroup mControlsView;
+        private ViewGroup mContainerView;
 
         ActionsDialog(Context context, MyAdapter adapter,
                 GlobalActionsPanelPlugin.PanelViewController plugin, BlurUtils blurUtils,
@@ -1672,6 +1683,14 @@
                 mScrimAlpha = ScrimController.BUSY_SCRIM_ALPHA;
             }
             getWindow().setBackgroundDrawable(mBackgroundDrawable);
+
+            if (mControlsView != null) {
+                mContainerView = findViewById(com.android.systemui.R.id.global_actions_container);
+                mContainerView.setOnTouchListener((v, e) -> {
+                    dismiss();
+                    return true;
+                });
+            }
         }
 
         private void fixNavBarClipping() {
@@ -1894,14 +1913,6 @@
         return isPanelDebugModeEnabled(context);
     }
 
-
-    /**
-     * Determines whether the Global Actions menu should use a separated view for emergency actions.
-     */
-    private static boolean shouldUseSeparatedView() {
-        return true;
-    }
-
     private boolean shouldShowControls() {
         return !mKeyguardManager.isDeviceLocked()
                 && mControlsUiController.getAvailable();
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
index 6749f1d..f102561 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsFlatLayout.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -23,164 +23,62 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.systemui.HardwareBgDrawable;
+import com.android.systemui.R;
 
 /**
- * Single row implementation of the button layout created by the global actions dialog.
+ * Flat, single-row implementation of the button layout created by the global actions dialog.
  */
 public class GlobalActionsFlatLayout extends GlobalActionsLayout {
+    private static final int MAX_ITEMS = 4;
     public GlobalActionsFlatLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
 
-    @Override
-    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        mBackgroundsSet = true;
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
-        // backgrounds set only once, the first time onMeasure is called after inflation
-        // if (getListView() != null && !mBackgroundsSet) {
-        //     setBackgrounds();
-        //     mBackgroundsSet = true;
-        // }
-    }
-
     @VisibleForTesting
-    protected void setupListView() {
-        ListGridLayout listView = getListView();
-        listView.setExpectedCount(Math.min(2, mAdapter.countListItems()));
-        listView.setReverseSublists(shouldReverseSublists());
-        listView.setReverseItems(shouldReverseListItems());
-        listView.setSwapRowsAndColumns(shouldSwapRowsAndColumns());
+    protected boolean shouldReverseListItems() {
+        int rotation = getCurrentRotation();
+        if (rotation == ROTATION_NONE) {
+            return false;
+        }
+        if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+            return rotation == ROTATION_LANDSCAPE;
+        }
+        return rotation == ROTATION_SEASCAPE;
     }
 
     @Override
-    public void onUpdateList() {
-        setupListView();
-        super.onUpdateList();
-        updateSeparatedItemSize();
-    }
-
-    /**
-     * If the separated view contains only one item, expand the bounds of that item to take up the
-     * entire view, so that the whole thing is touch-able.
-     */
-    @VisibleForTesting
-    protected void updateSeparatedItemSize() {
-        ViewGroup separated = getSeparatedView();
-        if (separated.getChildCount() == 0) {
-            return;
-        }
-        View firstChild = separated.getChildAt(0);
-        ViewGroup.LayoutParams childParams = firstChild.getLayoutParams();
-
-        if (separated.getChildCount() == 1) {
-            childParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
-            childParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
-        } else {
-            childParams.width = ViewGroup.LayoutParams.WRAP_CONTENT;
-            childParams.height = ViewGroup.LayoutParams.WRAP_CONTENT;
-        }
-    }
-
-    @Override
-    protected ListGridLayout getListView() {
-        return (ListGridLayout) super.getListView();
-    }
-
-    @Override
-    protected void removeAllListViews() {
-        ListGridLayout list = getListView();
-        if (list != null) {
-            list.removeAllItems();
-        }
+    protected HardwareBgDrawable getBackgroundDrawable(int backgroundColor) {
+        return null;
     }
 
     @Override
     protected void addToListView(View v, boolean reverse) {
-        ListGridLayout list = getListView();
-        if (list != null) {
-            list.addItem(v);
+        // only add items to the list view if we haven't hit our max yet
+        if (getListView().getChildCount() < MAX_ITEMS) {
+            super.addToListView(v, reverse);
         }
     }
 
-    @Override
-    public void removeAllItems() {
-        ViewGroup separatedList = getSeparatedView();
-        ListGridLayout list = getListView();
-        if (separatedList != null) {
-            separatedList.removeAllViews();
-        }
-        if (list != null) {
-            list.removeAllItems();
-        }
-    }
-
-    /**
-     * Determines whether the ListGridLayout should fill sublists in the reverse order.
-     * Used to account for sublist ordering changing between landscape and seascape views.
-     */
     @VisibleForTesting
-    protected boolean shouldReverseSublists() {
-        if (getCurrentRotation() == ROTATION_SEASCAPE) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Determines whether the ListGridLayout should fill rows first instead of columns.
-     * Used to account for vertical/horizontal changes due to landscape or seascape rotations.
-     */
-    @VisibleForTesting
-    protected boolean shouldSwapRowsAndColumns() {
-        if (getCurrentRotation() == ROTATION_NONE) {
-            return false;
-        }
-        return true;
-    }
-
-    @Override
-    protected boolean shouldReverseListItems() {
-        int rotation = getCurrentRotation();
-        boolean reverse = false; // should we add items to parents in the reverse order?
-        if (rotation == ROTATION_NONE
-                || rotation == ROTATION_SEASCAPE) {
-            reverse = !reverse; // if we're in portrait or seascape, reverse items
-        }
-        if (getCurrentLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-            reverse = !reverse; // if we're in an RTL language, reverse items (again)
-        }
-        return reverse;
+    protected float getGridItemSize() {
+        return getContext().getResources().getDimension(R.dimen.global_actions_grid_item_height);
     }
 
     @VisibleForTesting
     protected float getAnimationDistance() {
-        int rows = getListView().getRowCount();
-        float gridItemSize = getContext().getResources().getDimension(
-                com.android.systemui.R.dimen.global_actions_grid_item_height);
-        return rows * gridItemSize / 2;
+        return getGridItemSize() / 2;
     }
 
     @Override
     public float getAnimationOffsetX() {
-        switch (getCurrentRotation()) {
-            case ROTATION_LANDSCAPE:
-                return getAnimationDistance();
-            case ROTATION_SEASCAPE:
-                return -getAnimationDistance();
-            default: // Portrait
-                return 0;
-        }
+        return 0;
     }
 
     @Override
     public float getAnimationOffsetY() {
-        if (getCurrentRotation() == ROTATION_NONE) {
-            return getAnimationDistance();
-        }
-        return 0;
+        return -getAnimationDistance();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
index f755a93..183ee45 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsLayout.java
@@ -42,16 +42,30 @@
     }
 
     private void setBackgrounds() {
-        int gridBackgroundColor = getResources().getColor(
+        ViewGroup listView = getListView();
+        int listBgColor = getResources().getColor(
                 R.color.global_actions_grid_background, null);
-        int separatedBackgroundColor = getResources().getColor(
-                R.color.global_actions_separated_background, null);
-        HardwareBgDrawable listBackground  = new HardwareBgDrawable(true, true, getContext());
-        HardwareBgDrawable separatedBackground = new HardwareBgDrawable(true, true, getContext());
-        listBackground.setTint(gridBackgroundColor);
-        separatedBackground.setTint(separatedBackgroundColor);
-        getListView().setBackground(listBackground);
-        getSeparatedView().setBackground(separatedBackground);
+        HardwareBgDrawable listBackground = getBackgroundDrawable(listBgColor);
+        if (listBackground != null) {
+            listView.setBackground(listBackground);
+        }
+
+        ViewGroup separatedView = getSeparatedView();
+
+        if (separatedView != null) {
+            int separatedBgColor = getResources().getColor(
+                    R.color.global_actions_separated_background, null);
+            HardwareBgDrawable separatedBackground = getBackgroundDrawable(separatedBgColor);
+            if (separatedBackground != null) {
+                getSeparatedView().setBackground(separatedBackground);
+            }
+        }
+    }
+
+    protected HardwareBgDrawable getBackgroundDrawable(int backgroundColor) {
+        HardwareBgDrawable background = new HardwareBgDrawable(true, true, getContext());
+        background.setTint(backgroundColor);
+        return background;
     }
 
     @Override
@@ -74,10 +88,16 @@
     }
 
     protected void addToSeparatedView(View v, boolean reverse) {
-        if (reverse) {
-            getSeparatedView().addView(v, 0);
+        ViewGroup separated = getSeparatedView();
+        if (separated != null) {
+            if (reverse) {
+                separated.addView(v, 0);
+            } else {
+                separated.addView(v);
+            }
         } else {
-            getSeparatedView().addView(v);
+            // if no separated view exists, just use the list view
+            addToListView(v, reverse);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 83a6d75..b1db7df 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -86,6 +86,7 @@
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.keyguard.dagger.KeyguardModule;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -100,9 +101,6 @@
 import java.util.ArrayList;
 import java.util.concurrent.Executor;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 import dagger.Lazy;
 
 /**
@@ -146,7 +144,6 @@
  * directly to the keyguard UI is posted to a {@link android.os.Handler} to ensure it is taken on the UI
  * thread of the keyguard.
  */
-@Singleton
 public class KeyguardViewMediator extends SystemUI {
     private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
     private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
@@ -688,7 +685,9 @@
         }
     };
 
-    @Inject
+    /**
+     * Injected constructor. See {@link KeyguardModule}.
+     */
     public KeyguardViewMediator(
             Context context,
             FalsingManager falsingManager,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
new file mode 100644
index 0000000..2c023ca
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 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.keyguard.dagger;
+
+import android.content.Context;
+
+import com.android.internal.widget.LockPatternUtils;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.keyguard.DismissCallbackRegistry;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module providing {@link StatusBar}.
+ */
+@Module
+public class KeyguardModule {
+    /**
+     * Provides our instance of KeyguardViewMediator which is considered optional.
+     */
+    @Provides
+    @Singleton
+    public static KeyguardViewMediator newKeyguardViewMediator(
+            Context context,
+            FalsingManager falsingManager,
+            LockPatternUtils lockPatternUtils,
+            BroadcastDispatcher broadcastDispatcher,
+            NotificationShadeWindowController notificationShadeWindowController,
+            Lazy<StatusBarKeyguardViewManager> statusBarKeyguardViewManagerLazy,
+            DismissCallbackRegistry dismissCallbackRegistry,
+            @UiBackground Executor uiBgExecutor) {
+        return new KeyguardViewMediator(
+                context,
+                falsingManager,
+                lockPatternUtils,
+                broadcastDispatcher,
+                notificationShadeWindowController,
+                statusBarKeyguardViewManagerLazy,
+                dismissCallbackRegistry,
+                uiBgExecutor);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 029ab43..a827f59 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -19,6 +19,7 @@
 import static android.view.Display.DEFAULT_DISPLAY;
 
 import android.annotation.NonNull;
+import android.util.Log;
 
 import com.android.systemui.Dumpable;
 import com.android.systemui.shared.system.QuickStepContract;
@@ -37,6 +38,9 @@
 @Singleton
 public class SysUiState implements Dumpable {
 
+    private static final String TAG = SysUiState.class.getSimpleName();
+    public static final boolean DEBUG = true;
+
     private @QuickStepContract.SystemUiStateFlags int mFlags;
     private final List<SysUiStateCallback> mCallbacks = new ArrayList<>();
     private int mFlagsToSet = 0;
@@ -76,6 +80,7 @@
     private void updateFlags(int displayId) {
         if (displayId != DEFAULT_DISPLAY) {
             // Ignore non-default displays for now
+            Log.w(TAG, "Ignoring flag update for display: " + displayId, new Throwable());
             return;
         }
 
@@ -87,6 +92,9 @@
 
     /** Notify all those who are registered that the state has changed. */
     private void notifyAndSetSystemUiStateChanged(int newFlags, int oldFlags) {
+        if (DEBUG) {
+            Log.d(TAG, "SysUiState changed: old=" + oldFlags + " new=" + newFlags);
+        }
         if (newFlags != oldFlags) {
             mCallbacks.forEach(callback -> callback.onSystemUiStateChanged(newFlags));
             mFlags = newFlags;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index d4af154..8cd70cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -61,9 +61,9 @@
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.qs.QSDetail.Callback;
 import com.android.systemui.statusbar.CommandQueue;
-import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarIconController.TintedIconManager;
+import com.android.systemui.statusbar.phone.StatusBarWindowView;
 import com.android.systemui.statusbar.phone.StatusIconContainer;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.policy.DateView;
@@ -146,6 +146,7 @@
         }
     };
     private boolean mHasTopCutout = false;
+    private int mRoundedCornerPadding = 0;
 
     @Inject
     public QuickStatusBarHeader(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
@@ -325,6 +326,9 @@
         Resources resources = mContext.getResources();
         updateMinimumHeight();
 
+        mRoundedCornerPadding = resources.getDimensionPixelSize(
+                R.dimen.rounded_corner_content_padding);
+
         // Update height for a few views, especially due to landscape mode restricting space.
         mHeaderTextContainerView.getLayoutParams().height =
                 resources.getDimensionPixelSize(R.dimen.qs_header_tooltip_height);
@@ -432,16 +436,23 @@
     @Override
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         DisplayCutout cutout = insets.getDisplayCutout();
-        Pair<Integer, Integer> padding = PhoneStatusBarView.cornerCutoutMargins(
-                cutout, getDisplay());
-        if (padding == null) {
-            mSystemIconsView.setPaddingRelative(
-                    getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start), 0,
-                    getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end), 0);
-        } else {
-            mSystemIconsView.setPadding(padding.first, 0, padding.second, 0);
 
-        }
+        // Handle padding of QuickStatusBarHeader
+        Pair<Integer, Integer> cornerCutoutPadding = StatusBarWindowView.cornerCutoutMargins(
+                cutout, getDisplay());
+        Pair<Integer, Integer> padding =
+                StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
+                        cutout, cornerCutoutPadding, mRoundedCornerPadding);
+        setPadding(padding.first, 0, padding.second, getPaddingBottom());
+
+        // Handle padding of SystemIconsView
+        final int waterfallTopInset = cutout == null ? 0 : cutout.getWaterfallInsets().top;
+        mSystemIconsView.setPaddingRelative(
+                getResources().getDimensionPixelSize(R.dimen.status_bar_padding_start),
+                waterfallTopInset,
+                getResources().getDimensionPixelSize(R.dimen.status_bar_padding_end),
+                0);
+
         return super.onApplyWindowInsets(insets);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index 88a30a1..84891ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -31,20 +31,26 @@
 /**
  * Quick settings tile for screen recording
  */
-public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState> {
+public class ScreenRecordTile extends QSTileImpl<QSTile.BooleanState>
+        implements RecordingController.RecordingStateChangeCallback {
     private static final String TAG = "ScreenRecordTile";
     private RecordingController mController;
     private long mMillisUntilFinished = 0;
+    private Callback mCallback = new Callback();
 
     @Inject
     public ScreenRecordTile(QSHost host, RecordingController controller) {
         super(host);
         mController = controller;
+        mController.observe(this, mCallback);
     }
 
     @Override
     public BooleanState newTileState() {
-        return new BooleanState();
+        BooleanState state = new BooleanState();
+        state.label = mContext.getString(R.string.quick_settings_screen_record_label);
+        state.handlesLongClick = false;
+        return state;
     }
 
     @Override
@@ -59,24 +65,13 @@
         refreshState();
     }
 
-    /**
-     * Refresh tile state
-     * @param millisUntilFinished Time until countdown completes, or 0 if not counting down
-     */
-    public void refreshState(long millisUntilFinished) {
-        mMillisUntilFinished = millisUntilFinished;
-        refreshState();
-    }
-
     @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         boolean isStarting = mController.isStarting();
         boolean isRecording = mController.isRecording();
 
-        state.label = mContext.getString(R.string.quick_settings_screen_record_label);
         state.value = isRecording || isStarting;
         state.state = (isRecording || isStarting) ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
-        state.handlesLongClick = false;
 
         if (isRecording) {
             state.icon = ResourceIcon.get(R.drawable.ic_qs_screenrecord);
@@ -125,4 +120,22 @@
         Log.d(TAG, "Stopping recording from tile");
         mController.stopRecording();
     }
+
+    private final class Callback implements RecordingController.RecordingStateChangeCallback {
+        @Override
+        public void onCountdown(long millisUntilFinished) {
+            mMillisUntilFinished = millisUntilFinished;
+            refreshState();
+        }
+
+        @Override
+        public void onRecordingStart() {
+            refreshState();
+        }
+
+        @Override
+        public void onRecordingEnd() {
+            refreshState();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 9f64b39..34cad51 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -380,6 +380,14 @@
                     taskId, mHandler, null);
         }
 
+        @Override
+        public void setSplitScreenMinimized(boolean minimized) {
+            Divider divider = mDividerOptional.get();
+            if (divider != null) {
+                divider.setMinimized(minimized);
+            }
+        }
+
         private boolean verifyCaller(String reason) {
             final int callerId = Binder.getCallingUserHandle().getIdentifier();
             if (callerId != mCurrentBoundedUserId) {
@@ -410,6 +418,9 @@
     private final ServiceConnection mOverviewServiceConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
+            if (SysUiState.DEBUG) {
+                Log.d(TAG_OPS, "Overview proxy service connected");
+            }
             mConnectionBackoffAttempts = 0;
             mHandler.removeCallbacks(mDeferredConnectionCallback);
             try {
@@ -562,6 +573,10 @@
                 mNavBarController.getDefaultNavigationBarFragment();
         final NavigationBarView navBarView =
                 mNavBarController.getNavigationBarView(mContext.getDisplayId());
+        if (SysUiState.DEBUG) {
+            Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+                    + " navBarView=" + navBarView);
+        }
 
         if (navBarFragment != null) {
             navBarFragment.updateSystemUiStateFlags(-1);
@@ -576,6 +591,10 @@
     }
 
     private void notifySystemUiStateFlags(int flags) {
+        if (SysUiState.DEBUG) {
+            Log.d(TAG_OPS, "Notifying sysui state change to overview service: proxy="
+                    + mOverviewProxy + " flags=" + flags);
+        }
         try {
             if (mOverviewProxy != null) {
                 mOverviewProxy.onSystemUiStateChanged(flags);
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
index 188501e..6ad9c40 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/RecordingController.java
@@ -24,6 +24,9 @@
 import android.util.Log;
 
 import com.android.systemui.qs.tiles.ScreenRecordTile;
+import com.android.systemui.statusbar.policy.CallbackController;
+
+import java.util.ArrayList;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -32,7 +35,8 @@
  * Helper class to initiate a screen recording
  */
 @Singleton
-public class RecordingController {
+public class RecordingController
+        implements CallbackController<RecordingController.RecordingStateChangeCallback> {
     private static final String TAG = "RecordingController";
     private static final String SYSUI_PACKAGE = "com.android.systemui";
     private static final String SYSUI_SCREENRECORD_LAUNCHER =
@@ -41,10 +45,11 @@
     private final Context mContext;
     private boolean mIsStarting;
     private boolean mIsRecording;
-    private ScreenRecordTile mTileToUpdate;
     private PendingIntent mStopIntent;
     private CountDownTimer mCountDownTimer = null;
 
+    private ArrayList<RecordingStateChangeCallback> mListeners = new ArrayList<>();
+
     /**
      * Create a new RecordingController
      * @param context Context for the controller
@@ -63,10 +68,7 @@
         final Intent intent = new Intent();
         intent.setComponent(launcherComponent);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.putExtra("com.android.systemui.screenrecord.EXTRA_SETTINGS_ONLY", true);
         mContext.startActivity(intent);
-
-        mTileToUpdate = tileToUpdate;
     }
 
     /**
@@ -82,16 +84,21 @@
         mCountDownTimer = new CountDownTimer(ms, 1000) {
             @Override
             public void onTick(long millisUntilFinished) {
-                refreshTile(millisUntilFinished);
+                for (RecordingStateChangeCallback cb : mListeners) {
+                    cb.onCountdown(millisUntilFinished);
+                }
             }
 
             @Override
             public void onFinish() {
                 mIsStarting = false;
                 mIsRecording = true;
-                refreshTile();
+                for (RecordingStateChangeCallback cb : mListeners) {
+                    cb.onRecordingEnd();
+                }
                 try {
                     startIntent.send();
+                    Log.d(TAG, "sent start intent");
                 } catch (PendingIntent.CanceledException e) {
                     Log.e(TAG, "Pending intent was cancelled: " + e.getMessage());
                 }
@@ -101,18 +108,6 @@
         mCountDownTimer.start();
     }
 
-    private void refreshTile() {
-        refreshTile(0);
-    }
-
-    private void refreshTile(long millisUntilFinished) {
-        if (mTileToUpdate != null) {
-            mTileToUpdate.refreshState(millisUntilFinished);
-        } else {
-            Log.e(TAG, "No tile to refresh");
-        }
-    }
-
     /**
      * Cancel a countdown in progress. This will not stop the recording if it already started.
      */
@@ -123,7 +118,10 @@
             Log.e(TAG, "Timer was null");
         }
         mIsStarting = false;
-        refreshTile();
+
+        for (RecordingStateChangeCallback cb : mListeners) {
+            cb.onRecordingEnd();
+        }
     }
 
     /**
@@ -152,7 +150,10 @@
         } catch (PendingIntent.CanceledException e) {
             Log.e(TAG, "Error stopping: " + e.getMessage());
         }
-        refreshTile();
+
+        for (RecordingStateChangeCallback cb : mListeners) {
+            cb.onRecordingEnd();
+        }
     }
 
     /**
@@ -161,6 +162,44 @@
      */
     public void updateState(boolean isRecording) {
         mIsRecording = isRecording;
-        refreshTile();
+        for (RecordingStateChangeCallback cb : mListeners) {
+            if (isRecording) {
+                cb.onRecordingStart();
+            } else {
+                cb.onRecordingEnd();
+            }
+        }
+    }
+
+    @Override
+    public void addCallback(RecordingStateChangeCallback listener) {
+        mListeners.add(listener);
+    }
+
+    @Override
+    public void removeCallback(RecordingStateChangeCallback listener) {
+        mListeners.remove(listener);
+    }
+
+    /**
+     * A callback for changes in the screen recording state
+     */
+    public interface RecordingStateChangeCallback {
+        /**
+         * Called when a countdown to recording has updated
+         *
+         * @param millisUntilFinished Time in ms remaining in the countdown
+         */
+        default void onCountdown(long millisUntilFinished) {}
+
+        /**
+         * Called when a screen recording has started
+         */
+        default void onRecordingStart() {}
+
+        /**
+         * Called when a screen recording has ended
+         */
+        default void onRecordingEnd() {}
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 197fe21..9e1e347 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -27,7 +27,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.app.ActivityManager;
@@ -58,6 +57,7 @@
 import android.provider.DeviceConfig;
 import android.util.DisplayMetrics;
 import android.util.Log;
+import android.util.MathUtils;
 import android.util.Slog;
 import android.view.Display;
 import android.view.LayoutInflater;
@@ -68,6 +68,7 @@
 import android.view.ViewOutlineProvider;
 import android.view.ViewTreeObserver;
 import android.view.WindowManager;
+import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.FrameLayout;
 import android.widget.HorizontalScrollView;
@@ -136,13 +137,11 @@
 
     private static final String TAG = "GlobalScreenshot";
 
-    private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130;
-    private static final int SCREENSHOT_DROP_IN_DURATION = 430;
-    private static final int SCREENSHOT_DROP_OUT_DELAY = 500;
-    private static final int SCREENSHOT_DROP_OUT_DURATION = 430;
-    private static final int SCREENSHOT_DROP_OUT_SCALE_DURATION = 370;
-    private static final float BACKGROUND_ALPHA = 0.5f;
-    private static final float SCREENSHOT_DROP_IN_MIN_SCALE = 0.725f;
+    private static final long SCREENSHOT_FLASH_IN_DURATION_MS = 133;
+    private static final long SCREENSHOT_FLASH_OUT_DURATION_MS = 217;
+    private static final long SCREENSHOT_TO_CORNER_X_DURATION_MS = 234;
+    private static final long SCREENSHOT_TO_CORNER_Y_DURATION_MS = 500;
+    private static final long SCREENSHOT_TO_CORNER_SCALE_DURATION_MS = 234;
     private static final float ROUNDED_CORNER_RADIUS = .05f;
     private static final long SCREENSHOT_CORNER_TIMEOUT_MILLIS = 6000;
     private static final int MESSAGE_CORNER_TIMEOUT = 2;
@@ -166,18 +165,21 @@
     private final FrameLayout mDismissButton;
 
     private Bitmap mScreenBitmap;
-    private AnimatorSet mScreenshotAnimation;
+    private Animator mScreenshotAnimation;
 
     private float mScreenshotOffsetXPx;
     private float mScreenshotOffsetYPx;
     private float mScreenshotHeightPx;
-    private float mCornerScale;
     private float mDismissButtonSize;
+    private float mCornerSizeX;
 
     private AsyncTask<Void, Void, Void> mSaveInBgTask;
 
     private MediaActionSound mCameraSound;
 
+    // standard material ease
+    private final Interpolator mFastOutSlowIn;
+
     private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
         @Override
         public void handleMessage(Message msg) {
@@ -227,6 +229,8 @@
         mScreenshotLayout.setFocusable(true);
         mScreenshotSelectorView.setFocusable(true);
         mScreenshotSelectorView.setFocusableInTouchMode(true);
+        mScreenshotView.setPivotX(0);
+        mScreenshotView.setPivotY(0);
 
         // Setup the window that we are going to use
         mWindowLayoutParams = new WindowManager.LayoutParams(
@@ -250,10 +254,12 @@
         mScreenshotOffsetYPx = resources.getDimensionPixelSize(R.dimen.screenshot_offset_y);
         mScreenshotHeightPx =
                 resources.getDimensionPixelSize(R.dimen.screenshot_action_container_offset_y);
-        mCornerScale = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale)
-                / (float) mDisplayMetrics.widthPixels;
         mDismissButtonSize = resources.getDimensionPixelSize(
                 R.dimen.screenshot_dismiss_button_tappable_size);
+        mCornerSizeX = resources.getDimensionPixelSize(R.dimen.global_screenshot_x_scale);
+
+        mFastOutSlowIn =
+                AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
 
         // Setup the Camera shutter sound
         mCameraSound = new MediaActionSound();
@@ -305,7 +311,9 @@
         int width = crop.width();
         int height = crop.height();
 
-        takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, null);
+        Rect screenRect = new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels);
+
+        takeScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect);
     }
 
     private void takeScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect) {
@@ -325,7 +333,7 @@
         mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
 
         // Start the post-screenshot animation
-        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
+        startAnimation(finisher, screenRect.width(), screenRect.height(),
                 screenRect);
     }
 
@@ -406,7 +414,6 @@
         mScreenshotLayout.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
 
         // Clear any references to the bitmap
-        mScreenBitmap = null;
         mScreenshotView.setImageBitmap(null);
         mActionsContainer.setVisibility(View.GONE);
         mBackgroundView.setVisibility(View.GONE);
@@ -430,21 +437,8 @@
 
         // Add the view for the animation
         mScreenshotView.setImageBitmap(mScreenBitmap);
-        mScreenshotLayout.requestFocus();
 
-        // Setup the animation with the screenshot just taken
-        if (mScreenshotAnimation != null) {
-            if (mScreenshotAnimation.isStarted()) {
-                mScreenshotAnimation.end();
-            }
-            mScreenshotAnimation.removeAllListeners();
-        }
-
-        ValueAnimator screenshotDropInAnim = screenRect != null ? createRectAnimation(screenRect)
-                : createScreenshotDropInAnimation();
-        ValueAnimator screenshotToCornerAnimation = createScreenshotToCornerAnimation(w, h);
-        mScreenshotAnimation = new AnimatorSet();
-        mScreenshotAnimation.playSequentially(screenshotDropInAnim, screenshotToCornerAnimation);
+        mScreenshotAnimation = createScreenshotDropInAnimation(w, h, screenRect);
 
         saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
             @Override
@@ -487,147 +481,64 @@
         });
     }
 
-    private ValueAnimator createRectAnimation(Rect rect) {
-        mScreenshotView.setAdjustViewBounds(true);
-        mScreenshotView.setMaxHeight(rect.height());
-        mScreenshotView.setMaxWidth(rect.width());
+    private AnimatorSet createScreenshotDropInAnimation(int width, int height, Rect bounds) {
+        float cornerScale = mCornerSizeX / (float) width;
 
-        final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
-                / SCREENSHOT_DROP_IN_DURATION);
-        final float flashDurationPct = 2f * flashPeakDurationPct;
-        final Interpolator scaleInterpolator = x -> {
-            // We start scaling when the flash is at it's peak
-            if (x < flashPeakDurationPct) {
-                return 0;
+        AnimatorSet dropInAnimation = new AnimatorSet();
+        ValueAnimator flashInAnimator = ValueAnimator.ofFloat(0, 1);
+        flashInAnimator.setDuration(SCREENSHOT_FLASH_IN_DURATION_MS);
+        flashInAnimator.setInterpolator(mFastOutSlowIn);
+        flashInAnimator.addUpdateListener(animation ->
+                mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+        ValueAnimator flashOutAnimator = ValueAnimator.ofFloat(1, 0);
+        flashOutAnimator.setDuration(SCREENSHOT_FLASH_OUT_DURATION_MS);
+        flashOutAnimator.setInterpolator(mFastOutSlowIn);
+        flashOutAnimator.addUpdateListener(animation ->
+                mScreenshotFlash.setAlpha((float) animation.getAnimatedValue()));
+
+        final PointF startPos = new PointF((float) bounds.left, (float) bounds.top);
+        final PointF finalPos = new PointF(mScreenshotOffsetXPx,
+                mDisplayMetrics.heightPixels - mScreenshotOffsetYPx - height * cornerScale);
+
+        ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
+        toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
+        float xPositionPct =
+                SCREENSHOT_TO_CORNER_X_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+        float scalePct =
+                SCREENSHOT_TO_CORNER_SCALE_DURATION_MS / (float) SCREENSHOT_TO_CORNER_Y_DURATION_MS;
+        toCorner.addUpdateListener(animation -> {
+            float t = animation.getAnimatedFraction();
+            if (t < scalePct) {
+                float scale = MathUtils.lerp(
+                        1, cornerScale, mFastOutSlowIn.getInterpolation(t / scalePct));
+                mScreenshotView.setScaleX(scale);
+                mScreenshotView.setScaleY(scale);
             }
-            return (x - flashDurationPct) / (1f - flashDurationPct);
-        };
 
-        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
-        anim.addListener(new AnimatorListenerAdapter() {
+            if (t < xPositionPct) {
+                mScreenshotView.setX(MathUtils.lerp(
+                        startPos.x, finalPos.x, mFastOutSlowIn.getInterpolation(t / xPositionPct)));
+            }
+            mScreenshotView.setY(MathUtils.lerp(
+                    startPos.y, finalPos.y, mFastOutSlowIn.getInterpolation(t)));
+        });
+
+        toCorner.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationStart(Animator animation) {
-                mBackgroundView.setAlpha(0f);
-                mBackgroundView.setVisibility(View.VISIBLE);
-                mScreenshotView.setAlpha(0f);
-                mScreenshotView.setElevation(0f);
-                mScreenshotView.setTranslationX(0f);
-                mScreenshotView.setTranslationY(0f);
-                mScreenshotView.setScaleX(1f);
-                mScreenshotView.setScaleY(1f);
+                super.onAnimationStart(animation);
                 mScreenshotView.setVisibility(View.VISIBLE);
             }
         });
-        anim.addUpdateListener(animation -> {
-            float t = (Float) animation.getAnimatedValue();
-            mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
-            mScreenshotView.setAlpha(t);
-        });
-        return anim;
-    }
 
-    private ValueAnimator createScreenshotDropInAnimation() {
-        final float flashPeakDurationPct = ((float) (SCREENSHOT_FLASH_TO_PEAK_DURATION)
-                / SCREENSHOT_DROP_IN_DURATION);
-        final float flashDurationPct = 2f * flashPeakDurationPct;
-        final Interpolator flashAlphaInterpolator = new Interpolator() {
-            @Override
-            public float getInterpolation(float x) {
-                // Flash the flash view in and out quickly
-                if (x <= flashDurationPct) {
-                    return (float) Math.sin(Math.PI * (x / flashDurationPct));
-                }
-                return 0;
-            }
-        };
-        final Interpolator scaleInterpolator = new Interpolator() {
-            @Override
-            public float getInterpolation(float x) {
-                // We start scaling when the flash is at it's peak
-                if (x < flashPeakDurationPct) {
-                    return 0;
-                }
-                return (x - flashDurationPct) / (1f - flashDurationPct);
-            }
-        };
+        mScreenshotFlash.setAlpha(0f);
+        mScreenshotFlash.setVisibility(View.VISIBLE);
 
-        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.setDuration(SCREENSHOT_DROP_IN_DURATION);
-        anim.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mBackgroundView.setAlpha(0f);
-                mBackgroundView.setVisibility(View.VISIBLE);
-                mScreenshotView.setAlpha(0f);
-                mScreenshotView.setTranslationX(0f);
-                mScreenshotView.setTranslationY(0f);
-                mScreenshotView.setScaleX(1);
-                mScreenshotView.setScaleY(1);
-                mScreenshotView.setVisibility(View.VISIBLE);
-                mScreenshotFlash.setAlpha(0f);
-                mScreenshotFlash.setVisibility(View.VISIBLE);
-            }
+        dropInAnimation.play(flashOutAnimator).after(flashInAnimator);
+        dropInAnimation.play(flashOutAnimator).with(toCorner);
 
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                mScreenshotFlash.setVisibility(View.GONE);
-            }
-        });
-        anim.addUpdateListener(new AnimatorUpdateListener() {
-            @Override
-            public void onAnimationUpdate(ValueAnimator animation) {
-                float t = (Float) animation.getAnimatedValue();
-                float scaleT = 1 - (scaleInterpolator.getInterpolation(t)
-                        * (1 - SCREENSHOT_DROP_IN_MIN_SCALE));
-                mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA);
-                mScreenshotView.setAlpha(t);
-                mScreenshotView.setScaleX(scaleT);
-                mScreenshotView.setScaleY(scaleT);
-                mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t));
-            }
-        });
-        return anim;
-    }
-
-    private ValueAnimator createScreenshotToCornerAnimation(int w, int h) {
-        ValueAnimator anim = ValueAnimator.ofFloat(0f, 1f);
-        anim.setStartDelay(SCREENSHOT_DROP_OUT_DELAY);
-
-        final float scaleDurationPct =
-                (float) SCREENSHOT_DROP_OUT_SCALE_DURATION / SCREENSHOT_DROP_OUT_DURATION;
-        final Interpolator scaleInterpolator = new Interpolator() {
-            @Override
-            public float getInterpolation(float x) {
-                if (x < scaleDurationPct) {
-                    // Decelerate, and scale the input accordingly
-                    return (float) (1f - Math.pow(1f - (x / scaleDurationPct), 2f));
-                }
-                return 1f;
-            }
-        };
-
-        // Determine the bounds of how to scale
-        float halfScreenWidth = w / 2f;
-        float halfScreenHeight = h / 2f;
-        final PointF finalPos = new PointF(
-                -halfScreenWidth + mCornerScale * halfScreenWidth + mScreenshotOffsetXPx,
-                halfScreenHeight - mCornerScale * halfScreenHeight - mScreenshotOffsetYPx);
-
-        // Animate the screenshot to the bottom left corner
-        anim.setDuration(SCREENSHOT_DROP_OUT_DURATION);
-        anim.addUpdateListener(animation -> {
-            float t = (Float) animation.getAnimatedValue();
-            float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE)
-                    - scaleInterpolator.getInterpolation(t)
-                    * (SCREENSHOT_DROP_IN_MIN_SCALE - mCornerScale);
-            mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA);
-            mScreenshotView.setScaleX(scaleT);
-            mScreenshotView.setScaleY(scaleT);
-            mScreenshotView.setTranslationX(t * finalPos.x);
-            mScreenshotView.setTranslationY(t * finalPos.y);
-        });
-        anim.addListener(new AnimatorListenerAdapter() {
+        dropInAnimation.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
@@ -638,7 +549,8 @@
                 mDismissButton.setVisibility(View.VISIBLE);
             }
         });
-        return anim;
+
+        return dropInAnimation;
     }
 
     private ValueAnimator createScreenshotActionsShadeAnimation(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 92236ae..7de70f5 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -219,11 +219,6 @@
             mParams.mActionsReadyListener.onActionsReady(null, null, null);
         }
 
-        // Recycle the bitmap data
-        if (image != null) {
-            image.recycle();
-        }
-
         return null;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
index 5ae0954..4f20492 100644
--- a/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/shortcut/ShortcutKeyDispatcher.java
@@ -22,10 +22,8 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.RemoteException;
-import android.util.Log;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
-import android.view.WindowManager;
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.policy.DividerSnapAlgorithm;
@@ -94,29 +92,24 @@
     }
 
     private void handleDockKey(long shortcutCode) {
-        try {
-            int dockSide = mWindowManagerService.getDockedStackSide();
-            if (dockSide == WindowManager.DOCKED_INVALID) {
-                // Split the screen
-                mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
-                        ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                        : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
-            } else {
-                // If there is already a docked window, we respond by resizing the docking pane.
-                DividerView dividerView = mDivider.getView();
-                DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
-                int dividerPosition = dividerView.getCurrentPosition();
-                DividerSnapAlgorithm.SnapTarget currentTarget =
-                        snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
-                DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
-                        ? snapAlgorithm.getPreviousTarget(currentTarget)
-                        : snapAlgorithm.getNextTarget(currentTarget);
-                dividerView.startDragging(true /* animate */, false /* touching */);
-                dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
-                        true /* logMetrics */);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "handleDockKey() failed.");
+        if (mDivider == null || !mDivider.inSplitMode()) {
+            // Split the screen
+            mRecents.splitPrimaryTask((shortcutCode == SC_DOCK_LEFT)
+                    ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
+                    : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT, null, -1);
+        } else {
+            // If there is already a docked window, we respond by resizing the docking pane.
+            DividerView dividerView = mDivider.getView();
+            DividerSnapAlgorithm snapAlgorithm = dividerView.getSnapAlgorithm();
+            int dividerPosition = dividerView.getCurrentPosition();
+            DividerSnapAlgorithm.SnapTarget currentTarget =
+                    snapAlgorithm.calculateNonDismissingSnapTarget(dividerPosition);
+            DividerSnapAlgorithm.SnapTarget target = (shortcutCode == SC_DOCK_LEFT)
+                    ? snapAlgorithm.getPreviousTarget(currentTarget)
+                    : snapAlgorithm.getNextTarget(currentTarget);
+            dividerView.startDragging(true /* animate */, false /* touching */);
+            dividerView.stopDragging(target.position, 0f, false /* avoidDismissStart */,
+                    true /* logMetrics */);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index 90cc0e57..ba9eb4a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -17,69 +17,281 @@
 package com.android.systemui.stackdivider;
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.Display.DEFAULT_DISPLAY;
 
+import android.app.ActivityTaskManager;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Handler;
 import android.os.RemoteException;
+import android.provider.Settings;
 import android.util.Log;
-import android.view.IDockedStackListener;
+import android.util.Slog;
+import android.view.IWindowContainer;
 import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
 import android.view.View;
-import android.view.WindowManagerGlobal;
+import android.view.WindowContainerTransaction;
 
+import com.android.internal.policy.DividerSnapAlgorithm;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayChangeController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.DisplayLayout;
+import com.android.systemui.wm.SystemWindows;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.Optional;
+import java.util.function.Consumer;
+
+import javax.inject.Singleton;
 
 import dagger.Lazy;
 
 /**
  * Controls the docked stack divider.
  */
-public class Divider extends SystemUI implements DividerView.DividerCallbacks {
+@Singleton
+public class Divider extends SystemUI implements DividerView.DividerCallbacks,
+        DisplayController.OnDisplaysChangedListener {
     private static final String TAG = "Divider";
+
+    static final boolean DEBUG = true;
+
+    static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+
     private final Optional<Lazy<Recents>> mRecentsOptionalLazy;
 
     private DividerWindowManager mWindowManager;
     private DividerView mView;
     private final DividerState mDividerState = new DividerState();
-    private DockDividerVisibilityListener mDockDividerVisibilityListener;
     private boolean mVisible = false;
     private boolean mMinimized = false;
     private boolean mAdjustedForIme = false;
     private boolean mHomeStackResizable = false;
     private ForcedResizableInfoActivityController mForcedResizableController;
+    private SystemWindows mSystemWindows;
+    final SurfaceSession mSurfaceSession = new SurfaceSession();
+    private DisplayController mDisplayController;
+    private DisplayImeController mImeController;
 
-    public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
+    // Keeps track of real-time split geometry including snap positions and ime adjustments
+    private SplitDisplayLayout mSplitLayout;
+
+    // Transient: this contains the layout calculated for a new rotation requested by WM. This is
+    // kept around so that we can wait for a matching configuration change and then use the exact
+    // layout that we sent back to WM.
+    private SplitDisplayLayout mRotateSplitLayout;
+
+    private Handler mHandler;
+    private KeyguardStateController mKeyguardStateController;
+
+    private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
+            new ArrayList<>();
+
+    private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
+
+    private DisplayChangeController.OnDisplayChangingListener mRotationController =
+            (display, fromRotation, toRotation, t) -> {
+                DisplayLayout displayLayout =
+                        new DisplayLayout(mDisplayController.getDisplayLayout(display));
+                SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
+                sdl.rotateTo(toRotation);
+                mRotateSplitLayout = sdl;
+                int position = mMinimized ? mView.mSnapTargetBeforeMinimized.position
+                        : mView.getCurrentPosition();
+                DividerSnapAlgorithm snap = sdl.getSnapAlgorithm();
+                final DividerSnapAlgorithm.SnapTarget target =
+                        snap.calculateNonDismissingSnapTarget(position);
+                sdl.resizeSplits(target.position, t);
+
+                if (inSplitMode()) {
+                    WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
+                }
+            };
+
+    private IWindowContainer mLastImeTarget = null;
+    private boolean mShouldAdjustForIme = false;
+
+    private DisplayImeController.ImePositionProcessor mImePositionProcessor =
+            new DisplayImeController.ImePositionProcessor() {
+                private int mStartTop = 0;
+                private int mFinalTop = 0;
+                @Override
+                public void onImeStartPositioning(int displayId, int imeTop, int finalImeTop,
+                        boolean showing, SurfaceControl.Transaction t) {
+                    mStartTop = imeTop;
+                    mFinalTop = finalImeTop;
+                    if (showing) {
+                        try {
+                            mLastImeTarget = ActivityTaskManager.getTaskOrganizerController()
+                                    .getImeTarget(displayId);
+                            mShouldAdjustForIme = !mSplitLayout.mDisplayLayout.isLandscape()
+                                    && (mLastImeTarget.asBinder()
+                                    == mSplits.mSecondary.token.asBinder());
+                        } catch (RemoteException e) {
+                            Slog.w(TAG, "Failed to get IME target", e);
+                        }
+                    }
+                    if (!mShouldAdjustForIme) {
+                        setAdjustedForIme(false);
+                        return;
+                    }
+                    mView.setAdjustedForIme(showing, showing
+                            ? DisplayImeController.ANIMATION_DURATION_SHOW_MS
+                            : DisplayImeController.ANIMATION_DURATION_HIDE_MS);
+                    // Reposition the server's secondary split position so that it evaluates
+                    // insets properly.
+                    WindowContainerTransaction wct = new WindowContainerTransaction();
+                    if (showing) {
+                        mSplitLayout.updateAdjustedBounds(finalImeTop, imeTop, finalImeTop);
+                        wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mAdjustedSecondary);
+                    } else {
+                        wct.setBounds(mSplits.mSecondary.token, mSplitLayout.mSecondary);
+                    }
+                    try {
+                        ActivityTaskManager.getTaskOrganizerController()
+                                .applyContainerTransaction(wct, null /* organizer */);
+                    } catch (RemoteException e) {
+                    }
+                    setAdjustedForIme(showing);
+                }
+
+                @Override
+                public void onImePositionChanged(int displayId, int imeTop,
+                        SurfaceControl.Transaction t) {
+                    if (!mShouldAdjustForIme) {
+                        return;
+                    }
+                    mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+                    mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+                            mSplitLayout.mAdjustedSecondary);
+                    final boolean showing = mFinalTop < mStartTop;
+                    final float progress = ((float) (imeTop - mStartTop)) / (mFinalTop - mStartTop);
+                    final float fraction = showing ? progress : 1.f - progress;
+                    mView.setResizeDimLayer(t, true /* primary */, fraction * 0.3f);
+                }
+
+                @Override
+                public void onImeEndPositioning(int displayId, int imeTop,
+                        boolean showing, SurfaceControl.Transaction t) {
+                    if (!mShouldAdjustForIme) {
+                        return;
+                    }
+                    mSplitLayout.updateAdjustedBounds(imeTop, mStartTop, mFinalTop);
+                    mView.resizeSplitSurfaces(t, mSplitLayout.mAdjustedPrimary,
+                            mSplitLayout.mAdjustedSecondary);
+                    mView.setResizeDimLayer(t, true /* primary */, showing ? 0.3f : 0.f);
+                }
+            };
+
+    public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+            DisplayController displayController, SystemWindows systemWindows,
+            DisplayImeController imeController, Handler handler,
+            KeyguardStateController keyguardStateController) {
         super(context);
+        mDisplayController = displayController;
+        mSystemWindows = systemWindows;
+        mImeController = imeController;
+        mHandler = handler;
+        mKeyguardStateController = keyguardStateController;
         mRecentsOptionalLazy = recentsOptionalLazy;
+        mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
     }
 
     @Override
     public void start() {
-        mWindowManager = new DividerWindowManager(mContext);
-        update(mContext.getResources().getConfiguration());
-        mDockDividerVisibilityListener = new DockDividerVisibilityListener();
-        try {
-            WindowManagerGlobal.getWindowManagerService().registerDockedStackListener(
-                    mDockDividerVisibilityListener);
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to register docked stack listener", e);
-        }
-        mForcedResizableController = new ForcedResizableInfoActivityController(mContext);
+        mWindowManager = new DividerWindowManager(mSystemWindows);
+        mDisplayController.addDisplayWindowListener(this);
+        // Hide the divider when keyguard is showing. Even though keyguard/statusbar is above
+        // everything, it is actually transparent except for notifications, so we still need to
+        // hide any surfaces that are below it.
+        // TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
+        mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+            @Override
+            public void onUnlockedChanged() {
+
+            }
+
+            @Override
+            public void onKeyguardShowingChanged() {
+                if (!inSplitMode() || mView == null || mView.getViewRootImpl() == null
+                        || mView.getViewRootImpl().getSurfaceControl() == null) {
+                    return;
+                }
+                mView.setHidden(mKeyguardStateController.isShowing());
+            }
+
+            @Override
+            public void onKeyguardFadingAwayChanged() {
+
+            }
+        });
+        // Don't initialize the divider or anything until we get the default display.
     }
 
     @Override
-    protected void onConfigurationChanged(Configuration newConfig) {
-        super.onConfigurationChanged(newConfig);
+    public void onDisplayAdded(int displayId) {
+        if (displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+        mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+                mDisplayController.getDisplayLayout(displayId), mSplits);
+        mImeController.addPositionProcessor(mImePositionProcessor);
+        mDisplayController.addDisplayChangingController(mRotationController);
+        try {
+            mSplits.init(ActivityTaskManager.getTaskOrganizerController(), mSurfaceSession);
+            // Set starting tile bounds based on middle target
+            final WindowContainerTransaction tct = new WindowContainerTransaction();
+            int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+            mSplitLayout.resizeSplits(midPos, tct);
+            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+                    null /* organizer */);
+        } catch (Exception e) {
+            Slog.e(TAG, "Failed to register docked stack listener", e);
+        }
+        update(mDisplayController.getDisplayContext(displayId).getResources().getConfiguration());
+    }
+
+    @Override
+    public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+        if (displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+        mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+                mDisplayController.getDisplayLayout(displayId), mSplits);
+        if (mRotateSplitLayout == null) {
+            int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+            final WindowContainerTransaction tct = new WindowContainerTransaction();
+            mSplitLayout.resizeSplits(midPos, tct);
+            try {
+                ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(tct,
+                        null /* organizer */);
+            } catch (RemoteException e) {
+            }
+        } else if (mRotateSplitLayout != null
+                && mSplitLayout.mDisplayLayout.rotation()
+                        == mRotateSplitLayout.mDisplayLayout.rotation()) {
+            mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
+            mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
+            mRotateSplitLayout = null;
+        }
         update(newConfig);
     }
 
+    Handler getHandler() {
+        return mHandler;
+    }
+
     public DividerView getView() {
         return mView;
     }
@@ -92,18 +304,25 @@
         return mHomeStackResizable;
     }
 
+    /** {@code true} if this is visible */
+    public boolean inSplitMode() {
+        return mView != null && mView.getVisibility() == View.VISIBLE;
+    }
+
     private void addDivider(Configuration configuration) {
+        Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
         mView = (DividerView)
-                LayoutInflater.from(mContext).inflate(R.layout.docked_stack_divider, null);
-        mView.injectDependencies(mWindowManager, mDividerState, this);
+                LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
+        DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
+        mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout);
         mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
         mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
-        final int size = mContext.getResources().getDimensionPixelSize(
+        final int size = dctx.getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
-        final int width = landscape ? size : MATCH_PARENT;
-        final int height = landscape ? MATCH_PARENT : size;
-        mWindowManager.add(mView, width, height);
+        final int width = landscape ? size : displayLayout.width();
+        final int height = landscape ? displayLayout.height() : size;
+        mWindowManager.add(mView, width, height, mContext.getDisplayId());
     }
 
     private void removeDivider() {
@@ -116,65 +335,86 @@
     private void update(Configuration configuration) {
         removeDivider();
         addDivider(configuration);
-        if (mMinimized) {
+        if (mMinimized && mView != null) {
             mView.setMinimizedDockStack(true, mHomeStackResizable);
             updateTouchable();
         }
     }
 
-    private void updateVisibility(final boolean visible) {
-        mView.post(new Runnable() {
-            @Override
-            public void run() {
-                if (mVisible != visible) {
-                    mVisible = visible;
-                    mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+    void updateVisibility(final boolean visible) {
+        if (mVisible != visible) {
+            mVisible = visible;
+            mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
 
-                    // Update state because animations won't finish.
-                    mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
-                }
+            if (visible) {
+                mView.enterSplitMode(mHomeStackResizable);
+                // Update state because animations won't finish.
+                mView.setMinimizedDockStack(mMinimized, mHomeStackResizable);
+            } else {
+                mView.exitSplitMode();
+                // un-minimize so that next entry triggers minimize anim.
+                mView.setMinimizedDockStack(false /* minimized */, mHomeStackResizable);
             }
-        });
+            // Notify existence listeners
+            synchronized (mDockedStackExistsListeners) {
+                mDockedStackExistsListeners.removeIf(wf -> {
+                    Consumer<Boolean> l = wf.get();
+                    if (l != null) l.accept(visible);
+                    return l == null;
+                });
+            }
+        }
+    }
+
+    private void setHomeStackResizable(boolean resizable) {
+        if (mHomeStackResizable == resizable) {
+            return;
+        }
+        mHomeStackResizable = resizable;
+        if (!inSplitMode()) {
+            return;
+        }
+        WindowManagerProxy.applyHomeTasksMinimized(mSplitLayout, mSplits.mSecondary.token);
     }
 
     private void updateMinimizedDockedStack(final boolean minimized, final long animDuration,
             final boolean isHomeStackResizable) {
-        mView.post(new Runnable() {
-            @Override
-            public void run() {
-                mHomeStackResizable = isHomeStackResizable;
-                if (mMinimized != minimized) {
-                    mMinimized = minimized;
-                    updateTouchable();
-                    if (animDuration > 0) {
-                        mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
-                    } else {
-                        mView.setMinimizedDockStack(minimized, isHomeStackResizable);
-                    }
-                }
+        setHomeStackResizable(isHomeStackResizable);
+        if (animDuration > 0) {
+            mView.setMinimizedDockStack(minimized, animDuration, isHomeStackResizable);
+        } else {
+            mView.setMinimizedDockStack(minimized, isHomeStackResizable);
+        }
+        updateTouchable();
+    }
+
+    /** Switch to minimized state if appropriate */
+    public void setMinimized(final boolean minimized) {
+        mHandler.post(() -> {
+            if (!inSplitMode()) {
+                return;
             }
+            if (mMinimized == minimized) {
+                return;
+            }
+            mMinimized = minimized;
+            mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
+            updateTouchable();
         });
     }
 
-    private void notifyDockedStackExistsChanged(final boolean exists) {
-        mView.post(new Runnable() {
-            @Override
-            public void run() {
-                mForcedResizableController.notifyDockedStackExistsChanged(exists);
-            }
-        });
+    void setAdjustedForIme(boolean adjustedForIme) {
+        if (mAdjustedForIme == adjustedForIme) {
+            return;
+        }
+        mAdjustedForIme = adjustedForIme;
+        updateTouchable();
     }
 
     private void updateTouchable() {
         mWindowManager.setTouchable((mHomeStackResizable || !mMinimized) && !mAdjustedForIme);
     }
 
-    public void onRecentsActivityStarting() {
-        if (mView != null) {
-            mView.onRecentsActivityStarting();
-        }
-    }
-
     /**
      * Workaround for b/62528361, at the time recents has drawn, it may happen before a
      * configuration change to the Divider, and internally, the event will be posted to the
@@ -206,6 +446,9 @@
     }
 
     public void onAppTransitionFinished() {
+        if (mView == null) {
+            return;
+        }
         mForcedResizableController.onAppTransitionFinished();
     }
 
@@ -231,46 +474,66 @@
         pw.print("  mAdjustedForIme="); pw.println(mAdjustedForIme);
     }
 
-    class DockDividerVisibilityListener extends IDockedStackListener.Stub {
+    long getAnimDuration() {
+        float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
+                Settings.Global.TRANSITION_ANIMATION_SCALE,
+                mContext.getResources().getFloat(
+                        com.android.internal.R.dimen
+                                .config_appTransitionAnimationDurationScaleDefault));
+        final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION;
+        return (long) (transitionDuration * transitionScale);
+    }
 
-        @Override
-        public void onDividerVisibilityChanged(boolean visible) throws RemoteException {
-            updateVisibility(visible);
+    /** Register a listener that gets called whenever the existence of the divider changes */
+    public void registerInSplitScreenListener(Consumer<Boolean> listener) {
+        listener.accept(inSplitMode());
+        synchronized (mDockedStackExistsListeners) {
+            mDockedStackExistsListeners.add(new WeakReference<>(listener));
         }
+    }
 
-        @Override
-        public void onDockedStackExistsChanged(boolean exists) throws RemoteException {
-            notifyDockedStackExistsChanged(exists);
+    void startEnterSplit() {
+        // Set resizable directly here because applyEnterSplit already resizes home stack.
+        mHomeStackResizable = WindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
+    }
+
+    void ensureMinimizedSplit() {
+        final boolean wasMinimized = mMinimized;
+        mMinimized = true;
+        setHomeStackResizable(mSplits.mSecondary.isResizable());
+        if (!inSplitMode()) {
+            // Wasn't in split-mode yet, so enter now.
+            if (DEBUG) {
+                Log.d(TAG, " entering split mode with minimized=true");
+            }
+            updateVisibility(true /* visible */);
+        } else if (!wasMinimized) {
+            if (DEBUG) {
+                Log.d(TAG, " in split mode, but minimizing ");
+            }
+            // Was already in split-mode, update just minimized state.
+            updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+                    mHomeStackResizable);
         }
+    }
 
-        @Override
-        public void onDockedStackMinimizedChanged(boolean minimized, long animDuration,
-                boolean isHomeStackResizable) throws RemoteException {
-            mHomeStackResizable = isHomeStackResizable;
-            updateMinimizedDockedStack(minimized, animDuration, isHomeStackResizable);
+    void ensureNormalSplit() {
+        if (!inSplitMode()) {
+            // Wasn't in split-mode, so enter now.
+            if (DEBUG) {
+                Log.d(TAG, " enter split mode unminimized ");
+            }
+            mMinimized = false;
+            updateVisibility(true /* visible */);
         }
-
-        @Override
-        public void onAdjustedForImeChanged(boolean adjustedForIme, long animDuration)
-                throws RemoteException {
-            mView.post(() -> {
-                if (mAdjustedForIme != adjustedForIme) {
-                    mAdjustedForIme = adjustedForIme;
-                    updateTouchable();
-                    if (!mMinimized) {
-                        if (animDuration > 0) {
-                            mView.setAdjustedForIme(adjustedForIme, animDuration);
-                        } else {
-                            mView.setAdjustedForIme(adjustedForIme);
-                        }
-                    }
-                }
-            });
-        }
-
-        @Override
-        public void onDockSideChanged(final int newDockSide) throws RemoteException {
-            mView.post(() -> mView.notifyDockSideChanged(newDockSide));
+        if (mMinimized) {
+            // Was in minimized state, so leave that.
+            if (DEBUG) {
+                Log.d(TAG, " in split mode already, but unminimizing ");
+            }
+            mMinimized = false;
+            updateMinimizedDockedStack(mMinimized, getAnimDuration(),
+                    mHomeStackResizable);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index 49f4d5e..f3b2553 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -17,8 +17,14 @@
 package com.android.systemui.stackdivider;
 
 import android.content.Context;
+import android.os.Handler;
 
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.recents.Recents;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.wm.DisplayController;
+import com.android.systemui.wm.DisplayImeController;
+import com.android.systemui.wm.SystemWindows;
 
 import java.util.Optional;
 
@@ -35,7 +41,11 @@
 public class DividerModule {
     @Singleton
     @Provides
-    static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy) {
-        return new Divider(context, recentsOptionalLazy);
+    static Divider provideDivider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
+            DisplayController displayController, SystemWindows systemWindows,
+            DisplayImeController imeController, @Main Handler handler,
+            KeyguardStateController keyguardStateController) {
+        return new Divider(context, recentsOptionalLazy, displayController, systemWindows,
+                imeController, handler, keyguardStateController);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index 9fe6e84..fdd04b9 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -16,12 +16,8 @@
 
 package com.android.systemui.stackdivider;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.PointerIcon.TYPE_HORIZONTAL_DOUBLE_ARROW;
 import static android.view.PointerIcon.TYPE_VERTICAL_DOUBLE_ARROW;
-import static android.view.WindowManager.DOCKED_LEFT;
 import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 
@@ -40,10 +36,11 @@
 import android.util.AttributeSet;
 import android.view.Choreographer;
 import android.view.Display;
-import android.view.DisplayInfo;
 import android.view.InsetsState;
 import android.view.MotionEvent;
 import android.view.PointerIcon;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.View.OnTouchListener;
@@ -75,6 +72,7 @@
  */
 public class DividerView extends FrameLayout implements OnTouchListener,
         OnComputeInternalInsetsListener {
+    private static final String TAG = "DividerView";
 
     public interface DividerCallbacks {
         void onDraggingStart();
@@ -123,14 +121,11 @@
     private int mTouchSlop;
     private boolean mBackgroundLifted;
     private boolean mIsInMinimizeInteraction;
-    private SnapTarget mSnapTargetBeforeMinimized;
+    SnapTarget mSnapTargetBeforeMinimized;
 
     private int mDividerInsets;
     private final Display mDefaultDisplay;
-    private int mDisplayWidth;
-    private int mDisplayHeight;
-    private int mDisplayRotation;
-    private int mDividerWindowWidth;
+
     private int mDividerSize;
     private int mTouchElevation;
     private int mLongPressEntraceAnimDuration;
@@ -147,8 +142,7 @@
     private DividerWindowManager mWindowManager;
     private VelocityTracker mVelocityTracker;
     private FlingAnimationUtils mFlingAnimationUtils;
-    private DividerSnapAlgorithm mSnapAlgorithm;
-    private DividerSnapAlgorithm mMinimizedSnapAlgorithm;
+    private SplitDisplayLayout mSplitLayout;
     private DividerCallbacks mCallback;
     private final Rect mStableInsets = new Rect();
 
@@ -163,6 +157,10 @@
     private DividerState mState;
     private final SurfaceFlingerVsyncChoreographer mSfChoreographer;
 
+    private SplitScreenTaskOrganizer mTiles;
+    boolean mFirstLayout = true;
+    int mDividerPositionX;
+    int mDividerPositionY;
 
     // The view is removed or in the process of been removed from the system.
     private boolean mRemoved;
@@ -172,7 +170,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case MSG_RESIZE_STACK:
-                    resizeStack(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
+                    resizeStackSurfaces(msg.arg1, msg.arg2, (SnapTarget) msg.obj);
                     break;
                 default:
                     super.handleMessage(msg);
@@ -228,16 +226,17 @@
         public boolean performAccessibilityAction(View host, int action, Bundle args) {
             int currentPosition = getCurrentPosition();
             SnapTarget nextTarget = null;
+            DividerSnapAlgorithm snapAlgorithm = mSplitLayout.getSnapAlgorithm();
             if (action == R.id.action_move_tl_full) {
-                nextTarget = mSnapAlgorithm.getDismissEndTarget();
+                nextTarget = snapAlgorithm.getDismissEndTarget();
             } else if (action == R.id.action_move_tl_70) {
-                nextTarget = mSnapAlgorithm.getLastSplitTarget();
+                nextTarget = snapAlgorithm.getLastSplitTarget();
             } else if (action == R.id.action_move_tl_50) {
-                nextTarget = mSnapAlgorithm.getMiddleTarget();
+                nextTarget = snapAlgorithm.getMiddleTarget();
             } else if (action == R.id.action_move_tl_30) {
-                nextTarget = mSnapAlgorithm.getFirstSplitTarget();
+                nextTarget = snapAlgorithm.getFirstSplitTarget();
             } else if (action == R.id.action_move_rb_full) {
-                nextTarget = mSnapAlgorithm.getDismissStartTarget();
+                nextTarget = snapAlgorithm.getDismissStartTarget();
             }
             if (nextTarget != null) {
                 startDragging(true /* animate */, false /* touching */);
@@ -284,11 +283,11 @@
         mBackground = findViewById(R.id.docked_divider_background);
         mMinimizedShadow = findViewById(R.id.minimized_dock_shadow);
         mHandle.setOnTouchListener(this);
-        mDividerWindowWidth = getResources().getDimensionPixelSize(
+        final int dividerWindowWidth = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_thickness);
         mDividerInsets = getResources().getDimensionPixelSize(
                 com.android.internal.R.dimen.docked_stack_divider_insets);
-        mDividerSize = mDividerWindowWidth - 2 * mDividerInsets;
+        mDividerSize = dividerWindowWidth - 2 * mDividerInsets;
         mTouchElevation = getResources().getDimensionPixelSize(
                 R.dimen.docked_stack_divider_lift_elevation);
         mLongPressEntraceAnimDuration = getResources().getInteger(
@@ -296,7 +295,6 @@
         mGrowRecents = getResources().getBoolean(R.bool.recents_grow_in_multiwindow);
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         mFlingAnimationUtils = new FlingAnimationUtils(getResources().getDisplayMetrics(), 0.3f);
-        updateDisplayInfo();
         boolean landscape = getResources().getConfiguration().orientation
                 == Configuration.ORIENTATION_LANDSCAPE;
         mHandle.setPointerIcon(PointerIcon.getSystemIcon(getContext(),
@@ -314,6 +312,7 @@
                 && !mIsInMinimizeInteraction) {
             saveSnapTargetBeforeMinimized(mSnapTargetBeforeMinimized);
         }
+        mFirstLayout = true;
     }
 
     void onDividerRemoved() {
@@ -341,17 +340,17 @@
                 || mStableInsets.bottom != insets.getStableInsetBottom()) {
             mStableInsets.set(insets.getStableInsetLeft(), insets.getStableInsetTop(),
                     insets.getStableInsetRight(), insets.getStableInsetBottom());
-            if (mSnapAlgorithm != null || mMinimizedSnapAlgorithm != null) {
-                mSnapAlgorithm = null;
-                mMinimizedSnapAlgorithm = null;
-                initializeSnapAlgorithm();
-            }
         }
         return super.onApplyWindowInsets(insets);
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        if (mFirstLayout) {
+            // Wait for first layout so that the ViewRootImpl surface has been created.
+            initializeSurfaceState();
+            mFirstLayout = false;
+        }
         super.onLayout(changed, left, top, right, bottom);
         int minimizeLeft = 0;
         int minimizeTop = 0;
@@ -372,19 +371,16 @@
     }
 
     public void injectDependencies(DividerWindowManager windowManager, DividerState dividerState,
-            DividerCallbacks callback) {
+            DividerCallbacks callback, SplitScreenTaskOrganizer tiles, SplitDisplayLayout sdl) {
         mWindowManager = windowManager;
         mState = dividerState;
         mCallback = callback;
-
-        // Set the previous position ratio before minimized state after attaching this divider
-        if (mStableInsets.isEmpty()) {
-            WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
-        }
+        mTiles = tiles;
+        mSplitLayout = sdl;
 
         if (mState.mRatioPositionBeforeMinimized == 0) {
             // Set the middle target as the initial state
-            mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
+            mSnapTargetBeforeMinimized = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
         } else {
             repositionSnapTargetBeforeMinimized();
         }
@@ -411,18 +407,34 @@
         return mOtherTaskRect;
     }
 
+    private boolean inSplitMode() {
+        return getVisibility() == VISIBLE;
+    }
+
+    /** Unlike setVisible, this directly hides the surface without changing view visibility. */
+    void setHidden(boolean hidden) {
+        post(() -> {
+            if (!isViewSurfaceValid()) {
+                return;
+            }
+            Transaction t = mTiles.getTransaction();
+            if (hidden) {
+                t.hide(getViewRootImpl().getSurfaceControl());
+            } else {
+                t.show(getViewRootImpl().getSurfaceControl());
+            }
+            t.apply();
+            mTiles.releaseTransaction(t);
+        });
+    }
+
     public boolean startDragging(boolean animate, boolean touching) {
         cancelFlingAnimation();
         if (touching) {
             mHandle.setTouching(true, animate);
         }
-        mDockSide = mWindowManagerProxy.getDockSide();
+        mDockSide = mSplitLayout.getPrimarySplitSide();
 
-        // Update snap algorithm if rotation has occurred
-        if (mDisplayRotation != mDefaultDisplay.getRotation()) {
-            updateDisplayInfo();
-        }
-        initializeSnapAlgorithm();
         mWindowManagerProxy.setResizing(true);
         if (touching) {
             mWindowManager.setSlippery(false);
@@ -431,7 +443,7 @@
         if (mCallback != null) {
             mCallback.onDraggingStart();
         }
-        return mDockSide != WindowManager.DOCKED_INVALID;
+        return inSplitMode();
     }
 
     public void stopDragging(int position, float velocity, boolean avoidDismissStart,
@@ -467,38 +479,22 @@
     }
 
     private void updateDockSide() {
-        mDockSide = mWindowManagerProxy.getDockSide();
+        mDockSide = mSplitLayout.getPrimarySplitSide();
         mMinimizedShadow.setDockSide(mDockSide);
     }
 
-    private void initializeSnapAlgorithm() {
-        if (mSnapAlgorithm == null) {
-            mSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(), mDisplayWidth,
-                    mDisplayHeight, mDividerSize, isHorizontalDivision(), mStableInsets, mDockSide);
-            if (mSnapTargetBeforeMinimized != null && mSnapTargetBeforeMinimized.isMiddleTarget) {
-                mSnapTargetBeforeMinimized = mSnapAlgorithm.getMiddleTarget();
-            }
-        }
-        if (mMinimizedSnapAlgorithm == null) {
-            mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(getContext().getResources(),
-                    mDisplayWidth, mDisplayHeight, mDividerSize, isHorizontalDivision(),
-                    mStableInsets, mDockSide, mDockedStackMinimized && mHomeStackResizable);
-        }
-    }
-
     public DividerSnapAlgorithm getSnapAlgorithm() {
-        initializeSnapAlgorithm();
-        return mDockedStackMinimized && mHomeStackResizable ? mMinimizedSnapAlgorithm :
-                mSnapAlgorithm;
+        return mDockedStackMinimized
+                && mHomeStackResizable ? mSplitLayout.getMinimizedSnapAlgorithm()
+                        : mSplitLayout.getSnapAlgorithm();
     }
 
     public int getCurrentPosition() {
-        getLocationOnScreen(mTempInt2);
-        if (isHorizontalDivision()) {
-            return mTempInt2[1] + mDividerInsets;
-        } else {
-            return mTempInt2[0] + mDividerInsets;
-        }
+        return isHorizontalDivision() ? mDividerPositionY : mDividerPositionX;
+    }
+
+    public boolean isMinimized() {
+        return mDockedStackMinimized;
     }
 
     @Override
@@ -557,25 +553,25 @@
     }
 
     private void logResizeEvent(SnapTarget snapTarget) {
-        if (snapTarget == mSnapAlgorithm.getDismissStartTarget()) {
+        if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissStartTarget()) {
             MetricsLogger.action(
                     mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideTopLeft(mDockSide)
                             ? LOG_VALUE_UNDOCK_MAX_OTHER
                             : LOG_VALUE_UNDOCK_MAX_DOCKED);
-        } else if (snapTarget == mSnapAlgorithm.getDismissEndTarget()) {
+        } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getDismissEndTarget()) {
             MetricsLogger.action(
                     mContext, MetricsEvent.ACTION_WINDOW_UNDOCK_MAX, dockSideBottomRight(mDockSide)
                             ? LOG_VALUE_UNDOCK_MAX_OTHER
                             : LOG_VALUE_UNDOCK_MAX_DOCKED);
-        } else if (snapTarget == mSnapAlgorithm.getMiddleTarget()) {
+        } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getMiddleTarget()) {
             MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
                     LOG_VALUE_RESIZE_50_50);
-        } else if (snapTarget == mSnapAlgorithm.getFirstSplitTarget()) {
+        } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getFirstSplitTarget()) {
             MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
                     dockSideTopLeft(mDockSide)
                             ? LOG_VALUE_RESIZE_DOCKED_SMALLER
                             : LOG_VALUE_RESIZE_DOCKED_LARGER);
-        } else if (snapTarget == mSnapAlgorithm.getLastSplitTarget()) {
+        } else if (snapTarget == mSplitLayout.getSnapAlgorithm().getLastSplitTarget()) {
             MetricsLogger.action(mContext, MetricsEvent.ACTION_WINDOW_DOCK_RESIZE,
                     dockSideTopLeft(mDockSide)
                             ? LOG_VALUE_RESIZE_DOCKED_LARGER
@@ -625,12 +621,16 @@
                         : snapTarget.taskPosition,
                 snapTarget));
         Runnable endAction = () -> {
-            commitSnapFlags(snapTarget);
+            boolean dismissed = commitSnapFlags(snapTarget);
             mWindowManagerProxy.setResizing(false);
             updateDockSide();
             mCurrentAnimator = null;
             mEntranceAnimationRunning = false;
             mExitAnimationRunning = false;
+            if (!dismissed) {
+                WindowManagerProxy.applyResizeSplits((mIsInMinimizeInteraction
+                        ? mSnapTargetBeforeMinimized : snapTarget).position, mSplitLayout);
+            }
             if (mCallback != null) {
                 mCallback.onDraggingEnd();
             }
@@ -642,12 +642,13 @@
                 // position isn't negative.
                 final SnapTarget saveTarget;
                 if (snapTarget.position < 0) {
-                    saveTarget = mSnapAlgorithm.getMiddleTarget();
+                    saveTarget = mSplitLayout.getSnapAlgorithm().getMiddleTarget();
                 } else {
                     saveTarget = snapTarget;
                 }
-                if (saveTarget.position != mSnapAlgorithm.getDismissEndTarget().position
-                        && saveTarget.position != mSnapAlgorithm.getDismissStartTarget().position) {
+                final DividerSnapAlgorithm snapAlgo = mSplitLayout.getSnapAlgorithm();
+                if (saveTarget.position != snapAlgo.getDismissEndTarget().position
+                        && saveTarget.position != snapAlgo.getDismissStartTarget().position) {
                     saveSnapTargetBeforeMinimized(saveTarget);
                 }
             }
@@ -701,11 +702,11 @@
         }
     }
 
-    private void commitSnapFlags(SnapTarget target) {
+    private boolean commitSnapFlags(SnapTarget target) {
         if (target.flag == SnapTarget.FLAG_NONE) {
-            return;
+            return false;
         }
-        boolean dismissOrMaximize;
+        final boolean dismissOrMaximize;
         if (target.flag == SnapTarget.FLAG_DISMISS_START) {
             dismissOrMaximize = mDockSide == WindowManager.DOCKED_LEFT
                     || mDockSide == WindowManager.DOCKED_TOP;
@@ -713,12 +714,13 @@
             dismissOrMaximize = mDockSide == WindowManager.DOCKED_RIGHT
                     || mDockSide == WindowManager.DOCKED_BOTTOM;
         }
-        if (dismissOrMaximize) {
-            mWindowManagerProxy.dismissDockedStack();
-        } else {
-            mWindowManagerProxy.maximizeDockedStack();
-        }
-        mWindowManagerProxy.setResizeDimLayer(false, WINDOWING_MODE_UNDEFINED, 0f);
+        mWindowManagerProxy.dismissOrMaximizeDocked(mTiles, dismissOrMaximize);
+        Transaction t = mTiles.getTransaction();
+        setResizeDimLayer(t, true /* primary */, 0f);
+        setResizeDimLayer(t, false /* primary */, 0f);
+        t.apply();
+        mTiles.releaseTransaction(t);
+        return true;
     }
 
     private void liftBackground() {
@@ -765,6 +767,28 @@
         mBackgroundLifted = false;
     }
 
+    private void initializeSurfaceState() {
+        int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+        // Recalculate the split-layout's internal tile bounds
+        mSplitLayout.resizeSplits(midPos);
+        Transaction t = mTiles.getTransaction();
+        if (mDockedStackMinimized) {
+            int position = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget().position;
+            calculateBoundsForPosition(position, mDockSide, mDockedRect);
+            calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+                    mOtherRect);
+            mDividerPositionX = mDividerPositionY = position;
+            resizeSplitSurfaces(t, mDockedRect, mSplitLayout.mPrimary,
+                    mOtherRect, mSplitLayout.mSecondary);
+        } else {
+            resizeSplitSurfaces(t, mSplitLayout.mPrimary, null,
+                    mSplitLayout.mSecondary, null);
+        }
+        setResizeDimLayer(t, true /* primary */, 0.f /* alpha */);
+        setResizeDimLayer(t, false /* secondary */, 0.f /* alpha */);
+        t.apply();
+        mTiles.releaseTransaction(t);
+    }
 
     public void setMinimizedDockStack(boolean minimized, boolean isHomeStackResizable) {
         mHomeStackResizable = isHomeStackResizable;
@@ -789,15 +813,11 @@
             mDockedStackMinimized = minimized;
         } else if (mDockedStackMinimized != minimized) {
             mDockedStackMinimized = minimized;
-            if (mDisplayRotation != mDefaultDisplay.getRotation()) {
+            if (mSplitLayout.mDisplayLayout.rotation() != mDefaultDisplay.getRotation()) {
                 // Splitscreen to minimize is about to starts after rotating landscape to seascape,
                 // update insets, display info and snap algorithm targets
                 WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
                 repositionSnapTargetBeforeMinimized();
-                updateDisplayInfo();
-            } else {
-                mMinimizedSnapAlgorithm = null;
-                initializeSnapAlgorithm();
             }
             if (mIsInMinimizeInteraction != minimized || mCurrentAnimator != null) {
                 cancelFlingAnimation();
@@ -805,15 +825,51 @@
                     // Relayout to recalculate the divider shadow when minimizing
                     requestLayout();
                     mIsInMinimizeInteraction = true;
-                    resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
+                    resizeStackSurfaces(mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget());
                 } else {
-                    resizeStack(mSnapTargetBeforeMinimized);
+                    resizeStackSurfaces(mSnapTargetBeforeMinimized);
                     mIsInMinimizeInteraction = false;
                 }
             }
         }
     }
 
+    void enterSplitMode(boolean isHomeStackResizable) {
+        post(() -> {
+            if (!isViewSurfaceValid()) {
+                return;
+            }
+            Transaction t = mTiles.getTransaction();
+            t.show(getViewRootImpl().getSurfaceControl()).apply();
+            mTiles.releaseTransaction(t);
+        });
+        if (isHomeStackResizable) {
+            SnapTarget miniMid = mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget();
+            if (mDockedStackMinimized) {
+                mDividerPositionY = mDividerPositionX = miniMid.position;
+            }
+        }
+    }
+
+    private boolean isViewSurfaceValid() {
+        return getViewRootImpl() != null && getViewRootImpl().getSurfaceControl() != null
+                && getViewRootImpl().getSurfaceControl().isValid();
+    }
+
+    void exitSplitMode() {
+        // Reset tile bounds
+        post(() -> {
+            if (!isViewSurfaceValid()) {
+                return;
+            }
+            Transaction t = mTiles.getTransaction();
+            t.hide(getViewRootImpl().getSurfaceControl()).apply();
+            mTiles.releaseTransaction(t);
+        });
+        int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+        WindowManagerProxy.applyResizeSplits(midPos, mSplitLayout);
+    }
+
     public void setMinimizedDockStack(boolean minimized, long animDuration,
             boolean isHomeStackResizable) {
         mHomeStackResizable = isHomeStackResizable;
@@ -844,14 +900,12 @@
             mDockedStackMinimized = minimized;
         } else if (mDockedStackMinimized != minimized) {
             mIsInMinimizeInteraction = true;
-            mMinimizedSnapAlgorithm = null;
             mDockedStackMinimized = minimized;
-            initializeSnapAlgorithm();
             stopDragging(minimized
                             ? mSnapTargetBeforeMinimized.position
                             : getCurrentPosition(),
                     minimized
-                            ? mMinimizedSnapAlgorithm.getMiddleTarget()
+                            ? mSplitLayout.getMinimizedSnapAlgorithm().getMiddleTarget()
                             : mSnapTargetBeforeMinimized,
                     animDuration, Interpolators.FAST_OUT_SLOW_IN, 0);
             setAdjustedForIme(false, animDuration);
@@ -865,18 +919,6 @@
                 .start();
     }
 
-    public void setAdjustedForIme(boolean adjustedForIme) {
-        updateDockSide();
-        mHandle.setAlpha(adjustedForIme ? 0f : 1f);
-        if (!adjustedForIme) {
-            resetBackground();
-        } else if (mDockSide == WindowManager.DOCKED_TOP) {
-            mBackground.setPivotY(0);
-            mBackground.setScaleY(ADJUSTED_FOR_IME_SCALE);
-        }
-        mAdjustedForIme = adjustedForIme;
-    }
-
     public void setAdjustedForIme(boolean adjustedForIme, long animDuration) {
         updateDockSide();
         mHandle.animate()
@@ -902,7 +944,8 @@
     private void saveSnapTargetBeforeMinimized(SnapTarget target) {
         mSnapTargetBeforeMinimized = target;
         mState.mRatioPositionBeforeMinimized = (float) target.position /
-                (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth);
+                (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+                        : mSplitLayout.mDisplayLayout.width());
     }
 
     private void resetBackground() {
@@ -916,51 +959,17 @@
     @Override
     protected void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        updateDisplayInfo();
-    }
-
-    public void notifyDockSideChanged(int newDockSide) {
-        int oldDockSide = mDockSide;
-        mDockSide = newDockSide;
-        mMinimizedShadow.setDockSide(mDockSide);
-        requestLayout();
-
-        // Update the snap position to the new docked side with correct insets
-        WindowManagerWrapper.getInstance().getStableInsets(mStableInsets);
-        mMinimizedSnapAlgorithm = null;
-        initializeSnapAlgorithm();
-
-        if (oldDockSide == DOCKED_LEFT && mDockSide == DOCKED_RIGHT
-                || oldDockSide == DOCKED_RIGHT && mDockSide == DOCKED_LEFT) {
-            repositionSnapTargetBeforeMinimized();
-        }
-
-        // Landscape to seascape rotation requires minimized to resize docked app correctly
-        if (mHomeStackResizable && mDockedStackMinimized) {
-            resizeStack(mMinimizedSnapAlgorithm.getMiddleTarget());
-        }
     }
 
     private void repositionSnapTargetBeforeMinimized() {
         int position = (int) (mState.mRatioPositionBeforeMinimized *
-                (isHorizontalDivision() ? mDisplayHeight : mDisplayWidth));
-        mSnapAlgorithm = null;
-        initializeSnapAlgorithm();
+                (isHorizontalDivision() ? mSplitLayout.mDisplayLayout.height()
+                        : mSplitLayout.mDisplayLayout.width()));
 
         // Set the snap target before minimized but do not save until divider is attached and not
         // minimized because it does not know its minimized state yet.
-        mSnapTargetBeforeMinimized = mSnapAlgorithm.calculateNonDismissingSnapTarget(position);
-    }
-
-    private void updateDisplayInfo() {
-        mDisplayRotation = mDefaultDisplay.getRotation();
-        final DisplayInfo info = new DisplayInfo();
-        mDefaultDisplay.getDisplayInfo(info);
-        mDisplayWidth = info.logicalWidth;
-        mDisplayHeight = info.logicalHeight;
-        mSnapAlgorithm = null;
-        mMinimizedSnapAlgorithm = null;
-        initializeSnapAlgorithm();
+        mSnapTargetBeforeMinimized =
+                mSplitLayout.getSnapAlgorithm().calculateNonDismissingSnapTarget(position);
     }
 
     private int calculatePosition(int touchX, int touchY) {
@@ -994,8 +1003,9 @@
     }
 
     public void calculateBoundsForPosition(int position, int dockSide, Rect outRect) {
-        DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect, mDisplayWidth,
-                mDisplayHeight, mDividerSize);
+        DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outRect,
+                mSplitLayout.mDisplayLayout.width(), mSplitLayout.mDisplayLayout.height(),
+                mDividerSize);
     }
 
     public void resizeStackDelayed(int position, int taskPosition, SnapTarget taskSnapTarget) {
@@ -1005,16 +1015,61 @@
         mSfChoreographer.scheduleAtSfVsync(mHandler, message);
     }
 
-    private void resizeStack(SnapTarget taskSnapTarget) {
-        resizeStack(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
+    private void resizeStackSurfaces(SnapTarget taskSnapTarget) {
+        resizeStackSurfaces(taskSnapTarget.position, taskSnapTarget.position, taskSnapTarget);
     }
 
-    public void resizeStack(int position, int taskPosition, SnapTarget taskSnapTarget) {
+    void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect otherRect) {
+        post(() -> resizeSplitSurfaces(t, dockedRect, null, otherRect, null));
+    }
+
+    private void resizeSplitSurfaces(Transaction t, Rect dockedRect, Rect dockedTaskRect,
+            Rect otherRect, Rect otherTaskRect) {
+        dockedTaskRect = dockedTaskRect == null ? dockedRect : dockedTaskRect;
+        otherTaskRect = otherTaskRect == null ? otherRect : otherTaskRect;
+
+        mDividerPositionX = dockedRect.right;
+        mDividerPositionY = dockedRect.bottom;
+
+        t.setPosition(mTiles.mPrimarySurface, dockedTaskRect.left, dockedTaskRect.top);
+        Rect crop = new Rect(dockedRect);
+        crop.offsetTo(-Math.min(dockedTaskRect.left - dockedRect.left, 0),
+                -Math.min(dockedTaskRect.top - dockedRect.top, 0));
+        t.setWindowCrop(mTiles.mPrimarySurface, crop);
+        t.setPosition(mTiles.mSecondarySurface, otherTaskRect.left, otherTaskRect.top);
+        crop.set(otherRect);
+        crop.offsetTo(-(otherTaskRect.left - otherRect.left),
+                -(otherTaskRect.top - otherRect.top));
+        t.setWindowCrop(mTiles.mSecondarySurface, crop);
+        SurfaceControl dividerCtrl = getViewRootImpl() != null
+                ? getViewRootImpl().getSurfaceControl() : null;
+        if (dividerCtrl != null && dividerCtrl.isValid()) {
+            if (isHorizontalDivision()) {
+                t.setPosition(dividerCtrl, 0, mDividerPositionY - mDividerInsets);
+            } else {
+                t.setPosition(dividerCtrl, mDividerPositionX - mDividerInsets, 0);
+            }
+        }
+    }
+
+    void setResizeDimLayer(Transaction t, boolean primary, float alpha) {
+        SurfaceControl dim = primary ? mTiles.mPrimaryDim : mTiles.mSecondaryDim;
+        if (alpha <= 0.f) {
+            t.hide(dim);
+        } else {
+            t.setAlpha(dim, alpha);
+            t.show(dim);
+        }
+    }
+
+    void resizeStackSurfaces(int position, int taskPosition, SnapTarget taskSnapTarget) {
         if (mRemoved) {
             // This divider view has been removed so shouldn't have any additional influence.
             return;
         }
         calculateBoundsForPosition(position, mDockSide, mDockedRect);
+        calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
+                mOtherRect);
 
         if (mDockedRect.equals(mLastResizeRect) && !mEntranceAnimationRunning) {
             return;
@@ -1025,6 +1080,7 @@
             mBackground.invalidate();
         }
 
+        Transaction t = mTiles.getTransaction();
         mLastResizeRect.set(mDockedRect);
         if (mHomeStackResizable && mIsInMinimizeInteraction) {
             calculateBoundsForPosition(mSnapTargetBeforeMinimized.position, mDockSide,
@@ -1037,8 +1093,10 @@
                 mDockedTaskRect.offset(Math.max(position, mStableInsets.left - mDividerSize)
                         - mDockedTaskRect.left + mDividerSize, 0);
             }
-            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedTaskRect,
-                    mOtherTaskRect, null);
+            resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect,
+                    mOtherTaskRect);
+            t.apply();
+            mTiles.releaseTransaction(t);
             return;
         }
 
@@ -1052,8 +1110,7 @@
             }
             calculateBoundsForPosition(taskPosition, DockedDividerUtils.invertDockSide(mDockSide),
                     mOtherTaskRect);
-            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, null,
-                    mOtherTaskRect, null);
+            resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
         } else if (mExitAnimationRunning && taskPosition != TASK_POSITION_SAME) {
             calculateBoundsForPosition(taskPosition, mDockSide, mDockedTaskRect);
             mDockedInsetRect.set(mDockedTaskRect);
@@ -1066,8 +1123,7 @@
             if (mDockSide == DOCKED_RIGHT) {
                 mDockedTaskRect.offset(position - mStableInsets.left + mDividerSize, 0);
             }
-            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
-                    mOtherTaskRect, mOtherInsetRect);
+            resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
         } else if (taskPosition != TASK_POSITION_SAME) {
             calculateBoundsForPosition(position, DockedDividerUtils.invertDockSide(mDockSide),
                     mOtherRect);
@@ -1078,7 +1134,8 @@
                     restrictDismissingTaskPosition(taskPosition, dockSideInverted, taskSnapTarget);
             calculateBoundsForPosition(taskPositionDocked, mDockSide, mDockedTaskRect);
             calculateBoundsForPosition(taskPositionOther, dockSideInverted, mOtherTaskRect);
-            mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
+            mTmpRect.set(0, 0, mSplitLayout.mDisplayLayout.width(),
+                    mSplitLayout.mDisplayLayout.height());
             alignTopLeft(mDockedRect, mDockedTaskRect);
             alignTopLeft(mOtherRect, mOtherTaskRect);
             mDockedInsetRect.set(mDockedTaskRect);
@@ -1094,15 +1151,15 @@
                     taskPositionDocked);
             applyDismissingParallax(mOtherTaskRect, dockSideInverted, taskSnapTarget, position,
                     taskPositionOther);
-            mWindowManagerProxy.resizeDockedStack(mDockedRect, mDockedTaskRect, mDockedInsetRect,
-                    mOtherTaskRect, mOtherInsetRect);
+            resizeSplitSurfaces(t, mDockedRect, mDockedTaskRect, mOtherRect, mOtherTaskRect);
         } else {
-            mWindowManagerProxy.resizeDockedStack(mDockedRect, null, null, null, null);
+            resizeSplitSurfaces(t, mDockedRect, null, mOtherRect, null);
         }
         SnapTarget closestDismissTarget = getSnapAlgorithm().getClosestDismissTarget(position);
         float dimFraction = getDimFraction(position, closestDismissTarget);
-        mWindowManagerProxy.setResizeDimLayer(dimFraction != 0f,
-                getWindowingModeForDismissTarget(closestDismissTarget), dimFraction);
+        setResizeDimLayer(t, isDismissTargetPrimary(closestDismissTarget), dimFraction);
+        t.apply();
+        mTiles.releaseTransaction(t);
     }
 
     private void applyExitAnimationParallax(Rect taskRect, int position) {
@@ -1156,10 +1213,12 @@
     private int restrictDismissingTaskPosition(int taskPosition, int dockSide,
             SnapTarget snapTarget) {
         if (snapTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(dockSide)) {
-            return Math.max(mSnapAlgorithm.getFirstSplitTarget().position, mStartPosition);
+            return Math.max(mSplitLayout.getSnapAlgorithm().getFirstSplitTarget().position,
+                    mStartPosition);
         } else if (snapTarget.flag == SnapTarget.FLAG_DISMISS_END
                 && dockSideBottomRight(dockSide)) {
-            return Math.min(mSnapAlgorithm.getLastSplitTarget().position, mStartPosition);
+            return Math.min(mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position,
+                    mStartPosition);
         } else {
             return taskPosition;
         }
@@ -1171,19 +1230,19 @@
     private void applyDismissingParallax(Rect taskRect, int dockSide, SnapTarget snapTarget,
             int position, int taskPosition) {
         float fraction = Math.min(1, Math.max(0,
-                mSnapAlgorithm.calculateDismissingFraction(position)));
+                mSplitLayout.getSnapAlgorithm().calculateDismissingFraction(position)));
         SnapTarget dismissTarget = null;
         SnapTarget splitTarget = null;
         int start = 0;
-        if (position <= mSnapAlgorithm.getLastSplitTarget().position
+        if (position <= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
                 && dockSideTopLeft(dockSide)) {
-            dismissTarget = mSnapAlgorithm.getDismissStartTarget();
-            splitTarget = mSnapAlgorithm.getFirstSplitTarget();
+            dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
+            splitTarget = mSplitLayout.getSnapAlgorithm().getFirstSplitTarget();
             start = taskPosition;
-        } else if (position >= mSnapAlgorithm.getLastSplitTarget().position
+        } else if (position >= mSplitLayout.getSnapAlgorithm().getLastSplitTarget().position
                 && dockSideBottomRight(dockSide)) {
-            dismissTarget = mSnapAlgorithm.getDismissEndTarget();
-            splitTarget = mSnapAlgorithm.getLastSplitTarget();
+            dismissTarget = mSplitLayout.getSnapAlgorithm().getDismissEndTarget();
+            splitTarget = mSplitLayout.getSnapAlgorithm().getLastSplitTarget();
             start = splitTarget.position;
         }
         if (dismissTarget != null && fraction > 0f
@@ -1236,14 +1295,10 @@
         }
     }
 
-    private int getWindowingModeForDismissTarget(SnapTarget dismissTarget) {
-        if ((dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
+    private boolean isDismissTargetPrimary(SnapTarget dismissTarget) {
+        return (dismissTarget.flag == SnapTarget.FLAG_DISMISS_START && dockSideTopLeft(mDockSide))
                 || (dismissTarget.flag == SnapTarget.FLAG_DISMISS_END
-                        && dockSideBottomRight(mDockSide))) {
-            return WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-        } else {
-            return WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-        }
+                        && dockSideBottomRight(mDockSide));
     }
 
     /**
@@ -1297,7 +1352,7 @@
     }
 
     void onDockedFirstAnimationFrame() {
-        saveSnapTargetBeforeMinimized(mSnapAlgorithm.getMiddleTarget());
+        saveSnapTargetBeforeMinimized(mSplitLayout.getSnapAlgorithm().getMiddleTarget());
     }
 
     void onDockedTopTask() {
@@ -1307,8 +1362,9 @@
         updateDockSide();
         mEntranceAnimationRunning = true;
 
-        resizeStack(calculatePositionForInsetBounds(), mSnapAlgorithm.getMiddleTarget().position,
-                mSnapAlgorithm.getMiddleTarget());
+        resizeStackSurfaces(calculatePositionForInsetBounds(),
+                mSplitLayout.getSnapAlgorithm().getMiddleTarget().position,
+                mSplitLayout.getSnapAlgorithm().getMiddleTarget());
     }
 
     void onRecentsDrawn() {
@@ -1337,13 +1393,12 @@
     }
 
     void onUndockingTask() {
-        int dockSide = mWindowManagerProxy.getDockSide();
-        if (dockSide != WindowManager.DOCKED_INVALID && (mHomeStackResizable
-                || !mDockedStackMinimized)) {
+        int dockSide = mSplitLayout.getPrimarySplitSide();
+        if (inSplitMode() && (mHomeStackResizable || !mDockedStackMinimized)) {
             startDragging(false /* animate */, false /* touching */);
             SnapTarget target = dockSideTopLeft(dockSide)
-                    ? mSnapAlgorithm.getDismissEndTarget()
-                    : mSnapAlgorithm.getDismissStartTarget();
+                    ? mSplitLayout.getSnapAlgorithm().getDismissEndTarget()
+                    : mSplitLayout.getSnapAlgorithm().getDismissStartTarget();
 
             // Don't start immediately - give a little bit time to settle the drag resize change.
             mExitAnimationRunning = true;
@@ -1354,8 +1409,7 @@
     }
 
     private int calculatePositionForInsetBounds() {
-        mTmpRect.set(0, 0, mDisplayWidth, mDisplayHeight);
-        mTmpRect.inset(mStableInsets);
+        mSplitLayout.mDisplayLayout.getStableBounds(mTmpRect);
         return DockedDividerUtils.calculatePositionForBounds(mTmpRect, mDockSide, mDividerSize);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
index 2486d653..bd843b0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerWindowManager.java
@@ -26,12 +26,13 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
 
-import android.content.Context;
 import android.graphics.PixelFormat;
 import android.os.Binder;
 import android.view.View;
 import android.view.WindowManager;
 
+import com.android.systemui.wm.SystemWindows;
+
 /**
  * Manages the window parameters of the docked stack divider.
  */
@@ -39,15 +40,16 @@
 
     private static final String WINDOW_TITLE = "DockedStackDivider";
 
-    private final WindowManager mWindowManager;
+    private final SystemWindows mSystemWindows;
     private WindowManager.LayoutParams mLp;
     private View mView;
 
-    public DividerWindowManager(Context ctx) {
-        mWindowManager = ctx.getSystemService(WindowManager.class);
+    public DividerWindowManager(SystemWindows systemWindows) {
+        mSystemWindows = systemWindows;
     }
 
-    public void add(View view, int width, int height) {
+    /** Add a divider view */
+    public void add(View view, int width, int height, int displayId) {
         mLp = new WindowManager.LayoutParams(
                 width, height, TYPE_DOCK_DIVIDER,
                 FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL
@@ -60,13 +62,13 @@
         view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
-        mWindowManager.addView(view, mLp);
+        mSystemWindows.addView(view, mLp, displayId, TYPE_DOCK_DIVIDER);
         mView = view;
     }
 
     public void remove() {
         if (mView != null) {
-            mWindowManager.removeView(mView);
+            mSystemWindows.removeView(mView);
         }
         mView = null;
     }
@@ -81,7 +83,7 @@
             changed = true;
         }
         if (changed) {
-            mWindowManager.updateViewLayout(mView, mLp);
+            mSystemWindows.updateViewLayout(mView, mLp);
         }
     }
 
@@ -95,7 +97,7 @@
             changed = true;
         }
         if (changed) {
-            mWindowManager.updateViewLayout(mView, mLp);
+            mSystemWindows.updateViewLayout(mView, mLp);
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index c6ac309..db7996e 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -31,6 +31,8 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.TaskStackChangeListener;
 
+import java.util.function.Consumer;
+
 /**
  * Controller that decides when to show the {@link ForcedResizableInfoActivity}.
  */
@@ -52,6 +54,12 @@
         }
     };
 
+    private final Consumer<Boolean> mDockedStackExistsListener = exists -> {
+        if (!exists) {
+            mPackagesShownInSession.clear();
+        }
+    };
+
     /** Record of force resized task that's pending to be handled. */
     private class PendingTaskRecord {
         int taskId;
@@ -67,7 +75,7 @@
         }
     }
 
-    public ForcedResizableInfoActivityController(Context context) {
+    public ForcedResizableInfoActivityController(Context context, Divider divider) {
         mContext = context;
         ActivityManagerWrapper.getInstance().registerTaskStackListener(
                 new TaskStackChangeListener() {
@@ -87,12 +95,7 @@
                         activityLaunchOnSecondaryDisplayFailed();
                     }
                 });
-    }
-
-    public void notifyDockedStackExistsChanged(boolean exists) {
-        if (!exists) {
-            mPackagesShownInSession.clear();
-        }
+        divider.registerInSplitScreenListener(mDockedStackExistsListener);
     }
 
     public void onAppTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
new file mode 100644
index 0000000..b19f560
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitDisplayLayout.java
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.util.TypedValue;
+import android.view.WindowContainerTransaction;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.internal.policy.DockedDividerUtils;
+import com.android.systemui.wm.DisplayLayout;
+
+/**
+ * Handles split-screen related internal display layout. In general, this represents the
+ * WM-facing understanding of the splits.
+ */
+public class SplitDisplayLayout {
+    /** Minimum size of an adjusted stack bounds relative to original stack bounds. Used to
+     * restrict IME adjustment so that a min portion of top stack remains visible.*/
+    private static final float ADJUSTED_STACK_FRACTION_MIN = 0.3f;
+
+    private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
+
+    SplitScreenTaskOrganizer mTiles;
+    DisplayLayout mDisplayLayout;
+    Context mContext;
+
+    // Lazy stuff
+    boolean mResourcesValid = false;
+    int mDividerSize;
+    int mDividerSizeInactive;
+    private DividerSnapAlgorithm mSnapAlgorithm = null;
+    private DividerSnapAlgorithm mMinimizedSnapAlgorithm = null;
+    Rect mPrimary = null;
+    Rect mSecondary = null;
+    Rect mAdjustedPrimary = null;
+    Rect mAdjustedSecondary = null;
+
+    public SplitDisplayLayout(Context ctx, DisplayLayout dl, SplitScreenTaskOrganizer taskTiles) {
+        mTiles = taskTiles;
+        mDisplayLayout = dl;
+        mContext = ctx;
+    }
+
+    void rotateTo(int newRotation) {
+        mDisplayLayout.rotateTo(mContext.getResources(), newRotation);
+        final Configuration config = new Configuration();
+        config.unset();
+        config.orientation = mDisplayLayout.getOrientation();
+        Rect tmpRect = new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+        tmpRect.inset(mDisplayLayout.nonDecorInsets());
+        config.windowConfiguration.setAppBounds(tmpRect);
+        tmpRect.set(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
+        tmpRect.inset(mDisplayLayout.stableInsets());
+        config.screenWidthDp = (int) (tmpRect.width() / mDisplayLayout.density());
+        config.screenHeightDp = (int) (tmpRect.height() / mDisplayLayout.density());
+        mContext = mContext.createConfigurationContext(config);
+        mSnapAlgorithm = null;
+        mMinimizedSnapAlgorithm = null;
+        mResourcesValid = false;
+    }
+
+    private void updateResources() {
+        if (mResourcesValid) {
+            return;
+        }
+        mResourcesValid = true;
+        Resources res = mContext.getResources();
+        mDividerSize = DockedDividerUtils.getDividerSize(res,
+                DockedDividerUtils.getDividerInsets(res));
+        mDividerSizeInactive = (int) TypedValue.applyDimension(
+                TypedValue.COMPLEX_UNIT_DIP, DIVIDER_WIDTH_INACTIVE_DP, res.getDisplayMetrics());
+    }
+
+    int getPrimarySplitSide() {
+        return mDisplayLayout.isLandscape() ? DOCKED_LEFT : DOCKED_TOP;
+    }
+
+    boolean isMinimized() {
+        return mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+                || mTiles.mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS;
+    }
+
+    DividerSnapAlgorithm getSnapAlgorithm() {
+        if (mSnapAlgorithm == null) {
+            updateResources();
+            boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+            mSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+                    mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+                    isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide());
+        }
+        return mSnapAlgorithm;
+    }
+
+    DividerSnapAlgorithm getMinimizedSnapAlgorithm() {
+        if (mMinimizedSnapAlgorithm == null) {
+            updateResources();
+            boolean isHorizontalDivision = !mDisplayLayout.isLandscape();
+            mMinimizedSnapAlgorithm = new DividerSnapAlgorithm(mContext.getResources(),
+                    mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize,
+                    isHorizontalDivision, mDisplayLayout.stableInsets(), getPrimarySplitSide(),
+                    true /* isMinimized */);
+        }
+        return mMinimizedSnapAlgorithm;
+    }
+
+    void resizeSplits(int position) {
+        mPrimary = mPrimary == null ? new Rect() : mPrimary;
+        mSecondary = mSecondary == null ? new Rect() : mSecondary;
+        calcSplitBounds(position, mPrimary, mSecondary);
+    }
+
+    void resizeSplits(int position, WindowContainerTransaction t) {
+        resizeSplits(position);
+        t.setBounds(mTiles.mPrimary.token, mPrimary);
+        t.setBounds(mTiles.mSecondary.token, mSecondary);
+
+        t.setSmallestScreenWidthDp(mTiles.mPrimary.token,
+                getSmallestWidthDpForBounds(mContext, mDisplayLayout, mPrimary));
+        t.setSmallestScreenWidthDp(mTiles.mSecondary.token,
+                getSmallestWidthDpForBounds(mContext, mDisplayLayout, mSecondary));
+    }
+
+    void calcSplitBounds(int position, @NonNull Rect outPrimary, @NonNull Rect outSecondary) {
+        int dockSide = getPrimarySplitSide();
+        DockedDividerUtils.calculateBoundsForPosition(position, dockSide, outPrimary,
+                mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+
+        DockedDividerUtils.calculateBoundsForPosition(position,
+                DockedDividerUtils.invertDockSide(dockSide), outSecondary, mDisplayLayout.width(),
+                mDisplayLayout.height(), mDividerSize);
+    }
+
+    Rect calcMinimizedHomeStackBounds() {
+        DividerSnapAlgorithm.SnapTarget miniMid = getMinimizedSnapAlgorithm().getMiddleTarget();
+        Rect homeBounds = new Rect();
+        DockedDividerUtils.calculateBoundsForPosition(miniMid.position,
+                DockedDividerUtils.invertDockSide(getPrimarySplitSide()), homeBounds,
+                mDisplayLayout.width(), mDisplayLayout.height(), mDividerSize);
+        return homeBounds;
+    }
+
+    /**
+     * Updates the adjustment depending on it's current state.
+     */
+    void updateAdjustedBounds(int currImeTop, int startTop, int finalTop) {
+        updateAdjustedBounds(mDisplayLayout, currImeTop, startTop, finalTop, mDividerSize,
+                mDividerSizeInactive, mPrimary, mSecondary);
+    }
+
+    /**
+     * Updates the adjustment depending on it's current state.
+     */
+    private void updateAdjustedBounds(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+            int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+        adjustForIME(dl, currImeTop, startTop, finalTop, dividerWidth, dividerWidthInactive,
+                primaryBounds, secondaryBounds);
+    }
+
+    /** Assumes top/bottom split. Splits are not adjusted for left/right splits. */
+    private void adjustForIME(DisplayLayout dl, int currImeTop, int startTop, int finalTop,
+            int dividerWidth, int dividerWidthInactive, Rect primaryBounds, Rect secondaryBounds) {
+        if (mAdjustedPrimary == null) {
+            mAdjustedPrimary = new Rect();
+            mAdjustedSecondary = new Rect();
+        }
+
+        final Rect displayStableRect = new Rect();
+        dl.getStableBounds(displayStableRect);
+
+        final boolean showing = finalTop < startTop;
+        final float progress = ((float) (currImeTop - startTop)) / (finalTop - startTop);
+        final float dividerSquish = showing ? progress : 1.f - progress;
+        final int currDividerWidth =
+                (int) (dividerWidthInactive * dividerSquish + dividerWidth * (1.f - dividerSquish));
+
+        final int minTopStackBottom = displayStableRect.top
+                + (int) ((mPrimary.bottom - displayStableRect.top) * ADJUSTED_STACK_FRACTION_MIN);
+        final int minImeTop = minTopStackBottom + currDividerWidth;
+
+        // Calculate an offset which shifts the stacks up by the height of the IME, but still
+        // leaves at least 30% of the top stack visible.
+        final int yOffset = Math.max(0, dl.height() - Math.max(currImeTop, minImeTop));
+
+        // TOP
+        // Reduce the offset by an additional small amount to squish the divider bar.
+        mAdjustedPrimary.set(primaryBounds);
+        mAdjustedPrimary.offset(0, -yOffset + (dividerWidth - currDividerWidth));
+
+        // BOTTOM
+        mAdjustedSecondary.set(secondaryBounds);
+        mAdjustedSecondary.offset(0, -yOffset);
+    }
+
+    static int getSmallestWidthDpForBounds(@NonNull Context context, DisplayLayout dl,
+            Rect bounds) {
+        int dividerSize = DockedDividerUtils.getDividerSize(context.getResources(),
+                DockedDividerUtils.getDividerInsets(context.getResources()));
+
+        int minWidth = Integer.MAX_VALUE;
+
+        // Go through all screen orientations and find the orientation in which the task has the
+        // smallest width.
+        Rect tmpRect = new Rect();
+        Rect rotatedDisplayRect = new Rect();
+        Rect displayRect = new Rect(0, 0, dl.width(), dl.height());
+
+        DisplayLayout tmpDL = new DisplayLayout();
+        for (int rotation = 0; rotation < 4; rotation++) {
+            tmpDL.set(dl);
+            tmpDL.rotateTo(context.getResources(), rotation);
+            DividerSnapAlgorithm snap = initSnapAlgorithmForRotation(context, tmpDL, dividerSize);
+
+            tmpRect.set(bounds);
+            DisplayLayout.rotateBounds(tmpRect, displayRect, rotation - dl.rotation());
+            rotatedDisplayRect.set(0, 0, tmpDL.width(), tmpDL.height());
+            final int dockSide = getPrimarySplitSide(tmpRect, rotatedDisplayRect,
+                    tmpDL.getOrientation());
+            final int position = DockedDividerUtils.calculatePositionForBounds(tmpRect, dockSide,
+                    dividerSize);
+
+            final int snappedPosition =
+                    snap.calculateNonDismissingSnapTarget(position).position;
+            DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, tmpRect,
+                    tmpDL.width(), tmpDL.height(), dividerSize);
+            Rect insettedDisplay = new Rect(rotatedDisplayRect);
+            insettedDisplay.inset(tmpDL.stableInsets());
+            tmpRect.intersect(insettedDisplay);
+            minWidth = Math.min(tmpRect.width(), minWidth);
+        }
+        return (int) (minWidth / dl.density());
+    }
+
+    static DividerSnapAlgorithm initSnapAlgorithmForRotation(Context context, DisplayLayout dl,
+            int dividerSize) {
+        final Configuration config = new Configuration();
+        config.unset();
+        config.orientation = dl.getOrientation();
+        Rect tmpRect = new Rect(0, 0, dl.width(), dl.height());
+        tmpRect.inset(dl.nonDecorInsets());
+        config.windowConfiguration.setAppBounds(tmpRect);
+        tmpRect.set(0, 0, dl.width(), dl.height());
+        tmpRect.inset(dl.stableInsets());
+        config.screenWidthDp = (int) (tmpRect.width() / dl.density());
+        config.screenHeightDp = (int) (tmpRect.height() / dl.density());
+        final Context rotationContext = context.createConfigurationContext(config);
+        return new DividerSnapAlgorithm(
+                rotationContext.getResources(), dl.width(), dl.height(), dividerSize,
+                config.orientation == ORIENTATION_PORTRAIT, dl.stableInsets());
+    }
+
+    /**
+     * Get the current primary-split side. Determined by its location of {@param bounds} within
+     * {@param displayRect} but if both are the same, it will try to dock to each side and determine
+     * if allowed in its respected {@param orientation}.
+     *
+     * @param bounds bounds of the primary split task to get which side is docked
+     * @param displayRect bounds of the display that contains the primary split task
+     * @param orientation the origination of device
+     * @return current primary-split side
+     */
+    static int getPrimarySplitSide(Rect bounds, Rect displayRect, int orientation) {
+        if (orientation == ORIENTATION_PORTRAIT) {
+            // Portrait mode, docked either at the top or the bottom.
+            final int diff = (displayRect.bottom - bounds.bottom) - (bounds.top - displayRect.top);
+            if (diff < 0) {
+                return DOCKED_BOTTOM;
+            } else {
+                // Top is default
+                return DOCKED_TOP;
+            }
+        } else if (orientation == ORIENTATION_LANDSCAPE) {
+            // Landscape mode, docked either on the left or on the right.
+            final int diff = (displayRect.right - bounds.right) - (bounds.left - displayRect.left);
+            if (diff < 0) {
+                return DOCKED_RIGHT;
+            }
+            return DOCKED_LEFT;
+        }
+        return DOCKED_INVALID;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
new file mode 100644
index 0000000..0a54d2f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ITaskOrganizerController;
+import android.app.WindowConfiguration;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Pools;
+import android.view.Display;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
+import android.view.SurfaceSession;
+
+class SplitScreenTaskOrganizer extends ITaskOrganizer.Stub {
+    private static final String TAG = "SplitScreenTaskOrganizer";
+    private static final boolean DEBUG = Divider.DEBUG;
+
+    RunningTaskInfo mPrimary;
+    RunningTaskInfo mSecondary;
+    SurfaceControl mPrimarySurface;
+    SurfaceControl mSecondarySurface;
+    SurfaceControl mPrimaryDim;
+    SurfaceControl mSecondaryDim;
+    final Divider mDivider;
+
+    private final Pools.SynchronizedPool<SurfaceControl.Transaction> mTransactionPool =
+            new Pools.SynchronizedPool<>(4);
+
+    SplitScreenTaskOrganizer(Divider divider) {
+        mDivider = divider;
+    }
+
+    void init(ITaskOrganizerController organizerController, SurfaceSession session)
+            throws RemoteException {
+        organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        organizerController.registerTaskOrganizer(this, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mPrimary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        mSecondary = organizerController.createRootTask(Display.DEFAULT_DISPLAY,
+                WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        mPrimarySurface = mPrimary.token.getLeash();
+        mSecondarySurface = mSecondary.token.getLeash();
+
+        // Initialize dim surfaces:
+        mPrimaryDim = new SurfaceControl.Builder(session).setParent(mPrimarySurface)
+                .setColorLayer().setName("Primary Divider Dim").build();
+        mSecondaryDim = new SurfaceControl.Builder(session).setParent(mSecondarySurface)
+                .setColorLayer().setName("Secondary Divider Dim").build();
+        SurfaceControl.Transaction t = getTransaction();
+        t.setLayer(mPrimaryDim, Integer.MAX_VALUE);
+        t.setColor(mPrimaryDim, new float[]{0f, 0f, 0f});
+        t.setLayer(mSecondaryDim, Integer.MAX_VALUE);
+        t.setColor(mSecondaryDim, new float[]{0f, 0f, 0f});
+        t.apply();
+        releaseTransaction(t);
+    }
+
+    SurfaceControl.Transaction getTransaction() {
+        SurfaceControl.Transaction t = mTransactionPool.acquire();
+        if (t == null) {
+            return new SurfaceControl.Transaction();
+        }
+        return t;
+    }
+
+    void releaseTransaction(SurfaceControl.Transaction t) {
+        mTransactionPool.release(t);
+    }
+
+    @Override
+    public void taskAppeared(RunningTaskInfo taskInfo) {
+    }
+
+    @Override
+    public void taskVanished(IWindowContainer container) {
+    }
+
+    @Override
+    public void transactionReady(int id, SurfaceControl.Transaction t) {
+    }
+
+    @Override
+    public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
+        if (taskInfo.displayId != DEFAULT_DISPLAY) {
+            return;
+        }
+        mDivider.getHandler().post(() -> handleTaskInfoChanged(taskInfo));
+    }
+
+    /**
+     * This is effectively a finite state machine which moves between the various split-screen
+     * presentations based on the contents of the split regions.
+     */
+    private void handleTaskInfoChanged(RunningTaskInfo info) {
+        final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+        final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+        if (info.token.asBinder() == mPrimary.token.asBinder()) {
+            mPrimary = info;
+        } else if (info.token.asBinder() == mSecondary.token.asBinder()) {
+            mSecondary = info;
+        }
+        final boolean primaryIsEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+        final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
+        if (DEBUG) {
+            Log.d(TAG, "onTaskInfoChanged " + mPrimary + "  " + mSecondary);
+        }
+        if (primaryIsEmpty || secondaryIsEmpty) {
+            // At-least one of the splits is empty which means we are currently transitioning
+            // into or out-of split-screen mode.
+            if (DEBUG) {
+                Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType
+                        + "  " + mSecondary.topActivityType);
+            }
+            if (mDivider.inSplitMode()) {
+                // Was in split-mode, which means we are leaving split, so continue that.
+                // This happens when the stack in the primary-split is dismissed.
+                if (DEBUG) {
+                    Log.d(TAG, "    was in split, so this means leave it "
+                            + mPrimary.topActivityType + "  " + mSecondary.topActivityType);
+                }
+                WindowManagerProxy.applyDismissSplit(this, true /* dismissOrMaximize */);
+                mDivider.updateVisibility(false /* visible */);
+            } else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
+                // Wasn't in split-mode (both were empty), but now that the primary split is
+                // populated, we should fully enter split by moving everything else into secondary.
+                // This just tells window-manager to reparent things, the UI will respond
+                // when it gets new task info for the secondary split.
+                if (DEBUG) {
+                    Log.d(TAG, "   was not in split, but primary is populated, so enter it");
+                }
+                mDivider.startEnterSplit();
+            }
+        } else if (mSecondary.topActivityType == ACTIVITY_TYPE_HOME
+                || mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS) {
+            // Both splits are populated but the secondary split has a home/recents stack on top,
+            // so enter minimized mode.
+            mDivider.ensureMinimizedSplit();
+        } else {
+            // Both splits are populated by normal activities, so make sure we aren't minimized.
+            mDivider.ensureNormalSplit();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 228aab5..7685733 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -16,16 +16,25 @@
 
 package com.android.systemui.stackdivider;
 
-import static android.view.WindowManager.DOCKED_INVALID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.Display.DEFAULT_DISPLAY;
 
+import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.Display;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
 import android.view.WindowManagerGlobal;
 
 import com.android.internal.annotations.GuardedBy;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -35,88 +44,20 @@
 public class WindowManagerProxy {
 
     private static final String TAG = "WindowManagerProxy";
+    private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
 
     private static final WindowManagerProxy sInstance = new WindowManagerProxy();
 
     @GuardedBy("mDockedRect")
     private final Rect mDockedRect = new Rect();
-    private final Rect mTempDockedTaskRect = new Rect();
-    private final Rect mTempDockedInsetRect = new Rect();
-    private final Rect mTempOtherTaskRect = new Rect();
-    private final Rect mTempOtherInsetRect = new Rect();
 
     private final Rect mTmpRect1 = new Rect();
-    private final Rect mTmpRect2 = new Rect();
-    private final Rect mTmpRect3 = new Rect();
-    private final Rect mTmpRect4 = new Rect();
-    private final Rect mTmpRect5 = new Rect();
 
     @GuardedBy("mDockedRect")
     private final Rect mTouchableRegion = new Rect();
 
-    private boolean mDimLayerVisible;
-    private int mDimLayerTargetWindowingMode;
-    private float mDimLayerAlpha;
-
     private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
 
-    private final Runnable mResizeRunnable = new Runnable() {
-        @Override
-        public void run() {
-            synchronized (mDockedRect) {
-                mTmpRect1.set(mDockedRect);
-                mTmpRect2.set(mTempDockedTaskRect);
-                mTmpRect3.set(mTempDockedInsetRect);
-                mTmpRect4.set(mTempOtherTaskRect);
-                mTmpRect5.set(mTempOtherInsetRect);
-            }
-            try {
-                ActivityTaskManager.getService()
-                        .resizeDockedStack(mTmpRect1,
-                                mTmpRect2.isEmpty() ? null : mTmpRect2,
-                                mTmpRect3.isEmpty() ? null : mTmpRect3,
-                                mTmpRect4.isEmpty() ? null : mTmpRect4,
-                                mTmpRect5.isEmpty() ? null : mTmpRect5);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to resize stack: " + e);
-            }
-        }
-    };
-
-    private final Runnable mDismissRunnable = new Runnable() {
-        @Override
-        public void run() {
-            try {
-                ActivityTaskManager.getService().dismissSplitScreenMode(false /* onTop */);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to remove stack: " + e);
-            }
-        }
-    };
-
-    private final Runnable mMaximizeRunnable = new Runnable() {
-        @Override
-        public void run() {
-            try {
-                ActivityTaskManager.getService().dismissSplitScreenMode(true /* onTop */);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to resize stack: " + e);
-            }
-        }
-    };
-
-    private final Runnable mDimLayerRunnable = new Runnable() {
-        @Override
-        public void run() {
-            try {
-                WindowManagerGlobal.getWindowManagerService().setResizeDimLayer(mDimLayerVisible,
-                        mDimLayerTargetWindowingMode, mDimLayerAlpha);
-            } catch (RemoteException e) {
-                Log.w(TAG, "Failed to resize stack: " + e);
-            }
-        }
-    };
-
     private final Runnable mSetTouchableRegionRunnable = new Runnable() {
         @Override
         public void run() {
@@ -139,40 +80,9 @@
         return sInstance;
     }
 
-    public void resizeDockedStack(Rect docked, Rect tempDockedTaskRect, Rect tempDockedInsetRect,
-            Rect tempOtherTaskRect, Rect tempOtherInsetRect) {
-        synchronized (mDockedRect) {
-            mDockedRect.set(docked);
-            if (tempDockedTaskRect != null) {
-                mTempDockedTaskRect.set(tempDockedTaskRect);
-            } else {
-                mTempDockedTaskRect.setEmpty();
-            }
-            if (tempDockedInsetRect != null) {
-                mTempDockedInsetRect.set(tempDockedInsetRect);
-            } else {
-                mTempDockedInsetRect.setEmpty();
-            }
-            if (tempOtherTaskRect != null) {
-                mTempOtherTaskRect.set(tempOtherTaskRect);
-            } else {
-                mTempOtherTaskRect.setEmpty();
-            }
-            if (tempOtherInsetRect != null) {
-                mTempOtherInsetRect.set(tempOtherInsetRect);
-            } else {
-                mTempOtherInsetRect.setEmpty();
-            }
-        }
-        mExecutor.execute(mResizeRunnable);
-    }
-
-    public void dismissDockedStack() {
-        mExecutor.execute(mDismissRunnable);
-    }
-
-    public void maximizeDockedStack() {
-        mExecutor.execute(mMaximizeRunnable);
+    void dismissOrMaximizeDocked(
+            final SplitScreenTaskOrganizer tiles, final boolean dismissOrMaximize) {
+        mExecutor.execute(() -> applyDismissSplit(tiles, dismissOrMaximize));
     }
 
     public void setResizing(final boolean resizing) {
@@ -188,26 +98,204 @@
         });
     }
 
-    public int getDockSide() {
-        try {
-            return WindowManagerGlobal.getWindowManagerService().getDockedStackSide();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Failed to get dock side: " + e);
-        }
-        return DOCKED_INVALID;
-    }
-
-    public void setResizeDimLayer(boolean visible, int targetWindowingMode, float alpha) {
-        mDimLayerVisible = visible;
-        mDimLayerTargetWindowingMode = targetWindowingMode;
-        mDimLayerAlpha = alpha;
-        mExecutor.execute(mDimLayerRunnable);
-    }
-
+    /** Sets a touch region */
     public void setTouchRegion(Rect region) {
         synchronized (mDockedRect) {
             mTouchableRegion.set(region);
         }
         mExecutor.execute(mSetTouchableRegionRunnable);
     }
+
+    static void applyResizeSplits(int position, SplitDisplayLayout splitLayout) {
+        WindowContainerTransaction t = new WindowContainerTransaction();
+        splitLayout.resizeSplits(position, t);
+        try {
+            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(t,
+                    null /* organizer */);
+        } catch (RemoteException e) {
+        }
+    }
+
+    private static boolean getHomeAndRecentsTasks(List<IWindowContainer> out,
+            IWindowContainer parent) {
+        boolean resizable = false;
+        try {
+            List<ActivityManager.RunningTaskInfo> rootTasks = parent == null
+                    ? ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+                            Display.DEFAULT_DISPLAY, HOME_AND_RECENTS)
+                    : ActivityTaskManager.getTaskOrganizerController().getChildTasks(parent,
+                            HOME_AND_RECENTS);
+            for (int i = 0, n = rootTasks.size(); i < n; ++i) {
+                final ActivityManager.RunningTaskInfo ti = rootTasks.get(i);
+                out.add(ti.token);
+                if (ti.topActivityType == ACTIVITY_TYPE_HOME) {
+                    resizable = ti.isResizable();
+                }
+            }
+        } catch (RemoteException e) {
+        }
+        return resizable;
+    }
+
+    static void applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent) {
+        applyHomeTasksMinimized(layout, parent, null /* transaction */);
+    }
+
+    /**
+     * Assign a fixed override-bounds to home tasks that reflect their geometry while the primary
+     * split is minimized. This actually "sticks out" of the secondary split area, but when in
+     * minimized mode, the secondary split gets a 'negative' crop to expose it.
+     */
+    static boolean applyHomeTasksMinimized(SplitDisplayLayout layout, IWindowContainer parent,
+            WindowContainerTransaction t) {
+        // Resize the home/recents stacks to the larger minimized-state size
+        final Rect homeBounds;
+        final ArrayList<IWindowContainer> homeStacks = new ArrayList<>();
+        boolean isHomeResizable = getHomeAndRecentsTasks(homeStacks, parent);
+        if (isHomeResizable) {
+            homeBounds = layout.calcMinimizedHomeStackBounds();
+        } else {
+            homeBounds = new Rect(0, 0, layout.mDisplayLayout.width(),
+                    layout.mDisplayLayout.height());
+        }
+        WindowContainerTransaction wct = t != null ? t : new WindowContainerTransaction();
+        for (int i = homeStacks.size() - 1; i >= 0; --i) {
+            wct.setBounds(homeStacks.get(i), homeBounds);
+        }
+        if (t != null) {
+            return isHomeResizable;
+        }
+        try {
+            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+                    null /* organizer */);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to resize home stacks ", e);
+        }
+        return isHomeResizable;
+    }
+
+    /**
+     * Finishes entering split-screen by reparenting all FULLSCREEN tasks into the secondary split.
+     * This assumes there is already something in the primary split since that is usually what
+     * triggers a call to this. In the same transaction, this overrides the home task bounds via
+     * {@link #applyHomeTasksMinimized}.
+     *
+     * @return whether the home stack is resizable
+     */
+    static boolean applyEnterSplit(SplitScreenTaskOrganizer tiles, SplitDisplayLayout layout) {
+        try {
+            // Set launchtile first so that any stack created after
+            // getAllStackInfos and before reparent (even if unlikely) are placed
+            // correctly.
+            ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(
+                    DEFAULT_DISPLAY, tiles.mSecondary.token);
+            List<ActivityManager.RunningTaskInfo> rootTasks =
+                    ActivityTaskManager.getTaskOrganizerController().getRootTasks(DEFAULT_DISPLAY,
+                            null /* activityTypes */);
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            if (rootTasks.isEmpty()) {
+                return false;
+            }
+            for (int i = rootTasks.size() - 1; i >= 0; --i) {
+                if (rootTasks.get(i).configuration.windowConfiguration.getWindowingMode()
+                        != WINDOWING_MODE_FULLSCREEN) {
+                    continue;
+                }
+                wct.reparent(rootTasks.get(i).token, tiles.mSecondary.token,
+                        true /* onTop */);
+            }
+            boolean isHomeResizable = applyHomeTasksMinimized(layout, null /* parent */, wct);
+            ActivityTaskManager.getTaskOrganizerController()
+                    .applyContainerTransaction(wct, null /* organizer */);
+            return isHomeResizable;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Error moving fullscreen tasks to secondary split: " + e);
+        }
+        return false;
+    }
+
+    private static boolean isHomeOrRecentTask(ActivityManager.RunningTaskInfo ti) {
+        final int atype = ti.configuration.windowConfiguration.getActivityType();
+        return atype == ACTIVITY_TYPE_HOME || atype == ACTIVITY_TYPE_RECENTS;
+    }
+
+    /**
+     * Reparents all tile members back to their display and resets home task override bounds.
+     * @param dismissOrMaximize When {@code true} this resolves the split by closing the primary
+     *                          split (thus resulting in the top of the secondary split becoming
+     *                          fullscreen. {@code false} resolves the other way.
+     */
+    static void applyDismissSplit(SplitScreenTaskOrganizer tiles, boolean dismissOrMaximize) {
+        try {
+            // Set launch root first so that any task created after getChildContainers and
+            // before reparent (pretty unlikely) are put into fullscreen.
+            ActivityTaskManager.getTaskOrganizerController().setLaunchRoot(Display.DEFAULT_DISPLAY,
+                    null);
+            // TODO(task-org): Once task-org is more complete, consider using Appeared/Vanished
+            //                 plus specific APIs to clean this up.
+            List<ActivityManager.RunningTaskInfo> primaryChildren =
+                    ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+                            tiles.mPrimary.token, null /* activityTypes */);
+            List<ActivityManager.RunningTaskInfo> secondaryChildren =
+                    ActivityTaskManager.getTaskOrganizerController().getChildTasks(
+                            tiles.mSecondary.token, null /* activityTypes */);
+            // In some cases (eg. non-resizable is launched), system-server will leave split-screen.
+            // as a result, the above will not capture any tasks; yet, we need to clean-up the
+            // home task bounds.
+            List<ActivityManager.RunningTaskInfo> freeHomeAndRecents =
+                    ActivityTaskManager.getTaskOrganizerController().getRootTasks(
+                            Display.DEFAULT_DISPLAY, HOME_AND_RECENTS);
+            if (primaryChildren.isEmpty() && secondaryChildren.isEmpty()
+                    && freeHomeAndRecents.isEmpty()) {
+                return;
+            }
+            WindowContainerTransaction wct = new WindowContainerTransaction();
+            if (dismissOrMaximize) {
+                // Dismissing, so move all primary split tasks first
+                for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+                    wct.reparent(primaryChildren.get(i).token, null /* parent */,
+                            true /* onTop */);
+                }
+                // Don't need to worry about home tasks because they are already in the "proper"
+                // order within the secondary split.
+                for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+                    final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+                    wct.reparent(ti.token, null /* parent */, true /* onTop */);
+                    if (isHomeOrRecentTask(ti)) {
+                        wct.setBounds(ti.token, null);
+                    }
+                }
+            } else {
+                // Maximize, so move non-home secondary split first
+                for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+                    if (isHomeOrRecentTask(secondaryChildren.get(i))) {
+                        continue;
+                    }
+                    wct.reparent(secondaryChildren.get(i).token, null /* parent */,
+                            true /* onTop */);
+                }
+                // Find and place home tasks in-between. This simulates the fact that there was
+                // nothing behind the primary split's tasks.
+                for (int i = secondaryChildren.size() - 1; i >= 0; --i) {
+                    final ActivityManager.RunningTaskInfo ti = secondaryChildren.get(i);
+                    if (isHomeOrRecentTask(ti)) {
+                        wct.reparent(ti.token, null /* parent */, true /* onTop */);
+                        // reset bounds too
+                        wct.setBounds(ti.token, null);
+                    }
+                }
+                for (int i = primaryChildren.size() - 1; i >= 0; --i) {
+                    wct.reparent(primaryChildren.get(i).token, null /* parent */,
+                            true /* onTop */);
+                }
+            }
+            for (int i = freeHomeAndRecents.size() - 1; i >= 0; --i) {
+                wct.setBounds(freeHomeAndRecents.get(i).token, null);
+            }
+            ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct,
+                    null /* organizer */);
+        } catch (RemoteException e) {
+            Log.w(TAG, "Failed to remove stack: " + e);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
index 4d6764e..b43fe73 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationListener.java
@@ -31,20 +31,17 @@
 import android.util.Log;
 
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.phone.NotificationListenerWithPlugins;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /**
  * This class handles listening to notification updates and passing them along to
  * NotificationPresenter to be displayed to the user.
  */
 @SuppressLint("OverrideAbstract")
-@Singleton
 public class NotificationListener extends NotificationListenerWithPlugins {
     private static final String TAG = "NotificationListener";
 
@@ -54,7 +51,9 @@
     private final List<NotificationHandler> mNotificationHandlers = new ArrayList<>();
     private final ArrayList<NotificationSettingsListener> mSettingsListeners = new ArrayList<>();
 
-    @Inject
+    /**
+     * Injected constructor. See {@link StatusBarModule}.
+     */
     public NotificationListener(
             Context context,
             NotificationManager notificationManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index a457266..d0af106 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -53,6 +53,7 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -73,16 +74,12 @@
 import java.util.List;
 import java.util.Set;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 import dagger.Lazy;
 
 /**
  * Handles tasks and state related to media notifications. For example, there is a 'current' media
  * notification, which this class keeps track of.
  */
-@Singleton
 public class NotificationMediaManager implements Dumpable {
     private static final String TAG = "NotificationMediaManager";
     public static final boolean DEBUG_MEDIA = false;
@@ -101,11 +98,10 @@
         PAUSED_MEDIA_STATES.add(PlaybackState.STATE_ERROR);
     }
 
-
     private final NotificationEntryManager mEntryManager;
 
     @Nullable
-    private Lazy<NotificationShadeWindowController> mStatusBarWindowController;
+    private Lazy<NotificationShadeWindowController> mNotificationShadeWindowController;
 
     @Nullable
     private BiometricUnlockController mBiometricUnlockController;
@@ -176,11 +172,13 @@
         }
     };
 
-    @Inject
+    /**
+     * Injected constructor. See {@link StatusBarModule}.
+     */
     public NotificationMediaManager(
             Context context,
             Lazy<StatusBar> statusBarLazy,
-            Lazy<NotificationShadeWindowController> statusBarWindowController,
+            Lazy<NotificationShadeWindowController> notificationShadeWindowController,
             NotificationEntryManager notificationEntryManager,
             MediaArtworkProcessor mediaArtworkProcessor,
             KeyguardBypassController keyguardBypassController) {
@@ -194,7 +192,7 @@
                 Context.MEDIA_SESSION_SERVICE);
         // TODO: use KeyguardStateController#isOccluded to remove this dependency
         mStatusBarLazy = statusBarLazy;
-        mStatusBarWindowController = statusBarWindowController;
+        mNotificationShadeWindowController = notificationShadeWindowController;
         mEntryManager = notificationEntryManager;
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
@@ -525,7 +523,8 @@
             }
         }
 
-        NotificationShadeWindowController windowController = mStatusBarWindowController.get();
+        NotificationShadeWindowController windowController =
+                mNotificationShadeWindowController.get();
         boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded();
 
         final boolean hasArtwork = artworkDrawable != null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index f3783c8..ebc2fa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -54,6 +54,7 @@
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -70,9 +71,6 @@
 import java.util.Objects;
 import java.util.Set;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 import dagger.Lazy;
 
 /**
@@ -81,7 +79,6 @@
  * interaction, keeping track of notifications to remove when NotificationPresenter is collapsed,
  * and handling clicks on remote views.
  */
-@Singleton
 public class NotificationRemoteInputManager implements Dumpable {
     public static final boolean ENABLE_REMOTE_INPUT =
             SystemProperties.getBoolean("debug.enable_remote_input", true);
@@ -257,7 +254,9 @@
         }
     };
 
-    @Inject
+    /**
+     * Injected constructor. See {@link StatusBarModule}.
+     */
     public NotificationRemoteInputManager(
             Context context,
             NotificationLockscreenUserManager lockscreenUserManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 37f9f88..7e70c20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -29,6 +29,7 @@
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
@@ -46,9 +47,6 @@
 import java.util.List;
 import java.util.Stack;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /**
  * NotificationViewHierarchyManager manages updating the view hierarchy of notification views based
  * on their group structure. For example, if a notification becomes bundled with another,
@@ -56,7 +54,6 @@
  * tell NotificationListContainer which notifications to display, and inform it of changes to those
  * notifications that might affect their display.
  */
-@Singleton
 public class NotificationViewHierarchyManager implements DynamicPrivacyController.Listener {
     private static final String TAG = "NotificationViewHierarchyManager";
 
@@ -94,8 +91,12 @@
     // the problem.
     private boolean mIsHandleDynamicPrivacyChangeScheduled;
 
-    @Inject
-    public NotificationViewHierarchyManager(Context context, @Main Handler mainHandler,
+    /**
+     * Injected constructor. See {@link StatusBarModule}.
+     */
+    public NotificationViewHierarchyManager(
+            Context context,
+            @Main Handler mainHandler,
             NotificationLockscreenUserManager notificationLockscreenUserManager,
             NotificationGroupManager groupManager,
             VisualStabilityManager visualStabilityManager,
@@ -104,8 +105,7 @@
             KeyguardBypassController bypassController,
             BubbleController bubbleController,
             DynamicPrivacyController privacyController,
-            ForegroundServiceSectionController fgsSectionController
-    ) {
+            ForegroundServiceSectionController fgsSectionController) {
         mContext = context;
         mHandler = mainHandler;
         mLockscreenUserManager = notificationLockscreenUserManager;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
new file mode 100644
index 0000000..44ef6b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ScreenRecordDrawable.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.statusbar;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.DrawableWrapper;
+import android.util.AttributeSet;
+
+import com.android.systemui.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+/**
+ * The screen record drawable draws a colored background and either a countdown or circle to
+ * indicate that the screen is being recorded.
+ */
+public class ScreenRecordDrawable extends DrawableWrapper {
+    private Drawable mFillDrawable;
+    private int mHorizontalPadding;
+    private int mLevel;
+    private float mTextSize;
+    private float mIconRadius;
+    private Paint mPaint;
+
+    /** No-arg constructor used by drawable inflation. */
+    public ScreenRecordDrawable() {
+        super(null);
+    }
+
+    @Override
+    public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
+            @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
+            throws XmlPullParserException, IOException {
+        super.inflate(r, parser, attrs, theme);
+        setDrawable(r.getDrawable(R.drawable.ic_screen_record_background, theme).mutate());
+        mFillDrawable = r.getDrawable(R.drawable.ic_screen_record_background, theme).mutate();
+        mHorizontalPadding = r.getDimensionPixelSize(R.dimen.status_bar_horizontal_padding);
+
+        mTextSize = r.getDimensionPixelSize(R.dimen.screenrecord_status_text_size);
+        mIconRadius = r.getDimensionPixelSize(R.dimen.screenrecord_status_icon_radius);
+        mLevel = attrs.getAttributeIntValue(null, "level", 0);
+
+        mPaint = new Paint();
+        mPaint.setTextAlign(Paint.Align.CENTER);
+        mPaint.setColor(Color.WHITE);
+        mPaint.setTextSize(mTextSize);
+        mPaint.setFakeBoldText(true);
+    }
+
+    @Override
+    public boolean canApplyTheme() {
+        return mFillDrawable.canApplyTheme() || super.canApplyTheme();
+    }
+
+    @Override
+    public void applyTheme(Resources.Theme t) {
+        super.applyTheme(t);
+        mFillDrawable.applyTheme(t);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+        mFillDrawable.setBounds(bounds);
+    }
+
+    @Override
+    public boolean onLayoutDirectionChanged(int layoutDirection) {
+        mFillDrawable.setLayoutDirection(layoutDirection);
+        return super.onLayoutDirectionChanged(layoutDirection);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        super.draw(canvas);
+        mFillDrawable.draw(canvas);
+
+        Rect b = mFillDrawable.getBounds();
+        if (mLevel > 0) {
+            String val = String.valueOf(mLevel);
+            Rect textBounds = new Rect();
+            mPaint.getTextBounds(val, 0, val.length(), textBounds);
+            float yOffset = textBounds.height() / 4; // half, and half again since it's centered
+            canvas.drawText(val, b.centerX(), b.centerY() + yOffset, mPaint);
+        } else {
+            canvas.drawCircle(b.centerX(), b.centerY() - mIconRadius / 2, mIconRadius, mPaint);
+        }
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        padding.left += mHorizontalPadding;
+        padding.right += mHorizontalPadding;
+        padding.top = 0;
+        padding.bottom = 0;
+        android.util.Log.d("ScreenRecordDrawable", "set zero top/bottom pad");
+        return true;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        super.setAlpha(alpha);
+        mFillDrawable.setAlpha(alpha);
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        mFillDrawable.setVisible(visible, restart);
+        return super.setVisible(visible, restart);
+    }
+
+    @Override
+    public Drawable mutate() {
+        mFillDrawable.mutate();
+        return super.mutate();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
index 40f8e39..bb96f42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SmartReplyController.java
@@ -21,27 +21,26 @@
 
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.statusbar.dagger.StatusBarModule;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 
 import java.util.Set;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /**
  * Handles when smart replies are added to a notification
  * and clicked upon.
  */
-@Singleton
 public class SmartReplyController {
     private final IStatusBarService mBarService;
     private final NotificationEntryManager mEntryManager;
     private Set<String> mSendingKeys = new ArraySet<>();
     private Callback mCallback;
 
-    @Inject
+    /**
+     * Injected constructor. See {@link StatusBarModule}.
+     */
     public SmartReplyController(NotificationEntryManager entryManager,
             IStatusBarService statusBarService) {
         mBarService = statusBarService;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
deleted file mode 100644
index 493482a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarDependenciesModule.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar;
-
-import android.content.Context;
-
-import com.android.systemui.statusbar.notification.row.NotificationRowModule;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.tracing.ProtoTracer;
-
-import javax.inject.Singleton;
-
-import dagger.Module;
-import dagger.Provides;
-
-/**
- * Dagger Module providing common dependencies of StatusBar.
- */
-@Module(includes = {NotificationRowModule.class})
-public class StatusBarDependenciesModule {
-    /**
-     * Provides our instance of CommandQueue which is considered optional.
-     */
-    @Provides
-    @Singleton
-    public CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
-        return new CommandQueue(context, protoTracer);
-    }
-
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
new file mode 100644
index 0000000..0b37c22
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarDependenciesModule.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 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.dagger;
+
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.bubbles.BubbleController;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.MediaArtworkProcessor;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.NotificationViewHierarchyManager;
+import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.DynamicPrivacyController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.stack.ForegroundServiceSectionController;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.policy.RemoteInputUriController;
+import com.android.systemui.tracing.ProtoTracer;
+
+import javax.inject.Singleton;
+
+import dagger.Lazy;
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * This module provides instances needed to construct {@link StatusBar}. These are moved to this
+ * separate from {@link StatusBarModule} module so that components that wish to build their own
+ * version of StatusBar can include just dependencies, without injecting StatusBar itself.
+ */
+@Module
+public interface StatusBarDependenciesModule {
+    /** */
+    @Singleton
+    @Provides
+    static NotificationRemoteInputManager provideNotificationRemoteInputManager(
+            Context context,
+            NotificationLockscreenUserManager lockscreenUserManager,
+            SmartReplyController smartReplyController,
+            NotificationEntryManager notificationEntryManager,
+            Lazy<StatusBar> statusBarLazy,
+            StatusBarStateController statusBarStateController,
+            Handler mainHandler,
+            RemoteInputUriController remoteInputUriController) {
+        return new NotificationRemoteInputManager(
+                context,
+                lockscreenUserManager,
+                smartReplyController,
+                notificationEntryManager,
+                statusBarLazy,
+                statusBarStateController,
+                mainHandler,
+                remoteInputUriController);
+    }
+
+    /** */
+    @Singleton
+    @Provides
+    static NotificationMediaManager provideNotificationMediaManager(
+            Context context,
+            Lazy<StatusBar> statusBarLazy,
+            Lazy<NotificationShadeWindowController> notificationShadeWindowController,
+            NotificationEntryManager notificationEntryManager,
+            MediaArtworkProcessor mediaArtworkProcessor,
+            KeyguardBypassController keyguardBypassController) {
+        return new NotificationMediaManager(
+                context,
+                statusBarLazy,
+                notificationShadeWindowController,
+                notificationEntryManager,
+                mediaArtworkProcessor,
+                keyguardBypassController);
+    }
+
+    /** */
+    @Singleton
+    @Provides
+    static NotificationListener provideNotificationListener(
+            Context context,
+            NotificationManager notificationManager,
+            @Main Handler mainHandler) {
+        return new NotificationListener(
+                context, notificationManager, mainHandler);
+    }
+
+    /** */
+    @Singleton
+    @Provides
+    static SmartReplyController provideSmartReplyController(
+            NotificationEntryManager entryManager, IStatusBarService statusBarService) {
+        return new SmartReplyController(entryManager, statusBarService);
+    }
+
+    /** */
+    @Singleton
+    @Provides
+    static NotificationViewHierarchyManager provideNotificationViewHierarchyManager(
+            Context context,
+            @Main Handler mainHandler,
+            NotificationLockscreenUserManager notificationLockscreenUserManager,
+            NotificationGroupManager groupManager,
+            VisualStabilityManager visualStabilityManager,
+            StatusBarStateController statusBarStateController,
+            NotificationEntryManager notificationEntryManager,
+            KeyguardBypassController bypassController,
+            BubbleController bubbleController,
+            DynamicPrivacyController privacyController,
+            ForegroundServiceSectionController fgsSectionController) {
+        return new NotificationViewHierarchyManager(
+                context,
+                mainHandler,
+                notificationLockscreenUserManager,
+                groupManager,
+                visualStabilityManager,
+                statusBarStateController,
+                notificationEntryManager,
+                bypassController,
+                bubbleController,
+                privacyController,
+                fgsSectionController);
+    }
+
+    /**
+     * Provides our instance of CommandQueue which is considered optional.
+     */
+    @Provides
+    @Singleton
+    static CommandQueue provideCommandQueue(Context context, ProtoTracer protoTracer) {
+        return new CommandQueue(context, protoTracer);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java
new file mode 100644
index 0000000..ad5ef20
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2020 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.dagger;
+
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
+import com.android.systemui.statusbar.notification.row.NotificationRowModule;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
+
+import dagger.Module;
+
+/** */
+@Module(includes = {StatusBarPhoneModule.class, StatusBarDependenciesModule.class,
+        NotificationsModule.class, NotificationRowModule.class})
+public interface StatusBarModule {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
index 93f5805..55a20fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/InstantAppNotifier.java
@@ -51,10 +51,10 @@
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
 import com.android.systemui.R;
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.NotificationChannels;
@@ -80,11 +80,13 @@
     private final CommandQueue mCommandQueue;
     private boolean mDockedStackExists;
     private KeyguardStateController mKeyguardStateController;
+    private final Divider mDivider;
 
     @Inject
     public InstantAppNotifier(Context context, CommandQueue commandQueue,
-            @UiBackground Executor uiBgExecutor) {
+            @UiBackground Executor uiBgExecutor, Divider divider) {
         super(context);
+        mDivider = divider;
         mCommandQueue = commandQueue;
         mUiBgExecutor = uiBgExecutor;
     }
@@ -103,7 +105,7 @@
         mCommandQueue.addCallback(this);
         mKeyguardStateController.addCallback(this);
 
-        DockedStackExistsListener.register(
+        mDivider.registerInSplitScreenListener(
                 exists -> {
                     mDockedStackExists = exists;
                     updateForegroundInstantApps();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
index d0e238a..72a7e11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationAlertingManager.java
@@ -28,13 +28,10 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /** Handles heads-up and pulsing behavior driven by notification changes. */
-@Singleton
 public class NotificationAlertingManager {
 
     private static final String TAG = "NotifAlertManager";
@@ -47,7 +44,9 @@
 
     private HeadsUpManager mHeadsUpManager;
 
-    @Inject
+    /**
+     * Injected constructor. See {@link NotificationsModule}.
+     */
     public NotificationAlertingManager(
             NotificationEntryManager notificationEntryManager,
             NotificationRemoteInputManager remoteInputManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index b0bf813..4f55e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -47,6 +47,7 @@
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
@@ -64,9 +65,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 import dagger.Lazy;
 
 /**
@@ -94,7 +92,6 @@
  * aware of
  * @see #getActiveNotificationsForCurrentUser() to see every notification that the current user owns
  */
-@Singleton
 public class NotificationEntryManager implements
         CommonNotifCollection,
         Dumpable,
@@ -181,7 +178,9 @@
         }
     }
 
-    @Inject
+    /**
+     * Injected constructor. See {@link NotificationsModule}.
+     */
     public NotificationEntryManager(
             NotificationEntryManagerLogger logger,
             NotificationGroupManager groupManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
index 99718abb..616c110 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/VisualStabilityManager.java
@@ -26,6 +26,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
@@ -33,14 +34,10 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /**
  * A manager that ensures that notifications are visually stable. It will suppress reorderings
  * and reorder at the right time when they are out of view.
  */
-@Singleton
 public class VisualStabilityManager implements OnHeadsUpChangedListener, Dumpable {
 
     private static final long TEMPORARY_REORDERING_ALLOWED_DURATION = 1000;
@@ -60,7 +57,9 @@
     private ArraySet<View> mAddedChildren = new ArraySet<>();
     private boolean mPulsing;
 
-    @Inject
+    /**
+     * Injected constructor. See {@link NotificationsModule}.
+     */
     public VisualStabilityManager(
             NotificationEntryManager notificationEntryManager, @Main Handler handler) {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 39f4dfa..8f8f742 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -17,15 +17,39 @@
 package com.android.systemui.statusbar.notification.dagger;
 
 import android.content.Context;
+import android.os.Handler;
+import android.view.accessibility.AccessibilityManager;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.UiBackground;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationListener;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.ForegroundServiceDismissalFeatureController;
+import com.android.systemui.statusbar.notification.NotificationAlertingManager;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
+import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerImpl;
 import com.android.systemui.statusbar.notification.init.NotificationsControllerStub;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
+import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.util.leak.LeakDetector;
+
+import java.util.concurrent.Executor;
 
 import javax.inject.Singleton;
 
@@ -33,9 +57,111 @@
 import dagger.Module;
 import dagger.Provides;
 
-/** Module for classes related to the notifications data pipeline */
+/**
+ * Dagger Module for classes found within the com.android.systemui.statusbar.notification package.
+ */
 @Module
-public class NotificationsModule {
+public interface NotificationsModule {
+    /** Provides an instance of {@link NotificationEntryManager} */
+    @Singleton
+    @Provides
+    static NotificationEntryManager provideNotificationEntryManager(
+            NotificationEntryManagerLogger logger,
+            NotificationGroupManager groupManager,
+            NotificationRankingManager rankingManager,
+            NotificationEntryManager.KeyguardEnvironment keyguardEnvironment,
+            FeatureFlags featureFlags,
+            Lazy<NotificationRowBinder> notificationRowBinderLazy,
+            Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
+            LeakDetector leakDetector,
+            ForegroundServiceDismissalFeatureController fgsFeatureController) {
+        return new NotificationEntryManager(
+                logger,
+                groupManager,
+                rankingManager,
+                keyguardEnvironment,
+                featureFlags,
+                notificationRowBinderLazy,
+                notificationRemoteInputManagerLazy,
+                leakDetector,
+                fgsFeatureController);
+    }
+
+    /** Provides an instance of {@link NotificationGutsManager} */
+    @Singleton
+    @Provides
+    static NotificationGutsManager provideNotificationGutsManager(
+            Context context,
+            VisualStabilityManager visualStabilityManager,
+            Lazy<StatusBar> statusBarLazy,
+            @Main Handler mainHandler,
+            AccessibilityManager accessibilityManager,
+            HighPriorityProvider highPriorityProvider) {
+        return new NotificationGutsManager(
+                context,
+                visualStabilityManager,
+                statusBarLazy,
+                mainHandler,
+                accessibilityManager,
+                highPriorityProvider);
+    }
+
+    /** Provides an instance of {@link VisualStabilityManager} */
+    @Singleton
+    @Provides
+    static VisualStabilityManager provideVisualStabilityManager(
+            NotificationEntryManager notificationEntryManager, Handler handler) {
+        return new VisualStabilityManager(notificationEntryManager, handler);
+    }
+
+    /** Provides an instance of {@link NotificationAlertingManager} */
+    @Singleton
+    @Provides
+    static NotificationAlertingManager provideNotificationAlertingManager(
+            NotificationEntryManager notificationEntryManager,
+            NotificationRemoteInputManager remoteInputManager,
+            VisualStabilityManager visualStabilityManager,
+            StatusBarStateController statusBarStateController,
+            NotificationInterruptionStateProvider notificationInterruptionStateProvider,
+            NotificationListener notificationListener) {
+        return new NotificationAlertingManager(
+                notificationEntryManager,
+                remoteInputManager,
+                visualStabilityManager,
+                statusBarStateController,
+                notificationInterruptionStateProvider,
+                notificationListener);
+    }
+
+    /** Provides an instance of {@link NotificationLogger} */
+    @Singleton
+    @Provides
+    static NotificationLogger provideNotificationLogger(
+            NotificationListener notificationListener,
+            @UiBackground Executor uiBgExecutor,
+            NotificationEntryManager entryManager,
+            StatusBarStateController statusBarStateController,
+            NotificationLogger.ExpansionStateLogger expansionStateLogger) {
+        return new NotificationLogger(
+                notificationListener,
+                uiBgExecutor,
+                entryManager,
+                statusBarStateController,
+                expansionStateLogger);
+    }
+
+    /** Provides an instance of {@link NotificationBlockingHelperManager} */
+    @Singleton
+    @Provides
+    static NotificationBlockingHelperManager provideNotificationBlockingHelperManager(
+            Context context,
+            NotificationGutsManager notificationGutsManager,
+            NotificationEntryManager notificationEntryManager,
+            MetricsLogger metricsLogger) {
+        return new NotificationBlockingHelperManager(
+                context, notificationGutsManager, notificationEntryManager, metricsLogger);
+    }
+
     /** Initializes the notification data pipeline (can be disabled via config). */
     @Singleton
     @Provides
@@ -55,7 +181,7 @@
      */
     @Provides
     @Singleton
-    public CommonNotifCollection provideCommonNotifCollection(
+    static CommonNotifCollection provideCommonNotifCollection(
             FeatureFlags featureFlags,
             Lazy<NotifPipeline> pipeline,
             NotificationEntryManager entryManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index 89e5f55..becb758 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -39,6 +39,7 @@
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -50,13 +51,11 @@
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
-import javax.inject.Singleton;
 
 /**
  * Handles notification logging, in particular, logging which notifications are visible and which
  * are not.
  */
-@Singleton
 public class NotificationLogger implements StateListener {
     private static final String TAG = "NotificationLogger";
 
@@ -192,7 +191,9 @@
         }
     }
 
-    @Inject
+    /**
+     * Injected constructor. See {@link NotificationsModule}.
+     */
     public NotificationLogger(NotificationListener notificationListener,
             @UiBackground Executor uiBgExecutor,
             NotificationEntryManager entryManager,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
index 7b758aa..9212325 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManager.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.statusbar.notification.row;
 
-import static android.service.notification.NotificationListenerService.Ranking
-        .USER_SENTIMENT_NEGATIVE;
+import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 
 import android.content.Context;
 import android.metrics.LogMaker;
@@ -27,29 +26,28 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.logging.NotificationCounters;
 
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /**
  * Manager for the notification blocking helper - tracks and helps create the blocking helper
  * affordance.
  */
-@Singleton
 public class NotificationBlockingHelperManager {
     /** Enables debug logging and always makes the blocking helper show up after a dismiss. */
     private static final boolean DEBUG = false;
     private static final String TAG = "BlockingHelper";
 
     private final Context mContext;
+    private final NotificationGutsManager mNotificationGutsManager;
+    private final NotificationEntryManager mNotificationEntryManager;
+    private final MetricsLogger mMetricsLogger;
     /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */
     private ExpandableNotificationRow mBlockingHelperRow;
     private Set<String> mNonBlockablePkgs;
@@ -60,11 +58,18 @@
      */
     private boolean mIsShadeExpanded;
 
-    private MetricsLogger mMetricsLogger = new MetricsLogger();
-
-    @Inject
-    public NotificationBlockingHelperManager(Context context) {
+    /**
+     * Injected constructor. See {@link NotificationsModule}.
+     */
+    public NotificationBlockingHelperManager(
+            Context context,
+            NotificationGutsManager notificationGutsManager,
+            NotificationEntryManager notificationEntryManager,
+            MetricsLogger metricsLogger) {
         mContext = context;
+        mNotificationGutsManager = notificationGutsManager;
+        mNotificationEntryManager = notificationEntryManager;
+        mMetricsLogger = metricsLogger;
         mNonBlockablePkgs = new HashSet<>();
         Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_nonBlockableNotificationPackages));
@@ -99,7 +104,6 @@
             if (DEBUG) {
                 Log.d(TAG, "Manager.perhapsShowBlockingHelper: Showing new blocking helper");
             }
-            NotificationGutsManager manager = Dependency.get(NotificationGutsManager.class);
 
             // Enable blocking helper on the row before moving forward so everything in the guts is
             // correctly prepped.
@@ -113,10 +117,10 @@
 
             // We don't care about the touch origin (x, y) since we're opening guts without any
             // explicit user interaction.
-            manager.openGuts(mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
+            mNotificationGutsManager.openGuts(
+                    mBlockingHelperRow, 0, 0, menuRow.getLongpressMenuItem(mContext));
 
-            Dependency.get(MetricsLogger.class)
-                    .count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
+            mMetricsLogger.count(NotificationCounters.BLOCKING_HELPER_SHOWN, 1);
             return true;
         }
         return false;
@@ -139,8 +143,7 @@
 
             mBlockingHelperRow.setBlockingHelperShowing(false);
             if (mBlockingHelperRow.isAttachedToWindow()) {
-                Dependency.get(NotificationEntryManager.class).updateNotifications(
-                        "dismissCurrentBlockingHelper");
+                mNotificationEntryManager.updateNotifications("dismissCurrentBlockingHelper");
             }
             mBlockingHelperRow = null;
             return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 566da65..e3ca283 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -33,7 +33,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.ImageMessageConsumer;
-import com.android.systemui.Dependency;
 import com.android.systemui.statusbar.InflationTask;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.SmartReplyController;
@@ -53,6 +52,8 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
+import dagger.Lazy;
+
 /**
  * {@link NotificationContentInflater} binds content to a {@link ExpandableNotificationRow} by
  * asynchronously building the content's {@link RemoteViews} and applying it to the row.
@@ -66,13 +67,19 @@
     private boolean mInflateSynchronously = false;
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final NotifRemoteViewCache mRemoteViewCache;
+    private final Lazy<SmartReplyConstants> mSmartReplyConstants;
+    private final Lazy<SmartReplyController> mSmartReplyController;
 
     @Inject
     NotificationContentInflater(
             NotifRemoteViewCache remoteViewCache,
-            NotificationRemoteInputManager remoteInputManager) {
+            NotificationRemoteInputManager remoteInputManager,
+            Lazy<SmartReplyConstants> smartReplyConstants,
+            Lazy<SmartReplyController> smartReplyController) {
         mRemoteViewCache = remoteViewCache;
         mRemoteInputManager = remoteInputManager;
+        mSmartReplyConstants = smartReplyConstants;
+        mSmartReplyController = smartReplyController;
     }
 
     @Override
@@ -104,6 +111,8 @@
                 contentToBind,
                 mRemoteViewCache,
                 entry,
+                mSmartReplyConstants.get(),
+                mSmartReplyController.get(),
                 row,
                 bindParams.isLowPriority,
                 bindParams.isChildInGroup,
@@ -136,6 +145,7 @@
                 packageContext);
         result = inflateSmartReplyViews(result, reInflateFlags, entry,
                 row.getContext(), packageContext, row.getHeadsUpManager(),
+                mSmartReplyConstants.get(), mSmartReplyController.get(),
                 row.getExistingSmartRepliesAndActions());
 
         apply(
@@ -204,9 +214,8 @@
     private static InflationProgress inflateSmartReplyViews(InflationProgress result,
             @InflationFlag int reInflateFlags, NotificationEntry entry, Context context,
             Context packageContext, HeadsUpManager headsUpManager,
+            SmartReplyConstants smartReplyConstants, SmartReplyController smartReplyController,
             SmartRepliesAndActions previousSmartRepliesAndActions) {
-        SmartReplyConstants smartReplyConstants = Dependency.get(SmartReplyConstants.class);
-        SmartReplyController smartReplyController = Dependency.get(SmartReplyController.class);
         if ((reInflateFlags & FLAG_CONTENT_VIEW_EXPANDED) != 0 && result.newExpandedView != null) {
             result.expandedInflatedSmartReplies =
                     InflatedSmartReplies.inflate(
@@ -643,6 +652,8 @@
         private final boolean mUsesIncreasedHeadsUpHeight;
         private final @InflationFlag int mReInflateFlags;
         private final NotifRemoteViewCache mRemoteViewCache;
+        private final SmartReplyConstants mSmartReplyConstants;
+        private final SmartReplyController mSmartReplyController;
         private ExpandableNotificationRow mRow;
         private Exception mError;
         private RemoteViews.OnClickHandler mRemoteViewClickHandler;
@@ -653,6 +664,8 @@
                 @InflationFlag int reInflateFlags,
                 NotifRemoteViewCache cache,
                 NotificationEntry entry,
+                SmartReplyConstants smartReplyConstants,
+                SmartReplyController smartReplyController,
                 ExpandableNotificationRow row,
                 boolean isLowPriority,
                 boolean isChildInGroup,
@@ -662,6 +675,8 @@
                 RemoteViews.OnClickHandler remoteViewClickHandler) {
             mEntry = entry;
             mRow = row;
+            mSmartReplyConstants = smartReplyConstants;
+            mSmartReplyController = smartReplyController;
             mInflateSynchronously = inflateSynchronously;
             mReInflateFlags = reInflateFlags;
             mRemoteViewCache = cache;
@@ -701,6 +716,7 @@
                         mUsesIncreasedHeadsUpHeight, packageContext);
                 return inflateSmartReplyViews(inflationProgress, mReInflateFlags, mEntry,
                         mRow.getContext(), packageContext, mRow.getHeadsUpManager(),
+                        mSmartReplyConstants, mSmartReplyController,
                         mRow.getExistingSmartRepliesAndActions());
             } catch (Exception e) {
                 mError = e;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
index a0af4ac..923c348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfo.java
@@ -33,11 +33,9 @@
 import android.app.NotificationChannelGroup;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.graphics.drawable.Icon;
@@ -61,6 +59,7 @@
 import android.widget.TextView;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
@@ -87,6 +86,7 @@
     private PackageManager mPm;
     private VisualStabilityManager mVisualStabilityManager;
     private ShadeController mShadeController;
+    private ConversationIconFactory mIconFactory;
 
     private String mPackageName;
     private String mAppName;
@@ -186,6 +186,7 @@
             OnSettingsClickListener onSettingsClick,
             OnAppSettingsClickListener onAppSettingsClick,
             OnSnoozeClickListener onSnoozeClickListener,
+            ConversationIconFactory conversationIconFactory,
             boolean isDeviceProvisioned) {
         mSelectedAction = -1;
         mINotificationManager = iNotificationManager;
@@ -203,6 +204,7 @@
         mIsDeviceProvisioned = isDeviceProvisioned;
         mOnSnoozeClickListener = onSnoozeClickListener;
         mShadeController = Dependency.get(ShadeController.class);
+        mIconFactory = conversationIconFactory;
 
         mShortcutManager = shortcutManager;
         mLauncherApps = launcherApps;
@@ -320,8 +322,8 @@
     private void bindIcon() {
         ImageView image = findViewById(R.id.conversation_icon);
         if (mShortcutInfo != null) {
-            image.setImageDrawable(mLauncherApps.getShortcutBadgedIconDrawable(mShortcutInfo,
-                    mContext.getResources().getDisplayMetrics().densityDpi));
+            image.setImageBitmap(mIconFactory.getConversationBitmap(
+                    mShortcutInfo, mPackageName, mAppUid));
         } else {
             if (mSbn.getNotification().extras.getBoolean(EXTRA_IS_GROUP_CONVERSATION, false)) {
                 // TODO: maybe use a generic group icon, or a composite of recent senders
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 352abcf..c01f6c4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -34,6 +34,7 @@
 import android.provider.Settings;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
+import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.view.HapticFeedbackConstants;
 import android.view.View;
@@ -42,8 +43,10 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
+import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -56,25 +59,21 @@
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
+import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
 import com.android.systemui.statusbar.notification.row.NotificationInfo.CheckSaveListener;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
-import com.android.systemui.statusbar.phone.ShadeController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 import dagger.Lazy;
 
 /**
  * Handles various NotificationGuts related tasks, such as binding guts to a row, opening and
  * closing guts, and keeping track of the currently exposed notification guts.
  */
-@Singleton
 public class NotificationGutsManager implements Dumpable, NotificationLifetimeExtender {
     private static final String TAG = "NotificationGutsManager";
 
@@ -111,7 +110,9 @@
     private final Handler mMainHandler;
     private Runnable mOpenRunnable;
 
-    @Inject
+    /**
+     * Injected constructor. See {@link NotificationsModule}.
+     */
     public NotificationGutsManager(Context context, VisualStabilityManager visualStabilityManager,
             Lazy<StatusBar> statusBarLazy, @Main Handler mainHandler,
             AccessibilityManager accessibilityManager,
@@ -388,6 +389,10 @@
                 notificationInfoView.closeControls(v, false);
             };
         }
+        ConversationIconFactory iconFactoryLoader = new ConversationIconFactory(mContext,
+                launcherApps, pmUser, IconDrawableFactory.newInstance(mContext),
+                mContext.getResources().getDimensionPixelSize(
+                        R.dimen.notification_guts_conversation_icon_size));
 
         notificationInfoView.bindNotification(
                 shortcutManager,
@@ -401,8 +406,8 @@
                 onSettingsClick,
                 onAppSettingsClick,
                 onSnoozeClickListener,
+                iconFactoryLoader,
                 mDeviceProvisionedController.isDeviceProvisioned());
-
     }
 
     /**
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 b2b46d5..2eeda1f 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
@@ -60,6 +60,7 @@
 import android.util.MathUtils;
 import android.util.Pair;
 import android.view.ContextThemeWrapper;
+import android.view.DisplayCutout;
 import android.view.InputDevice;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
@@ -524,6 +525,8 @@
     private float mLastSentExpandedHeight;
     private boolean mWillExpand;
 
+    private int mWaterfallTopInset;
+
     @Inject
     public NotificationStackScrollLayout(
             @Named(VIEW_CONTEXT) Context context,
@@ -1739,6 +1742,12 @@
     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
         mBottomInset = insets.getSystemWindowInsetBottom();
 
+        mWaterfallTopInset = 0;
+        final DisplayCutout cutout = insets.getDisplayCutout();
+        if (cutout != null) {
+            mWaterfallTopInset = cutout.getWaterfallInsets().top;
+        }
+
         if (ANCHOR_SCROLLING) {
             // TODO
         } else {
@@ -5335,7 +5344,9 @@
 
     @ShadeViewRefactor(RefactorComponent.COORDINATOR)
     public int getMinExpansionHeight() {
-        return mShelf.getIntrinsicHeight() - (mShelf.getIntrinsicHeight() - mStatusBarHeight) / 2;
+        return mShelf.getIntrinsicHeight()
+                - (mShelf.getIntrinsicHeight() - mStatusBarHeight + mWaterfallTopInset) / 2
+                + mWaterfallTopInset;
     }
 
     @ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
index 3165597..971f045 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideController.java
@@ -25,8 +25,7 @@
 
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
-
-import javax.inject.Inject;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 
 /** A controller to control all auto-hide things. */
 public class AutoHideController {
@@ -51,7 +50,9 @@
         }
     };
 
-    @Inject
+    /**
+     * Injected constructor. See {@link StatusBarPhoneModule}.
+     */
     public AutoHideController(Context context, @Main Handler handler,
             NotificationRemoteInputManager notificationRemoteInputManager,
             IWindowManager iWindowManager) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index b24942a..b46ca40 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -102,6 +102,9 @@
      */
     private int mCutoutSideNudge = 0;
 
+    private DisplayCutout mDisplayCutout;
+    private int mRoundedCornerPadding = 0;
+
     public KeyguardStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -159,9 +162,15 @@
                 getResources().getDimensionPixelSize(R.dimen.keyguard_carrier_text_margin));
         mCarrierLabel.setLayoutParams(lp);
 
-        lp = (MarginLayoutParams) getLayoutParams();
+        updateKeyguardStatusBarHeight();
+    }
+
+    private void updateKeyguardStatusBarHeight() {
+        final int waterfallTop =
+                mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
+        MarginLayoutParams lp =  (MarginLayoutParams) getLayoutParams();
         lp.height =  getResources().getDimensionPixelSize(
-                R.dimen.status_bar_header_height_keyguard);
+                R.dimen.status_bar_header_height_keyguard) + waterfallTop;
         setLayoutParams(lp);
     }
 
@@ -175,6 +184,8 @@
                 R.dimen.display_cutout_margin_consumption);
         mShowPercentAvailable = getContext().getResources().getBoolean(
                 com.android.internal.R.bool.config_battery_percentage_setting_available);
+        mRoundedCornerPadding = res.getDimensionPixelSize(
+                R.dimen.rounded_corner_content_padding);
     }
 
     private void updateVisibilities() {
@@ -225,23 +236,26 @@
     }
 
     private boolean updateLayoutConsideringCutout() {
-        DisplayCutout dc = getRootWindowInsets().getDisplayCutout();
+        mDisplayCutout = getRootWindowInsets().getDisplayCutout();
+        updateKeyguardStatusBarHeight();
+
         Pair<Integer, Integer> cornerCutoutMargins =
-                PhoneStatusBarView.cornerCutoutMargins(dc, getDisplay());
-        updateCornerCutoutPadding(cornerCutoutMargins);
-        if (dc == null || cornerCutoutMargins != null) {
+                StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
+        updatePadding(cornerCutoutMargins);
+        if (mDisplayCutout == null || cornerCutoutMargins != null) {
             return updateLayoutParamsNoCutout();
         } else {
-            return updateLayoutParamsForCutout(dc);
+            return updateLayoutParamsForCutout();
         }
     }
 
-    private void updateCornerCutoutPadding(Pair<Integer, Integer> cornerCutoutMargins) {
-        if (cornerCutoutMargins != null) {
-            setPadding(cornerCutoutMargins.first, 0, cornerCutoutMargins.second, 0);
-        } else {
-            setPadding(0, 0, 0, 0);
-        }
+    private void updatePadding(Pair<Integer, Integer> cornerCutoutMargins) {
+        final int waterfallTop =
+                mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
+        Pair<Integer, Integer> padding =
+                StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
+                        mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
+        setPadding(padding.first, waterfallTop, padding.second, 0);
     }
 
     private boolean updateLayoutParamsNoCutout() {
@@ -268,7 +282,7 @@
         return true;
     }
 
-    private boolean updateLayoutParamsForCutout(DisplayCutout dc) {
+    private boolean updateLayoutParamsForCutout() {
         if (mLayoutState == LAYOUT_CUTOUT) {
             return false;
         }
@@ -279,7 +293,7 @@
         }
 
         Rect bounds = new Rect();
-        boundsFromDirection(dc, Gravity.TOP, bounds);
+        boundsFromDirection(mDisplayCutout, Gravity.TOP, bounds);
 
         mCutoutSpace.setVisibility(View.VISIBLE);
         RelativeLayout.LayoutParams lp = (LayoutParams) mCutoutSpace.getLayoutParams();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index ba9ba6c..84aecd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -63,7 +63,6 @@
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.Dependency;
-import com.android.systemui.DockedStackExistsListener;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
 import com.android.systemui.assist.AssistHandleViewController;
@@ -75,6 +74,7 @@
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
 import com.android.systemui.shared.system.WindowManagerWrapper;
+import com.android.systemui.stackdivider.Divider;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NavigationBarController;
 import com.android.systemui.statusbar.policy.DeadZone;
@@ -770,7 +770,14 @@
 
     public void updatePanelSystemUiStateFlags() {
         int displayId = mContext.getDisplayId();
+        if (SysUiState.DEBUG) {
+            Log.d(TAG, "Updating panel sysui state flags: panelView=" + mPanelView);
+        }
         if (mPanelView != null) {
+            if (SysUiState.DEBUG) {
+                Log.d(TAG, "Updating panel sysui state flags: fullyExpanded="
+                        + mPanelView.isFullyExpanded() + " inQs=" + mPanelView.isInSettings());
+            }
             mSysUiFlagContainer.setFlag(SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED,
                     mPanelView.isFullyExpanded() && !mPanelView.isInSettings())
                     .setFlag(SYSUI_STATE_QUICK_SETTINGS_EXPANDED,
@@ -862,7 +869,8 @@
 
         getImeSwitchButton().setOnClickListener(mImeSwitcherClickListener);
 
-        DockedStackExistsListener.register(mDockedListener);
+        Divider divider = Dependency.get(Divider.class);
+        divider.registerInSplitScreenListener(mDockedListener);
         updateOrientationViews();
         reloadNavIcons();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
index 826af66..67e7f23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationModeController.java
@@ -73,7 +73,7 @@
 public class NavigationModeController implements Dumpable {
 
     private static final String TAG = NavigationModeController.class.getSimpleName();
-    private static final boolean DEBUG = false;
+    private static final boolean DEBUG = true;
 
     public interface ModeChangedListener {
         void onNavigationModeChanged(int mode);
@@ -248,8 +248,7 @@
                     Secure.NAVIGATION_MODE, String.valueOf(mode));
         });
         if (DEBUG) {
-            Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode
-                    + " contextUser=" + mCurrentUserContext.getUserId());
+            Log.e(TAG, "updateCurrentInteractionMode: mode=" + mMode);
             dumpAssetPaths(mCurrentUserContext);
         }
 
@@ -293,6 +292,7 @@
                     0 /* flags */, UserHandle.of(userId));
         } catch (PackageManager.NameNotFoundException e) {
             // Never happens for the sysui package
+            Log.e(TAG, "Failed to create package context", e);
             return null;
         }
     }
@@ -404,11 +404,17 @@
             defaultOverlays = "failed_to_fetch";
         }
         pw.println("  defaultOverlays=" + defaultOverlays);
+        pw.println("  restoreGesturalNavMode:");
+        for (int i = 0; i < mRestoreGesturalNavBarMode.size(); i++) {
+            pw.println("    userId=" + mRestoreGesturalNavBarMode.keyAt(i)
+                    + " shouldRestore=" + mRestoreGesturalNavBarMode.valueAt(i));
+        }
         dumpAssetPaths(mCurrentUserContext);
     }
 
     private void dumpAssetPaths(Context context) {
-        Log.d(TAG, "assetPaths=");
+        Log.d(TAG, "  contextUser=" + mCurrentUserContext.getUserId());
+        Log.d(TAG, "  assetPaths=");
         ApkAssets[] assets = context.getResources().getAssets().getApkAssets();
         for (ApkAssets a : assets) {
             Log.d(TAG, "    " + a.getAssetPath());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
index bdca9a4..d709e02 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationGroupAlertTransferHelper.java
@@ -36,21 +36,18 @@
 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.NotificationGroup;
 import com.android.systemui.statusbar.phone.NotificationGroupManager.OnGroupChangeListener;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 
 import java.util.ArrayList;
 import java.util.Objects;
 
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
 /**
  * A helper class dealing with the alert interactions between {@link NotificationGroupManager} and
  * {@link HeadsUpManager}. In particular, this class deals with keeping
  * the correct notification in a group alerting based off the group suppression.
  */
-@Singleton
 public class NotificationGroupAlertTransferHelper implements OnHeadsUpChangedListener,
         StateListener {
 
@@ -76,7 +73,9 @@
 
     private boolean mIsDozing;
 
-    @Inject
+    /**
+     * Injected constructor. See {@link StatusBarPhoneModule}.
+     */
     public NotificationGroupAlertTransferHelper(RowContentBindStage bindStage) {
         Dependency.get(StatusBarStateController.class).addCallback(this);
         mRowContentBindStage = bindStage;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index 7650a3a..bc80a1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -88,33 +88,11 @@
             boolean paddingChanged = insets.top != getPaddingTop()
                     || insets.bottom != getPaddingBottom();
 
-            int rightCutout = 0;
-            int leftCutout = 0;
-            DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
-            if (displayCutout != null) {
-                leftCutout = displayCutout.getSafeInsetLeft();
-                rightCutout = displayCutout.getSafeInsetRight();
-            }
-
-            int targetLeft = Math.max(insets.left, leftCutout);
-            int targetRight = Math.max(insets.right, rightCutout);
-
-            // Super-special right inset handling, because scrims and backdrop need to ignore it.
-            if (targetRight != mRightInset || targetLeft != mLeftInset) {
-                mRightInset = targetRight;
-                mLeftInset = targetLeft;
-                applyMargins();
-            }
             // Drop top inset, and pass through bottom inset.
             if (paddingChanged) {
                 setPadding(0, 0, 0, 0);
             }
         } else {
-            if (mRightInset != 0 || mLeftInset != 0) {
-                mRightInset = 0;
-                mLeftInset = 0;
-                applyMargins();
-            }
             boolean changed = getPaddingLeft() != 0
                     || getPaddingRight() != 0
                     || getPaddingTop() != 0
@@ -123,6 +101,17 @@
                 setPadding(0, 0, 0, 0);
             }
         }
+
+        mLeftInset = 0;
+        mRightInset = 0;
+        DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+        if (displayCutout != null) {
+            mLeftInset = displayCutout.getSafeInsetLeft();
+            mRightInset = displayCutout.getSafeInsetRight();
+        }
+        mLeftInset = Math.max(insets.left, mLeftInset);
+        mRightInset = Math.max(insets.right, mRightInset);
+        applyMargins();
         return windowInsets;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 41d8968..260f94c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -43,6 +43,7 @@
 import com.android.systemui.dagger.qualifiers.UiBackground;
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.RotationLockTile;
+import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.statusbar.policy.CastController;
@@ -76,7 +77,8 @@
                 ZenModeController.Callback,
                 DeviceProvisionedListener,
                 KeyguardStateController.Callback,
-                LocationController.LocationChangeCallback {
+                LocationController.LocationChangeCallback,
+                RecordingController.RecordingStateChangeCallback {
     private static final String TAG = "PhoneStatusBarPolicy";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -98,6 +100,7 @@
     private final String mSlotMicrophone;
     private final String mSlotCamera;
     private final String mSlotSensorsOff;
+    private final String mSlotScreenRecord;
 
     private final Context mContext;
     private final Handler mHandler = new Handler();
@@ -116,6 +119,7 @@
     private final LocationController mLocationController;
     private final Executor mUiBgExecutor;
     private final SensorPrivacyController mSensorPrivacyController;
+    private final RecordingController mRecordingController;
 
     // Assume it's all good unless we hear otherwise.  We don't always seem
     // to get broadcasts that it *is* there.
@@ -149,6 +153,7 @@
         mKeyguardStateController = Dependency.get(KeyguardStateController.class);
         mLocationController = Dependency.get(LocationController.class);
         mSensorPrivacyController = Dependency.get(SensorPrivacyController.class);
+        mRecordingController = Dependency.get(RecordingController.class);
         mUiBgExecutor = uiBgExecutor;
 
         mSlotCast = context.getString(com.android.internal.R.string.status_bar_cast);
@@ -167,6 +172,8 @@
         mSlotMicrophone = context.getString(com.android.internal.R.string.status_bar_microphone);
         mSlotCamera = context.getString(com.android.internal.R.string.status_bar_camera);
         mSlotSensorsOff = context.getString(com.android.internal.R.string.status_bar_sensors_off);
+        mSlotScreenRecord = context.getString(
+                com.android.internal.R.string.status_bar_screen_record);
 
         // listen for broadcasts
         IntentFilter filter = new IntentFilter();
@@ -235,6 +242,10 @@
         mIconController.setIconVisibility(mSlotSensorsOff,
                 mSensorPrivacyController.isSensorPrivacyEnabled());
 
+        // screen record
+        mIconController.setIcon(mSlotScreenRecord, R.drawable.stat_sys_screen_record, null);
+        mIconController.setIconVisibility(mSlotScreenRecord, false);
+
         mRotationLockController.addCallback(this);
         mBluetooth.addCallback(this);
         mProvisionedController.addCallback(this);
@@ -246,6 +257,7 @@
         mKeyguardStateController.addCallback(this);
         mSensorPrivacyController.addCallback(mSensorPrivacyListener);
         mLocationController.addCallback(this);
+        mRecordingController.addCallback(this);
 
         commandQueue.addCallback(this);
     }
@@ -438,7 +450,7 @@
         }
         if (DEBUG) Log.v(TAG, "updateCast: isCasting: " + isCasting);
         mHandler.removeCallbacks(mRemoveCastIconRunnable);
-        if (isCasting) {
+        if (isCasting && !mRecordingController.isRecording()) { // screen record has its own icon
             mIconController.setIcon(mSlotCast, R.drawable.stat_sys_cast,
                     mContext.getString(R.string.accessibility_casting));
             mIconController.setIconVisibility(mSlotCast, true);
@@ -643,4 +655,40 @@
             mIconController.setIconVisibility(mSlotCast, false);
         }
     };
+
+    // Screen Recording
+    @Override
+    public void onCountdown(long millisUntilFinished) {
+        if (DEBUG) Log.d(TAG, "screenrecord: countdown " + millisUntilFinished);
+        int countdown = (int) Math.floorDiv(millisUntilFinished + 500, 1000);
+        int resourceId = R.drawable.stat_sys_screen_record;
+        switch (countdown) {
+            case 1:
+                resourceId = R.drawable.stat_sys_screen_record_1;
+                break;
+            case 2:
+                resourceId = R.drawable.stat_sys_screen_record_2;
+                break;
+            case 3:
+                resourceId = R.drawable.stat_sys_screen_record_3;
+                break;
+        }
+        mIconController.setIcon(mSlotScreenRecord, resourceId, null);
+        mIconController.setIconVisibility(mSlotScreenRecord, true);
+    }
+
+    @Override
+    public void onRecordingStart() {
+        if (DEBUG) Log.d(TAG, "screenrecord: showing icon");
+        mIconController.setIcon(mSlotScreenRecord,
+                R.drawable.stat_sys_screen_record, null);
+        mIconController.setIconVisibility(mSlotScreenRecord, true);
+    }
+
+    @Override
+    public void onRecordingEnd() {
+        // Ensure this is on the main thread, since it could be called during countdown
+        if (DEBUG) Log.d(TAG, "screenrecord: hiding icon");
+        mHandler.post(() -> mIconController.setIconVisibility(mSlotScreenRecord, false));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index f3b0a79..156a7e5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -23,12 +23,10 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.util.AttributeSet;
 import android.util.EventLog;
 import android.util.Pair;
-import android.view.Display;
 import android.view.DisplayCutout;
 import android.view.Gravity;
 import android.view.MotionEvent;
@@ -36,7 +34,6 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
-import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
 import com.android.systemui.Dependency;
@@ -84,6 +81,8 @@
     private int mCutoutSideNudge = 0;
     private boolean mHeadsUpVisible;
 
+    private int mRoundedCornerPadding = 0;
+
     public PhoneStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
 
@@ -294,15 +293,25 @@
     public void updateResources() {
         mCutoutSideNudge = getResources().getDimensionPixelSize(
                 R.dimen.display_cutout_margin_consumption);
+        mRoundedCornerPadding = getResources().getDimensionPixelSize(
+                R.dimen.rounded_corner_content_padding);
 
+        updateStatusBarHeight();
+    }
+
+    private void updateStatusBarHeight() {
+        final int waterfallTopInset =
+                mDisplayCutout == null ? 0 : mDisplayCutout.getWaterfallInsets().top;
         ViewGroup.LayoutParams layoutParams = getLayoutParams();
-        layoutParams.height = getResources().getDimensionPixelSize(R.dimen.status_bar_height);
+        layoutParams.height =
+                getResources().getDimensionPixelSize(R.dimen.status_bar_height) - waterfallTopInset;
         setLayoutParams(layoutParams);
     }
 
     private void updateLayoutForCutout() {
-        Pair<Integer, Integer> cornerCutoutMargins = cornerCutoutMargins(mDisplayCutout,
-                getDisplay());
+        updateStatusBarHeight();
+        Pair<Integer, Integer> cornerCutoutMargins =
+                StatusBarWindowView.cornerCutoutMargins(mDisplayCutout, getDisplay());
         updateCutoutLocation(cornerCutoutMargins);
         updateSafeInsets(cornerCutoutMargins);
     }
@@ -337,47 +346,11 @@
         // Depending on our rotation, we may have to work around a cutout in the middle of the view,
         // or letterboxing from the right or left sides.
 
-        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
-        if (mDisplayCutout == null || mDisplayCutout.isEmpty()
-                || mLastOrientation != ORIENTATION_PORTRAIT || cornerCutoutMargins == null) {
-            lp.leftMargin = 0;
-            lp.rightMargin = 0;
-            return;
-        }
+        Pair<Integer, Integer> padding =
+                StatusBarWindowView.paddingNeededForCutoutAndRoundedCorner(
+                        mDisplayCutout, cornerCutoutMargins, mRoundedCornerPadding);
 
-        lp.leftMargin = Math.max(lp.leftMargin, cornerCutoutMargins.first);
-        lp.rightMargin = Math.max(lp.rightMargin, cornerCutoutMargins.second);
-
-        // If we're already inset enough (e.g. on the status bar side), we can have 0 margin
-        WindowInsets insets = getRootWindowInsets();
-        int leftInset = insets.getSystemWindowInsetLeft();
-        int rightInset = insets.getSystemWindowInsetRight();
-        if (lp.leftMargin <= leftInset) {
-            lp.leftMargin = 0;
-        }
-        if (lp.rightMargin <= rightInset) {
-            lp.rightMargin = 0;
-        }
-    }
-
-    public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
-            Display display) {
-        if (cutout == null) {
-            return null;
-        }
-        Point size = new Point();
-        display.getRealSize(size);
-
-        Rect bounds = new Rect();
-        boundsFromDirection(cutout, Gravity.TOP, bounds);
-
-        if (bounds.left <= 0) {
-            return new Pair<>(bounds.right, 0);
-        }
-        if (bounds.right >= size.x) {
-            return new Pair<>(0, size.x - bounds.left);
-        }
-        return null;
+        setPadding(padding.first, getPaddingTop(), padding.second, getPaddingBottom());
     }
 
     public void setHeadsUpVisible(boolean headsUpVisible) {
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 823adff..6298afe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -163,7 +163,6 @@
 import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.stackdivider.Divider;
-import com.android.systemui.stackdivider.WindowManagerProxy;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.CrossFadeHelper;
@@ -199,6 +198,7 @@
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.dagger.StatusBarPhoneModule;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BatteryController.BatteryStateChangeCallback;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
@@ -597,7 +597,7 @@
      * Public constructor for StatusBar.
      *
      * StatusBar is considered optional, and therefore can not be marked as @Inject directly.
-     * Instead, an @Provide method is included.
+     * Instead, an @Provide method is included. See {@link StatusBarPhoneModule}.
      */
     @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
     public StatusBar(
@@ -1409,8 +1409,11 @@
         if (!mRecentsOptional.isPresent()) {
             return false;
         }
-        int dockSide = WindowManagerProxy.getInstance().getDockSide();
-        if (dockSide == WindowManager.DOCKED_INVALID) {
+        Divider divider = null;
+        if (mDividerOptional.isPresent()) {
+            divider = mDividerOptional.get();
+        }
+        if (divider == null || !divider.inSplitMode()) {
             final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
             if (navbarPos == NAV_BAR_POS_INVALID) {
                 return false;
@@ -1420,16 +1423,13 @@
                     : SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
             return mRecentsOptional.get().splitPrimaryTask(createMode, null, metricsDockAction);
         } else {
-            if (mDividerOptional.isPresent()) {
-                Divider divider = mDividerOptional.get();
-                if (divider.isMinimized() && !divider.isHomeStackResizable()) {
-                    // Undocking from the minimized state is not supported
-                    return false;
-                } else {
-                    divider.onUndockingTask();
-                    if (metricsUndockAction != -1) {
-                        mMetricsLogger.action(metricsUndockAction);
-                    }
+            if (divider.isMinimized() && !divider.isHomeStackResizable()) {
+                // Undocking from the minimized state is not supported
+                return false;
+            } else {
+                divider.onUndockingTask();
+                if (metricsUndockAction != -1) {
+                    mMetricsLogger.action(metricsUndockAction);
                 }
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index da5df6a..22bf513 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -16,8 +16,21 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.view.WindowInsets.Type.systemBars;
+
+import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
+
 import android.content.Context;
+import android.graphics.Insets;
+import android.graphics.Point;
+import android.graphics.Rect;
 import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.Display;
+import android.view.DisplayCutout;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowInsets;
 import android.widget.FrameLayout;
 
 /**
@@ -28,7 +41,118 @@
     public static final String TAG = "PhoneStatusBarWindowView";
     public static final boolean DEBUG = StatusBar.DEBUG;
 
+    private int mLeftInset = 0;
+    private int mRightInset = 0;
+    private int mTopInset = 0;
+
     public StatusBarWindowView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
+
+    @Override
+    public WindowInsets onApplyWindowInsets(WindowInsets windowInsets) {
+        final Insets insets = windowInsets.getInsetsIgnoringVisibility(systemBars());
+        mLeftInset = 0;
+        mRightInset = 0;
+        mTopInset = 0;
+        DisplayCutout displayCutout = getRootWindowInsets().getDisplayCutout();
+        if (displayCutout != null) {
+            mTopInset = displayCutout.getWaterfallInsets().top;
+            mLeftInset = displayCutout.getSafeInsetLeft();
+            mRightInset = displayCutout.getSafeInsetRight();
+        }
+        mLeftInset = Math.max(insets.left, mLeftInset);
+        mRightInset = Math.max(insets.right, mRightInset);
+        applyMargins();
+        return windowInsets;
+    }
+
+    private void applyMargins() {
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            View child = getChildAt(i);
+            if (child.getLayoutParams() instanceof LayoutParams) {
+                LayoutParams lp = (LayoutParams) child.getLayoutParams();
+                if (lp.rightMargin != mRightInset || lp.leftMargin != mLeftInset
+                        || lp.topMargin != mTopInset) {
+                    lp.rightMargin = mRightInset;
+                    lp.leftMargin = mLeftInset;
+                    lp.topMargin = mTopInset;
+                    child.requestLayout();
+                }
+            }
+        }
+    }
+
+    /**
+     * Compute the padding needed for status bar related views, e.g., PhoneStatusBar,
+     * QuickStatusBarHeader and KeyguardStatusBarView).
+     *
+     * @param cutout
+     * @param cornerCutoutPadding
+     * @param roundedCornerContentPadding
+     * @return
+     */
+    public static Pair<Integer, Integer> paddingNeededForCutoutAndRoundedCorner(
+            DisplayCutout cutout, Pair<Integer, Integer> cornerCutoutPadding,
+            int roundedCornerContentPadding) {
+        if (cutout == null) {
+            return new Pair<>(roundedCornerContentPadding, roundedCornerContentPadding);
+        }
+
+        // compute the padding needed for corner cutout.
+        final int leftMargin = cutout.getSafeInsetLeft();
+        final int rightMargin = cutout.getSafeInsetRight();
+        int leftCornerCutoutPadding = 0;
+        int rightCornerCutoutPadding = 0;
+        if (cornerCutoutPadding != null) {
+            if (cornerCutoutPadding.first > leftMargin) {
+                leftCornerCutoutPadding = cornerCutoutPadding.first - leftMargin;
+            }
+            if (cornerCutoutPadding.second > rightMargin) {
+                rightCornerCutoutPadding = cornerCutoutPadding.second - rightMargin;
+            }
+        }
+
+        // compute the padding needed for rounded corner
+        int leftRoundedCornerPadding = 0;
+        int rightRoundedCornerPadding = 0;
+        if (roundedCornerContentPadding > leftMargin) {
+            leftRoundedCornerPadding = roundedCornerContentPadding - leftMargin;
+        }
+        if (roundedCornerContentPadding > rightMargin) {
+            rightRoundedCornerPadding = roundedCornerContentPadding - rightMargin;
+        }
+
+        return new Pair<>(
+                Math.max(leftCornerCutoutPadding, leftRoundedCornerPadding),
+                Math.max(rightCornerCutoutPadding, rightRoundedCornerPadding));
+    }
+
+    /**
+     * Compute the corner cutout margins
+     *
+     * @param cutout
+     * @param display
+     * @return
+     */
+    public static Pair<Integer, Integer> cornerCutoutMargins(DisplayCutout cutout,
+            Display display) {
+        if (cutout == null) {
+            return null;
+        }
+        Point size = new Point();
+        display.getRealSize(size);
+
+        Rect bounds = new Rect();
+        boundsFromDirection(cutout, Gravity.TOP, bounds);
+
+        if (bounds.left <= 0) {
+            return new Pair<>(bounds.right, 0);
+        }
+        if (bounds.right >= size.x) {
+            return new Pair<>(0, size.x - bounds.left);
+        }
+        return null;
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java
new file mode 100644
index 0000000..fcf698c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneDependenciesModule.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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.dagger;
+
+import android.content.Context;
+import android.os.Handler;
+import android.view.IWindowManager;
+
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.notification.row.RowContentBindStage;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
+import com.android.systemui.statusbar.phone.StatusBar;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * This module provides instances needed to construct {@link StatusBar}. These are moved to this
+ * separate from {@link StatusBarPhoneModule} module so that components that wish to build their own
+ * version of StatusBar can include just dependencies, without injecting StatusBar itself.
+ */
+@Module
+public interface StatusBarPhoneDependenciesModule {
+    /** */
+    @Singleton
+    @Provides
+    static AutoHideController newAutoHideController(Context context,
+            @Main Handler handler,
+            NotificationRemoteInputManager notificationRemoteInputManager,
+            IWindowManager iWindowManager) {
+        return new AutoHideController(context, handler, notificationRemoteInputManager,
+                iWindowManager);
+    }
+
+    /** */
+    @Singleton
+    @Provides
+    static NotificationGroupAlertTransferHelper provideNotificationGroupAlertTransferHelper(
+            RowContentBindStage bindStage) {
+        return new NotificationGroupAlertTransferHelper(bindStage);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
rename to packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 15a0e08..26459a9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone;
+package com.android.systemui.statusbar.phone.dagger;
 
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
 
@@ -52,7 +52,6 @@
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationViewHierarchyManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.StatusBarDependenciesModule;
 import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.VibratorHelper;
@@ -65,7 +64,27 @@
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
-import com.android.systemui.statusbar.phone.dagger.StatusBarComponent;
+import com.android.systemui.statusbar.phone.AutoHideController;
+import com.android.systemui.statusbar.phone.BiometricUnlockController;
+import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.phone.DozeScrimController;
+import com.android.systemui.statusbar.phone.DozeServiceHost;
+import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
+import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
+import com.android.systemui.statusbar.phone.KeyguardLiftController;
+import com.android.systemui.statusbar.phone.LightBarController;
+import com.android.systemui.statusbar.phone.LightsOutNotifController;
+import com.android.systemui.statusbar.phone.LockscreenLockIconController;
+import com.android.systemui.statusbar.phone.LockscreenWallpaper;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarIconController;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -91,8 +110,8 @@
 /**
  * Dagger Module providing {@link StatusBar}.
  */
-@Module(includes = {StatusBarDependenciesModule.class})
-public class StatusBarModule {
+@Module(includes = {StatusBarPhoneDependenciesModule.class})
+public interface StatusBarPhoneModule {
     /**
      * Provides our instance of StatusBar which is considered optional.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
index 70bcc214..0487ce6 100644
--- a/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
@@ -98,6 +98,14 @@
     private val allContentBounds: MutableMap<FloatingContent, Rect> = HashMap()
 
     /**
+     * Whether we are currently resolving conflicts by asking content to move. If we are, we'll
+     * temporarily ignore calls to [onContentMoved] - those calls are from the content that is
+     * moving to new, conflict-free bounds, so we don't need to perform conflict detection
+     * calculations in response.
+     */
+    private var currentlyResolvingConflicts = false
+
+    /**
      * Makes the coordinator aware of a new piece of floating content, and moves any existing
      * content out of the way, if necessary.
      *
@@ -126,6 +134,13 @@
      */
     @JvmOverloads
     fun onContentMoved(content: FloatingContent) {
+
+        // Ignore calls when we are currently resolving conflicts, since those calls are from
+        // content that is moving to new, conflict-free bounds.
+        if (currentlyResolvingConflicts) {
+            return
+        }
+
         if (!allContentBounds.containsKey(content)) {
             Log.wtf(TAG, "Received onContentMoved call before onContentAdded! " +
                     "This should never happen.")
@@ -162,6 +177,8 @@
      * them to move out of the way.
      */
     private fun maybeMoveConflictingContent(fromContent: FloatingContent) {
+        currentlyResolvingConflicts = true
+
         val conflictingNewBounds = allContentBounds[fromContent]!!
         allContentBounds
                 // Filter to content that intersects with the new bounds. That's content that needs
@@ -182,6 +199,8 @@
                                             .minus(conflictingNewBounds)))
                     allContentBounds[content] = content.getFloatingBoundsOnScreen()
                 }
+
+        currentlyResolvingConflicts = false
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
index 7dad05d..9227cf0 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayImeController.java
@@ -48,8 +48,8 @@
 public class DisplayImeController implements DisplayController.OnDisplaysChangedListener {
     private static final String TAG = "DisplayImeController";
 
-    static final int ANIMATION_DURATION_SHOW_MS = 275;
-    static final int ANIMATION_DURATION_HIDE_MS = 340;
+    public static final int ANIMATION_DURATION_SHOW_MS = 275;
+    public static final int ANIMATION_DURATION_HIDE_MS = 340;
     static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
     private static final int DIRECTION_NONE = 0;
     private static final int DIRECTION_SHOW = 1;
diff --git a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
index 64b0b66..4652abf 100644
--- a/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java
@@ -35,6 +35,7 @@
 import android.graphics.Rect;
 import android.os.SystemProperties;
 import android.provider.Settings;
+import android.util.DisplayMetrics;
 import android.util.RotationUtils;
 import android.util.Size;
 import android.view.Display;
@@ -191,6 +192,11 @@
         return mDensityDpi;
     }
 
+    /** Get the density scale for the display. */
+    public float density() {
+        return mDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+    }
+
     /** Get whether this layout is landscape. */
     public boolean isLandscape() {
         return mWidth > mHeight;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 5706bee..79188ce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -17,25 +17,18 @@
 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;
 
 import static org.hamcrest.Matchers.is;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertThat;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.Fragment;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Handler;
@@ -43,21 +36,12 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.Display;
-import android.view.View;
 import android.view.WindowManager;
 
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.R.dimen;
-import com.android.systemui.ScreenDecorations.TunablePaddingTagListener;
 import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.fragments.FragmentHostManager;
-import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.statusbar.phone.NotificationShadeWindowView;
-import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.phone.StatusBarWindowView;
-import com.android.systemui.tuner.TunablePadding;
-import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
 import com.android.systemui.tuner.TunerService;
 
 import org.junit.Before;
@@ -68,8 +52,6 @@
 
 import java.util.Collections;
 
-import dagger.Lazy;
-
 @RunWithLooper
 @RunWith(AndroidTestingRunner.class)
 @SmallTest
@@ -77,19 +59,12 @@
 
     private TestableLooper mTestableLooper;
     private ScreenDecorations mScreenDecorations;
-    @Mock private StatusBar mStatusBar;
     private WindowManager mWindowManager;
-    private FragmentService mFragmentService;
-    private FragmentHostManager mFragmentHostManager;
-    private NotificationShadeWindowView mView;
-    private StatusBarWindowView mStatusBarWindowView;
-    private TunablePaddingService mTunablePaddingService;
     private Handler mMainHandler;
     @Mock
     private TunerService mTunerService;
     @Mock
     private BroadcastDispatcher mBroadcastDispatcher;
-    @Mock private Lazy<StatusBar> mStatusBarLazy;
 
     @Before
     public void setup() {
@@ -97,25 +72,14 @@
 
         mTestableLooper = TestableLooper.get(this);
         mMainHandler = new Handler(mTestableLooper.getLooper());
-        mTunablePaddingService = mDependency.injectMockDependency(TunablePaddingService.class);
-        mFragmentService = mDependency.injectMockDependency(FragmentService.class);
 
         mWindowManager = mock(WindowManager.class);
-        mView = spy(new NotificationShadeWindowView(mContext, null));
-        mStatusBarWindowView = spy(new StatusBarWindowView(mContext, null));
-        when(mStatusBarLazy.get()).thenReturn(mStatusBar);
-        when(mStatusBar.getNotificationShadeWindowView()).thenReturn(mView);
-        when(mStatusBar.getStatusBarWindow()).thenReturn(mStatusBarWindowView);
 
         Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
         when(mWindowManager.getDefaultDisplay()).thenReturn(display);
         mContext.addMockSystemService(WindowManager.class, mWindowManager);
 
-        mFragmentHostManager = mock(FragmentHostManager.class);
-        when(mFragmentService.getFragmentHostManager(any())).thenReturn(mFragmentHostManager);
-
-
-        mScreenDecorations = new ScreenDecorations(mContext, mStatusBarLazy, mMainHandler,
+        mScreenDecorations = new ScreenDecorations(mContext, mMainHandler,
                 mBroadcastDispatcher, mTunerService) {
             @Override
             public void start() {
@@ -159,8 +123,6 @@
         mScreenDecorations.start();
         // No views added.
         verify(mWindowManager, never()).addView(any(), any());
-        // No Fragments watched.
-        verify(mFragmentHostManager, never()).addTagListener(any(), any());
         // No Tuners tuned.
         verify(mTunerService, never()).addTunable(any(), any());
     }
@@ -178,12 +140,8 @@
         // Add 2 windows for rounded corners (top and bottom).
         verify(mWindowManager, times(2)).addView(any(), any());
 
-        // Add 2 tag listeners for each of the fragments that are needed.
-        verify(mFragmentHostManager, times(2)).addTagListener(any(), any());
         // One tunable.
         verify(mTunerService, times(1)).addTunable(any(), any());
-        // One TunablePadding.
-        verify(mTunablePaddingService, times(1)).add(any(), anyString(), anyInt(), anyInt());
     }
 
     @Test
@@ -227,29 +185,6 @@
     }
 
     @Test
-    public void testPaddingTagListener() {
-        TunablePaddingTagListener tagListener = new TunablePaddingTagListener(14, 5);
-        View v = mock(View.class);
-        View child = mock(View.class);
-        Fragment f = mock(Fragment.class);
-        TunablePadding padding = mock(TunablePadding.class);
-
-        when(mTunablePaddingService.add(any(), anyString(), anyInt(), anyInt()))
-                .thenReturn(padding);
-        when(f.getView()).thenReturn(v);
-        when(v.findViewById(5)).thenReturn(child);
-
-        // Trigger callback and verify we get a TunablePadding created.
-        tagListener.onFragmentViewCreated(null, f);
-        verify(mTunablePaddingService).add(eq(child), eq(ScreenDecorations.PADDING), eq(14),
-                eq(FLAG_START | FLAG_END));
-
-        // Call again and verify destroy is called.
-        tagListener.onFragmentViewCreated(null, f);
-        verify(padding).destroy();
-    }
-
-    @Test
     public void testUpdateRoundedCorners() {
         mContext.getOrCreateTestableResources().addOverride(
                 com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout, false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 7d47f6b..fc79fcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -44,7 +44,6 @@
 import android.app.IActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.face.FaceManager;
 import android.service.notification.ZenModeConfig;
@@ -859,42 +858,6 @@
         verify(mNotificationGroupManager, times(1)).onEntryRemoved(groupSummary.getEntry());
     }
 
-    static class TestableBubbleController extends BubbleController {
-        // Let's assume surfaces can be synchronized immediately.
-        TestableBubbleController(Context context,
-                NotificationShadeWindowController notificationShadeWindowController,
-                StatusBarStateController statusBarStateController,
-                ShadeController shadeController,
-                BubbleData data,
-                ConfigurationController configurationController,
-                NotificationInterruptionStateProvider interruptionStateProvider,
-                ZenModeController zenModeController,
-                NotificationLockscreenUserManager lockscreenUserManager,
-                NotificationGroupManager groupManager,
-                NotificationEntryManager entryManager,
-                NotifPipeline notifPipeline,
-                FeatureFlags featureFlags,
-                DumpController dumpController) {
-            super(context,
-                    notificationShadeWindowController, statusBarStateController, shadeController,
-                    data, Runnable::run, configurationController, interruptionStateProvider,
-                    zenModeController, lockscreenUserManager, groupManager, entryManager,
-                     notifPipeline, featureFlags, dumpController);
-            setInflateSynchronously(true);
-        }
-    }
-
-    static class TestableNotificationInterruptionStateProvider extends
-            NotificationInterruptionStateProvider {
-
-        TestableNotificationInterruptionStateProvider(Context context,
-                NotificationFilter filter, StatusBarStateController controller,
-                BatteryController batteryController) {
-            super(context, filter, controller, batteryController);
-            mUseHeadsUp = true;
-        }
-    }
-
     /**
      * Sets the bubble metadata flags for this entry. These ]flags are normally set by
      * NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 5a1bef9..24f8a7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -40,7 +40,6 @@
 import android.app.IActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
-import android.content.Context;
 import android.content.res.Resources;
 import android.hardware.face.FaceManager;
 import android.service.notification.ZenModeConfig;
@@ -788,42 +787,6 @@
                 groupSummary.getEntry()));
     }
 
-    static class TestableBubbleController extends BubbleController {
-        // Let's assume surfaces can be synchronized immediately.
-        TestableBubbleController(Context context,
-                NotificationShadeWindowController notificationShadeWindowController,
-                StatusBarStateController statusBarStateController,
-                ShadeController shadeController,
-                BubbleData data,
-                ConfigurationController configurationController,
-                NotificationInterruptionStateProvider interruptionStateProvider,
-                ZenModeController zenModeController,
-                NotificationLockscreenUserManager lockscreenUserManager,
-                NotificationGroupManager groupManager,
-                NotificationEntryManager entryManager,
-                NotifPipeline notifPipeline,
-                FeatureFlags featureFlags,
-                DumpController dumpController) {
-            super(context,
-                    notificationShadeWindowController, statusBarStateController, shadeController,
-                    data, Runnable::run, configurationController, interruptionStateProvider,
-                    zenModeController, lockscreenUserManager, groupManager, entryManager,
-                    notifPipeline, featureFlags, dumpController);
-            setInflateSynchronously(true);
-        }
-    }
-
-    static class TestableNotificationInterruptionStateProvider extends
-            NotificationInterruptionStateProvider {
-
-        TestableNotificationInterruptionStateProvider(Context context,
-                NotificationFilter filter, StatusBarStateController controller,
-                BatteryController batteryController) {
-            super(context, filter, controller, batteryController);
-            mUseHeadsUp = true;
-        }
-    }
-
     /**
      * Sets the bubble metadata flags for this entry. These flags are normally set by
      * NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
new file mode 100644
index 0000000..338abf5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableBubbleController.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 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.bubbles;
+
+import android.content.Context;
+
+import com.android.systemui.DumpController;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.FeatureFlags;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
+import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.ZenModeController;
+
+/**
+ * Testable BubbleController subclass that immediately synchronizes surfaces.
+ */
+public class TestableBubbleController extends BubbleController {
+
+    // Let's assume surfaces can be synchronized immediately.
+    TestableBubbleController(Context context,
+            NotificationShadeWindowController notificationShadeWindowController,
+            StatusBarStateController statusBarStateController,
+            ShadeController shadeController,
+            BubbleData data,
+            ConfigurationController configurationController,
+            NotificationInterruptionStateProvider interruptionStateProvider,
+            ZenModeController zenModeController,
+            NotificationLockscreenUserManager lockscreenUserManager,
+            NotificationGroupManager groupManager,
+            NotificationEntryManager entryManager,
+            NotifPipeline notifPipeline,
+            FeatureFlags featureFlags,
+            DumpController dumpController) {
+        super(context,
+                notificationShadeWindowController, statusBarStateController, shadeController,
+                data, Runnable::run, configurationController, interruptionStateProvider,
+                zenModeController, lockscreenUserManager, groupManager, entryManager,
+                notifPipeline, featureFlags, dumpController);
+        setInflateSynchronously(true);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
new file mode 100644
index 0000000..5d192b2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/TestableNotificationInterruptionStateProvider.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 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.bubbles;
+
+import android.content.Context;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationFilter;
+import com.android.systemui.statusbar.notification.NotificationInterruptionStateProvider;
+import com.android.systemui.statusbar.policy.BatteryController;
+
+public class TestableNotificationInterruptionStateProvider
+        extends NotificationInterruptionStateProvider {
+
+    TestableNotificationInterruptionStateProvider(Context context,
+            NotificationFilter filter, StatusBarStateController controller,
+            BatteryController batteryController) {
+        super(context, filter, controller, batteryController);
+        mUseHeadsUp = true;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 5a0a495..f1fba79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -282,6 +282,9 @@
                 .inflationCallback(any()))
                 .thenReturn(mExpandableNotificationRowComponentBuilder);
         when(mExpandableNotificationRowComponentBuilder
+                .rowContentBindStage(any()))
+                .thenReturn(mExpandableNotificationRowComponentBuilder);
+        when(mExpandableNotificationRowComponentBuilder
                 .onExpandClickListener(any()))
                 .thenReturn(mExpandableNotificationRowComponentBuilder);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 1dfe7bc..a8c438a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -28,6 +28,7 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
@@ -43,6 +44,7 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
+import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
@@ -83,13 +85,12 @@
                 any(NotificationMenuRowPlugin.MenuItem.class)))
                 .thenReturn(true);
         when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
-        mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
-        mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
         mDependency.injectMockDependency(BubbleController.class);
 
         mHelper = new NotificationTestHelper(mContext, mDependency);
 
-        mBlockingHelperManager = new NotificationBlockingHelperManager(mContext);
+        mBlockingHelperManager = new NotificationBlockingHelperManager(
+                mContext, mGutsManager, mEntryManager, mock(MetricsLogger.class));
         // By default, have the shade visible/expanded.
         mBlockingHelperManager.setNotificationShadeExpanded(1f);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
index 8a42e5f..149a95a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java
@@ -50,10 +50,12 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.statusbar.NotificationRemoteInputManager;
+import com.android.systemui.statusbar.SmartReplyController;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback;
 import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.tests.R;
 
 import org.junit.Assert;
@@ -92,9 +94,14 @@
         ExpandableNotificationRow row = new NotificationTestHelper(mContext, mDependency).createRow(
                 mBuilder.build());
         mRow = spy(row);
+
+        final SmartReplyConstants smartReplyConstants = mock(SmartReplyConstants.class);
+        final SmartReplyController smartReplyController = mock(SmartReplyController.class);
         mNotificationInflater = new NotificationContentInflater(
                 mCache,
-                mock(NotificationRemoteInputManager.class));
+                mock(NotificationRemoteInputManager.class),
+                () -> smartReplyConstants,
+                () -> smartReplyController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
index e8de10f7..27b263f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationConversationInfoTest.java
@@ -51,7 +51,8 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
-import android.graphics.drawable.Drawable;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Icon;
 import android.os.UserHandle;
 import android.service.notification.StatusBarNotification;
@@ -60,12 +61,12 @@
 import android.testing.TestableLooper;
 import android.view.LayoutInflater;
 import android.view.View;
-import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.ImageView;
 import android.widget.TextView;
 
 import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.notification.ConversationIconFactory;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
@@ -112,7 +113,8 @@
     private NotificationEntry mBubbleEntry;
     @Mock
     private ShortcutInfo mShortcutInfo;
-    private Drawable mImage;
+    @Mock
+    private Bitmap mImage;
 
     @Rule
     public MockitoRule mockito = MockitoJUnit.rule();
@@ -134,6 +136,8 @@
     private NotificationGuts mNotificationGuts;
     @Mock
     private ShadeController mShadeController;
+    @Mock
+    private ConversationIconFactory mIconFactory;
 
     @Before
     public void setUp() throws Exception {
@@ -176,9 +180,8 @@
         when(mShortcutInfo.getShortLabel()).thenReturn("Convo name");
         List<ShortcutInfo> shortcuts = Arrays.asList(mShortcutInfo);
         when(mLauncherApps.getShortcuts(any(), any())).thenReturn(shortcuts);
-        mImage = mContext.getDrawable(R.drawable.ic_remove);
-        when(mLauncherApps.getShortcutBadgedIconDrawable(eq(mShortcutInfo),
-                anyInt())).thenReturn(mImage);
+        when(mIconFactory.getConversationBitmap(any(ShortcutInfo.class), anyString(), anyInt()))
+                .thenReturn(mImage);
 
         mNotificationChannel = new NotificationChannel(
                 TEST_CHANNEL, TEST_CHANNEL_NAME, IMPORTANCE_LOW);
@@ -224,9 +227,10 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
         final ImageView view = mNotificationInfo.findViewById(R.id.conversation_icon);
-        assertEquals(mImage, view.getDrawable());
+        assertEquals(mImage, ((BitmapDrawable) view.getDrawable()).getBitmap());
     }
 
     @Test
@@ -244,6 +248,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
         final TextView textView = mNotificationInfo.findViewById(R.id.pkg_name);
         assertTrue(textView.getText().toString().contains("App Name"));
@@ -290,6 +295,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
         final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
         assertTrue(textView.getText().toString().contains(group.getName()));
@@ -312,6 +318,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
         final TextView textView = mNotificationInfo.findViewById(R.id.group_name);
         assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility());
@@ -333,6 +340,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(GONE, nameView.getVisibility());
@@ -361,6 +369,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
         final TextView nameView = mNotificationInfo.findViewById(R.id.delegate_name);
         assertEquals(VISIBLE, nameView.getVisibility());
@@ -385,6 +394,7 @@
                 },
                 null,
                 null,
+                mIconFactory,
                 true);
 
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
@@ -407,6 +417,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -430,6 +441,7 @@
                 },
                 null,
                 null,
+                mIconFactory,
                 false);
         final View settingsButton = mNotificationInfo.findViewById(R.id.info);
         assertTrue(settingsButton.getVisibility() != View.VISIBLE);
@@ -449,6 +461,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
@@ -469,6 +482,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         View bubbleView = mNotificationInfo.findViewById(R.id.bubble);
@@ -491,9 +505,9 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
-
         // Promote it
         mNotificationInfo.findViewById(R.id.home).performClick();
         mTestableLooper.processAllMessages();
@@ -522,9 +536,9 @@
                 (View v, int hours) -> {
                     latch.countDown();
                 },
+                mIconFactory,
                 true);
 
-
         // Promote it
         mNotificationInfo.findViewById(R.id.snooze).performClick();
         mTestableLooper.processAllMessages();
@@ -551,6 +565,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         assertFalse(mBubbleEntry.isBubble());
@@ -583,6 +598,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         assertTrue(mBubbleEntry.isBubble());
@@ -613,6 +629,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         assertFalse(mBubbleEntry.isBubble());
@@ -641,9 +658,9 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
-
         ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
         assertEquals(mContext.getString(R.string.notification_conversation_unfavorite),
                 fave.getContentDescription().toString());
@@ -675,6 +692,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         ImageButton fave = mNotificationInfo.findViewById(R.id.fave);
@@ -708,6 +726,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
@@ -743,9 +762,9 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
-
         ImageButton mute = mNotificationInfo.findViewById(R.id.mute);
         assertEquals(mContext.getString(R.string.notification_conversation_mute),
                 mute.getContentDescription().toString());
@@ -774,7 +793,9 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
+
         verify(mMockINotificationManager, times(1)).createConversationNotificationChannelForPackage(
                 anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
     }
@@ -794,7 +815,9 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
+
         verify(mMockINotificationManager, never()).createConversationNotificationChannelForPackage(
                 anyString(), anyInt(), anyString(), any(), eq(CONVERSATION_ID));
     }
@@ -815,6 +838,7 @@
                 null,
                 null,
                 null,
+                mIconFactory,
                 true);
 
         mNotificationInfo.findViewById(R.id.mute).performClick();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 7a1bd05..9a52ee8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -60,6 +60,7 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
+import com.android.systemui.statusbar.policy.SmartReplyConstants;
 import com.android.systemui.tests.R;
 
 import org.mockito.ArgumentCaptor;
@@ -98,7 +99,6 @@
         dependency.injectMockDependency(NotificationMediaManager.class);
         dependency.injectMockDependency(BubbleController.class);
         dependency.injectMockDependency(NotificationShadeWindowController.class);
-        dependency.injectMockDependency(SmartReplyController.class);
         mStatusBarStateController = mock(StatusBarStateController.class);
         mGroupManager = new NotificationGroupManager(mStatusBarStateController);
         mHeadsUpManager = new HeadsUpManagerPhone(mContext, mStatusBarStateController,
@@ -106,10 +106,11 @@
         mHeadsUpManager.setUp(null, mGroupManager, null, null);
         mGroupManager.setHeadsUpManager(mHeadsUpManager);
 
-
         NotificationContentInflater contentBinder = new NotificationContentInflater(
                 mock(NotifRemoteViewCache.class),
-                mock(NotificationRemoteInputManager.class));
+                mock(NotificationRemoteInputManager.class),
+                () -> mock(SmartReplyConstants.class),
+                () -> mock(SmartReplyController.class));
         contentBinder.setInflateSynchronously(true);
         mBindStage = new RowContentBindStage(contentBinder,
                 mock(NotifInflationErrorManager.class),
diff --git a/packages/services/PacProcessor/jni/Android.bp b/packages/services/PacProcessor/jni/Android.bp
index 351e92c..0e7e101 100644
--- a/packages/services/PacProcessor/jni/Android.bp
+++ b/packages/services/PacProcessor/jni/Android.bp
@@ -37,8 +37,7 @@
         "-Wunused",
         "-Wunreachable-code",
     ],
-    // Re-enable when b/145990493 is fixed
-    // sanitize: {
-    //    cfi: true,
-    // },
+    sanitize: {
+       cfi: true,
+    },
 }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 63dd99e..eda3fb9 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -248,6 +248,10 @@
     // Package: android
     NOTE_LOCATION_CHANGED = 59;
 
+    // Notify user that a SIM is required to connect to Wifi network
+    // Package: android
+    NOTE_ID_WIFI_SIM_REQUIRED = 60;
+
     // ADD_NEW_IDS_ABOVE_THIS_LINE
     // Legacy IDs with arbitrary values appear below
     // Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4474f60..872f0eb 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -119,6 +119,9 @@
     private final LocalLog mUiLatencyHistory;
     private final LocalLog mWtfHistory;
     private final FieldClassificationStrategy mFieldClassificationStrategy;
+
+    @GuardedBy("mLock")
+    @Nullable
     private RemoteInlineSuggestionRenderService mRemoteInlineSuggestionRenderService;
 
     /**
@@ -236,17 +239,8 @@
             sendStateToClients(/* resetClient= */ false);
         }
         updateRemoteAugmentedAutofillService();
+        updateRemoteInlineSuggestionRenderServiceLocked();
 
-        final ComponentName componentName = RemoteInlineSuggestionRenderService
-                .getServiceComponentName(getContext(), mUserId);
-        if (componentName != null) {
-            mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService(
-                    getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE,
-                    mUserId, new InlineSuggestionRenderCallbacksImpl(),
-                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
-        } else {
-            mRemoteInlineSuggestionRenderService = null;
-        }
         return enabledChanged;
     }
 
@@ -1644,7 +1638,29 @@
         return mFieldClassificationStrategy.getDefaultAlgorithm();
     }
 
-    RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderService() {
+    private void updateRemoteInlineSuggestionRenderServiceLocked() {
+        if (mRemoteInlineSuggestionRenderService != null) {
+            if (sVerbose) {
+                Slog.v(TAG, "updateRemoteInlineSuggestionRenderService(): "
+                        + "destroying old remote service");
+            }
+            mRemoteInlineSuggestionRenderService = null;
+        }
+
+        mRemoteInlineSuggestionRenderService = getRemoteInlineSuggestionRenderServiceLocked();
+    }
+
+    RemoteInlineSuggestionRenderService getRemoteInlineSuggestionRenderServiceLocked() {
+        final ComponentName componentName = RemoteInlineSuggestionRenderService
+                .getServiceComponentName(getContext(), mUserId);
+
+        if (mRemoteInlineSuggestionRenderService == null) {
+            mRemoteInlineSuggestionRenderService = new RemoteInlineSuggestionRenderService(
+                    getContext(), componentName, InlineSuggestionRenderService.SERVICE_INTERFACE,
+                    mUserId, new InlineSuggestionRenderCallbacksImpl(),
+                    mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+        }
+
         return mRemoteInlineSuggestionRenderService;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index d93ac68..1eb7692 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -146,7 +146,8 @@
             @Nullable AutofillValue focusedValue,
             @Nullable InlineSuggestionsRequest inlineSuggestionsRequest,
             @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
-            @NonNull Runnable onErrorCallback) {
+            @NonNull Runnable onErrorCallback,
+            @NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
         long requestTime = SystemClock.elapsedRealtime();
         AtomicReference<ICancellationSignal> cancellationRef = new AtomicReference<>();
 
@@ -168,7 +169,7 @@
                                     maybeRequestShowInlineSuggestions(sessionId,
                                             inlineSuggestionsRequest, inlineSuggestionsData,
                                             focusedId, inlineSuggestionsCallback, client,
-                                            onErrorCallback);
+                                            onErrorCallback, remoteRenderService);
                                     requestAutofill.complete(null);
                                 }
 
@@ -234,7 +235,8 @@
             @Nullable InlineSuggestionsRequest request, @Nullable Dataset[] inlineSuggestionsData,
             @NonNull AutofillId focusedId,
             @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
-            @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback) {
+            @NonNull IAutoFillManagerClient client, @NonNull Runnable onErrorCallback,
+            @NonNull RemoteInlineSuggestionRenderService remoteRenderService) {
         if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null
                 || request == null) {
             return;
@@ -254,7 +256,7 @@
                                 } catch (RemoteException e) {
                                     Slog.w(TAG, "Encounter exception autofilling the values");
                                 }
-                            }, onErrorCallback));
+                            }, onErrorCallback, remoteRenderService));
         } catch (RemoteException e) {
             Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
         }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
index 31dc23f..5d5af53 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteInlineSuggestionRenderService.java
@@ -36,7 +36,10 @@
 
 import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
 
-final class RemoteInlineSuggestionRenderService extends
+/**
+ * Remote service to help connect to InlineSuggestionRenderService in ExtServices.
+ */
+public final class RemoteInlineSuggestionRenderService extends
         AbstractMultiplePendingRequestsRemoteService<RemoteInlineSuggestionRenderService,
                 IInlineSuggestionRenderService> {
 
@@ -81,9 +84,11 @@
      * Called by {@link Session} to generate a call to the
      * {@link RemoteInlineSuggestionRenderService} to request rendering a slice .
      */
-    void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
-            @NonNull InlinePresentation presentation, int width, int height) {
-        scheduleAsyncRequest((s) -> s.renderSuggestion(callback, presentation, width, height));
+    public void renderSuggestion(@NonNull IInlineSuggestionUiCallback callback,
+            @NonNull InlinePresentation presentation, int width, int height,
+            @Nullable IBinder hostInputToken) {
+        scheduleAsyncRequest(
+                (s) -> s.renderSuggestion(callback, presentation, width, height, hostInputToken));
     }
 
     @Nullable
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 72029d1..697e5d7 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1018,7 +1018,8 @@
 
     // FillServiceCallbacks
     @Override
-    public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras) {
+    public void authenticate(int requestId, int datasetIndex, IntentSender intent, Bundle extras,
+            boolean authenticateInline) {
         if (sDebug) {
             Slog.d(TAG, "authenticate(): requestId=" + requestId + "; datasetIdx=" + datasetIndex
                     + "; intentSender=" + intent);
@@ -1042,7 +1043,7 @@
         final int authenticationId = AutofillManager.makeAuthenticationId(requestId, datasetIndex);
         mHandler.sendMessage(obtainMessage(
                 Session::startAuthentication,
-                this, authenticationId, intent, fillInIntent));
+                this, authenticationId, intent, fillInIntent, authenticateInline));
     }
 
     // VultureCallback
@@ -2623,8 +2624,6 @@
                 mService.getServicePackageName(), mComponentName,
                 serviceLabel, serviceIcon, this, id, mCompatMode);
 
-        mService.logDatasetShown(id, mClientState);
-
         synchronized (mLock) {
             if (mUiShownTime == 0) {
                 // Log first time UI is shown.
@@ -2656,10 +2655,6 @@
     private boolean requestShowInlineSuggestionsLocked(@NonNull FillResponse response,
             @Nullable String filterText) {
         final List<Dataset> datasets = response.getDatasets();
-        if (datasets == null) {
-            Log.w(TAG, "response returned null datasets");
-            return false;
-        }
 
         final InlineSuggestionSession.ImeResponse imeResponse =
                 mInlineSuggestionSession.waitAndGetImeResponse();
@@ -2671,13 +2666,12 @@
         final InlineSuggestionsRequest request = imeResponse.getRequest();
         InlineSuggestionsResponse inlineSuggestionsResponse =
                 InlineSuggestionFactory.createInlineSuggestionsResponse(request,
-                        response.getRequestId(),
-                        datasets.toArray(new Dataset[]{}), filterText, response.getInlineActions(),
-                        mCurrentViewId, mContext, this, () -> {
+                        response, filterText, response.getInlineActions(), mCurrentViewId, mContext,
+                        this, () -> {
                             synchronized (mLock) {
                                 requestHideFillUi(mCurrentViewId);
                             }
-                        });
+                        }, mService.getRemoteInlineSuggestionRenderServiceLocked());
         try {
             imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
         } catch (RemoteException e) {
@@ -2981,7 +2975,7 @@
                     synchronized (mLock) {
                         cancelAugmentedAutofillLocked();
                     }
-                });
+                }, mService.getRemoteInlineSuggestionRenderServiceLocked());
 
         if (mAugmentedAutofillDestroyer == null) {
             mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
@@ -3160,7 +3154,8 @@
             }
             final int authenticationId = AutofillManager.makeAuthenticationId(requestId,
                     datasetIndex);
-            startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent);
+            startAuthentication(authenticationId, dataset.getAuthentication(), fillInIntent,
+                    /* authenticateInline= */false);
 
         }
     }
@@ -3184,10 +3179,11 @@
     }
 
     private void startAuthentication(int authenticationId, IntentSender intent,
-            Intent fillInIntent) {
+            Intent fillInIntent, boolean authenticateInline) {
         try {
             synchronized (mLock) {
-                mClient.authenticate(id, authenticationId, intent, fillInIntent);
+                mClient.authenticate(id, authenticationId, intent, fillInIntent,
+                        authenticateInline);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Error launching auth intent", e);
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 26bb7c3..71c3c16 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -79,7 +79,7 @@
 
     public interface AutoFillUiCallback {
         void authenticate(int requestId, int datasetIndex, @NonNull IntentSender intent,
-                @Nullable Bundle extras);
+                @Nullable Bundle extras, boolean authenticateInline);
         void fill(int requestId, int datasetIndex, @NonNull Dataset dataset);
         void save();
         void cancelSave();
@@ -217,7 +217,8 @@
                     if (mCallback != null) {
                         mCallback.authenticate(response.getRequestId(),
                                 AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED,
-                                response.getAuthentication(), response.getClientState());
+                                response.getAuthentication(), response.getClientState(),
+                                /* authenticateInline= */ false);
                     }
                 }
 
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
index 5f6e47b..a8886fc 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -22,14 +22,17 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.os.IBinder;
 import android.os.RemoteException;
 import android.service.autofill.Dataset;
+import android.service.autofill.FillResponse;
+import android.service.autofill.IInlineSuggestionUiCallback;
 import android.service.autofill.InlinePresentation;
 import android.text.TextUtils;
 import android.util.Slog;
 import android.view.SurfaceControl;
-import android.view.View;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.view.inline.InlinePresentationSpec;
 import android.view.inputmethod.InlineSuggestion;
@@ -40,11 +43,14 @@
 
 import com.android.internal.view.inline.IInlineContentCallback;
 import com.android.internal.view.inline.IInlineContentProvider;
+import com.android.server.LocalServices;
 import com.android.server.UiThread;
+import com.android.server.autofill.RemoteInlineSuggestionRenderService;
+import com.android.server.wm.WindowManagerInternal;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.function.BiFunction;
+import java.util.function.BiConsumer;
 import java.util.regex.Pattern;
 
 public final class InlineSuggestionFactory {
@@ -61,8 +67,42 @@
     }
 
     /**
-     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
-     * augmented autofill service.
+     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
+     * autofill service, potentially filtering the datasets.
+     */
+    public static InlineSuggestionsResponse createInlineSuggestionsResponse(
+            @NonNull InlineSuggestionsRequest request, @NonNull FillResponse response,
+            @Nullable String filterText,
+            @Nullable List<InlinePresentation> inlineActions,
+            @NonNull AutofillId autofillId,
+            @NonNull Context context,
+            @NonNull AutoFillUI.AutoFillUiCallback client,
+            @NonNull Runnable onErrorCallback,
+            @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+        if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
+        final BiConsumer<Dataset, Integer> onClickFactory;
+        if (response.getAuthentication() != null) {
+            onClickFactory = (dataset, datasetIndex) -> client.authenticate(response.getRequestId(),
+                    datasetIndex, response.getAuthentication(), response.getClientState(),
+                    /* authenticateInline= */ true);
+        } else {
+            onClickFactory = (dataset, datasetIndex) ->
+                    client.fill(response.getRequestId(), datasetIndex, dataset);
+        }
+
+        final List<Dataset> datasetList = response.getDatasets();
+        final Dataset[] datasets = datasetList == null
+                ? null
+                : datasetList.toArray(new Dataset[]{});
+
+        return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, response,
+                datasets, filterText, inlineActions, autofillId, context, onErrorCallback,
+                onClickFactory, remoteRenderService);
+    }
+
+    /**
+     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
+     * autofill service.
      */
     public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
             @NonNull InlineSuggestionsRequest request,
@@ -70,44 +110,42 @@
             @NonNull AutofillId autofillId,
             @NonNull Context context,
             @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
-            @NonNull Runnable onErrorCallback) {
+            @NonNull Runnable onErrorCallback,
+            @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
         if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
         return createInlineSuggestionsResponseInternal(/* isAugmented= */ true, request,
-                datasets, /* filterText= */ null, /* inlineActions= */ null, autofillId, context,
-                onErrorCallback,
-                (dataset, filedIndex) -> (v -> inlineSuggestionUiCallback.autofill(dataset)));
-    }
-
-    /**
-     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by the
-     * autofill service, potentially filtering the datasets.
-     */
-    public static InlineSuggestionsResponse createInlineSuggestionsResponse(
-            @NonNull InlineSuggestionsRequest request, int requestId,
-            @NonNull Dataset[] datasets,
-            @Nullable String filterText,
-            @Nullable List<InlinePresentation> inlineActions,
-            @NonNull AutofillId autofillId,
-            @NonNull Context context,
-            @NonNull AutoFillUI.AutoFillUiCallback client,
-            @NonNull Runnable onErrorCallback) {
-        if (sDebug) Slog.d(TAG, "createInlineSuggestionsResponse called");
-        return createInlineSuggestionsResponseInternal(/* isAugmented= */ false, request, datasets,
-                filterText, inlineActions, autofillId, context, onErrorCallback,
-                (dataset, filedIndex) -> (v -> client.fill(requestId, filedIndex, dataset)));
+                /* fillResponse= */ null, datasets, /* filterText= */ null,
+                /* inlineActions= */ null, autofillId, context, onErrorCallback,
+                (dataset, fieldIndex) ->
+                        inlineSuggestionUiCallback.autofill(dataset), remoteRenderService);
     }
 
     private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
             boolean isAugmented, @NonNull InlineSuggestionsRequest request,
-            @NonNull Dataset[] datasets, @Nullable String filterText,
+            @Nullable FillResponse response, @Nullable Dataset[] datasets,
+            @Nullable String filterText,
             @Nullable List<InlinePresentation> inlineActions, @NonNull AutofillId autofillId,
             @NonNull Context context, @NonNull Runnable onErrorCallback,
-            @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
+            @NonNull BiConsumer<Dataset, Integer> onClickFactory,
+            @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
+
         final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
-        final InlineSuggestionUi inlineSuggestionUi = new InlineSuggestionUi(context,
-                onErrorCallback);
-        for (int i = 0; i < datasets.length; i++) {
-            final Dataset dataset = datasets[i];
+        if (response.getAuthentication() != null) {
+            InlineSuggestion inlineAuthSuggestion = createInlineAuthSuggestion(response,
+                    remoteRenderService, onClickFactory, onErrorCallback,
+                    request.getHostInputToken());
+            inlineSuggestions.add(inlineAuthSuggestion);
+
+            return new InlineSuggestionsResponse(inlineSuggestions);
+        }
+
+        if (datasets == null) {
+            Slog.w(TAG, "Datasets should not be null here");
+            return null;
+        }
+
+        for (int datasetIndex = 0; datasetIndex < datasets.length; datasetIndex++) {
+            final Dataset dataset = datasets[datasetIndex];
             final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
             if (fieldIndex < 0) {
                 Slog.w(TAG, "AutofillId=" + autofillId + " not found in dataset");
@@ -123,15 +161,17 @@
                 continue;
             }
             InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
-                    fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
-                    inlineSuggestionUi, onClickListenerFactory);
+                    fieldIndex, mergedInlinePresentation(request, datasetIndex, inlinePresentation),
+                    onClickFactory, remoteRenderService, onErrorCallback,
+                    request.getHostInputToken());
+
             inlineSuggestions.add(inlineSuggestion);
         }
         if (inlineActions != null) {
             for (InlinePresentation inlinePresentation : inlineActions) {
                 final InlineSuggestion inlineAction = createInlineAction(isAugmented, context,
                         mergedInlinePresentation(request, 0, inlinePresentation),
-                        inlineSuggestionUi);
+                        remoteRenderService, onErrorCallback, request.getHostInputToken());
                 inlineSuggestions.add(inlineAction);
             }
         }
@@ -173,43 +213,59 @@
     private static InlineSuggestion createInlineAction(boolean isAugmented,
             @NonNull Context context,
             @NonNull InlinePresentation inlinePresentation,
-            @NonNull InlineSuggestionUi inlineSuggestionUi) {
+            @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
                 isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
                         : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
                 InlineSuggestionInfo.TYPE_ACTION);
-        final View.OnClickListener onClickListener = v -> {
-            // TODO(b/148567875): Launch the intent provided through the slice. This
-            //  should be part of the UI renderer therefore will be moved to the support
-            //  library.
+        final Runnable onClickAction = () -> {
             Toast.makeText(context, "icon clicked", Toast.LENGTH_SHORT).show();
         };
         return new InlineSuggestion(inlineSuggestionInfo,
-                createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
-                        onClickListener));
+                createInlineContentProvider(inlinePresentation, onClickAction, onErrorCallback,
+                        remoteRenderService, hostInputToken));
     }
 
     private static InlineSuggestion createInlineSuggestion(boolean isAugmented,
-            @NonNull Dataset dataset,
-            int fieldIndex, @NonNull InlinePresentation inlinePresentation,
-            @NonNull InlineSuggestionUi inlineSuggestionUi,
-            @NonNull BiFunction<Dataset, Integer, View.OnClickListener> onClickListenerFactory) {
+            @NonNull Dataset dataset, int datasetIndex,
+            @NonNull InlinePresentation inlinePresentation,
+            @NonNull BiConsumer<Dataset, Integer> onClickFactory,
+            @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
+            @NonNull Runnable onErrorCallback, @Nullable IBinder hostInputToken) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
                 isAugmented ? InlineSuggestionInfo.SOURCE_PLATFORM
                         : InlineSuggestionInfo.SOURCE_AUTOFILL, new String[]{""},
                 InlineSuggestionInfo.TYPE_SUGGESTION);
-        final View.OnClickListener onClickListener = onClickListenerFactory.apply(dataset,
-                fieldIndex);
+
         final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
-                createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
-                        onClickListener));
+                createInlineContentProvider(inlinePresentation,
+                        () -> onClickFactory.accept(dataset, datasetIndex), onErrorCallback,
+                        remoteRenderService, hostInputToken));
+
         return inlineSuggestion;
     }
 
+    private static InlineSuggestion createInlineAuthSuggestion(@NonNull FillResponse response,
+            @NonNull RemoteInlineSuggestionRenderService remoteRenderService,
+            @NonNull BiConsumer<Dataset, Integer> onClickFactory, @NonNull Runnable onErrorCallback,
+            @Nullable IBinder hostInputToken) {
+        final InlinePresentation inlinePresentation = response.getInlinePresentation();
+        final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
+                inlinePresentation.getInlinePresentationSpec(),
+                InlineSuggestionInfo.SOURCE_AUTOFILL, null, InlineSuggestionInfo.TYPE_SUGGESTION);
+
+        return new InlineSuggestion(inlineSuggestionInfo,
+                createInlineContentProvider(inlinePresentation,
+                        () -> onClickFactory.accept(null,
+                                AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED),
+                        onErrorCallback, remoteRenderService, hostInputToken));
+    }
+
     /**
      * Returns an {@link InlinePresentation} with the style spec from the request/host, and
      * everything else from the provided {@code inlinePresentation}.
@@ -231,27 +287,64 @@
     }
 
     private static IInlineContentProvider.Stub createInlineContentProvider(
-            @NonNull InlinePresentation inlinePresentation,
-            @NonNull InlineSuggestionUi inlineSuggestionUi,
-            @Nullable View.OnClickListener onClickListener) {
+            @NonNull InlinePresentation inlinePresentation, @Nullable Runnable onClickAction,
+            @NonNull Runnable onErrorCallback,
+            @Nullable RemoteInlineSuggestionRenderService remoteRenderService,
+            @Nullable IBinder hostInputToken) {
         return new IInlineContentProvider.Stub() {
             @Override
-            public void provideContent(int width, int height,
-                    IInlineContentCallback callback) {
+            public void provideContent(int width, int height, IInlineContentCallback callback) {
                 UiThread.getHandler().post(() -> {
-                    SurfaceControl sc = inlineSuggestionUi.inflate(inlinePresentation, width,
-                            height,
-                            onClickListener);
-                    try {
-                        callback.onContent(sc);
-                    } catch (RemoteException e) {
-                        Slog.w(TAG, "Encounter exception calling back with inline content.");
+                    final IInlineSuggestionUiCallback uiCallback = createInlineSuggestionUiCallback(
+                            callback, onClickAction, onErrorCallback);
+
+                    if (remoteRenderService == null) {
+                        Slog.e(TAG, "RemoteInlineSuggestionRenderService is null");
+                        return;
                     }
+
+                    remoteRenderService.renderSuggestion(uiCallback, inlinePresentation,
+                            width, height, hostInputToken);
                 });
             }
         };
     }
 
+    private static IInlineSuggestionUiCallback.Stub createInlineSuggestionUiCallback(
+            @NonNull IInlineContentCallback callback, @NonNull Runnable onAutofillCallback,
+            @NonNull Runnable onErrorCallback) {
+        return new IInlineSuggestionUiCallback.Stub() {
+            @Override
+            public void onAutofill() throws RemoteException {
+                onAutofillCallback.run();
+            }
+
+            @Override
+            public void onContent(SurfaceControl surface)
+                    throws RemoteException {
+                callback.onContent(surface);
+            }
+
+            @Override
+            public void onError() throws RemoteException {
+                onErrorCallback.run();
+            }
+
+            @Override
+            public void onTransferTouchFocusToImeWindow(IBinder sourceInputToken, int displayId)
+                    throws RemoteException {
+                //TODO(b/149574510): Move logic to IMMS
+                final WindowManagerInternal windowManagerInternal = LocalServices.getService(
+                        WindowManagerInternal.class);
+                if (!windowManagerInternal.transferTouchFocusToImeWindow(sourceInputToken,
+                        displayId)) {
+                    Slog.e(TAG, "Cannot transfer touch focus from suggestion to IME");
+                    onErrorCallback.run();
+                }
+            }
+        };
+    }
+
     private InlineSuggestionFactory() {
     }
-}
+}
\ No newline at end of file
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
deleted file mode 100644
index bf148a6..0000000
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionUi.java
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * Copyright (C) 2019 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.autofill.ui;
-
-import static android.app.slice.SliceItem.FORMAT_IMAGE;
-import static android.app.slice.SliceItem.FORMAT_TEXT;
-
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.slice.Slice;
-import android.app.slice.SliceItem;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.PixelFormat;
-import android.graphics.drawable.Icon;
-import android.graphics.fonts.SystemFonts;
-import android.os.IBinder;
-import android.service.autofill.InlinePresentation;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.ContextThemeWrapper;
-import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.WindowManager;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.internal.R;
-
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This is a temporary inline suggestion UI inflater which will be replaced by the ExtServices
- * implementation.
- *
- * TODO(b/146453086): remove this class once autofill ext service is implemented.
- *
- * @hide
- */
-public class InlineSuggestionUi {
-
-    private static final String TAG = "InlineSuggestionUi";
-
-    // The pattern to match the value can be obtained by calling {@code Resources#getResourceName
-    // (int)}. This name is a single string of the form "package:type/entry".
-    private static final Pattern RESOURCE_NAME_PATTERN = Pattern.compile("([^:]+):([^/]+)/(\\S+)");
-
-    private final @NonNull Context mContext;
-    private final @NonNull Runnable mOnErrorCallback;
-
-    InlineSuggestionUi(@NonNull Context context, @NonNull Runnable onErrorCallback) {
-        this.mContext = context;
-        mOnErrorCallback = onErrorCallback;
-    }
-
-    /**
-     * Returns a {@link SurfaceControl} with the inflated content embedded in it.
-     */
-    @MainThread
-    @Nullable
-    public SurfaceControl inflate(@NonNull InlinePresentation inlinePresentation, int width,
-            int height, @Nullable View.OnClickListener onClickListener) {
-        Log.d(TAG, "Inflating the inline suggestion UI");
-
-        //TODO(b/137800469): Pass in inputToken from IME.
-        final SurfaceControlViewHost wvr = new SurfaceControlViewHost(mContext,
-                mContext.getDisplay(), (IBinder) null);
-        final SurfaceControl sc = wvr.getSurfacePackage().getSurfaceControl();
-
-        Context contextThemeWrapper = getContextThemeWrapper(mContext,
-                inlinePresentation.getInlinePresentationSpec().getStyle());
-        if (contextThemeWrapper == null) {
-            contextThemeWrapper = getDefaultContextThemeWrapper(mContext);
-        }
-        final View suggestionView = renderSlice(inlinePresentation.getSlice(),
-                contextThemeWrapper);
-
-        final InlineSuggestionRoot suggestionRoot = new InlineSuggestionRoot(
-                mContext, mOnErrorCallback);
-        suggestionRoot.addView(suggestionView);
-        suggestionRoot.setOnClickListener(onClickListener);
-
-        WindowManager.LayoutParams lp =
-                new WindowManager.LayoutParams(width, height,
-                        WindowManager.LayoutParams.TYPE_APPLICATION, 0,
-                        PixelFormat.TRANSPARENT);
-        wvr.addView(suggestionRoot, lp);
-        return sc;
-    }
-
-    private static View renderSlice(Slice slice, Context context) {
-        final LayoutInflater inflater = LayoutInflater.from(context);
-        final ViewGroup suggestionView =
-                (ViewGroup) inflater.inflate(R.layout.autofill_inline_suggestion, null);
-
-        final ImageView startIconView =
-                suggestionView.findViewById(R.id.autofill_inline_suggestion_start_icon);
-        final TextView titleView =
-                suggestionView.findViewById(R.id.autofill_inline_suggestion_title);
-        final TextView subtitleView =
-                suggestionView.findViewById(R.id.autofill_inline_suggestion_subtitle);
-        final ImageView endIconView =
-                suggestionView.findViewById(R.id.autofill_inline_suggestion_end_icon);
-
-        boolean hasStartIcon = false;
-        boolean hasEndIcon = false;
-        boolean hasSubtitle = false;
-        final List<SliceItem> sliceItems = slice.getItems();
-        for (int i = 0; i < sliceItems.size(); i++) {
-            final SliceItem sliceItem = sliceItems.get(i);
-            if (sliceItem.getFormat().equals(FORMAT_IMAGE)) {
-                final Icon sliceIcon = sliceItem.getIcon();
-                if (i == 0) { // start icon
-                    startIconView.setImageIcon(sliceIcon);
-                    hasStartIcon = true;
-                } else { // end icon
-                    endIconView.setImageIcon(sliceIcon);
-                    hasEndIcon = true;
-                }
-            } else if (sliceItem.getFormat().equals(FORMAT_TEXT)) {
-                final List<String> sliceHints = sliceItem.getHints();
-                final String sliceText = sliceItem.getText().toString();
-                if (sliceHints.contains("inline_title")) { // title
-                    titleView.setText(sliceText);
-                } else { // subtitle
-                    subtitleView.setText(sliceText);
-                    hasSubtitle = true;
-                }
-            }
-        }
-        if (!hasStartIcon) {
-            startIconView.setVisibility(View.GONE);
-        }
-        if (!hasEndIcon) {
-            endIconView.setVisibility(View.GONE);
-        }
-        if (!hasSubtitle) {
-            subtitleView.setVisibility(View.GONE);
-        }
-
-        return suggestionView;
-    }
-
-    private Context getDefaultContextThemeWrapper(@NonNull Context context) {
-        Resources.Theme theme = context.getResources().newTheme();
-        theme.applyStyle(android.R.style.Theme_AutofillInlineSuggestion, true);
-        return new ContextThemeWrapper(context, theme);
-    }
-
-    /**
-     * Returns a context wrapping the theme in the provided {@code style}, or null if {@code
-     * style} doesn't pass validation.
-     */
-    @Nullable
-    private static Context getContextThemeWrapper(@NonNull Context context,
-            @Nullable String style) {
-        if (style == null) {
-            return null;
-        }
-        Matcher matcher = RESOURCE_NAME_PATTERN.matcher(style);
-        if (!matcher.matches()) {
-            Log.d(TAG, "Can not parse the style=" + style);
-            return null;
-        }
-        String packageName = matcher.group(1);
-        String type = matcher.group(2);
-        String entry = matcher.group(3);
-        if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(type) || TextUtils.isEmpty(entry)) {
-            Log.d(TAG, "Can not proceed with empty field values in the style=" + style);
-            return null;
-        }
-        Resources resources = null;
-        try {
-            resources = context.getPackageManager().getResourcesForApplication(
-                    packageName);
-        } catch (PackageManager.NameNotFoundException e) {
-            return null;
-        }
-        int resId = resources.getIdentifier(entry, type, packageName);
-        if (resId == Resources.ID_NULL) {
-            return null;
-        }
-        Resources.Theme theme = resources.newTheme();
-        theme.applyStyle(resId, true);
-        if (!validateBaseTheme(theme, resId)) {
-            Log.d(TAG, "Provided theme is not a child of Theme.InlineSuggestion, ignoring it.");
-            return null;
-        }
-        if (!validateFontFamilyForTextViewStyles(theme)) {
-            Log.d(TAG,
-                    "Provided theme specifies a font family that is not system font, ignoring it.");
-            return null;
-        }
-        return new ContextThemeWrapper(context, theme);
-    }
-
-    private static boolean validateFontFamilyForTextViewStyles(Resources.Theme theme) {
-        return validateFontFamily(theme, android.R.attr.autofillInlineSuggestionTitle)
-                && validateFontFamily(theme, android.R.attr.autofillInlineSuggestionSubtitle);
-    }
-
-    private static boolean validateFontFamily(Resources.Theme theme, int styleAttr) {
-        TypedArray ta = null;
-        try {
-            ta = theme.obtainStyledAttributes(null, new int[]{android.R.attr.fontFamily},
-                    styleAttr,
-                    0);
-            if (ta.getIndexCount() == 0) {
-                return true;
-            }
-            String fontFamily = ta.getString(ta.getIndex(0));
-            return SystemFonts.getRawSystemFallbackMap().containsKey(fontFamily);
-        } finally {
-            if (ta != null) {
-                ta.recycle();
-            }
-        }
-    }
-
-    private static boolean validateBaseTheme(Resources.Theme theme, int styleAttr) {
-        TypedArray ta = null;
-        try {
-            ta = theme.obtainStyledAttributes(null,
-                    new int[]{android.R.attr.isAutofillInlineSuggestionTheme}, styleAttr, 0);
-            if (ta.getIndexCount() == 0) {
-                return false;
-            }
-            return ta.getBoolean(ta.getIndex(0), false);
-        } finally {
-            if (ta != null) {
-                ta.recycle();
-            }
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 311a494..a4a42bc 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -69,6 +69,7 @@
 import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
@@ -196,6 +197,12 @@
                     + " due to " + getEnableDisableReasonString(mReason) + " by " + mPackageName;
         }
 
+        void dump(ProtoOutputStream proto) {
+            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.TIMESTAMP_MS, mTimestamp);
+            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.ENABLE, mEnable);
+            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.PACKAGE_NAME, mPackageName);
+            proto.write(BluetoothManagerServiceDumpProto.ActiveLog.REASON, mReason);
+        }
     }
 
     private final LinkedList<ActiveLog> mActiveLogs = new LinkedList<>();
@@ -2408,56 +2415,56 @@
         if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) {
             return;
         }
+        if ((args.length > 0) && args[0].startsWith("--proto")) {
+            dumpProto(fd);
+            return;
+        }
         String errorMsg = null;
 
-        boolean protoOut = (args.length > 0) && args[0].startsWith("--proto");
+        writer.println("Bluetooth Status");
+        writer.println("  enabled: " + isEnabled());
+        writer.println("  state: " + BluetoothAdapter.nameForState(mState));
+        writer.println("  address: " + mAddress);
+        writer.println("  name: " + mName);
+        if (mEnable) {
+            long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
+            String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
+                    (int) (onDuration / (1000 * 60 * 60)),
+                    (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
+                    (int) (onDuration % 1000));
+            writer.println("  time since enabled: " + onDurationString);
+        }
 
-        if (!protoOut) {
-            writer.println("Bluetooth Status");
-            writer.println("  enabled: " + isEnabled());
-            writer.println("  state: " + BluetoothAdapter.nameForState(mState));
-            writer.println("  address: " + mAddress);
-            writer.println("  name: " + mName);
-            if (mEnable) {
-                long onDuration = SystemClock.elapsedRealtime() - mLastEnabledTime;
-                String onDurationString = String.format(Locale.US, "%02d:%02d:%02d.%03d",
-                        (int) (onDuration / (1000 * 60 * 60)),
-                        (int) ((onDuration / (1000 * 60)) % 60), (int) ((onDuration / 1000) % 60),
-                        (int) (onDuration % 1000));
-                writer.println("  time since enabled: " + onDurationString);
+        if (mActiveLogs.size() == 0) {
+            writer.println("\nBluetooth never enabled!");
+        } else {
+            writer.println("\nEnable log:");
+            for (ActiveLog log : mActiveLogs) {
+                writer.println("  " + log);
             }
+        }
 
-            if (mActiveLogs.size() == 0) {
-                writer.println("\nBluetooth never enabled!");
-            } else {
-                writer.println("\nEnable log:");
-                for (ActiveLog log : mActiveLogs) {
-                    writer.println("  " + log);
-                }
-            }
+        writer.println(
+                "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
+        if (mCrashes == CRASH_LOG_MAX_SIZE) {
+            writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
+        }
+        for (Long time : mCrashTimestamps) {
+            writer.println("  " + timeToLog(time));
+        }
 
-            writer.println(
-                    "\nBluetooth crashed " + mCrashes + " time" + (mCrashes == 1 ? "" : "s"));
-            if (mCrashes == CRASH_LOG_MAX_SIZE) {
-                writer.println("(last " + CRASH_LOG_MAX_SIZE + ")");
-            }
-            for (Long time : mCrashTimestamps) {
-                writer.println("  " + timeToLog(time));
-            }
+        writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
+                + " registered");
+        for (ClientDeathRecipient app : mBleApps.values()) {
+            writer.println("  " + app.getPackageName());
+        }
 
-            writer.println("\n" + mBleApps.size() + " BLE app" + (mBleApps.size() == 1 ? "" : "s")
-                    + "registered");
-            for (ClientDeathRecipient app : mBleApps.values()) {
-                writer.println("  " + app.getPackageName());
-            }
-
-            writer.println("");
-            writer.flush();
-            if (args.length == 0) {
-                // Add arg to produce output
-                args = new String[1];
-                args[0] = "--print";
-            }
+        writer.println("");
+        writer.flush();
+        if (args.length == 0) {
+            // Add arg to produce output
+            args = new String[1];
+            args[0] = "--print";
         }
 
         if (mBluetoothBinder == null) {
@@ -2470,14 +2477,42 @@
             }
         }
         if (errorMsg != null) {
-            // Silently return if we are extracting metrics in Protobuf format
-            if (protoOut) {
-                return;
-            }
             writer.println(errorMsg);
         }
     }
 
+    private void dumpProto(FileDescriptor fd) {
+        final ProtoOutputStream proto = new ProtoOutputStream(fd);
+        proto.write(BluetoothManagerServiceDumpProto.ENABLED, isEnabled());
+        proto.write(BluetoothManagerServiceDumpProto.STATE, mState);
+        proto.write(BluetoothManagerServiceDumpProto.STATE_NAME,
+                BluetoothAdapter.nameForState(mState));
+        proto.write(BluetoothManagerServiceDumpProto.ADDRESS, mAddress);
+        proto.write(BluetoothManagerServiceDumpProto.NAME, mName);
+        if (mEnable) {
+            proto.write(BluetoothManagerServiceDumpProto.LAST_ENABLED_TIME_MS, mLastEnabledTime);
+        }
+        proto.write(BluetoothManagerServiceDumpProto.CURR_TIMESTAMP_MS,
+                SystemClock.elapsedRealtime());
+        for (ActiveLog log : mActiveLogs) {
+            long token = proto.start(BluetoothManagerServiceDumpProto.ACTIVE_LOGS);
+            log.dump(proto);
+            proto.end(token);
+        }
+        proto.write(BluetoothManagerServiceDumpProto.NUM_CRASHES, mCrashes);
+        proto.write(BluetoothManagerServiceDumpProto.CRASH_LOG_MAXED,
+                mCrashes == CRASH_LOG_MAX_SIZE);
+        for (Long time : mCrashTimestamps) {
+            proto.write(BluetoothManagerServiceDumpProto.CRASH_TIMESTAMPS_MS, time);
+        }
+        proto.write(BluetoothManagerServiceDumpProto.NUM_BLE_APPS, mBleApps.size());
+        for (ClientDeathRecipient app : mBleApps.values()) {
+            proto.write(BluetoothManagerServiceDumpProto.BLE_APP_PACKAGE_NAMES,
+                    app.getPackageName());
+        }
+        proto.flush();
+    }
+
     private static String getEnableDisableReasonString(int reason) {
         switch (reason) {
             case BluetoothProtoEnums.ENABLE_DISABLE_REASON_APPLICATION_REQUEST:
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index b5b22f1..033f63b 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -28,6 +28,10 @@
 import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
 import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES;
 import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE;
+import static android.content.pm.PackageManager.MATCH_ANY_USER;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
 import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED;
@@ -136,6 +140,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.AppFuseMount;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.FuseUnavailableMountException;
@@ -149,6 +154,7 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.server.SystemService.TargetUser;
 import com.android.server.pm.Installer;
 import com.android.server.storage.AppFuseBridge;
 import com.android.server.storage.StorageSessionController;
@@ -184,6 +190,7 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -270,6 +277,11 @@
         public void onStopUser(int userHandle) {
             mStorageManagerService.onStopUser(userHandle);
         }
+
+        @Override
+        public void onStartUser(TargetUser user) {
+            mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle());
+        }
     }
 
     private static final boolean DEBUG_EVENTS = false;
@@ -578,6 +590,12 @@
 
     private final boolean mIsFuseEnabled;
 
+    @GuardedBy("mLock")
+    private final Set<Integer> mUidsWithLegacyExternalStorage = new ArraySet<>();
+    // Not guarded by lock, always used on the ActivityManager thread
+    private final Map<Integer, PackageMonitor> mPackageMonitorsForUser = new ArrayMap<>();
+
+
     class ObbState implements IBinder.DeathRecipient {
         public ObbState(String rawPath, String canonicalPath, int callingUid,
                 IObbActionListener token, int nonce, String volId) {
@@ -1145,6 +1163,10 @@
         } catch (Exception e) {
             Slog.wtf(TAG, e);
         }
+        PackageMonitor monitor = mPackageMonitorsForUser.remove(userId);
+        if (monitor != null) {
+            monitor.unregister();
+        }
     }
 
     private boolean supportsBlockCheckpoint() throws RemoteException {
@@ -1836,6 +1858,49 @@
         }
     }
 
+    private void updateLegacyStorageApps(String packageName, int uid, boolean hasLegacy) {
+        synchronized (mLock) {
+            if (hasLegacy) {
+                Slog.v(TAG, "Package " + packageName + " has legacy storage");
+                mUidsWithLegacyExternalStorage.add(uid);
+            } else {
+                // TODO(b/149391976): Handle shared user id. Check if there's any other
+                // installed app with legacy external storage before removing
+                Slog.v(TAG, "Package " + packageName + " does not have legacy storage");
+                mUidsWithLegacyExternalStorage.remove(uid);
+            }
+        }
+    }
+
+    private void snapshotAndMonitorLegacyStorageAppOp(UserHandle user) {
+        int userId = user.getIdentifier();
+
+        // TODO(b/149391976): Use mIAppOpsService.getPackagesForOps instead of iterating below
+        // It should improve performance but the AppOps method doesn't return any app here :(
+        // This operation currently takes about ~20ms on a freshly flashed device
+        for (ApplicationInfo ai : mPmInternal.getInstalledApplications(MATCH_DIRECT_BOOT_AWARE
+                        | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER,
+                        userId, Process.myUid())) {
+            try {
+                boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ai.uid,
+                        ai.packageName) == MODE_ALLOWED;
+                updateLegacyStorageApps(ai.packageName, ai.uid, hasLegacy);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to check legacy op for package " + ai.packageName, e);
+            }
+        }
+
+        PackageMonitor monitor = new PackageMonitor() {
+                @Override
+                public void onPackageRemoved(String packageName, int uid) {
+                    updateLegacyStorageApps(packageName, uid, false);
+                }
+            };
+        // TODO(b/149391976): Use different handler?
+        monitor.register(mContext, user, true, mHandler);
+        mPackageMonitorsForUser.put(userId, monitor);
+    }
+
     private static long getLastAccessTime(AppOpsManager manager,
             int uid, String packageName, int[] ops) {
         long maxTime = 0;
@@ -3975,7 +4040,7 @@
             if (mIsFuseEnabled && hasMtp) {
                 ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName,
                         0, UserHandle.getUserId(uid));
-                if (ai.isSignedWithPlatformKey()) {
+                if (ai != null && ai.isSignedWithPlatformKey()) {
                     // Platform processes hosting the MTP server should be able to write in Android/
                     return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE;
                 }
@@ -4337,6 +4402,13 @@
             mHandler.obtainMessage(H_RESET).sendToTarget();
         }
 
+        @Override
+        public boolean hasLegacyExternalStorage(int uid) {
+            synchronized (mLock) {
+                return mUidsWithLegacyExternalStorage.contains(uid);
+            }
+        }
+
         public boolean hasExternalStorage(int uid, String packageName) {
             // No need to check for system uid. This avoids a deadlock between
             // PackageManagerService and AppOpsService.
@@ -4382,8 +4454,11 @@
                             // volumes, USB OTGs that are rarely mounted. The app will get the
                             // external_storage gid on next organic restart.
                             killAppForOpChange(code, uid, packageName);
-                            return;
                         }
+                        return;
+                    case OP_LEGACY_STORAGE:
+                        updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED);
+                        return;
                 }
             }
 
diff --git a/services/core/java/com/android/server/SystemService.java b/services/core/java/com/android/server/SystemService.java
index e3c7325..aabe98b 100644
--- a/services/core/java/com/android/server/SystemService.java
+++ b/services/core/java/com/android/server/SystemService.java
@@ -168,6 +168,11 @@
         public int getUserIdentifier() {
             return mUserInfo.id;
         }
+
+        @Override
+        public String toString() {
+            return Integer.toString(getUserIdentifier());
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ad550c5..1da07ef 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7156,7 +7156,7 @@
                     } finally {
                         Binder.restoreCallingIdentity(ident);
                     }
-                } else if (dyingProc == cpr.proc) {
+                } else if (dyingProc == cpr.proc && dyingProc != null) {
                     // The old stable connection's client should be killed during proc cleaning up,
                     // so do not re-use the old ContentProviderRecord, otherwise the new clients
                     // could get killed unexpectedly.
@@ -8387,6 +8387,9 @@
 
     @Override
     public void setActivityController(IActivityController controller, boolean imAMonkey) {
+        if (controller != null) {
+            Binder.allowBlocking(controller.asBinder());
+        }
         mActivityTaskManager.setActivityController(controller, imAMonkey);
     }
 
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 88eb885..fb48db4 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -30,6 +30,7 @@
 import static android.os.Process.SHELL_UID;
 import static android.os.Process.SYSTEM_UID;
 
+import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_LOCKED_BOOT_COMPLETED;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_MU;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -92,8 +93,8 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.logging.MetricsLogger;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -398,13 +399,14 @@
             // Do not report secondary users, runtime restarts or first boot/upgrade
             if (userId == UserHandle.USER_SYSTEM
                     && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
-                int uptimeSeconds = (int)(SystemClock.elapsedRealtime() / 1000);
-                MetricsLogger.histogram(mInjector.getContext(),
-                        "framework_locked_boot_completed", uptimeSeconds);
-                final int MAX_UPTIME_SECONDS = 120;
-                if (uptimeSeconds > MAX_UPTIME_SECONDS) {
+                final long elapsedTimeMs = SystemClock.elapsedRealtime();
+                FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+                        BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_LOCKED_BOOT_COMPLETED,
+                        elapsedTimeMs);
+                final long maxElapsedTimeMs = 120_000;
+                if (elapsedTimeMs > maxElapsedTimeMs) {
                     Slog.wtf("SystemServerTiming",
-                            "finishUserBoot took too long. uptimeSeconds=" + uptimeSeconds);
+                            "finishUserBoot took too long. elapsedTimeMs=" + elapsedTimeMs);
                 }
             }
 
@@ -618,9 +620,10 @@
         // Do not report secondary users, runtime restarts or first boot/upgrade
         if (userId == UserHandle.USER_SYSTEM
                 && !mInjector.isRuntimeRestarted() && !mInjector.isFirstBootOrUpgrade()) {
-            int uptimeSeconds = (int) (SystemClock.elapsedRealtime() / 1000);
-            MetricsLogger.histogram(mInjector.getContext(), "framework_boot_completed",
-                    uptimeSeconds);
+            final long elapsedTimeMs = SystemClock.elapsedRealtime();
+            FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
+                    FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__FRAMEWORK_BOOT_COMPLETED,
+                    elapsedTimeMs);
         }
         final Intent bootIntent = new Intent(Intent.ACTION_BOOT_COMPLETED, null);
         bootIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3ffe1be..5e1582c 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -34,6 +34,7 @@
 import static android.app.AppOpsManager.OP_COARSE_LOCATION;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
 import static android.app.AppOpsManager.OP_FLAG_SELF;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
 import static android.app.AppOpsManager.OP_NONE;
 import static android.app.AppOpsManager.OP_PLAY_AUDIO;
 import static android.app.AppOpsManager.OP_RECORD_AUDIO;
@@ -52,10 +53,14 @@
 import static android.app.AppOpsManager.makeKey;
 import static android.app.AppOpsManager.modeToName;
 import static android.app.AppOpsManager.opToName;
+import static android.app.AppOpsManager.opToPublicName;
 import static android.app.AppOpsManager.resolveFirstUnrestrictedUidState;
 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
 import static android.content.Intent.EXTRA_REPLACING;
 import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS;
+import static android.content.pm.PermissionInfo.PROTECTION_FLAG_APPOP;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+import static android.util.StatsLogInternal.RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
 
 import static com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
 
@@ -78,6 +83,8 @@
 import android.app.AppOpsManagerInternal;
 import android.app.AppOpsManagerInternal.CheckOpsDelegate;
 import android.app.AsyncNotedAppOp;
+import android.app.RuntimeAppOpAccessMessage;
+import android.app.SyncNotedAppOp;
 import android.compat.Compatibility;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
@@ -86,6 +93,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.PermissionInfo;
@@ -136,6 +144,7 @@
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.MessageSamplingConfig;
 import com.android.internal.os.Zygote;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
@@ -146,6 +155,7 @@
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemServerInitThreadPool;
+import com.android.server.pm.PackageList;
 
 import libcore.util.EmptyArray;
 
@@ -162,6 +172,8 @@
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -171,6 +183,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.function.Consumer;
 
 public class AppOpsService extends IAppOpsService.Stub {
     static final String TAG = "AppOps";
@@ -307,6 +321,34 @@
 
     private ActivityManagerInternal mActivityManagerInternal;
 
+    /** Package sampled for message collection in the current session */
+    @GuardedBy("this")
+    private String mSampledPackage = null;
+
+    /** Appop sampled for message collection in the current session */
+    @GuardedBy("this")
+    private int mSampledAppOpCode = OP_NONE;
+
+    /** Maximum distance for appop to be considered for message collection in the current session */
+    @GuardedBy("this")
+    private int mAcceptableLeftDistance = 0;
+
+    /** Number of messages collected for sampled package and appop in the current session */
+    @GuardedBy("this")
+    private float mMessagesCollectedCount;
+
+    /** List of rarely used packages priorities for message collection */
+    @GuardedBy("this")
+    private ArraySet<String> mRarelyUsedPackages = new ArraySet<>();
+
+    /** Sampling strategy used for current session */
+    @GuardedBy("this")
+    @AppOpsManager.SamplingStrategy
+    private int mSamplingStrategy;
+
+    /** Last runtime permission access message collected and ready for reporting */
+    @GuardedBy("this")
+    private RuntimeAppOpAccessMessage mCollectedRuntimePermissionMessage;
     /**
      * An unsynchronized pool of {@link OpEventProxyInfo} objects.
      */
@@ -1235,9 +1277,16 @@
             UserHandle.formatUid(sb, mWatchingUid);
             sb.append(" flags=0x");
             sb.append(Integer.toHexString(mFlags));
-            if (mWatchedOpCode != OP_NONE) {
-                sb.append(" op=");
-                sb.append(opToName(mWatchedOpCode));
+            switch (mWatchedOpCode) {
+                case OP_NONE:
+                    break;
+                case ALL_OPS:
+                    sb.append(" op=(all)");
+                    break;
+                default:
+                    sb.append(" op=");
+                    sb.append(opToName(mWatchedOpCode));
+                    break;
             }
             sb.append(" from uid=");
             UserHandle.formatUid(sb, mCallingUid);
@@ -1534,6 +1583,38 @@
             }
         }, packageSuspendFilter);
 
+        final IntentFilter packageAddedFilter = new IntentFilter();
+        packageAddedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageAddedFilter.addDataScheme("package");
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final Uri data = intent.getData();
+
+                final String packageName = data.getSchemeSpecificPart();
+                PackageInfo pi = LocalServices.getService(
+                        PackageManagerInternal.class).getPackageInfo(packageName,
+                        PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
+                if (isSamplingTarget(pi)) {
+                    synchronized (this) {
+                        mRarelyUsedPackages.add(packageName);
+                    }
+                }
+            }
+        }, packageAddedFilter);
+
+        List<String> packageNames = getPackageNamesForSampling();
+        synchronized (this) {
+            resamplePackageAndAppOpLocked(packageNames);
+        }
+
+        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
+            @Override
+            public void run() {
+                initializeRarelyUsedPackagesList(new ArraySet<>(packageNames));
+            }
+        });
+
         PackageManagerInternal packageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
         packageManagerInternal.setExternalSourcesPolicy(
@@ -3029,6 +3110,9 @@
                         featureId, message, System.currentTimeMillis());
                 final boolean[] wasNoteForwarded = {false};
 
+                reportRuntimeAppOpAccessMessageAsyncLocked(uid, packageName, opCode, featureId,
+                        message);
+
                 if (callbacks != null) {
                     callbacks.broadcast((cb) -> {
                         try {
@@ -3365,7 +3449,8 @@
             return false;
         }
 
-        return permInfo.getProtection() == PROTECTION_DANGEROUS;
+        return permInfo.getProtection() == PROTECTION_DANGEROUS
+                || (permInfo.getProtectionFlags() & PROTECTION_FLAG_APPOP) != 0;
     }
 
     private void verifyIncomingUid(int uid) {
@@ -5492,6 +5577,227 @@
         mHistoricalRegistry.clearHistory();
     }
 
+    /**
+     * Report runtime access to AppOp together with message (including stack trace)
+     *
+     * @param packageName The package which reported the op
+     * @param notedAppOp contains code of op and featureId provided by developer
+     * @param message Message describing AppOp access (can be stack trace)
+     *
+     * @return Config for future sampling to reduce amount of reporting
+     */
+    @Override
+    public MessageSamplingConfig reportRuntimeAppOpAccessMessageAndGetConfig(
+            String packageName, SyncNotedAppOp notedAppOp, String message) {
+        int uid = Binder.getCallingUid();
+        Objects.requireNonNull(packageName);
+        synchronized (this) {
+            switchPackageIfRarelyUsedLocked(packageName);
+            if (!packageName.equals(mSampledPackage)) {
+                return new MessageSamplingConfig(OP_NONE, 0,
+                        Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
+            }
+
+            Objects.requireNonNull(notedAppOp);
+            Objects.requireNonNull(message);
+
+            reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName,
+                    AppOpsManager.strOpToOp(notedAppOp.getOp()),
+                    notedAppOp.getFeatureId(), message);
+
+            return new MessageSamplingConfig(mSampledAppOpCode, mAcceptableLeftDistance,
+                    Instant.now().plus(1, ChronoUnit.HOURS).toEpochMilli());
+        }
+    }
+
+    /**
+     * Report runtime access to AppOp together with message (entry point for reporting
+     * asynchronous access)
+     * @param uid Uid of the package which reported the op
+     * @param packageName The package which reported the op
+     * @param opCode Code of AppOp
+     * @param featureId FeautreId of AppOp reported
+     * @param message Message describing AppOp access (can be stack trace)
+     */
+    private void reportRuntimeAppOpAccessMessageAsyncLocked(int uid,
+            @NonNull String packageName, int opCode, @Nullable String featureId,
+            @NonNull String message) {
+        switchPackageIfRarelyUsedLocked(packageName);
+        if (!Objects.equals(mSampledPackage, packageName)) {
+            return;
+        }
+        reportRuntimeAppOpAccessMessageInternalLocked(uid, packageName, opCode, featureId, message);
+    }
+
+    /**
+     * Decides whether reported message is within the range of watched AppOps and picks it for
+     * reporting uniformly at random across all received messages.
+     */
+    private void reportRuntimeAppOpAccessMessageInternalLocked(int uid,
+            @NonNull String packageName, int opCode, @Nullable String featureId,
+            @NonNull String message) {
+        int newLeftDistance = AppOpsManager.leftCircularDistance(opCode,
+                mSampledAppOpCode, _NUM_OP);
+
+        if (mAcceptableLeftDistance < newLeftDistance) {
+            return;
+        }
+
+        if (mAcceptableLeftDistance > newLeftDistance) {
+            mAcceptableLeftDistance = newLeftDistance;
+            mMessagesCollectedCount = 0.0f;
+        }
+
+        mMessagesCollectedCount += 1.0f;
+        if (ThreadLocalRandom.current().nextFloat() <= 1.0f / mMessagesCollectedCount) {
+            mCollectedRuntimePermissionMessage = new RuntimeAppOpAccessMessage(uid, opCode,
+                    packageName, featureId, message, mSamplingStrategy);
+        }
+        return;
+    }
+
+    /** Pulls current AppOps access report and resamples package and app op to watch */
+    @Override
+    public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
+        mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
+                Binder.getCallingPid(), Binder.getCallingUid(), null);
+        RuntimeAppOpAccessMessage result;
+        List<String> packageNames = getPackageNamesForSampling();
+        synchronized (this) {
+            result = mCollectedRuntimePermissionMessage;
+            resamplePackageAndAppOpLocked(packageNames);
+        }
+        return result;
+    }
+
+    /**
+     * Checks if package is in the list of rarely used package and starts watching the new package
+     * to collect incoming message.
+     * @param packageName
+     */
+    private void switchPackageIfRarelyUsedLocked(@NonNull String packageName) {
+        if (mRarelyUsedPackages.contains(packageName)) {
+            mRarelyUsedPackages.remove(packageName);
+            if (ThreadLocalRandom.current().nextFloat() < 0.5f) {
+                mSamplingStrategy = RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__RARELY_USED;
+                resampleAppOpForPackageLocked(packageName);
+            }
+        }
+    }
+
+    /** Resamples package and appop to watch from the list provided. */
+    private void resamplePackageAndAppOpLocked(@NonNull List<String> packageNames) {
+        if (!packageNames.isEmpty()) {
+            mSamplingStrategy = RUNTIME_APP_OP_ACCESS__SAMPLING_STRATEGY__UNIFORM;
+            resampleAppOpForPackageLocked(packageNames.get(
+                    ThreadLocalRandom.current().nextInt(packageNames.size())));
+        }
+    }
+
+    /** Resamples appop for the chosen package and initializes sampling state */
+    private void resampleAppOpForPackageLocked(@NonNull String packageName) {
+        mMessagesCollectedCount = 0.0f;
+        mSampledAppOpCode = ThreadLocalRandom.current().nextInt(_NUM_OP);
+        mAcceptableLeftDistance = _NUM_OP;
+        mSampledPackage = packageName;
+        mCollectedRuntimePermissionMessage = null;
+    }
+
+    /**
+     * Creates list of rarely used packages - packages which were not used over last week or
+     * which declared but did not use permissions over last week.
+     *  */
+    private void initializeRarelyUsedPackagesList(@NonNull ArraySet<String> candidates) {
+        AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class);
+        List<String> runtimeAppOpsList = getRuntimeAppOpsList();
+        AppOpsManager.HistoricalOpsRequest histOpsRequest =
+                new AppOpsManager.HistoricalOpsRequest.Builder(
+                        Instant.now().minus(7, ChronoUnit.DAYS).toEpochMilli(),
+                        Long.MAX_VALUE).setOpNames(runtimeAppOpsList).setFlags(
+                        OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED).build();
+        appOps.getHistoricalOps(histOpsRequest, AsyncTask.THREAD_POOL_EXECUTOR,
+                new Consumer<HistoricalOps>() {
+                    @Override
+                    public void accept(HistoricalOps histOps) {
+                        int uidCount = histOps.getUidCount();
+                        for (int uidIdx = 0; uidIdx < uidCount; uidIdx++) {
+                            final AppOpsManager.HistoricalUidOps uidOps = histOps.getUidOpsAt(
+                                    uidIdx);
+                            int pkgCount = uidOps.getPackageCount();
+                            for (int pkgIdx = 0; pkgIdx < pkgCount; pkgIdx++) {
+                                String packageName = uidOps.getPackageOpsAt(
+                                        pkgIdx).getPackageName();
+                                if (!candidates.contains(packageName)) {
+                                    continue;
+                                }
+                                AppOpsManager.HistoricalPackageOps packageOps =
+                                        uidOps.getPackageOpsAt(pkgIdx);
+                                if (packageOps.getOpCount() != 0) {
+                                    candidates.remove(packageName);
+                                }
+                            }
+                        }
+                        synchronized (this) {
+                            mRarelyUsedPackages = candidates;
+                        }
+                    }
+                });
+    }
+
+    /** List of app ops related to runtime permissions */
+    private List<String> getRuntimeAppOpsList() {
+        ArrayList<String> result = new ArrayList();
+        for (int i = 0; i < _NUM_OP; i++) {
+            if (shouldCollectNotes(i)) {
+                result.add(opToPublicName(i));
+            }
+        }
+        return result;
+    }
+
+    /** Returns list of packages to be used for package sampling */
+    private @NonNull List<String> getPackageNamesForSampling() {
+        List<String> packageNames = new ArrayList<>();
+        PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
+        PackageList packages = packageManagerInternal.getPackageList();
+        for (String packageName : packages.getPackageNames()) {
+            PackageInfo pkg = packageManagerInternal.getPackageInfo(packageName,
+                    PackageManager.GET_PERMISSIONS, Process.myUid(), mContext.getUserId());
+            if (isSamplingTarget(pkg)) {
+                packageNames.add(pkg.packageName);
+            }
+        }
+        return packageNames;
+    }
+
+    /** Checks whether package should be included in sampling pool */
+    private boolean isSamplingTarget(@Nullable PackageInfo pkg) {
+        if (pkg == null) {
+            return false;
+        }
+        if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.Q) {
+            return false;
+        }
+
+        String[] requestedPermissions = pkg.requestedPermissions;
+        if (requestedPermissions == null) {
+            return false;
+        }
+        for (String permission : requestedPermissions) {
+            PermissionInfo permissionInfo;
+            try {
+                permissionInfo = mContext.getPackageManager().getPermissionInfo(permission, 0);
+            } catch (PackageManager.NameNotFoundException ignored) {
+                continue;
+            }
+            if (permissionInfo.getProtection() == PROTECTION_DANGEROUS) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     private void removeUidsForUserLocked(int userHandle) {
         for (int i = mUidStates.size() - 1; i >= 0; --i) {
             final int uid = mUidStates.keyAt(i);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 67d7530..4be74b5 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -30,6 +30,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
@@ -158,6 +159,7 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 import java.util.Objects;
 import java.util.Set;
@@ -1001,11 +1003,14 @@
         mDeviceBroker.onAudioServerDied();
 
         // Restore call state
-        if (AudioSystem.setPhoneState(mMode) ==  AudioSystem.AUDIO_STATUS_OK) {
-            mModeLogger.log(new AudioEventLogger.StringEvent(
-                "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode) + ")"));
+        synchronized (mDeviceBroker.mSetModeLock) {
+            if (AudioSystem.setPhoneState(mMode, getModeOwnerUid())
+                    ==  AudioSystem.AUDIO_STATUS_OK) {
+                mModeLogger.log(new AudioEventLogger.StringEvent(
+                        "onAudioServerDied causes setPhoneState(" + AudioSystem.modeToString(mMode)
+                        + ", uid=" + getModeOwnerUid() + ")"));
+            }
         }
-
         final int forSys;
         synchronized (mSettingsLock) {
             forSys = mCameraSoundForced ?
@@ -1072,6 +1077,15 @@
                             + AudioSystem.audioSystemErrorToString(status)
                             + " when connecting mixes for policy " + policy.toLogFriendlyString());
                     policy.release();
+                } else {
+                    final int deviceAffinitiesStatus = policy.setupDeviceAffinities();
+                    if (deviceAffinitiesStatus != AudioSystem.SUCCESS) {
+                        Log.e(TAG, "onAudioServerDied: error "
+                                + AudioSystem.audioSystemErrorToString(deviceAffinitiesStatus)
+                                + " when connecting device affinities for policy "
+                                + policy.toLogFriendlyString());
+                        policy.release();
+                    }
                 }
             }
         }
@@ -3425,14 +3439,30 @@
         return modeOwnerPid;
     }
 
+    /**
+     * Return the uid of the current audio mode owner
+     * @return 0 if nobody owns the mode
+     */
+    /*package*/ int getModeOwnerUid() {
+        int modeOwnerUid = 0;
+        try {
+            modeOwnerUid = mSetModeDeathHandlers.get(0).getUid();
+        } catch (Exception e) {
+            // nothing to do, modeOwnerUid is not modified
+        }
+        return modeOwnerUid;
+    }
+
     private class SetModeDeathHandler implements IBinder.DeathRecipient {
-        private IBinder mCb; // To be notified of client's death
-        private int mPid;
+        private final IBinder mCb; // To be notified of client's death
+        private final int mPid;
+        private final int mUid;
         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
 
-        SetModeDeathHandler(IBinder cb, int pid) {
+        SetModeDeathHandler(IBinder cb, int pid, int uid) {
             mCb = cb;
             mPid = pid;
+            mUid = uid;
         }
 
         public void binderDied() {
@@ -3445,7 +3475,7 @@
                 if (index < 0) {
                     Log.w(TAG, "unregistered setMode() client died");
                 } else {
-                    newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, TAG);
+                    newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid, mUid, TAG);
                 }
             }
             // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
@@ -3470,6 +3500,10 @@
         public IBinder getBinder() {
             return mCb;
         }
+
+        public int getUid() {
+            return mUid;
+        }
     }
 
     /** @see AudioManager#setMode(int) */
@@ -3518,7 +3552,8 @@
                         + " without permission or being mode owner");
                 return;
             }
-            newModeOwnerPid = setModeInt(mode, cb, callingPid, callingPackage);
+            newModeOwnerPid = setModeInt(
+                mode, cb, callingPid, Binder.getCallingUid(), callingPackage);
         }
         // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
         // SCO connections not started by the application changing the mode when pid changes
@@ -3530,9 +3565,11 @@
     // setModeInt() returns a valid PID if the audio mode was successfully set to
     // any mode other than NORMAL.
     @GuardedBy("mDeviceBroker.mSetModeLock")
-    private int setModeInt(int mode, IBinder cb, int pid, String caller) {
-        if (DEBUG_MODE) { Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid + ", caller="
-                + caller + ")"); }
+    private int setModeInt(int mode, IBinder cb, int pid, int uid, String caller) {
+        if (DEBUG_MODE) {
+            Log.v(TAG, "setModeInt(mode=" + mode + ", pid=" + pid
+                    + ", uid=" + uid + ", caller=" + caller + ")");
+        }
         int newModeOwnerPid = 0;
         if (cb == null) {
             Log.e(TAG, "setModeInt() called with null binder");
@@ -3569,7 +3606,7 @@
                 }
             } else {
                 if (hdlr == null) {
-                    hdlr = new SetModeDeathHandler(cb, pid);
+                    hdlr = new SetModeDeathHandler(cb, pid, uid);
                 }
                 // Register for client death notification
                 try {
@@ -3587,7 +3624,7 @@
 
             if (actualMode != mMode) {
                 final long identity = Binder.clearCallingIdentity();
-                status = AudioSystem.setPhoneState(actualMode);
+                status = AudioSystem.setPhoneState(actualMode, getModeOwnerUid());
                 Binder.restoreCallingIdentity(identity);
                 if (status == AudioSystem.AUDIO_STATUS_OK) {
                     if (DEBUG_MODE) { Log.v(TAG, " mode successfully set to " + actualMode); }
@@ -6634,7 +6671,7 @@
     static final int LOG_NB_EVENTS_DYN_POLICY = 10;
 
     final private AudioEventLogger mModeLogger = new AudioEventLogger(LOG_NB_EVENTS_PHONE_STATE,
-            "phone state (logged after successfull call to AudioSystem.setPhoneState(int))");
+            "phone state (logged after successful call to AudioSystem.setPhoneState(int, int))");
 
     // logs for wired + A2DP device connections:
     // - wired: logged before onSetWiredDeviceConnectionState() is executed
@@ -7872,22 +7909,16 @@
 
         int setUidDeviceAffinities(int uid, @NonNull int[] types, @NonNull String[] addresses) {
             final Integer Uid = new Integer(uid);
-            int res;
             if (mUidDeviceAffinities.remove(Uid) != null) {
-                final long identity = Binder.clearCallingIdentity();
-                res = AudioSystem.removeUidDeviceAffinities(uid);
-                Binder.restoreCallingIdentity(identity);
-                if (res != AudioSystem.SUCCESS) {
+                if (removeUidDeviceAffinitiesFromSystem(uid) != AudioSystem.SUCCESS) {
                     Log.e(TAG, "AudioSystem. removeUidDeviceAffinities(" + uid + ") failed, "
                             + " cannot call AudioSystem.setUidDeviceAffinities");
                     return AudioManager.ERROR;
                 }
             }
-            final long identity = Binder.clearCallingIdentity();
-            res = AudioSystem.setUidDeviceAffinities(uid, types, addresses);
-            Binder.restoreCallingIdentity(identity);
-            if (res == AudioSystem.SUCCESS) {
-                mUidDeviceAffinities.put(Uid, new AudioDeviceArray(types, addresses));
+            AudioDeviceArray deviceArray = new AudioDeviceArray(types, addresses);
+            if (setUidDeviceAffinitiesOnSystem(uid, deviceArray) == AudioSystem.SUCCESS) {
+                mUidDeviceAffinities.put(Uid, deviceArray);
                 return AudioManager.SUCCESS;
             }
             Log.e(TAG, "AudioSystem. setUidDeviceAffinities(" + uid + ") failed");
@@ -7896,10 +7927,7 @@
 
         int removeUidDeviceAffinities(int uid) {
             if (mUidDeviceAffinities.remove(new Integer(uid)) != null) {
-                final long identity = Binder.clearCallingIdentity();
-                final int res = AudioSystem.removeUidDeviceAffinities(uid);
-                Binder.restoreCallingIdentity(identity);
-                if (res == AudioSystem.SUCCESS) {
+                if (removeUidDeviceAffinitiesFromSystem(uid) == AudioSystem.SUCCESS) {
                     return AudioManager.SUCCESS;
                 }
             }
@@ -7907,26 +7935,41 @@
             return AudioManager.ERROR;
         }
 
+        @AudioSystem.AudioSystemError private int removeUidDeviceAffinitiesFromSystem(int uid) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return AudioSystem.removeUidDeviceAffinities(uid);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @AudioSystem.AudioSystemError private int setUidDeviceAffinitiesOnSystem(int uid,
+                AudioDeviceArray deviceArray) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return AudioSystem.setUidDeviceAffinities(uid, deviceArray.mDeviceTypes,
+                        deviceArray.mDeviceAddresses);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
         int setUserIdDeviceAffinities(int userId,
                 @NonNull int[] types, @NonNull String[] addresses) {
             final Integer UserId = new Integer(userId);
-            int res;
             if (mUserIdDeviceAffinities.remove(UserId) != null) {
-                final long identity = Binder.clearCallingIdentity();
-                res = AudioSystem.removeUserIdDeviceAffinities(UserId);
-                Binder.restoreCallingIdentity(identity);
-                if (res != AudioSystem.SUCCESS) {
+                if (removeUserIdDeviceAffinitiesFromSystem(userId) != AudioSystem.SUCCESS) {
                     Log.e(TAG, "AudioSystem. removeUserIdDeviceAffinities("
                             + UserId + ") failed, "
                             + " cannot call AudioSystem.setUserIdDeviceAffinities");
                     return AudioManager.ERROR;
                 }
             }
-            final long identity = Binder.clearCallingIdentity();
-            res = AudioSystem.setUserIdDeviceAffinities(userId, types, addresses);
-            Binder.restoreCallingIdentity(identity);
-            if (res == AudioSystem.SUCCESS) {
-                mUserIdDeviceAffinities.put(UserId, new AudioDeviceArray(types, addresses));
+            AudioDeviceArray audioDeviceArray = new AudioDeviceArray(types, addresses);
+            if (setUserIdDeviceAffinitiesOnSystem(userId, audioDeviceArray)
+                    == AudioSystem.SUCCESS) {
+                mUserIdDeviceAffinities.put(UserId, audioDeviceArray);
                 return AudioManager.SUCCESS;
             }
             Log.e(TAG, "AudioSystem.setUserIdDeviceAffinities(" + userId + ") failed");
@@ -7935,10 +7978,7 @@
 
         int removeUserIdDeviceAffinities(int userId) {
             if (mUserIdDeviceAffinities.remove(new Integer(userId)) != null) {
-                final long identity = Binder.clearCallingIdentity();
-                final int res = AudioSystem.removeUserIdDeviceAffinities(userId);
-                Binder.restoreCallingIdentity(identity);
-                if (res == AudioSystem.SUCCESS) {
+                if (removeUserIdDeviceAffinitiesFromSystem(userId) == AudioSystem.SUCCESS) {
                     return AudioManager.SUCCESS;
                 }
             }
@@ -7946,9 +7986,76 @@
             return AudioManager.ERROR;
         }
 
+        @AudioSystem.AudioSystemError private int removeUserIdDeviceAffinitiesFromSystem(
+                @UserIdInt int userId) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return AudioSystem.removeUserIdDeviceAffinities(userId);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @AudioSystem.AudioSystemError private int setUserIdDeviceAffinitiesOnSystem(
+                @UserIdInt int userId, AudioDeviceArray deviceArray) {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                return AudioSystem.setUserIdDeviceAffinities(userId, deviceArray.mDeviceTypes,
+                        deviceArray.mDeviceAddresses);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        @AudioSystem.AudioSystemError int setupDeviceAffinities() {
+            for (Map.Entry<Integer, AudioDeviceArray> uidEntry : mUidDeviceAffinities.entrySet()) {
+                int uidStatus = removeUidDeviceAffinitiesFromSystem(uidEntry.getKey());
+                if (uidStatus != AudioSystem.SUCCESS) {
+                    Log.e(TAG,
+                            "setupDeviceAffinities failed to remove device affinity for uid "
+                                    + uidEntry.getKey());
+                    return uidStatus;
+                }
+                uidStatus = setUidDeviceAffinitiesOnSystem(uidEntry.getKey(), uidEntry.getValue());
+                if (uidStatus != AudioSystem.SUCCESS) {
+                    Log.e(TAG,
+                            "setupDeviceAffinities failed to set device affinity for uid "
+                                    + uidEntry.getKey());
+                    return uidStatus;
+                }
+            }
+
+            for (Map.Entry<Integer, AudioDeviceArray> userIdEntry :
+                    mUserIdDeviceAffinities.entrySet()) {
+                int userIdStatus = removeUserIdDeviceAffinitiesFromSystem(userIdEntry.getKey());
+                if (userIdStatus != AudioSystem.SUCCESS) {
+                    Log.e(TAG,
+                            "setupDeviceAffinities failed to remove device affinity for userId "
+                                    + userIdEntry.getKey());
+                    return userIdStatus;
+                }
+                userIdStatus = setUserIdDeviceAffinitiesOnSystem(userIdEntry.getKey(),
+                                userIdEntry.getValue());
+                if (userIdStatus != AudioSystem.SUCCESS) {
+                    Log.e(TAG,
+                            "setupDeviceAffinities failed to set device affinity for userId "
+                                    + userIdEntry.getKey());
+                    return userIdStatus;
+                }
+            }
+            return AudioSystem.SUCCESS;
+        }
+
         /** @return human readable debug informations summarizing the state of the object. */
         public String toLogFriendlyString() {
             String textDump = super.toLogFriendlyString();
+            textDump += " Uid Device Affinities:\n";
+            String spacer = "     ";
+            textDump += logFriendlyAttributeDeviceArrayMap("Uid",
+                    mUidDeviceAffinities, spacer);
+            textDump += " UserId Device Affinities:\n";
+            textDump += logFriendlyAttributeDeviceArrayMap("UserId",
+                    mUserIdDeviceAffinities, spacer);
             textDump += " Proxy:\n";
             textDump += "   is focus policy= " + mIsFocusPolicy + "\n";
             if (mIsFocusPolicy) {
@@ -7959,6 +8066,24 @@
             textDump += "   media projection= " + mProjection + "\n";
             return textDump;
         }
+
+        private String logFriendlyAttributeDeviceArrayMap(String attribute,
+                Map<Integer, AudioDeviceArray> map, String spacer) {
+            final StringBuilder stringBuilder = new StringBuilder();
+            for (Map.Entry<Integer, AudioDeviceArray> mapEntry : map.entrySet()) {
+                stringBuilder.append(spacer).append(attribute).append(": ")
+                        .append(mapEntry.getKey()).append("\n");
+                AudioDeviceArray deviceArray = mapEntry.getValue();
+                String deviceSpacer = spacer + "   ";
+                for (int i = 0; i < deviceArray.mDeviceTypes.length; i++) {
+                    stringBuilder.append(deviceSpacer).append("Type: 0x")
+                            .append(Integer.toHexString(deviceArray.mDeviceTypes[i]))
+                            .append(" Address: ").append(deviceArray.mDeviceAddresses[i])
+                                    .append("\n");
+                }
+            }
+            return stringBuilder.toString();
+        }
     };
 
     //======================
diff --git a/services/core/java/com/android/server/connectivity/NetworkRanker.java b/services/core/java/com/android/server/connectivity/NetworkRanker.java
index d0aabf95..1ae7dc5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkRanker.java
+++ b/services/core/java/com/android/server/connectivity/NetworkRanker.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.net.NetworkRequest;
 
+import java.util.ArrayList;
 import java.util.Collection;
 
 /**
@@ -31,15 +32,15 @@
     /**
      * Find the best network satisfying this request among the list of passed networks.
      */
-    // Almost equivalent to Collections.max(nais), but allows returning null if no network
-    // satisfies the request.
     @Nullable
     public NetworkAgentInfo getBestNetwork(@NonNull final NetworkRequest request,
             @NonNull final Collection<NetworkAgentInfo> nais) {
+        final ArrayList<NetworkAgentInfo> candidates = new ArrayList<>(nais);
+        candidates.removeIf(nai -> !nai.satisfies(request));
+
         NetworkAgentInfo bestNetwork = null;
         int bestScore = Integer.MIN_VALUE;
-        for (final NetworkAgentInfo nai : nais) {
-            if (!nai.satisfies(request)) continue;
+        for (final NetworkAgentInfo nai : candidates) {
             if (nai.getCurrentScore() > bestScore) {
                 bestNetwork = nai;
                 bestScore = nai.getCurrentScore();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 9140589..1ff8a94 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -121,7 +121,7 @@
  * The display manager service relies on a collection of {@link DisplayAdapter} components,
  * for discovering and configuring physical display devices attached to the system.
  * There are separate display adapters for each manner that devices are attached:
- * one display adapter for built-in local displays, one for simulated non-functional
+ * one display adapter for physical displays, one for simulated non-functional
  * displays when the system is headless, one for simulated overlay displays used for
  * development, one for wifi displays, etc.
  * </p><p>
@@ -231,7 +231,7 @@
     private int mGlobalDisplayState = Display.STATE_ON;
 
     // The overall display brightness.
-    // For now, this only applies to the built-in display but we may split it up eventually.
+    // For now, this only applies to the default display but we may split it up eventually.
     private float mGlobalDisplayBrightness;
 
     // Set to true when there are pending display changes that have yet to be applied
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 2b225e5..4ebbdda 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -35,7 +35,6 @@
 import android.view.DisplayAddress;
 import android.view.DisplayCutout;
 import android.view.DisplayEventReceiver;
-import android.view.Surface;
 import android.view.SurfaceControl;
 
 import com.android.internal.BrightnessSynchronizer;
@@ -53,7 +52,7 @@
 import java.util.Objects;
 
 /**
- * A display adapter for the local displays managed by Surface Flinger.
+ * A display adapter for the local displays managed by SurfaceFlinger.
  * <p>
  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
  * </p>
@@ -129,10 +128,10 @@
             LocalDisplayDevice device = mDevices.get(physicalDisplayId);
             if (device == null) {
                 // Display was added.
-                final boolean isInternal = mDevices.size() == 0;
+                final boolean isDefaultDisplay = mDevices.size() == 0;
                 device = new LocalDisplayDevice(displayToken, physicalDisplayId, info,
                         configs, activeConfig, configSpecs, colorModes, activeColorMode,
-                        hdrCapabilities, isInternal);
+                        hdrCapabilities, isDefaultDisplay);
                 mDevices.put(physicalDisplayId, device);
                 sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
             } else if (device.updateDisplayProperties(configs, activeConfig,
@@ -175,7 +174,7 @@
         private final LogicalLight mBacklight;
         private final SparseArray<DisplayModeRecord> mSupportedModes = new SparseArray<>();
         private final ArrayList<Integer> mSupportedColorModes = new ArrayList<>();
-        private final boolean mIsInternal;
+        private final boolean mIsDefaultDisplay;
 
         private DisplayDeviceInfo mInfo;
         private boolean mHavePendingChanges;
@@ -207,15 +206,15 @@
                 SurfaceControl.DisplayInfo info, SurfaceControl.DisplayConfig[] configs,
                 int activeConfigId, SurfaceControl.DesiredDisplayConfigSpecs configSpecs,
                 int[] colorModes, int activeColorMode, Display.HdrCapabilities hdrCapabilities,
-                boolean isInternal) {
+                boolean isDefaultDisplay) {
             super(LocalDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + physicalDisplayId);
             mPhysicalDisplayId = physicalDisplayId;
-            mIsInternal = isInternal;
+            mIsDefaultDisplay = isDefaultDisplay;
             mDisplayInfo = info;
             updateDisplayProperties(configs, activeConfigId, configSpecs, colorModes,
                     activeColorMode, hdrCapabilities);
             mSidekickInternal = LocalServices.getService(SidekickInternal.class);
-            if (mIsInternal) {
+            if (mIsDefaultDisplay) {
                 LightsManager lights = LocalServices.getService(LightsManager.class);
                 mBacklight = lights.getLight(LightsManager.LIGHT_ID_BACKLIGHT);
             } else {
@@ -523,11 +522,10 @@
                 }
 
                 final Resources res = getOverlayContext().getResources();
-                if (mIsInternal) {
-                    mInfo.name = res.getString(
-                            com.android.internal.R.string.display_manager_built_in_display_name);
-                    mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY
-                            | DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+
+                if (mIsDefaultDisplay) {
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY;
+
                     if (res.getBoolean(com.android.internal.R.bool.config_mainBuiltInDisplayIsRound)
                             || (Build.IS_EMULATOR
                             && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))) {
@@ -539,28 +537,7 @@
                     }
                     mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
                             mInfo.width, mInfo.height);
-                    mInfo.type = Display.TYPE_BUILT_IN;
-                    mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
                 } else {
-                    mInfo.displayCutout = null;
-                    mInfo.type = Display.TYPE_HDMI;
-                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
-                    mInfo.name = getContext().getResources().getString(
-                            com.android.internal.R.string.display_manager_hdmi_display_name);
-                    mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
-
-                    // For demonstration purposes, allow rotation of the external display.
-                    // In the future we might allow the user to configure this directly.
-                    if ("portrait".equals(SystemProperties.get("persist.demo.hdmirotation"))) {
-                        mInfo.rotation = Surface.ROTATION_270;
-                    }
-
-                    // For demonstration purposes, allow rotation of the external display
-                    // to follow the built-in display.
-                    if (SystemProperties.getBoolean("persist.demo.hdmirotates", false)) {
-                        mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
-                    }
-
                     if (!res.getBoolean(
                                 com.android.internal.R.bool.config_localDisplaysMirrorContent)) {
                         mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
@@ -570,6 +547,20 @@
                         mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
                     }
                 }
+
+                if (mDisplayInfo.isInternal) {
+                    mInfo.type = Display.TYPE_INTERNAL;
+                    mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
+                    mInfo.name = res.getString(
+                            com.android.internal.R.string.display_manager_built_in_display_name);
+                } else {
+                    mInfo.type = Display.TYPE_EXTERNAL;
+                    mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
+                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
+                    mInfo.name = getContext().getResources().getString(
+                            com.android.internal.R.string.display_manager_hdmi_display_name);
+                }
             }
             return mInfo;
         }
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 648e07a..ac3a653 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -395,7 +395,7 @@
     static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
 
     // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
-    //            True by default.
+    //            False by default.
     static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 0944324..5541b11 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -45,7 +45,7 @@
     private static final String TAG = "HdmiCecLocalDevicePlayback";
 
     private static final boolean WAKE_ON_HOTPLUG =
-            SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
+            SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, false);
 
     private static final boolean SET_MENU_LANGUAGE =
             HdmiProperties.set_menu_language().orElse(false);
diff --git a/services/core/java/com/android/server/integrity/TEST_MAPPING b/services/core/java/com/android/server/integrity/TEST_MAPPING
index b45b4ea..ca7f396 100644
--- a/services/core/java/com/android/server/integrity/TEST_MAPPING
+++ b/services/core/java/com/android/server/integrity/TEST_MAPPING
@@ -5,12 +5,6 @@
       "options": [
         {
           "include-filter": "com.android.server.integrity."
-        },
-        {
-          "include-annotation": "android.platform.test.annotations.Presubmit"
-        },
-        {
-          "exclude-annotation": "androidx.test.filters.FlakyTest"
         }
       ]
     }
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
index 997f21c..433ec43 100644
--- a/services/core/java/com/android/server/location/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -16,6 +16,8 @@
 
 package com.android.server.location;
 
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.location.Location;
@@ -335,7 +337,8 @@
      */
     public final void setRequest(ProviderRequest request) {
         // all calls into the provider must be moved onto the provider thread to prevent deadlock
-        mExecutor.execute(() -> onSetRequest(request));
+        mExecutor.execute(obtainRunnable(AbstractLocationProvider::onSetRequest, this, request)
+                .recycleOnUse());
     }
 
     /**
@@ -348,7 +351,13 @@
      */
     public final void sendExtraCommand(int uid, int pid, String command, Bundle extras) {
         // all calls into the provider must be moved onto the provider thread to prevent deadlock
-        mExecutor.execute(() -> onExtraCommand(uid, pid, command, extras));
+
+        // the integer boxing done here likely cancels out any gains from removing lambda
+        // allocation, but since this an infrequently used api with no real performance needs, we
+        // we use pooled lambdas anyways for consistency.
+        mExecutor.execute(
+                obtainRunnable(AbstractLocationProvider::onExtraCommand, this, uid, pid, command,
+                        extras).recycleOnUse());
     }
 
     /**
@@ -361,7 +370,9 @@
      */
     public final void requestSetAllowed(boolean allowed) {
         // all calls into the provider must be moved onto the provider thread to prevent deadlock
-        mExecutor.execute(() -> onRequestSetAllowed(allowed));
+        mExecutor.execute(
+                obtainRunnable(AbstractLocationProvider::onRequestSetAllowed, this, allowed)
+                        .recycleOnUse());
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/SettingsHelper.java b/services/core/java/com/android/server/location/SettingsHelper.java
index 370a3f7..5fe21bd 100644
--- a/services/core/java/com/android/server/location/SettingsHelper.java
+++ b/services/core/java/com/android/server/location/SettingsHelper.java
@@ -27,6 +27,9 @@
 import static android.provider.Settings.Secure.LOCATION_MODE;
 import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
 
+import static com.android.server.LocationManagerService.D;
+import static com.android.server.LocationManagerService.TAG;
+
 import android.app.ActivityManager;
 import android.content.Context;
 import android.database.ContentObserver;
@@ -37,6 +40,7 @@
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
@@ -423,6 +427,10 @@
 
         @Override
         public void onChange(boolean selfChange, Uri uri, int userId) {
+            if (D) {
+                Log.d(TAG, "location setting changed [u" + userId + "]: " + uri);
+            }
+
             for (UserSettingChangedListener listener : mListeners) {
                 listener.onSettingChanged(userId);
             }
diff --git a/services/core/java/com/android/server/location/UserInfoHelper.java b/services/core/java/com/android/server/location/UserInfoHelper.java
index a33e2da..28f3f47 100644
--- a/services/core/java/com/android/server/location/UserInfoHelper.java
+++ b/services/core/java/com/android/server/location/UserInfoHelper.java
@@ -18,6 +18,9 @@
 
 import static android.os.UserManager.DISALLOW_SHARE_LOCATION;
 
+import static com.android.server.LocationManagerService.D;
+import static com.android.server.LocationManagerService.TAG;
+
 import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -30,6 +33,7 @@
 import android.os.Build;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
@@ -122,13 +126,13 @@
                     case Intent.ACTION_USER_STARTED:
                         userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                         if (userId != UserHandle.USER_NULL) {
-                            onUserChanged(userId, UserListener.USER_STARTED);
+                            onUserStarted(userId);
                         }
                         break;
                     case Intent.ACTION_USER_STOPPED:
                         userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
                         if (userId != UserHandle.USER_NULL) {
-                            onUserChanged(userId, UserListener.USER_STOPPED);
+                            onUserStopped(userId);
                         }
                         break;
                     case Intent.ACTION_MANAGED_PROFILE_ADDED:
@@ -161,6 +165,10 @@
             return;
         }
 
+        if (D) {
+            Log.d(TAG, "current user switched from u" + mCurrentUserId + " to u" + newUserId);
+        }
+
         int oldUserId = mCurrentUserId;
         mCurrentUserId = newUserId;
 
@@ -168,6 +176,22 @@
         onUserChanged(newUserId, UserListener.USER_SWITCHED);
     }
 
+    private void onUserStarted(@UserIdInt int userId) {
+        if (D) {
+            Log.d(TAG, "u" + userId + " started");
+        }
+
+        onUserChanged(userId, UserListener.USER_STARTED);
+    }
+
+    private void onUserStopped(@UserIdInt int userId) {
+        if (D) {
+            Log.d(TAG, "u" + userId + " stopped");
+        }
+
+        onUserChanged(userId, UserListener.USER_STOPPED);
+    }
+
     private void onUserChanged(@UserIdInt int userId, @UserListener.UserChange int change) {
         for (UserListener listener : mListeners) {
             listener.onUserChanged(userId, change);
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 6474303..8379614 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -29,7 +29,6 @@
 import android.content.IntentFilter;
 import android.media.AudioManager;
 import android.media.MediaRoute2Info;
-import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 
@@ -49,6 +48,8 @@
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
     @SuppressWarnings("WeakerAccess") /* synthetic access */
+    BluetoothRouteInfo mSelectedRoute = null;
+    @SuppressWarnings("WeakerAccess") /* synthetic access */
     BluetoothA2dp mA2dpProfile;
     @SuppressWarnings("WeakerAccess") /* synthetic access */
     BluetoothHearingAid mHearingAidProfile;
@@ -62,8 +63,6 @@
     private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
     private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
 
-    private BluetoothDevice mActiveDevice = null;
-
     static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
             @NonNull BluetoothRoutesUpdatedListener listener) {
         Objects.requireNonNull(context);
@@ -108,27 +107,21 @@
     }
 
     /**
-     * Clears the active device for all known profiles.
+     * Transfers to a given bluetooth route.
+     * The dedicated BT device with the route would be activated.
+     *
+     * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
+     *               BT routes.
      */
-    public void clearActiveDevices() {
-        BluetoothA2dp a2dpProfile = mA2dpProfile;
-        BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
-        if (a2dpProfile != null) {
-            a2dpProfile.setActiveDevice(null);
+    public void transferTo(@Nullable String routeId) {
+        if (routeId == null) {
+            clearActiveDevices();
+            return;
         }
-        if (hearingAidProfile != null) {
-            hearingAidProfile.setActiveDevice(null);
-        }
-    }
 
-    /**
-     * Sets the active device.
-     * @param deviceId the id of the Bluetooth device
-     */
-    public void setActiveDevice(@NonNull String deviceId) {
-        BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(deviceId);
+        BluetoothRouteInfo btRouteInfo = mBluetoothRoutes.get(routeId);
         if (btRouteInfo == null) {
-            Slog.w(TAG, "setActiveDevice: unknown device id=" + deviceId);
+            Slog.w(TAG, "setActiveDevice: unknown route id=" + routeId);
             return;
         }
         BluetoothA2dp a2dpProfile = mA2dpProfile;
@@ -144,6 +137,20 @@
         }
     }
 
+    /**
+     * Clears the active device for all known profiles.
+     */
+    private void clearActiveDevices() {
+        BluetoothA2dp a2dpProfile = mA2dpProfile;
+        BluetoothHearingAid hearingAidProfile = mHearingAidProfile;
+        if (a2dpProfile != null) {
+            a2dpProfile.setActiveDevice(null);
+        }
+        if (hearingAidProfile != null) {
+            hearingAidProfile.setActiveDevice(null);
+        }
+    }
+
     private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) {
         mEventReceiverMap.put(action, eventReceiver);
         mIntentFilter.addAction(action);
@@ -159,7 +166,8 @@
         }
     }
 
-    @NonNull List<MediaRoute2Info> getBluetoothRoutes() {
+    @NonNull
+    List<MediaRoute2Info> getAllBluetoothRoutes() {
         ArrayList<MediaRoute2Info> routes = new ArrayList<>();
         for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
             routes.add(btRoute.route);
@@ -167,17 +175,14 @@
         return routes;
     }
 
-    @Nullable String getActiveDeviceAddress() {
-        BluetoothDevice device = mActiveDevice;
-        if (device == null) {
-            return null;
-        }
-        return device.getAddress();
+    @Nullable
+    String getSelectedRouteId() {
+        return mSelectedRoute == null ? null : mSelectedRoute.route.getId();
     }
 
     private void notifyBluetoothRoutesUpdated() {
         if (mListener != null) {
-            mListener.onBluetoothRoutesUpdated(getBluetoothRoutes());
+            mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
         }
     }
 
@@ -198,15 +203,10 @@
         return newBtRoute;
     }
 
-    private void setRouteConnectionStateForDevice(BluetoothDevice device,
+    private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute,
             @MediaRoute2Info.ConnectionState int state) {
-        if (device == null) {
-            Slog.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null");
-            return;
-        }
-        BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
         if (btRoute == null) {
-            Slog.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null");
+            Slog.w(TAG, "setRouteConnectionState: route shouldn't be null");
             return;
         }
         if (btRoute.route.getConnectionState() == state) {
@@ -260,8 +260,8 @@
                     mBluetoothRoutes.put(device.getAddress(), btRoute);
                 }
                 if (activeDevices.contains(device)) {
-                    mActiveDevice = device;
-                    setRouteConnectionStateForDevice(device,
+                    mSelectedRoute = btRoute;
+                    setRouteConnectionState(mSelectedRoute,
                             MediaRoute2Info.CONNECTION_STATE_CONNECTED);
                 }
 
@@ -344,20 +344,18 @@
         public void onReceive(Context context, Intent intent, BluetoothDevice device) {
             switch (intent.getAction()) {
                 case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
-                    String prevActiveDeviceAddress =
-                            (mActiveDevice == null) ? null : mActiveDevice.getAddress();
-                    String curActiveDeviceAddress =
-                            (device == null) ? null : device.getAddress();
-                    if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) {
-                        if (mActiveDevice != null) {
-                            setRouteConnectionStateForDevice(mActiveDevice,
+                    if (mSelectedRoute == null
+                            || !mSelectedRoute.btDevice.equals(device)) {
+                        if (mSelectedRoute != null) {
+                            setRouteConnectionState(mSelectedRoute,
                                     MediaRoute2Info.CONNECTION_STATE_DISCONNECTED);
                         }
-                        if (device != null) {
-                            setRouteConnectionStateForDevice(device,
+                        mSelectedRoute = (device == null) ? null
+                                : mBluetoothRoutes.get(device.getAddress());
+                        if (mSelectedRoute != null) {
+                            setRouteConnectionState(mSelectedRoute,
                                     MediaRoute2Info.CONNECTION_STATE_CONNECTED);
                         }
-                        mActiveDevice = device;
                         notifyBluetoothRoutesUpdated();
                     }
                     break;
@@ -386,10 +384,8 @@
                     btRoute.connectedProfiles.delete(profile);
                     if (btRoute.connectedProfiles.size() == 0) {
                         mBluetoothRoutes.remove(device.getAddress());
-                        if (mActiveDevice != null
-                                && TextUtils.equals(mActiveDevice.getAddress(),
-                                device.getAddress())) {
-                            mActiveDevice = null;
+                        if (mSelectedRoute != null && mSelectedRoute.btDevice.equals(device)) {
+                            mSelectedRoute = null;
                         }
                         notifyBluetoothRoutesUpdated();
                     }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 18383c4..7f7a663 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -145,9 +145,9 @@
     @Override
     public void transferToRoute(String sessionId, String routeId) {
         if (TextUtils.equals(routeId, mDefaultRoute.getId())) {
-            mBtRouteProvider.clearActiveDevices();
+            mBtRouteProvider.transferTo(null);
         } else {
-            mBtRouteProvider.setActiveDevice(routeId);
+            mBtRouteProvider.transferTo(routeId);
         }
     }
 
@@ -196,7 +196,7 @@
     }
 
     private void initializeSessionInfo() {
-        mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes();
+        mBluetoothRoutes = mBtRouteProvider.getAllBluetoothRoutes();
 
         MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
         builder.addRoute(mDefaultRoute);
@@ -251,18 +251,18 @@
         RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
                 SYSTEM_SESSION_ID, "" /* clientPackageName */)
                 .setSystemSession(true);
-        String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+        String activeBtDeviceAddress = mBtRouteProvider.getSelectedRouteId();
         mSelectedRouteId = TextUtils.isEmpty(activeBtDeviceAddress) ? mDefaultRoute.getId()
                 : activeBtDeviceAddress;
         builder.addSelectedRoute(mSelectedRouteId);
 
         if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
-            builder.addTransferrableRoute(mDefaultRoute.getId());
+            builder.addTransferableRoute(mDefaultRoute.getId());
         }
 
         for (MediaRoute2Info route : mBluetoothRoutes) {
             if (!TextUtils.equals(mSelectedRouteId, route.getId())) {
-                builder.addTransferrableRoute(route.getId());
+                builder.addTransferableRoute(route.getId());
             }
         }
 
@@ -311,7 +311,7 @@
                     AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
 
             if (newVolume != oldVolume) {
-                String activeBtDeviceAddress = mBtRouteProvider.getActiveDeviceAddress();
+                String activeBtDeviceAddress = mBtRouteProvider.getSelectedRouteId();
                 if (!TextUtils.isEmpty(activeBtDeviceAddress)) {
                     for (int i = mBluetoothRoutes.size() - 1; i >= 0; i--) {
                         MediaRoute2Info route = mBluetoothRoutes.get(i);
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java b/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java
index 2831d37..7ba993b 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryProtoHelper.java
@@ -19,6 +19,7 @@
 import android.app.NotificationHistory.HistoricalNotification;
 import android.content.res.Resources;
 import android.graphics.drawable.Icon;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.util.proto.ProtoInputStream;
 import android.util.proto.ProtoOutputStream;
@@ -141,6 +142,16 @@
                     loadIcon(parser, notification, pkg);
                     parser.end(iconToken);
                     break;
+                case (int) Notification.CONVERSATION_ID_INDEX:
+                    String conversationId =
+                            stringPool.get(parser.readInt(Notification.CONVERSATION_ID_INDEX) - 1);
+                    notification.setConversationId(conversationId);
+                    break;
+                case (int) Notification.CONVERSATION_ID:
+                    conversationId = parser.readString(Notification.CONVERSATION_ID);
+                    notification.setConversationId(conversationId);
+                    stringPool.add(conversationId);
+                    break;
                 case ProtoInputStream.NO_MORE_FIELDS:
                     return notification.build();
             }
@@ -271,6 +282,17 @@
                     + ") not found in string cache");
             proto.write(Notification.CHANNEL_ID, notification.getChannelId());
         }
+        if (!TextUtils.isEmpty(notification.getConversationId())) {
+            final int conversationIdIndex = Arrays.binarySearch(
+                    stringPool, notification.getConversationId());
+            if (conversationIdIndex >= 0) {
+                proto.write(Notification.CONVERSATION_ID_INDEX, conversationIdIndex + 1);
+            } else {
+                Slog.w(TAG, "notification conversation id (" + notification.getConversationId()
+                        + ") not found in string cache");
+                proto.write(Notification.CONVERSATION_ID, notification.getConversationId());
+            }
+        }
         proto.write(Notification.UID, notification.getUid());
         proto.write(Notification.USER_ID, notification.getUserId());
         proto.write(Notification.POSTED_TIME_MS, notification.getPostedTimeMs());
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d0d0f5a..2e4a977 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6243,6 +6243,7 @@
         private final int mRank;
         private final int mCount;
         private final ManagedServiceInfo mListener;
+        private final long mWhen;
 
         CancelNotificationRunnable(final int callingUid, final int callingPid,
                 final String pkg, final String tag, final int id,
@@ -6262,6 +6263,7 @@
             this.mRank = rank;
             this.mCount = count;
             this.mListener = listener;
+            this.mWhen = System.currentTimeMillis();
         }
 
         @Override
@@ -6273,13 +6275,28 @@
             }
 
             synchronized (mNotificationLock) {
-                // If the notification is currently enqueued, repost this runnable so it has a
-                // chance to notify listeners
-                if ((findNotificationByListLocked(mEnqueuedNotifications, mPkg, mTag, mId, mUserId))
-                        != null) {
+                // Check to see if there is a notification in the enqueued list that hasn't had a
+                // chance to post yet.
+                List<NotificationRecord> enqueued = findEnqueuedNotificationsForCriteria(
+                        mPkg, mTag, mId, mUserId);
+                boolean repost = false;
+                if (enqueued.size() > 0) {
+                    // Found something, let's see what it was
+                    repost = true;
+                    // If all enqueues happened before this cancel then wait for them to happen,
+                    // otherwise we should let this cancel through so the next enqueue happens
+                    for (NotificationRecord r : enqueued) {
+                        if (r.mUpdateTimeMs > mWhen) {
+                            // At least one enqueue was posted after the cancel, so we're invalid
+                            return;
+                        }
+                    }
+                }
+                if (repost) {
                     mHandler.post(this);
                     return;
                 }
+
                 // Look for the notification in the posted list, since we already checked enqueued.
                 NotificationRecord r =
                         findNotificationByListLocked(mNotificationList, mPkg, mTag, mId, mUserId);
@@ -6298,6 +6315,10 @@
                     if ((r.getNotification().flags & mMustNotHaveFlags) != 0) {
                         return;
                     }
+                    if (r.getUpdateTimeMs() > mWhen) {
+                        // In this case, a post must have slipped by when this runnable reposted
+                        return;
+                    }
 
                     // Bubbled children get to stick around if the summary was manually cancelled
                     // (user removed) from systemui.
@@ -8220,6 +8241,29 @@
         return null;
     }
 
+    /**
+     * There may be multiple records that match your criteria. For instance if there have been
+     * multiple notifications posted which are enqueued for the same pkg, tag, id, userId. This
+     * method will find all of them in the given list
+     * @return
+     */
+    @GuardedBy("mNotificationLock")
+    private List<NotificationRecord> findEnqueuedNotificationsForCriteria(
+            String pkg, String tag, int id, int userId) {
+        final ArrayList<NotificationRecord> records = new ArrayList<>();
+        final int n = mEnqueuedNotifications.size();
+        for (int i = 0; i < n; i++) {
+            NotificationRecord r = mEnqueuedNotifications.get(i);
+            if (notificationMatchesUserId(r, userId)
+                    && r.getSbn().getId() == id
+                    && TextUtils.equals(r.getSbn().getTag(), tag)
+                    && r.getSbn().getPackageName().equals(pkg)) {
+                records.add(r);
+            }
+        }
+        return records;
+    }
+
     @GuardedBy("mNotificationLock")
     int indexOfNotificationLocked(String key) {
         final int N = mNotificationList.size();
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 0ada58e..f92e1fc 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -906,6 +906,10 @@
         return (int) (now - mInterruptionTimeMs);
     }
 
+    public long getUpdateTimeMs() {
+        return mUpdateTimeMs;
+    }
+
     /**
      * Set the visibility of the notification.
      */
diff --git a/services/core/java/com/android/server/people/PeopleServiceInternal.java b/services/core/java/com/android/server/people/PeopleServiceInternal.java
index c5b868f..31fa4d1 100644
--- a/services/core/java/com/android/server/people/PeopleServiceInternal.java
+++ b/services/core/java/com/android/server/people/PeopleServiceInternal.java
@@ -17,6 +17,8 @@
 package com.android.server.people;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.os.CancellationSignal;
 import android.service.appprediction.IPredictionService;
 
 /**
@@ -25,16 +27,23 @@
 public abstract class PeopleServiceInternal extends IPredictionService.Stub {
 
     /**
+     * Prunes the data for the specified user. Called by {@link
+     * com.android.server.people.data.DataMaintenanceService} when the device is idle.
+     */
+    public abstract void pruneDataForUser(@UserIdInt int userId,
+            @NonNull CancellationSignal signal);
+
+    /**
      * The number conversation infos will be dynamic, based on the currently installed apps on the
      * device. All of which should be combined into a single blob to be backed up.
      */
-    public abstract byte[] backupConversationInfos(@NonNull int userId);
+    public abstract byte[] backupConversationInfos(@UserIdInt int userId);
 
     /**
      * Multiple conversation infos may exist in the restore payload, child classes are required to
      * manage the restoration based on how individual conversation infos were originally combined
      * during backup.
      */
-    public abstract void restoreConversationInfos(@NonNull int userId, @NonNull String key,
+    public abstract void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
             @NonNull byte[] payload);
 }
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 96d3cc1..3939f26 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -68,7 +68,6 @@
 
     private Context mContext;
     private Injector mInjector;
-    private AppOpsManager mAppOpsManager;
 
     public CrossProfileAppsServiceImpl(Context context) {
         this(context, new InjectorImpl(context));
@@ -217,19 +216,27 @@
 
         mInjector.getActivityTaskManagerInternal().startActivityAsUser(caller, callingPackage,
                 callingFeatureId, launchIntent, /* options= */ null, userId);
+        logStartActivityByIntent(callingPackage);
+    }
+
+    private void logStartActivityByIntent(String packageName) {
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.START_ACTIVITY_BY_INTENT)
+                .setStrings(packageName)
+                .setBoolean(isCallingUserAManagedProfile())
+                .write();
     }
 
     @Override
     public boolean canRequestInteractAcrossProfiles(String callingPackage) {
         Objects.requireNonNull(callingPackage);
         verifyCallingPackage(callingPackage);
-        return canRequestInteractAcrossProfilesUnchecked(
-                callingPackage, mInjector.getCallingUserId());
+        return canRequestInteractAcrossProfilesUnchecked(callingPackage);
     }
 
-    private boolean canRequestInteractAcrossProfilesUnchecked(
-            String packageName, @UserIdInt int userId) {
-        List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(packageName, userId);
+    private boolean canRequestInteractAcrossProfilesUnchecked(String packageName) {
+        List<UserHandle> targetUserProfiles =
+                getTargetUserProfilesUnchecked(packageName, mInjector.getCallingUserId());
         if (targetUserProfiles.isEmpty()) {
             return false;
         }
@@ -256,12 +263,11 @@
         Objects.requireNonNull(callingPackage);
         verifyCallingPackage(callingPackage);
 
-        final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(callingPackage,
-                mInjector.getCallingUserId());
+        final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked(
+                callingPackage, mInjector.getCallingUserId());
         if (targetUserProfiles.isEmpty()) {
             return false;
         }
-
         return hasInteractAcrossProfilesPermission(callingPackage);
     }
 
@@ -363,6 +369,12 @@
         });
     }
 
+    /**
+     * See {@link android.content.pm.CrossProfileApps#setInteractAcrossProfilesAppOp(String, int)}.
+     *
+     * <p>Logs metrics. Use {@link #setInteractAcrossProfilesAppOpUnchecked(String, int, boolean)}
+     * to avoid permission checks or to specify not to log metrics.
+     */
     @Override
     public void setInteractAcrossProfilesAppOp(String packageName, @Mode int newMode) {
         final int callingUid = mInjector.getCallingUid();
@@ -379,6 +391,11 @@
                     "MANAGE_APP_OPS_MODES or CONFIGURE_INTERACT_ACROSS_PROFILES is required to set"
                             + " the app-op for interacting across profiles.");
         }
+        setInteractAcrossProfilesAppOpUnchecked(packageName, newMode, /* logMetrics= */ true);
+    }
+
+    private void setInteractAcrossProfilesAppOpUnchecked(
+            String packageName, @Mode int newMode, boolean logMetrics) {
         if (newMode == AppOpsManager.MODE_ALLOWED
                 && !canConfigureInteractAcrossProfiles(packageName)) {
             // The user should not be prompted for apps that cannot request to interact across
@@ -394,7 +411,7 @@
             if (!isPackageInstalled(packageName, profileId)) {
                 continue;
             }
-            setInteractAcrossProfilesAppOpForUser(packageName, newMode, profileId);
+            setInteractAcrossProfilesAppOpForUser(packageName, newMode, profileId, logMetrics);
         }
     }
 
@@ -413,16 +430,16 @@
     }
 
     private void setInteractAcrossProfilesAppOpForUser(
-            String packageName, @Mode int newMode, @UserIdInt int userId) {
+            String packageName, @Mode int newMode, @UserIdInt int userId, boolean logMetrics) {
         try {
-            setInteractAcrossProfilesAppOpForUserOrThrow(packageName, newMode, userId);
+            setInteractAcrossProfilesAppOpForUserOrThrow(packageName, newMode, userId, logMetrics);
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, "Missing package " + packageName + " on user ID " + userId, e);
         }
     }
 
     private void setInteractAcrossProfilesAppOpForUserOrThrow(
-            String packageName, @Mode int newMode, @UserIdInt int userId)
+            String packageName, @Mode int newMode, @UserIdInt int userId, boolean logMetrics)
             throws PackageManager.NameNotFoundException {
         final int uid = mInjector.getPackageManager()
                 .getPackageUidAsUser(packageName, /* flags= */ 0, userId);
@@ -444,6 +461,28 @@
                     .setMode(OP_INTERACT_ACROSS_PROFILES, uid, packageName, newMode);
         }
         sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId));
+        maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, userId, logMetrics, uid);
+    }
+
+    private void maybeLogSetInteractAcrossProfilesAppOp(
+            String packageName,
+            @Mode int newMode,
+            @UserIdInt int userId,
+            boolean logMetrics,
+            int uid) {
+        if (!logMetrics) {
+            return;
+        }
+        if (userId != mInjector.getCallingUserId()) {
+            // Only log once per profile group by checking for the calling user ID.
+            return;
+        }
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_INTERACT_ACROSS_PROFILES_APP_OP)
+                .setStrings(packageName)
+                .setInt(newMode)
+                .setBoolean(appDeclaresCrossProfileAttribute(uid))
+                .write();
     }
 
     /**
@@ -541,11 +580,13 @@
                 permission, uid, /* owningUid= */-1, /* exported= */ true);
     }
 
-    private AppOpsManager getAppOpsManager() {
-        if (mAppOpsManager == null) {
-            mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
-        }
-        return mAppOpsManager;
+    private boolean isCallingUserAManagedProfile() {
+        return isManagedProfile(mInjector.getCallingUserId());
+    }
+
+    private boolean isManagedProfile(@UserIdInt int userId) {
+        return mInjector.withCleanCallingIdentity(()
+                -> mContext.getSystemService(UserManager.class).isManagedProfile(userId));
     }
 
     private static class InjectorImpl implements Injector {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 064fd3f..8c6e6916 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -14707,8 +14707,7 @@
         void handleVerificationFinished() {
             if (!mVerificationCompleted) {
                 mVerificationCompleted = true;
-                if (mIntegrityVerificationCompleted || mRet != INSTALL_SUCCEEDED) {
-                    mIntegrityVerificationCompleted = true;
+                if (mIntegrityVerificationCompleted) {
                     handleReturnCode();
                 }
                 // integrity verification still pending.
@@ -14718,8 +14717,7 @@
         void handleIntegrityVerificationFinished() {
             if (!mIntegrityVerificationCompleted) {
                 mIntegrityVerificationCompleted = true;
-                if (mVerificationCompleted || mRet != INSTALL_SUCCEEDED) {
-                    mVerificationCompleted = true;
+                if (mVerificationCompleted) {
                     handleReturnCode();
                 }
                 // verifier still pending
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 9395c97..a9035b2 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -578,7 +578,9 @@
 
     /** Returns true if standard APK Verity is enabled. */
     static boolean isApkVerityEnabled() {
-        return SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED) == FSVERITY_ENABLED;
+        return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
+                || SystemProperties.getInt("ro.apk_verity.mode", FSVERITY_DISABLED)
+                        == FSVERITY_ENABLED;
     }
 
     static boolean isLegacyApkVerityEnabled() {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 832c9b7..d451152 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5233,9 +5233,7 @@
                         List<RuntimePermissionsState.PermissionState> permissions =
                                 getPermissionsFromPermissionsState(
                                         packageSetting.getPermissionsState(), userId);
-                        if (permissions != null) {
-                            packagePermissions.put(packageName, permissions);
-                        }
+                        packagePermissions.put(packageName, permissions);
                     }
                 }
 
@@ -5248,9 +5246,7 @@
                     List<RuntimePermissionsState.PermissionState> permissions =
                             getPermissionsFromPermissionsState(
                                     sharedUserSetting.getPermissionsState(), userId);
-                    if (permissions != null) {
-                        sharedUserPermissions.put(sharedUserName, permissions);
-                    }
+                    sharedUserPermissions.put(sharedUserName, permissions);
                 }
 
                 runtimePermissions = new RuntimePermissionsState(version, fingerprint,
@@ -5260,15 +5256,11 @@
             mPersistence.write(runtimePermissions, UserHandle.of(userId));
         }
 
-        @Nullable
+        @NonNull
         private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
                 @NonNull PermissionsState permissionsState, @UserIdInt int userId) {
             List<PermissionState> permissionStates = permissionsState.getRuntimePermissionStates(
                     userId);
-            if (permissionStates.isEmpty()) {
-                return null;
-            }
-
             List<RuntimePermissionsState.PermissionState> permissions =
                     new ArrayList<>();
             int permissionStatesSize = permissionStates.size();
@@ -5340,31 +5332,60 @@
             boolean defaultPermissionsGranted = Build.FINGERPRINT.equals(fingerprint);
             mDefaultPermissionsGranted.put(userId, defaultPermissionsGranted);
 
-            for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
-                    : runtimePermissions.getPackagePermissions().entrySet()) {
-                String packageName = entry.getKey();
-                List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
+            boolean isUpgradeToR = getInternalVersion().sdkVersion < Build.VERSION_CODES.R;
 
-                PackageSetting packageSetting = mPackages.get(packageName);
-                if (packageSetting == null) {
-                    Slog.w(PackageManagerService.TAG, "Unknown package:" + packageName);
-                    continue;
+            Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
+                    runtimePermissions.getPackagePermissions();
+            int packagesSize = mPackages.size();
+            for (int i = 0; i < packagesSize; i++) {
+                String packageName = mPackages.keyAt(i);
+                PackageSetting packageSetting = mPackages.valueAt(i);
+
+                List<RuntimePermissionsState.PermissionState> permissions =
+                        packagePermissions.get(packageName);
+                if (permissions != null) {
+                    readPermissionsStateLpr(permissions, packageSetting.getPermissionsState(),
+                            userId);
+                } else if (packageSetting.sharedUser == null && !isUpgradeToR) {
+                    Slog.w(TAG, "Missing permission state for package: " + packageName);
+                    generateFallbackPermissionsStateLpr(
+                            packageSetting.pkg.getRequestedPermissions(),
+                            packageSetting.pkg.getTargetSdkVersion(),
+                            packageSetting.getPermissionsState(), userId);
                 }
-                readPermissionsStateLpr(permissions, packageSetting.getPermissionsState(), userId);
             }
 
-            for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
-                    : runtimePermissions.getSharedUserPermissions().entrySet()) {
-                String sharedUserName = entry.getKey();
-                List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
+            Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
+                    runtimePermissions.getSharedUserPermissions();
+            int sharedUsersSize = mSharedUsers.size();
+            for (int i = 0; i < sharedUsersSize; i++) {
+                String sharedUserName = mSharedUsers.keyAt(i);
+                SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
 
-                SharedUserSetting sharedUserSetting = mSharedUsers.get(sharedUserName);
-                if (sharedUserSetting == null) {
-                    Slog.w(PackageManagerService.TAG, "Unknown shared user:" + sharedUserName);
-                    continue;
+                List<RuntimePermissionsState.PermissionState> permissions =
+                        sharedUserPermissions.get(sharedUserName);
+                if (permissions != null) {
+                    readPermissionsStateLpr(permissions, sharedUserSetting.getPermissionsState(),
+                            userId);
+                } else if (!isUpgradeToR) {
+                    Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
+                    ArraySet<String> requestedPermissions = new ArraySet<>();
+                    int targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
+                    int sharedUserPackagesSize = sharedUserSetting.packages.size();
+                    for (int packagesI = 0; packagesI < sharedUserPackagesSize; packagesI++) {
+                        PackageSetting packageSetting = sharedUserSetting.packages.valueAt(
+                                packagesI);
+                        if (packageSetting == null || packageSetting.pkg == null
+                                || !packageSetting.getInstalled(userId)) {
+                            continue;
+                        }
+                        AndroidPackage pkg = packageSetting.pkg;
+                        requestedPermissions.addAll(pkg.getRequestedPermissions());
+                        targetSdkVersion = Math.min(targetSdkVersion, pkg.getTargetSdkVersion());
+                    }
+                    generateFallbackPermissionsStateLpr(requestedPermissions, targetSdkVersion,
+                            sharedUserSetting.getPermissionsState(), userId);
                 }
-                readPermissionsStateLpr(permissions, sharedUserSetting.getPermissionsState(),
-                        userId);
             }
         }
 
@@ -5395,6 +5416,30 @@
             }
         }
 
+        private void generateFallbackPermissionsStateLpr(
+                @NonNull Collection<String> requestedPermissions, int targetSdkVersion,
+                @NonNull PermissionsState permissionsState, @UserIdInt int userId) {
+            for (String permissionName : requestedPermissions) {
+                BasePermission permission = mPermissions.getPermission(permissionName);
+                if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME)
+                        && permission.isRuntime() && !permission.isRemoved()) {
+                    if (permission.isHardOrSoftRestricted() || permission.isImmutablyRestricted()) {
+                        permissionsState.updatePermissionFlags(permission, userId,
+                                PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
+                                PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
+                    }
+                    if (targetSdkVersion < Build.VERSION_CODES.M) {
+                        permissionsState.updatePermissionFlags(permission, userId,
+                                PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+                                        | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
+                                PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
+                                        | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
+                        permissionsState.grantRuntimePermission(permission, userId);
+                    }
+                }
+            }
+        }
+
         @GuardedBy("Settings.this.mLock")
         private void readLegacyStateForUserSyncLPr(int userId) {
             File permissionsFile = getUserRuntimePermissionsFile(userId);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a45a996..19e6062 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -45,7 +45,6 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
@@ -2058,11 +2057,8 @@
 
     /** {@inheritDoc} */
     @Override
-    public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
-        final int type = attrs.type;
-        final boolean isRoundedCornerOverlay =
-                (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
-
+    public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+            int[] outAppOp) {
         if (isRoundedCornerOverlay && mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
                 != PERMISSION_GRANTED) {
             return ADD_PERMISSION_DENIED;
@@ -2119,7 +2115,7 @@
         ApplicationInfo appInfo;
         try {
             appInfo = mPackageManager.getApplicationInfoAsUser(
-                            attrs.packageName,
+                            packageName,
                             0 /* flags */,
                             UserHandle.getUserId(callingUid));
         } catch (PackageManager.NameNotFoundException e) {
@@ -2139,7 +2135,7 @@
 
         // check if user has enabled this operation. SecurityException will be thrown if this app
         // has not been allowed by the user
-        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, attrs.packageName);
+        final int mode = mAppOpsManager.noteOpNoThrow(outAppOp[0], callingUid, packageName);
         switch (mode) {
             case AppOpsManager.MODE_ALLOWED:
             case AppOpsManager.MODE_IGNORED:
diff --git a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
index a83c58d..740472e 100644
--- a/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
+++ b/services/core/java/com/android/server/policy/SoftRestrictedPermissionPolicy.java
@@ -35,14 +35,17 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
 import android.util.Log;
 
 import com.android.internal.compat.IPlatformCompat;
+import com.android.server.LocalServices;
 
 /**
  * The behavior of soft restricted permissions is different for each permission. This class collects
@@ -107,11 +110,16 @@
                 final boolean isWhiteListed;
                 boolean shouldApplyRestriction;
                 final boolean hasRequestedLegacyExternalStorage;
+                final boolean shouldPreserveLegacyExternalStorage;
                 final boolean hasWriteMediaStorageGrantedForUid;
                 final boolean isScopedStorageEnabled;
 
                 if (appInfo != null) {
                     PackageManager pm = context.getPackageManager();
+                    PackageManagerInternal pmInternal =
+                            LocalServices.getService(PackageManagerInternal.class);
+                    StorageManagerInternal smInternal =
+                            LocalServices.getService(StorageManagerInternal.class);
                     int flags = pm.getPermissionFlags(permission, appInfo.packageName, user);
                     isWhiteListed = (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
                     hasRequestedLegacyExternalStorage = hasUidRequestedLegacyExternalStorage(
@@ -123,12 +131,16 @@
                     isScopedStorageEnabled =
                             isChangeEnabledForUid(context, appInfo, user, ENABLE_SCOPED_STORAGE)
                             || isScopedStorageRequired;
+                    shouldPreserveLegacyExternalStorage = pmInternal.getPackage(
+                            appInfo.packageName).hasPreserveLegacyExternalStorage()
+                            && smInternal.hasLegacyExternalStorage(appInfo.uid);
                     shouldApplyRestriction = (flags & FLAG_PERMISSION_APPLY_RESTRICTION) != 0
-                            || isScopedStorageRequired;
+                            || (isScopedStorageRequired && !shouldPreserveLegacyExternalStorage);
                 } else {
                     isWhiteListed = false;
                     shouldApplyRestriction = false;
                     hasRequestedLegacyExternalStorage = false;
+                    shouldPreserveLegacyExternalStorage = false;
                     hasWriteMediaStorageGrantedForUid = false;
                     isScopedStorageEnabled = false;
                 }
@@ -150,7 +162,8 @@
                     public boolean mayAllowExtraAppOp() {
                         return !shouldApplyRestriction
                                 && (hasRequestedLegacyExternalStorage
-                                        || hasWriteMediaStorageGrantedForUid);
+                                        || hasWriteMediaStorageGrantedForUid
+                                        || shouldPreserveLegacyExternalStorage);
                     }
                     @Override
                     public boolean mayDenyExtraAppOpIfGranted() {
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 39093ae..9d417c9 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -686,17 +686,25 @@
             WindowManagerFuncs windowManagerFuncs);
 
     /**
-     * Check permissions when adding a window.
+     * Check permissions when adding a window or a window token from
+     * {@link android.app.WindowContext}.
      *
-     * @param attrs The window's LayoutParams.
+     * @param type The window type
+     * @param isRoundedCornerOverlay {@code true} to indicate the adding window is
+     *                                           round corner overlay.
+     * @param packageName package name
      * @param outAppOp First element will be filled with the app op corresponding to
      *                 this window, or OP_NONE.
      *
      * @return {@link WindowManagerGlobal#ADD_OKAY} if the add can proceed;
      *      else an error code, usually
      *      {@link WindowManagerGlobal#ADD_PERMISSION_DENIED}, to abort the add.
+     *
+     * @see IWindowManager#addWindowTokenWithOptions(IBinder, int, int, Bundle, String)
+     * @see WindowManager.LayoutParams#PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY
      */
-    public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp);
+    int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+            int[] outAppOp);
 
     /**
      * After the window manager has computed the current configuration based
diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java
index e90c02a..8cf2d03 100644
--- a/services/core/java/com/android/server/security/FileIntegrityService.java
+++ b/services/core/java/com/android/server/security/FileIntegrityService.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Build;
 import android.os.IBinder;
 import android.os.Process;
 import android.os.SystemProperties;
@@ -55,7 +56,8 @@
     private final IBinder mService = new IFileIntegrityService.Stub() {
         @Override
         public boolean isApkVeritySupported() {
-            return SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
+            return Build.VERSION.FIRST_SDK_INT >= Build.VERSION_CODES.R
+                    || SystemProperties.getInt("ro.apk_verity.mode", 0) == 2;
         }
 
         @Override
diff --git a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index 372c1f5..cd5e360 100644
--- a/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/core/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -169,7 +169,7 @@
     }
 
     @Override
-    public synchronized  void serviceDied(long cookie) {
+    public synchronized void serviceDied(long cookie) {
         Log.w(TAG, String.format("Underlying HAL driver died."));
         for (Session session : mActiveSessions) {
             session.moduleDied();
@@ -248,44 +248,77 @@
         @Override
         public int loadModel(@NonNull SoundModel model) {
             Log.d(TAG, String.format("loadModel(model=%s)", model));
-            synchronized (SoundTriggerModule.this) {
-                checkValid();
-                if (mNumLoadedModels == mProperties.maxSoundModels) {
-                    throw new RecoverableException(Status.RESOURCE_CONTENTION,
-                            "Maximum number of models loaded.");
+
+            // We must do this outside the lock, to avoid possible deadlocks with the remote process
+            // that provides the audio sessions, which may also be calling into us.
+            SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+                    mAudioSessionProvider.acquireSession();
+
+            try {
+                synchronized (SoundTriggerModule.this) {
+                    checkValid();
+                    if (mNumLoadedModels == mProperties.maxSoundModels) {
+                        throw new RecoverableException(Status.RESOURCE_CONTENTION,
+                                "Maximum number of models loaded.");
+                    }
+                    Model loadedModel = new Model();
+                    int result = loadedModel.load(model, audioSession);
+                    ++mNumLoadedModels;
+                    return result;
                 }
-                Model loadedModel = new Model();
-                int result = loadedModel.load(model);
-                ++mNumLoadedModels;
-                return result;
+            } catch (Exception e) {
+                // We must do this outside the lock, to avoid possible deadlocks with the remote
+                // process that provides the audio sessions, which may also be calling into us.
+                mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                throw e;
             }
         }
 
         @Override
         public int loadPhraseModel(@NonNull PhraseSoundModel model) {
             Log.d(TAG, String.format("loadPhraseModel(model=%s)", model));
-            synchronized (SoundTriggerModule.this) {
-                checkValid();
-                if (mNumLoadedModels == mProperties.maxSoundModels) {
-                    throw new RecoverableException(Status.RESOURCE_CONTENTION,
-                            "Maximum number of models loaded.");
+
+            // We must do this outside the lock, to avoid possible deadlocks with the remote process
+            // that provides the audio sessions, which may also be calling into us.
+            SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession =
+                    mAudioSessionProvider.acquireSession();
+
+            try {
+                synchronized (SoundTriggerModule.this) {
+                    checkValid();
+                    if (mNumLoadedModels == mProperties.maxSoundModels) {
+                        throw new RecoverableException(Status.RESOURCE_CONTENTION,
+                                "Maximum number of models loaded.");
+                    }
+                    Model loadedModel = new Model();
+                    int result = loadedModel.load(model, audioSession);
+                    ++mNumLoadedModels;
+                    Log.d(TAG, String.format("loadPhraseModel()->%d", result));
+                    return result;
                 }
-                Model loadedModel = new Model();
-                int result = loadedModel.load(model);
-                ++mNumLoadedModels;
-                Log.d(TAG, String.format("loadPhraseModel()->%d", result));
-                return result;
+            } catch (Exception e) {
+                // We must do this outside the lock, to avoid possible deadlocks with the remote
+                // process that provides the audio sessions, which may also be calling into us.
+                mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
+                throw e;
             }
         }
 
         @Override
         public void unloadModel(int modelHandle) {
             Log.d(TAG, String.format("unloadModel(handle=%d)", modelHandle));
+
+            int sessionId;
+
             synchronized (SoundTriggerModule.this) {
                 checkValid();
-                mLoadedModels.get(modelHandle).unload();
+                sessionId = mLoadedModels.get(modelHandle).unload();
                 --mNumLoadedModels;
             }
+
+            // We must do this outside the lock, to avoid possible deadlocks with the remote process
+            // that provides the audio sessions, which may also be calling into us.
+            mAudioSessionProvider.releaseSession(sessionId);
         }
 
         @Override
@@ -413,45 +446,40 @@
                 SoundTriggerModule.this.notifyAll();
             }
 
-            private int load(@NonNull SoundModel model) {
+            private int load(@NonNull SoundModel model,
+                    SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
                 mModelType = model.type;
+                mSession = audioSession;
                 ISoundTriggerHw.SoundModel hidlModel = ConversionUtil.aidl2hidlSoundModel(model);
 
-                mSession = mAudioSessionProvider.acquireSession();
-                try {
-                    mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
-                } catch (Exception e) {
-                    mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
-                    throw e;
-                }
-
+                mHandle = mHalService.loadSoundModel(hidlModel, this, 0);
                 setState(ModelState.LOADED);
                 mLoadedModels.put(mHandle, this);
                 return mHandle;
             }
 
-            private int load(@NonNull PhraseSoundModel model) {
+            private int load(@NonNull PhraseSoundModel model,
+                    SoundTriggerMiddlewareImpl.AudioSessionProvider.AudioSession audioSession) {
                 mModelType = model.common.type;
+                mSession = audioSession;
                 ISoundTriggerHw.PhraseSoundModel hidlModel =
                         ConversionUtil.aidl2hidlPhraseSoundModel(model);
 
-                mSession = mAudioSessionProvider.acquireSession();
-                try {
-                    mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
-                } catch (Exception e) {
-                    mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
-                    throw e;
-                }
+                mHandle = mHalService.loadPhraseSoundModel(hidlModel, this, 0);
 
                 setState(ModelState.LOADED);
                 mLoadedModels.put(mHandle, this);
                 return mHandle;
             }
 
-            private void unload() {
-                mAudioSessionProvider.releaseSession(mSession.mSessionHandle);
+            /**
+             * Unloads the model.
+             * @return The audio session handle.
+             */
+            private int unload() {
                 mHalService.unloadSoundModel(mHandle);
                 mLoadedModels.remove(mHandle);
+                return mSession.mSessionHandle;
             }
 
             private void startRecognition(@NonNull RecognitionConfig config) {
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 0502d0b..68a7188 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -581,6 +581,9 @@
 
     /**
      * Notifies the tracker that all windows of the app have been drawn.
+     *
+     * @return Non-null info if the activity was pending to draw, otherwise it might have been set
+     *         to invisible (removed from active transition) or it was already drawn.
      */
     @Nullable
     TransitionInfoSnapshot notifyWindowsDrawn(@NonNull ActivityRecord r, long timestampNs) {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e26dac6..b3b8159 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2679,7 +2679,8 @@
         // DisplayContent#topRunningActivity().
         final ActivityRecord next = display.topRunningActivity();
         final boolean isLastStackOverEmptyHome =
-                next == null && stack.isFocusedStackOnDisplay() && display.getRootHomeTask() != null;
+                next == null && stack.isFocusedStackOnDisplay()
+                        && display.getOrCreateRootHomeTask() != null;
         if (isLastStackOverEmptyHome) {
             // Don't destroy activity immediately if this is the last activity on the display and
             // the display contains home stack. Although there is no next activity at the moment,
@@ -5201,11 +5202,17 @@
         }
         final TransitionInfoSnapshot info = mStackSupervisor
                 .getActivityMetricsLogger().notifyWindowsDrawn(this, timestampNs);
-        final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
-        final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
-        mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
-                windowsDrawnDelayMs, launchState);
-        mStackSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs);
+        final boolean validInfo = info != null;
+        final int windowsDrawnDelayMs = validInfo ? info.windowsDrawnDelayMs : INVALID_DELAY;
+        final @LaunchState int launchState = validInfo ? info.getLaunchState() : -1;
+        // The activity may have been requested to be invisible (another activity has been launched)
+        // so there is no valid info. But if it is the current top activity (e.g. sleeping), the
+        // invalid state is still reported to make sure the waiting result is notified.
+        if (validInfo || this == mDisplayContent.topRunningActivity()) {
+            mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
+                    windowsDrawnDelayMs, launchState);
+            mStackSupervisor.stopWaitingForActivityVisible(this, windowsDrawnDelayMs);
+        }
         finishLaunchTickingLocked();
         if (task != null) {
             task.hasBeenVisible = true;
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ab8e975..3c39b39 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -111,6 +111,7 @@
 import static com.android.server.wm.TaskProto.ADJUST_IME_AMOUNT;
 import static com.android.server.wm.TaskProto.ANIMATING_BOUNDS;
 import static com.android.server.wm.TaskProto.BOUNDS;
+import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
 import static com.android.server.wm.TaskProto.DEFER_REMOVAL;
 import static com.android.server.wm.TaskProto.DISPLAYED_BOUNDS;
 import static com.android.server.wm.TaskProto.DISPLAY_ID;
@@ -731,53 +732,14 @@
                         newBounds);
                 hasNewOverrideBounds = true;
             }
-
-            // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
-            if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
-                    || overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-                // If entering split screen or if something about the available split area changes,
-                // recalculate the split windows to match the new configuration.
-                if (rotationChanged || windowingModeChanged
-                        || prevDensity != getConfiguration().densityDpi
-                        || prevScreenW != getConfiguration().screenWidthDp
-                        || prevScreenH != getConfiguration().screenHeightDp) {
-                    calculateDockedBoundsForConfigChange(newParentConfig, newBounds);
-                    hasNewOverrideBounds = true;
-                }
-            }
         }
 
         if (windowingModeChanged) {
-            // Use override windowing mode to prevent extra bounds changes if inheriting the mode.
-            if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
-                        newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
-                // immediately resize so docked bounds are available in onSplitScreenModeActivated
-                setTaskDisplayedBounds(null);
-                setTaskBounds(newBounds);
-                setBounds(newBounds);
-                newBounds.set(newBounds);
-            } else if (overrideWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY) {
-                Rect dockedBounds = display.getRootSplitScreenPrimaryTask().getBounds();
-                final boolean isMinimizedDock =
-                        display.mDisplayContent.getDockedDividerController().isMinimizedDock();
-                if (isMinimizedDock) {
-                    Task topTask = display.getRootSplitScreenPrimaryTask().getTopMostTask();
-                    if (topTask != null) {
-                        dockedBounds = topTask.getBounds();
-                    }
-                }
-                getStackDockedModeBounds(dockedBounds, null /* currentTempTaskBounds */,
-                        newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
-                hasNewOverrideBounds = true;
-            }
+            display.onStackWindowingModeChanged(this);
         }
         if (hasNewOverrideBounds) {
-            if (inSplitScreenPrimaryWindowingMode()) {
-                mStackSupervisor.resizeDockedStackLocked(new Rect(newBounds),
-                        null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                        null /* tempOtherTaskBounds */, null /* tempOtherTaskInsetBounds */,
-                        PRESERVE_WINDOWS, true /* deferResume */);
+            if (inSplitScreenWindowingMode()) {
+                setBounds(newBounds);
             } else if (overrideWindowingMode != WINDOWING_MODE_PINNED) {
                 // For pinned stack, resize is now part of the {@link WindowContainerTransaction}
                 resize(new Rect(newBounds), null /* tempTaskBounds */,
@@ -920,11 +882,7 @@
                 // warning toast about it.
                 mAtmService.getTaskChangeNotificationController()
                         .notifyActivityDismissingDockedStack();
-                final ActivityStack primarySplitStack = display.getRootSplitScreenPrimaryTask();
-                primarySplitStack.setWindowingModeInSurfaceTransaction(WINDOWING_MODE_UNDEFINED,
-                        false /* animate */, false /* showRecents */,
-                        false /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
-                        primarySplitStack == this ? creating : false);
+                display.onSplitScreenModeDismissed();
             }
         }
 
@@ -1218,7 +1176,7 @@
                     display.getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
             if (topFullScreenStack != null) {
                 final ActivityStack primarySplitScreenStack = display.getRootSplitScreenPrimaryTask();
-                if (display.getIndexOf(topFullScreenStack)
+                if (primarySplitScreenStack != null && display.getIndexOf(topFullScreenStack)
                         > display.getIndexOf(primarySplitScreenStack)) {
                     primarySplitScreenStack.moveToFront(reason + " splitScreenToTop");
                 }
@@ -3994,17 +3952,6 @@
                 ? ((WindowContainer) oldParent).getDisplayContent() : null;
         super.onParentChanged(newParent, oldParent);
 
-        if (display != null && inSplitScreenPrimaryWindowingMode()
-                // only do this for the base stack
-                && !newParent.inSplitScreenPrimaryWindowingMode()) {
-            // If we created a docked stack we want to resize it so it resizes all other stacks
-            // in the system.
-            getStackDockedModeBounds(null /* dockedBounds */, null /* currentTempTaskBounds */,
-                    mTmpRect /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
-            mStackSupervisor.resizeDockedStackLocked(getRequestedOverrideBounds(), mTmpRect,
-                    mTmpRect2, null, null, PRESERVE_WINDOWS);
-        }
-
         // Resume next focusable stack after reparenting to another display if we aren't removing
         // the prevous display.
         if (oldDisplay != null && oldDisplay.isRemoving()) {
@@ -4950,6 +4897,12 @@
     }
 
     @Override
+    public SurfaceControl getParentSurfaceControl() {
+        // Tile is a "virtual" parent, so we need to intercept the parent surface here
+        return mTile != null ? mTile.getSurfaceControl() : super.getParentSurfaceControl();
+    }
+
+    @Override
     void removeImmediately() {
         // TODO(task-hierarchy): remove this override when tiles are in hierarchy
         if (mTile != null) {
@@ -5002,6 +4955,10 @@
         if (!matchParentBounds()) {
             final Rect bounds = getRequestedOverrideBounds();
             bounds.dumpDebug(proto, BOUNDS);
+        } else if (getStack().getTile() != null) {
+            // use tile's bounds here for cts.
+            final Rect bounds = getStack().getTile().getRequestedOverrideBounds();
+            bounds.dumpDebug(proto, BOUNDS);
         }
         getOverrideDisplayedBounds().dumpDebug(proto, DISPLAYED_BOUNDS);
         mAdjustedBounds.dumpDebug(proto, ADJUSTED_BOUNDS);
@@ -5021,6 +4978,8 @@
             proto.write(SURFACE_HEIGHT, mSurfaceControl.getHeight());
         }
 
+        proto.write(CREATED_BY_ORGANIZER, this instanceof TaskTile);
+
         proto.end(token);
     }
 }
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a582f21..a2e8801 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -2447,7 +2447,9 @@
                 // split-screen in split-screen.
                 mService.getTaskChangeNotificationController()
                         .notifyActivityDismissingDockedStack();
-                moveTasksToFullscreenStackLocked(dockedStack, actualStack == dockedStack);
+                dockedStack.getDisplay().onSplitScreenModeDismissed();
+                dockedStack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                        true /* notifyClients */);
             }
             return;
         }
@@ -2809,7 +2811,7 @@
                 final DisplayContent display = task.getStack().getDisplay();
                 final ActivityStack topSecondaryStack =
                         display.getTopStackInWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                if (topSecondaryStack.isActivityTypeHome()) {
+                if (topSecondaryStack != null && topSecondaryStack.isActivityTypeHome()) {
                     // 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
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8ed798c..e77d8ae 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1374,7 +1374,7 @@
                     break;
                 case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
                     final ActivityStack homeStack =
-                            startedActivityStack.getDisplay().getRootHomeTask();
+                            startedActivityStack.getDisplay().getOrCreateRootHomeTask();
                     if (homeStack != null && homeStack.shouldBeVisible(null /* starting */)) {
                         mService.mWindowManager.showRecentApps();
                     }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 1859fae..f777e90 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -37,7 +37,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -229,6 +228,7 @@
 import android.view.IRecentsAnimationRunner;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
+import android.view.WindowContainerTransaction;
 import android.view.WindowManager;
 
 import com.android.internal.R;
@@ -2269,6 +2269,9 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
+                if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+                    return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop);
+                }
                 final Task task = mRootWindowContainer.anyTaskForId(taskId,
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
@@ -2286,10 +2289,16 @@
                 }
 
                 final ActivityStack stack = task.getStack();
+                // Convert some windowing-mode changes into root-task reparents for split-screen.
+                if (stack.getTile() != null) {
+                    stack.getDisplay().onSplitScreenModeDismissed();
+                }
                 if (toTop) {
                     stack.moveToFront("setTaskWindowingMode", task);
                 }
                 stack.setWindowingMode(windowingMode);
+                stack.getDisplay().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS,
+                        true /* notifyClients */);
                 return true;
             } finally {
                 Binder.restoreCallingIdentity(ident);
@@ -2719,36 +2728,8 @@
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
             try {
-                if (isInLockTaskMode()) {
-                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: Is in lock task mode="
-                            + getLockTaskModeState());
-                    return false;
-                }
-
-                final Task task = mRootWindowContainer.anyTaskForId(taskId,
-                        MATCH_TASK_IN_STACKS_ONLY);
-                if (task == null) {
-                    Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
-                    return false;
-                }
-                if (!task.isActivityTypeStandardOrUndefined()) {
-                    throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
-                            + " non-standard task " + taskId + " to split-screen windowing mode");
-                }
-
-                if (DEBUG_STACK) Slog.d(TAG_STACK,
-                        "setTaskWindowingModeSplitScreenPrimary: moving task=" + taskId
-                                + " to createMode=" + createMode + " toTop=" + toTop);
-                mWindowManager.setDockedStackCreateStateLocked(createMode, initialBounds);
-                final int windowingMode = task.getWindowingMode();
-                final ActivityStack stack = task.getStack();
-                if (toTop) {
-                    stack.moveToFront("setTaskWindowingModeSplitScreenPrimary", task);
-                }
-                stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, animate, showRecents,
-                        false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
-                        false /* creating */);
-                return windowingMode != task.getWindowingMode();
+                return setTaskWindowingModeSplitScreen(taskId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+                        toTop);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -2756,6 +2737,49 @@
     }
 
     /**
+     * Moves the specified task into a split-screen tile.
+     */
+    private boolean setTaskWindowingModeSplitScreen(int taskId, int windowingMode, boolean toTop) {
+        if (!WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
+            throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non"
+                    + "split-screen mode: " + windowingMode);
+        }
+        if (isInLockTaskMode()) {
+            Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode="
+                    + getLockTaskModeState());
+            return false;
+        }
+
+        final Task task = mRootWindowContainer.anyTaskForId(taskId,
+                MATCH_TASK_IN_STACKS_ONLY);
+        if (task == null) {
+            Slog.w(TAG, "setTaskWindowingModeSplitScreenPrimary: No task for id=" + taskId);
+            return false;
+        }
+        if (!task.isActivityTypeStandardOrUndefined()) {
+            throw new IllegalArgumentException("setTaskWindowingMode: Attempt to move"
+                    + " non-standard task " + taskId + " to split-screen windowing mode");
+        }
+
+        final int prevMode = task.getWindowingMode();
+        final ActivityStack stack = task.getStack();
+        TaskTile tile = null;
+        for (int i = stack.getDisplay().getStackCount() - 1; i >= 0; --i) {
+            tile = stack.getDisplay().getStackAt(i).asTile();
+            if (tile != null && tile.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                break;
+            }
+        }
+        if (tile == null) {
+            throw new IllegalStateException("Can't enter split without associated tile");
+        }
+        WindowContainerTransaction wct = new WindowContainerTransaction();
+        wct.reparent(stack.mRemoteToken, tile.mRemoteToken, toTop);
+        mTaskOrganizerController.applyContainerTransaction(wct, null);
+        return prevMode != task.getWindowingMode();
+    }
+
+    /**
      * Removes stacks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
@@ -3963,46 +3987,6 @@
     }
 
     /**
-     * Dismisses split-screen multi-window mode.
-     * @param toTop If true the current primary split-screen stack will be placed or left on top.
-     */
-    @Override
-    public void dismissSplitScreenMode(boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(
-                MANAGE_ACTIVITY_STACKS, "dismissSplitScreenMode()");
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityStack stack =
-                        mRootWindowContainer.getDefaultDisplay().getRootSplitScreenPrimaryTask();
-                if (stack == null) {
-                    Slog.w(TAG, "dismissSplitScreenMode: primary split-screen stack not found.");
-                    return;
-                }
-
-                if (toTop) {
-                    // Caller wants the current split-screen primary stack to be the top stack after
-                    // it goes fullscreen, so move it to the front.
-                    stack.moveToFront("dismissSplitScreenMode");
-                } else {
-                    // In this case the current split-screen primary stack shouldn't be the top
-                    // stack after it goes fullscreen, so we move the focus to the top-most
-                    // split-screen secondary stack next to it.
-                    final ActivityStack otherStack = stack.getDisplay().getTopStackInWindowingMode(
-                            WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                    if (otherStack != null) {
-                        otherStack.moveToFront("dismissSplitScreenMode_other");
-                    }
-                }
-
-                stack.setWindowingMode(WINDOWING_MODE_UNDEFINED);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
      * Dismisses Pip
      * @param animate True if the dismissal should be animated.
      * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
@@ -5568,11 +5552,20 @@
         if (mLastResumedActivity != null && r.mUserId != mLastResumedActivity.mUserId) {
             mAmInternal.sendForegroundProfileChanged(r.mUserId);
         }
+        final Task prevTask = mLastResumedActivity != null ? mLastResumedActivity.getTask() : null;
+
         updateResumedAppTrace(r);
         mLastResumedActivity = r;
 
         r.getDisplay().setFocusedApp(r, true);
 
+        if (prevTask == null || task != prevTask) {
+            if (prevTask != null) {
+                mTaskChangeNotificationController.notifyTaskFocusChanged(prevTask.mTaskId, false);
+            }
+            mTaskChangeNotificationController.notifyTaskFocusChanged(task.mTaskId, true);
+        }
+
         applyUpdateLockStateLocked(r);
         applyUpdateVrModeLocked(r);
 
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index f72020e..3f4e791 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -713,7 +713,11 @@
 
         final WindowState wallpaperTarget = mWallpaperControllerLocked.getWallpaperTarget();
         final boolean showWallpaper = wallpaperTarget != null
-                && (wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+                && ((wallpaperTarget.mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0
+                // Update task open transition to wallpaper transition when wallpaper is visible.
+                // (i.e.launching app info activity from recent tasks)
+                || ((transit == TRANSIT_TASK_OPEN || transit == TRANSIT_TASK_TO_FRONT)
+                && mWallpaperControllerLocked.isWallpaperVisible()));
         // If wallpaper is animating or wallpaperTarget doesn't have SHOW_WALLPAPER flag set,
         // don't consider upgrading to wallpaper transition.
         final WindowState oldWallpaper =
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 3b658c0..55a41ab 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -16,9 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityTaskManager.INVALID_STACK_ID;
 import static android.app.ActivityTaskManager.INVALID_TASK_ID;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
@@ -32,6 +30,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -56,9 +55,6 @@
 import static android.view.View.GONE;
 import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
 import static android.view.View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
-import static android.view.WindowManager.DOCKED_TOP;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
@@ -84,9 +80,6 @@
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 
-import static com.android.server.wm.DisplayContentProto.FOCUSED_ROOT_TASK_ID;
-import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
-import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG;
 import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT;
@@ -105,12 +98,15 @@
 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.FOCUSED_ROOT_TASK_ID;
 import static com.android.server.wm.DisplayContentProto.ID;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
 import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.RESUMED_ACTIVITY;
 import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
+import static com.android.server.wm.DisplayContentProto.SINGLE_TASK_INSTANCE;
 import static com.android.server.wm.DisplayContentProto.TASKS;
 import static com.android.server.wm.DisplayContentProto.WINDOW_CONTAINER;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
@@ -1938,6 +1934,20 @@
         return mTaskContainers.getRootHomeTask();
     }
 
+    /**
+     * Returns the existing home stack or creates and returns a new one if it should exist for the
+     * display.
+     */
+    @Nullable
+    ActivityStack getOrCreateRootHomeTask() {
+        ActivityStack homeTask = getRootHomeTask();
+        if (homeTask == null && supportsSystemDecorations() && !isUntrustedVirtualDisplay()) {
+            homeTask = createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME,
+                    false /* onTop */);
+        }
+        return homeTask;
+    }
+
     /** @return The primary split-screen task, and {@code null} otherwise. */
     ActivityStack getRootSplitScreenPrimaryTask() {
         return mTaskContainers.getRootSplitScreenPrimaryTask();
@@ -2613,54 +2623,9 @@
 
     void adjustForImeIfNeeded() {
         final WindowState imeWin = mInputMethodWindow;
-        final boolean imeVisible = imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
-                && !mDividerControllerLocked.isImeHideRequested();
-        final ActivityStack dockedStack = getRootSplitScreenPrimaryTask();
-        final boolean dockVisible = dockedStack != null;
-        final Task topDockedTask = dockVisible ? dockedStack.getTask((t) -> true): null;
-        final ActivityStack imeTargetStack = mWmService.getImeFocusStackLocked();
-        final int imeDockSide = (dockVisible && imeTargetStack != null) ?
-                imeTargetStack.getDockSide() : DOCKED_INVALID;
-        final boolean imeOnTop = (imeDockSide == DOCKED_TOP);
-        final boolean imeOnBottom = (imeDockSide == DOCKED_BOTTOM);
+        final boolean imeVisible = imeWin != null && imeWin.isVisibleLw()
+                && imeWin.isDisplayedLw();
         final int imeHeight = mDisplayFrames.getInputMethodWindowVisibleHeight();
-        final boolean imeHeightChanged = imeVisible &&
-                imeHeight != mDividerControllerLocked.getImeHeightAdjustedFor();
-
-        // This includes a case where the docked stack is unminimizing and IME is visible for the
-        // bottom side stack. The condition prevents adjusting the override task bounds for IME to
-        // the minimized docked stack bounds.
-        final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock()
-                || (topDockedTask != null && imeOnBottom && !dockedStack.isAdjustedForIme()
-                && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
-
-        // The divider could be adjusted for IME position, or be thinner than usual,
-        // or both. There are three possible cases:
-        // - If IME is visible, and focus is on top, divider is not moved for IME but thinner.
-        // - If IME is visible, and focus is on bottom, divider is moved for IME and thinner.
-        // - If IME is not visible, divider is not moved and is normal width.
-
-        if (imeVisible && dockVisible && (imeOnTop || imeOnBottom) && !dockMinimized) {
-            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = mTaskContainers.getChildAt(i);
-                final boolean isDockedOnBottom = stack.getDockSide() == DOCKED_BOTTOM;
-                if (stack.isVisible() && (imeOnBottom || isDockedOnBottom)
-                        && stack.inSplitScreenWindowingMode()) {
-                    stack.setAdjustedForIme(imeWin, imeOnBottom && imeHeightChanged);
-                } else {
-                    stack.resetAdjustedForIme(false);
-                }
-            }
-            mDividerControllerLocked.setAdjustedForIme(
-                    imeOnBottom /*ime*/, true /*divider*/, true /*animate*/, imeWin, imeHeight);
-        } else {
-            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = mTaskContainers.getChildAt(i);
-                stack.resetAdjustedForIme(!dockVisible);
-            }
-            mDividerControllerLocked.setAdjustedForIme(
-                    false /*ime*/, false /*divider*/, dockVisible /*animate*/, imeWin, imeHeight);
-        }
         mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight);
     }
 
@@ -3255,9 +3220,9 @@
             mWmService.mAtmInternal.onImeWindowSetOnDisplay(imePid,
                     mInputMethodWindow.getDisplayId());
         }
-        computeImeTarget(true /* updateImeTarget */);
         mInsetsStateController.getSourceProvider(ITYPE_IME).setWindow(win,
                 null /* frameProvider */, null /* imeFrameProvider */);
+        computeImeTarget(true /* updateImeTarget */);
     }
 
     /**
@@ -3440,8 +3405,6 @@
         mInputMethodTarget = target;
         mInputMethodTargetWaitingAnim = targetWaitingAnim;
         assignWindowLayers(false /* setLayoutNeeded */);
-        mInputMethodControlTarget = computeImeControlTarget();
-        mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
         updateImeParent();
     }
 
@@ -3452,7 +3415,7 @@
      *
      * @see #getImeControlTarget()
      */
-    void updateImeControlTarget(WindowState target) {
+    void updateImeControlTarget(InsetsControlTarget target) {
         mInputMethodControlTarget = target;
         mInsetsStateController.onImeControlTargetChanged(mInputMethodControlTarget);
     }
@@ -3490,13 +3453,13 @@
      * Computes which control-target the IME should be attached to.
      */
     @VisibleForTesting
-    InsetsControlTarget computeImeControlTarget() {
+    InsetsControlTarget computeImeControlTarget(InsetsControlTarget appTarget) {
         if (!isImeAttachedToApp() && mRemoteInsetsControlTarget != null) {
             return mRemoteInsetsControlTarget;
         }
 
         // Otherwise, we just use the ime target
-        return mInputMethodTarget;
+        return appTarget;
     }
 
     void setLayoutNeeded() {
@@ -3817,6 +3780,10 @@
         }
         prepareSurfaces();
 
+        // This should be called after the insets have been dispatched to clients and we have
+        // committed finish drawing windows.
+        mInsetsStateController.getImeSourceProvider().checkShowImePostLayout();
+
         mLastHasContent = mTmpApplySurfaceChangesTransactionState.displayHasContent;
         mWmService.mDisplayManagerInternal.setDisplayProperties(mDisplayId,
                 mLastHasContent,
@@ -3880,7 +3847,7 @@
         }
     }
 
-    /** @returns the orientation of the display when it's rotation is ROTATION_0. */
+    /** @return the orientation of the display when it's rotation is ROTATION_0. */
     int getNaturalOrientation() {
         return mBaseDisplayWidth < mBaseDisplayHeight
                 ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
@@ -4255,9 +4222,6 @@
         }
 
         ActivityStack getRootHomeTask() {
-            if (mRootHomeTask == null && mDisplayId == DEFAULT_DISPLAY) {
-                Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
-            }
             return mRootHomeTask;
         }
 
@@ -4324,8 +4288,6 @@
                     }
                 } else {
                     mRootSplitScreenPrimaryTask = stack;
-                    mDisplayContent.onSplitScreenModeActivated();
-                    mDividerControllerLocked.notifyDockedStackExistsChanged(true);
                 }
             }
         }
@@ -4337,11 +4299,6 @@
                 mRootPinnedTask = null;
             } else if (stack == mRootSplitScreenPrimaryTask) {
                 mRootSplitScreenPrimaryTask = null;
-                mDisplayContent.onSplitScreenModeDismissed();
-                // Re-set the split-screen create mode whenever the split-screen stack is removed.
-                mWmService.setDockedStackCreateStateLocked(
-                        SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
-                mDividerControllerLocked.notifyDockedStackExistsChanged(false);
             }
         }
 
@@ -5757,6 +5714,33 @@
         return createStackUnchecked(windowingMode, activityType, stackId, onTop, info, intent);
     }
 
+    /** @return the tile to create the next stack in. */
+    private TaskTile updateLaunchTile(int windowingMode) {
+        if (!isSplitScreenWindowingMode(windowingMode)) {
+            // Only split-screen windowing modes interact with tiles.
+            return null;
+        }
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            final TaskTile t = getStackAt(i).asTile();
+            if (t == null || t.getRequestedOverrideWindowingMode() != windowingMode) {
+                continue;
+            }
+            // If not already set, pick a launch tile which is not the one we are launching
+            // into.
+            if (mLaunchTile == null) {
+                for (int j = 0, n = getStackCount(); j < n; ++j) {
+                    TaskTile tt = getStackAt(j).asTile();
+                    if (tt != t) {
+                        mLaunchTile = tt;
+                        break;
+                    }
+                }
+            }
+            return t;
+        }
+        return mLaunchTile;
+    }
+
     @VisibleForTesting
     ActivityStack createStackUnchecked(int windowingMode, int activityType,
             int stackId, boolean onTop, ActivityInfo info, Intent intent) {
@@ -5769,13 +5753,20 @@
             info.applicationInfo = new ApplicationInfo();
         }
 
+        TaskTile tile = updateLaunchTile(windowingMode);
+        if (tile != null) {
+            // Since this stack will be put into a tile, its windowingMode will be inherited.
+            windowingMode = WINDOWING_MODE_UNDEFINED;
+        }
         final ActivityStack stack = new ActivityStack(this, stackId,
                 mRootWindowContainer.mStackSupervisor, activityType, info, intent);
         addStack(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
         stack.setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 true /* creating */);
-
+        if (tile != null) {
+            tile.addChild(stack, 0 /* index */);
+        }
         return stack;
     }
 
@@ -5995,20 +5986,19 @@
     void onSplitScreenModeDismissed() {
         mAtmService.deferWindowLayout();
         try {
-            // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
+            mLaunchTile = null;
             for (int i = getStackCount() - 1; i >= 0; --i) {
-                final ActivityStack otherStack = getStackAt(i);
-                if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
-                    continue;
+                final TaskTile t = getStackAt(i).asTile();
+                if (t != null) {
+                    t.removeAllChildren();
                 }
-                otherStack.setWindowingMode(WINDOWING_MODE_UNDEFINED, false /* animate */,
-                        false /* showRecents */, false /* enteringSplitScreenMode */,
-                        true /* deferEnsuringVisibility */, false /* creating */);
             }
+            mDividerControllerLocked.setMinimizedDockedStack(false /* minimized */,
+                    false /* animate */);
         } finally {
             final ActivityStack topFullscreenStack =
                     getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            final ActivityStack homeStack = getRootHomeTask();
+            final ActivityStack homeStack = getOrCreateRootHomeTask();
             if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
                 // Whenever split-screen is dismissed we want the home stack directly behind the
                 // current top fullscreen stack so it shows up when the top stack is finished.
@@ -6022,27 +6012,6 @@
         }
     }
 
-    void onSplitScreenModeActivated() {
-        mAtmService.deferWindowLayout();
-        try {
-            // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
-            final ActivityStack splitScreenPrimaryStack = getRootSplitScreenPrimaryTask();
-            for (int i = getStackCount() - 1; i >= 0; --i) {
-                final ActivityStack otherStack = getStackAt(i);
-                if (otherStack == splitScreenPrimaryStack
-                        || !otherStack.affectedBySplitScreenResize()) {
-                    continue;
-                }
-                otherStack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
-                        false /* animate */, false /* showRecents */,
-                        true /* enteringSplitScreenMode */, true /* deferEnsuringVisibility */,
-                        false /* creating */);
-            }
-        } finally {
-            mAtmService.continueWindowLayout();
-        }
-    }
-
     /**
      * Returns true if the {@param windowingMode} is supported based on other parameters passed in.
      * @param windowingMode The windowing mode we are checking support for.
@@ -6574,7 +6543,7 @@
     }
 
     void moveHomeStackToFront(String reason) {
-        final ActivityStack homeStack = getRootHomeTask();
+        final ActivityStack homeStack = getOrCreateRootHomeTask();
         if (homeStack != null) {
             homeStack.moveToFront(reason);
         }
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 38b0ca8..3302445 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -24,7 +24,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
-import static android.view.Display.TYPE_BUILT_IN;
+import static android.view.Display.TYPE_INTERNAL;
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
@@ -934,17 +934,17 @@
      * @return If ok, WindowManagerImpl.ADD_OKAY.  If too many singletons,
      * WindowManagerImpl.ADD_MULTIPLE_SINGLETON
      */
-    int validateAddingWindowLw(WindowManager.LayoutParams attrs) {
+    int validateAddingWindowLw(WindowManager.LayoutParams attrs, int callingPid, int callingUid) {
         if ((attrs.privateFlags & PRIVATE_FLAG_IS_SCREEN_DECOR) != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.STATUS_BAR_SERVICE,
+            mContext.enforcePermission(
+                    android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
                     "DisplayPolicy");
         }
 
         switch (attrs.type) {
             case TYPE_STATUS_BAR:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                mContext.enforcePermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
                         "DisplayPolicy");
                 if (mStatusBar != null) {
                     if (mStatusBar.isAlive()) {
@@ -953,8 +953,8 @@
                 }
                 break;
             case TYPE_NOTIFICATION_SHADE:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                mContext.enforcePermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
                         "DisplayPolicy");
                 if (mNotificationShade != null) {
                     if (mNotificationShade.isAlive()) {
@@ -963,8 +963,8 @@
                 }
                 break;
             case TYPE_NAVIGATION_BAR:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                mContext.enforcePermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
                         "DisplayPolicy");
                 if (mNavigationBar != null) {
                     if (mNavigationBar.isAlive()) {
@@ -974,16 +974,17 @@
                 break;
             case TYPE_NAVIGATION_BAR_PANEL:
                 // Check for permission if the caller is not the recents component.
-                if (!mService.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
-                    mContext.enforceCallingOrSelfPermission(
-                            android.Manifest.permission.STATUS_BAR_SERVICE, "DisplayPolicy");
+                if (!mService.mAtmInternal.isCallerRecents(callingUid)) {
+                    mContext.enforcePermission(
+                            android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
+                            "DisplayPolicy");
                 }
                 break;
             case TYPE_STATUS_BAR_PANEL:
             case TYPE_STATUS_BAR_SUB_PANEL:
             case TYPE_VOICE_INTERACTION_STARTING:
-                mContext.enforceCallingOrSelfPermission(
-                        android.Manifest.permission.STATUS_BAR_SERVICE,
+                mContext.enforcePermission(
+                        android.Manifest.permission.STATUS_BAR_SERVICE, callingPid, callingUid,
                         "DisplayPolicy");
                 break;
         }
@@ -2983,11 +2984,12 @@
 
     /**
      * Return corner radius in pixels that should be used on windows in order to cover the display.
-     * The radius is only valid for built-in displays since the one who configures window corner
-     * radius cannot know the corner radius of non-built-in display.
+     *
+     * The radius is only valid for internal displays, since the corner radius of external displays
+     * is not known at build time when window corners are configured.
      */
     float getWindowCornerRadius() {
-        return mDisplayContent.getDisplay().getType() == TYPE_BUILT_IN
+        return mDisplayContent.getDisplay().getType() == TYPE_INTERNAL
                 ? ScreenDecorationsUtils.getWindowCornerRadius(mContext.getResources()) : 0f;
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index e71371a..539853b 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -525,9 +525,15 @@
             }
             mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
             mIsWaitingForRemoteRotation = false;
-            mDisplayContent.sendNewConfiguration();
-            if (t != null) {
-                mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t, null);
+            mService.mAtmService.deferWindowLayout();
+            try {
+                mDisplayContent.sendNewConfiguration();
+                if (t != null) {
+                    mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t,
+                            null /* organizer */);
+                }
+            } finally {
+                mService.mAtmService.continueWindowLayout();
             }
         }
     }
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 872379e..6431e11 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -745,7 +745,7 @@
      * @param minimizedDock Whether the docked stack is currently minimized.
      * @param animate Whether to animate the change.
      */
-    private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
+    void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
         final boolean wasMinimized = mMinimizedDock;
         mMinimizedDock = minimizedDock;
         if (minimizedDock == wasMinimized) {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 3ff1b95..370d1257 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -39,13 +39,6 @@
     }
 
     /**
-     * Called when Insets have been dispatched to client. This gets called just after onPostLayout.
-     */
-    void onPostInsetsDispatched() {
-        checkShowImePostLayout();
-    }
-
-    /**
      * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
      * requests to show IME on {@param imeTarget}.
      *
@@ -80,7 +73,7 @@
         };
     }
 
-    private void checkShowImePostLayout() {
+    void checkShowImePostLayout() {
         // check if IME is drawn
         if (mIsImeLayoutDrawn
                 || (mImeTargetFromIme != null
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 2d7d3f1..a8fe349 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -168,7 +168,6 @@
             mLastState.set(mState, true /* copySources */);
             notifyInsetsChanged();
         }
-        getImeSourceProvider().onPostInsetsDispatched();
     }
 
     void onInsetsModified(InsetsControlTarget windowState, InsetsState state) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 00947d7..44034ed 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -31,14 +31,14 @@
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE;
 import static android.view.WindowManagerPolicyConstants.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER;
 
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
+import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.KeyguardControllerProto.AOD_SHOWING;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_OCCLUDED_STATES;
 import static com.android.server.wm.KeyguardControllerProto.KEYGUARD_SHOWING;
 import static com.android.server.wm.KeyguardOccludedProto.DISPLAY_ID;
 import static com.android.server.wm.KeyguardOccludedProto.KEYGUARD_OCCLUDED;
-import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -411,8 +411,7 @@
             if (stack == null) {
                 return;
             }
-            mStackSupervisor.moveTasksToFullscreenStackLocked(stack,
-                    stack.isFocusedStackOnDisplay());
+            mRootWindowContainer.getDefaultDisplay().onSplitScreenModeDismissed();
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 944e0ae..634d7e9 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -20,7 +20,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.RemoteAnimationTarget.MODE_CLOSING;
 import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
@@ -414,12 +413,7 @@
         }
 
         // Save the minimized home height
-        final ActivityStack dockedStack =
-                mDisplayContent.getRootSplitScreenPrimaryTask();
-        mDisplayContent.getDockedDividerController().getHomeStackBoundsInDockedMode(
-                mDisplayContent.getConfiguration(),
-                dockedStack == null ? DOCKED_INVALID : dockedStack.getDockSide(),
-                mMinimizedHomeBounds);
+        mMinimizedHomeBounds = mDisplayContent.getRootHomeTask().getBounds();
 
         mService.mWindowPlacerLocked.performSurfacePlacement();
 
@@ -748,8 +742,10 @@
     }
 
     boolean isWallpaperVisible(WindowState w) {
-        return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION && w.mActivityRecord != null
-                && mTargetActivityRecord == w.mActivityRecord && isTargetOverWallpaper();
+        return w != null && w.mAttrs.type == TYPE_BASE_APPLICATION &&
+                ((w.mActivityRecord != null && mTargetActivityRecord == w.mActivityRecord)
+                        || isAnimatingTask(w.getTask()))
+                && isTargetOverWallpaper();
     }
 
     /**
@@ -842,8 +838,8 @@
             mTask = task;
             mIsRecentTaskInvisible = isRecentTaskInvisible;
             final WindowContainer container = mTask.getParent();
-            container.getRelativeDisplayedPosition(mPosition);
             mBounds.set(container.getDisplayedBounds());
+            mPosition.set(mBounds.left, mBounds.top);
         }
 
         RemoteAnimationTarget createRemoteAnimationTarget() {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e3d85c8..2596452 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1998,8 +1998,6 @@
         removeStacksInWindowingModes(WINDOWING_MODE_PINNED);
 
         mUserStackInFront.put(mCurrentUser, focusStackId);
-        final int restoreStackId =
-                mUserStackInFront.get(userId, getDefaultDisplay().getRootHomeTask().getRootTaskId());
         mCurrentUser = userId;
 
         mStackSupervisor.mStartingUsers.add(uss);
@@ -2011,9 +2009,10 @@
             }
         }
 
+        final int restoreStackId = mUserStackInFront.get(userId);
         ActivityStack stack = getStack(restoreStackId);
         if (stack == null) {
-            stack = getDefaultDisplay().getRootHomeTask();
+            stack = getDefaultDisplay().getOrCreateRootHomeTask();
         }
         final boolean homeInFront = stack.isActivityTypeHome();
         if (stack.isOnHomeDisplay()) {
@@ -2035,8 +2034,11 @@
      */
     void updateUserStack(int userId, ActivityStack stack) {
         if (userId != mCurrentUser) {
-            mUserStackInFront.put(userId, stack != null ? stack.getRootTaskId()
-                    : getDefaultDisplay().getRootHomeTask().getRootTaskId());
+            if (stack == null) {
+                stack = getDefaultDisplay().getOrCreateRootHomeTask();
+            }
+
+            mUserStackInFront.put(userId, stack.getRootTaskId());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 0aed691..c7f2cc7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -199,6 +199,7 @@
     static final int INVALID_MIN_SIZE = -1;
     private float mShadowRadius = 0;
     private final Rect mLastSurfaceCrop = new Rect();
+    private static final boolean ENABLE_FREEFORM_COMPOSITOR_SHADOWS = false;
 
     /**
      * The modes to control how the stack is moved to the front when calling {@link Task#reparent}.
@@ -2144,12 +2145,6 @@
                     // For floating tasks, calculate the smallest width from the bounds of the task
                     inOutConfig.smallestScreenWidthDp = (int) (
                             Math.min(mTmpFullBounds.width(), mTmpFullBounds.height()) / density);
-                } else if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
-                    // Iterating across all screen orientations, and return the minimum of the task
-                    // width taking into account that the bounds might change because the snap
-                    // algorithm snaps to a different value
-                    inOutConfig.smallestScreenWidthDp =
-                            getSmallestScreenWidthDpForDockedBounds(mTmpFullBounds);
                 }
                 // otherwise, it will just inherit
             }
@@ -2553,8 +2548,10 @@
     }
 
     private void updateSurfaceCrop() {
+        // TODO(b/149585281) remove when root task has the correct bounds for freeform
         // Only update the crop if we are drawing shadows on the task.
-        if (mSurfaceControl == null || !mWmService.mRenderShadowsInCompositor) {
+        if (mSurfaceControl == null || !mWmService.mRenderShadowsInCompositor
+                || !isRootTask() || !ENABLE_FREEFORM_COMPOSITOR_SHADOWS) {
             return;
         }
 
@@ -2978,8 +2975,14 @@
     }
 
     @Override
+    void onSurfaceShown(SurfaceControl.Transaction t) {
+        super.onSurfaceShown(t);
+        t.unsetColor(mSurfaceControl);
+    }
+
+    @Override
     SurfaceControl.Builder makeSurface() {
-        return super.makeSurface().setMetadata(METADATA_TASK_ID, mTaskId);
+        return super.makeSurface().setColorLayer().setMetadata(METADATA_TASK_ID, mTaskId);
     }
 
     boolean isTaskAnimating() {
@@ -3248,6 +3251,10 @@
         return this;
     }
 
+    TaskTile asTile() {
+        return null;
+    }
+
     // TODO(task-merge): Figure-out how this should work with hierarchy tasks.
     boolean shouldBeVisible(ActivityRecord starting) {
         return true;
@@ -3934,7 +3941,8 @@
             return dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP,
                     mDisplayContent.getDisplayMetrics());
         }
-        if (inFreeformWindowingMode()) {
+        // TODO(b/149585281) remove when root task has the correct bounds for freeform
+        if (ENABLE_FREEFORM_COMPOSITOR_SHADOWS && inFreeformWindowingMode()) {
             final int elevation = taskIsFocused
                     ? DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
             return dipToPixel(elevation, mDisplayContent.getDisplayMetrics());
@@ -3949,7 +3957,7 @@
      */
     private void updateShadowsRadius(boolean taskIsFocused,
             SurfaceControl.Transaction pendingTransaction) {
-        if (!mWmService.mRenderShadowsInCompositor) return;
+        if (!mWmService.mRenderShadowsInCompositor || !isRootTask()) return;
 
         final float newShadowRadius = getShadowRadius(taskIsFocused);
         if (mShadowRadius != newShadowRadius) {
diff --git a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
index 2dde0ba..f715d8f 100644
--- a/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
+++ b/services/core/java/com/android/server/wm/TaskChangeNotificationController.java
@@ -59,6 +59,7 @@
     private static final int NOTIFY_TASK_LIST_UPDATED_LISTENERS_MSG = 24;
     private static final int NOTIFY_SINGLE_TASK_DISPLAY_EMPTY = 25;
     private static final int NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG = 26;
+    private static final int NOTIFY_TASK_FOCUS_CHANGED_MSG = 27;
 
     // Delay in notifying task stack change listeners (in millis)
     private static final int NOTIFY_TASK_STACK_CHANGE_LISTENERS_DELAY = 100;
@@ -179,6 +180,10 @@
         l.onRecentTaskListFrozenChanged(m.arg1 != 0);
     };
 
+    private final TaskStackConsumer mNotifyTaskFocusChanged = (l, m) -> {
+        l.onTaskFocusChanged(m.arg1, m.arg2 != 0);
+    };
+
     @FunctionalInterface
     public interface TaskStackConsumer {
         void accept(ITaskStackListener t, Message m) throws RemoteException;
@@ -273,6 +278,9 @@
                 case NOTIFY_TASK_LIST_FROZEN_UNFROZEN_MSG:
                     forAllRemoteListeners(mNotifyTaskListFrozen, msg);
                     break;
+                case NOTIFY_TASK_FOCUS_CHANGED_MSG:
+                    forAllRemoteListeners(mNotifyTaskFocusChanged, msg);
+                    break;
             }
         }
     }
@@ -565,4 +573,12 @@
         forAllLocalListeners(mNotifyTaskListFrozen, msg);
         msg.sendToTarget();
     }
+
+    /** @see ITaskStackListener#onTaskFocusChanged(int, boolean) */
+    void notifyTaskFocusChanged(int taskId, boolean focused) {
+        final Message msg = mHandler.obtainMessage(NOTIFY_TASK_FOCUS_CHANGED_MSG,
+                taskId, focused ? 1 : 0);
+        forAllLocalListeners(mNotifyTaskFocusChanged, msg);
+        msg.sendToTarget();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 4b13a0c..4d5621c 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -492,6 +492,10 @@
         if (!(container instanceof Task)) {
             throw new IllegalArgumentException("Invalid container in hierarchy op");
         }
+        if (container.getDisplayContent() == null) {
+            Slog.w(TAG, "Container is no longer attached: " + container);
+            return 0;
+        }
         if (hop.isReparent()) {
             // special case for tiles since they are "virtual" parents
             if (container instanceof ActivityStack && ((ActivityStack) container).isRootTask()) {
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
index 369db05..74d5c33 100644
--- a/services/core/java/com/android/server/wm/TaskTile.java
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -31,7 +31,6 @@
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.util.Slog;
-import android.view.SurfaceControl;
 
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -78,30 +77,9 @@
         // Virtual parent, so don't notify children.
     }
 
-    /**
-     * If there is a disconnection, this will clean up any vestigial surfaces left on the tile
-     * leash by moving known children to a new surfacecontrol and then removing the old one.
-     */
-    void cleanupSurfaces() {
-        if (mSurfaceControl == null) {
-            return;
-        }
-        SurfaceControl oldSurface = mSurfaceControl;
-        WindowContainer parentWin = getParent();
-        if (parentWin == null) {
-            return;
-        }
-        mSurfaceControl = parentWin.makeChildSurface(null).setName("TaskTile " + mTaskId + " - "
-                    + getRequestedOverrideWindowingMode()).setContainerLayer().build();
-        SurfaceControl.Transaction t = parentWin.getPendingTransaction();
-        t.show(mSurfaceControl);
-        for (int i = 0; i < mChildren.size(); ++i) {
-            if (mChildren.get(i).getSurfaceControl() == null) {
-                continue;
-            }
-            mChildren.get(i).reparentSurfaceControl(t, mSurfaceControl);
-        }
-        t.remove(oldSurface);
+    @Override
+    TaskTile asTile() {
+        return this;
     }
 
     @Override
@@ -215,6 +193,12 @@
         super.removeImmediately();
     }
 
+    @Override
+    void taskOrganizerDied() {
+        super.taskOrganizerDied();
+        removeImmediately();
+    }
+
     static TaskTile forToken(IBinder token) {
         try {
             return (TaskTile) ((TaskToken) token).getContainer();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 0bb4e03..294c36a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -365,6 +365,7 @@
             // build a surface.
             setSurfaceControl(makeSurface().build());
             getPendingTransaction().show(mSurfaceControl);
+            onSurfaceShown(getPendingTransaction());
             updateSurfacePosition();
         } else {
             // If we have a surface but a new parent, we just need to perform a reparent. Go through
@@ -383,6 +384,13 @@
         scheduleAnimation();
     }
 
+    /**
+     * Called when the surface is shown for the first time.
+     */
+    void onSurfaceShown(Transaction t) {
+        // do nothing
+    }
+
     // Temp. holders for a chain of containers we are currently processing.
     private final LinkedList<WindowContainer> mTmpChain1 = new LinkedList<>();
     private final LinkedList<WindowContainer> mTmpChain2 = new LinkedList<>();
@@ -1908,7 +1916,7 @@
 
     @Override
     public Builder makeAnimationLeash() {
-        return makeSurface();
+        return makeSurface().setContainerLayer();
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2bb6703..bb3b66b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1336,14 +1336,18 @@
             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
             InsetsState outInsetsState) {
         int[] appOp = new int[1];
-        int res = mPolicy.checkAddPermission(attrs, appOp);
+        final boolean isRoundedCornerOverlay = (attrs.privateFlags
+                & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
+        int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
+                appOp);
         if (res != WindowManagerGlobal.ADD_OKAY) {
             return res;
         }
 
         WindowState parentWindow = null;
-        long origId;
         final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final long origId = Binder.clearCallingIdentity();
         final int type = attrs.type;
 
         synchronized (mGlobalLock) {
@@ -1410,8 +1414,6 @@
                     return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                 }
                 final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
-                final boolean isRoundedCornerOverlay =
-                        (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;
                 token = new WindowToken(this, binder, type, false, displayContent,
                         session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
             } else if (rootType >= FIRST_APPLICATION_WINDOW
@@ -1504,10 +1506,9 @@
             }
 
             final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
-            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
-                    Binder.getCallingUid());
+            displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
 
-            res = displayPolicy.validateAddingWindowLw(attrs);
+            res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
             if (res != WindowManagerGlobal.ADD_OKAY) {
                 return res;
             }
@@ -1559,8 +1560,6 @@
                 displayContent.mTapExcludedWindows.add(win);
             }
 
-            origId = Binder.clearCallingIdentity();
-
             win.attach();
             mWindowMap.put(client.asBinder(), win);
             win.initAppOpsState();
@@ -2560,43 +2559,48 @@
         final boolean callerCanManageAppTokens =
                 checkCallingPermission(MANAGE_APP_TOKENS, "addWindowToken()");
         if (!callerCanManageAppTokens) {
-            // TODO(window-context): refactor checkAddPermission to not take attrs.
-            LayoutParams attrs = new LayoutParams(type);
-            attrs.packageName = packageName;
-            final int res = mPolicy.checkAddPermission(attrs, new int[1]);
+            final int res = mPolicy.checkAddPermission(type, false /* isRoundedCornerOverlay */,
+                    packageName, new int[1]);
             if (res != ADD_OKAY) {
                 return res;
             }
         }
 
-        synchronized (mGlobalLock) {
-            if (!callerCanManageAppTokens) {
-                if (!unprivilegedAppCanCreateTokenWith(null, Binder.getCallingUid(), type, type,
-                        null, packageName) || packageName == null) {
-                    throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+        final int callingUid = Binder.getCallingUid();
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                if (!callerCanManageAppTokens) {
+                    if (packageName == null || !unprivilegedAppCanCreateTokenWith(
+                            null /* parentWindow */, callingUid, type, type, null /* tokenForLog */,
+                            packageName)) {
+                        throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
+                    }
+                }
+
+                final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
+                if (dc == null) {
+                    ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
+                            + " for non-exiting displayId=%d", binder, displayId);
+                    return WindowManagerGlobal.ADD_INVALID_DISPLAY;
+                }
+
+                WindowToken token = dc.getWindowToken(binder);
+                if (token != null) {
+                    ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
+                            + " for already created window token: %s"
+                            + " displayId=%d", binder, token, displayId);
+                    return WindowManagerGlobal.ADD_DUPLICATE_ADD;
+                }
+                // TODO(window-container): Clean up dead tokens
+                if (type == TYPE_WALLPAPER) {
+                    new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
+                } else {
+                    new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens);
                 }
             }
-
-            final DisplayContent dc = getDisplayContentOrCreate(displayId, null /* token */);
-            if (dc == null) {
-                ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add token: %s"
-                        + " for non-exiting displayId=%d", binder, displayId);
-                return WindowManagerGlobal.ADD_INVALID_DISPLAY;
-            }
-
-            WindowToken token = dc.getWindowToken(binder);
-            if (token != null) {
-                ProtoLog.w(WM_ERROR, "addWindowToken: Attempted to add binder token: %s"
-                        + " for already created window token: %s"
-                        + " displayId=%d", binder, token, displayId);
-                return WindowManagerGlobal.ADD_DUPLICATE_ADD;
-            }
-            // TODO(window-container): Clean up dead tokens
-            if (type == TYPE_WALLPAPER) {
-                new WallpaperWindowToken(this, binder, true, dc, callerCanManageAppTokens);
-            } else {
-                new WindowToken(this, binder, type, true, dc, callerCanManageAppTokens);
-            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
         return WindowManagerGlobal.ADD_OKAY;
     }
@@ -6843,34 +6847,32 @@
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
         }
 
-        synchronized (mGlobalLock) {
-            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
-            if (displayContent == null) {
-                ProtoLog.w(WM_ERROR,
-                        "Attempted to set windowing mode to a display that does not exist: %d",
-                        displayId);
-                return;
-            }
-
-            int lastWindowingMode = displayContent.getWindowingMode();
-            mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode);
-
-            displayContent.reconfigureDisplayLocked();
-
-            if (lastWindowingMode != displayContent.getWindowingMode()) {
-                // reconfigure won't detect this change in isolation because the windowing mode is
-                // already set on the display, so fire off a new config now.
-
-                final long origId = Binder.clearCallingIdentity();
-                try {
-                    // direct call since lock is shared.
-                    displayContent.sendNewConfiguration();
-                } finally {
-                    Binder.restoreCallingIdentity(origId);
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+                if (displayContent == null) {
+                    ProtoLog.w(WM_ERROR,
+                            "Attempted to set windowing mode to a display that does not exist: %d",
+                            displayId);
+                    return;
                 }
-                // Now that all configurations are updated, execute pending transitions
-                displayContent.executeAppTransition();
+
+                int lastWindowingMode = displayContent.getWindowingMode();
+                mDisplayWindowSettings.setWindowingModeLocked(displayContent, mode);
+
+                displayContent.reconfigureDisplayLocked();
+
+                if (lastWindowingMode != displayContent.getWindowingMode()) {
+                    // reconfigure won't detect this change in isolation because the windowing mode
+                    // is already set on the display, so fire off a new config now.
+                    displayContent.sendNewConfiguration();
+                    // Now that all configurations are updated, execute pending transitions.
+                    displayContent.executeAppTransition();
+                }
             }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -6898,18 +6900,22 @@
             throw new SecurityException("Requires INTERNAL_SYSTEM_WINDOW permission");
         }
 
-        synchronized (mGlobalLock) {
-            final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
-            if (displayContent == null) {
-                ProtoLog.w(WM_ERROR,
-                        "Attempted to set remove mode to a display that does not exist: %d",
-                        displayId);
-                return;
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                final DisplayContent displayContent = getDisplayContentOrCreate(displayId, null);
+                if (displayContent == null) {
+                    ProtoLog.w(WM_ERROR,
+                            "Attempted to set remove mode to a display that does not exist: %d",
+                            displayId);
+                    return;
+                }
+
+                mDisplayWindowSettings.setRemoveContentModeLocked(displayContent, mode);
+                displayContent.reconfigureDisplayLocked();
             }
-
-            mDisplayWindowSettings.setRemoveContentModeLocked(displayContent, mode);
-
-            displayContent.reconfigureDisplayLocked();
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
     }
 
@@ -7320,7 +7326,9 @@
             synchronized (mGlobalLock) {
                 final WindowState imeTarget = mWindowMap.get(imeTargetWindowToken);
                 if (imeTarget != null) {
-                    imeTarget.getDisplayContent().updateImeControlTarget(imeTarget);
+                    InsetsControlTarget controlTarget =
+                            imeTarget.getDisplayContent().computeImeControlTarget(imeTarget);
+                    imeTarget.getDisplayContent().updateImeControlTarget(controlTarget);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b336f8d..ed4e684 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1493,7 +1493,8 @@
         // Some system windows (e.g. "Power off" dialog) don't have a task, but we would still
         // associate them with some stack to enable dimming.
         final DisplayContent dc = getDisplayContent();
-        return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null ? dc.getRootHomeTask() : null;
+        return mAttrs.type >= FIRST_SYSTEM_WINDOW && dc != null
+                ? dc.getOrCreateRootHomeTask() : null;
     }
 
     /**
@@ -2661,18 +2662,6 @@
                             mWmService.mTaskSnapshotController.onAppDied(win.mActivityRecord);
                         }
                         win.removeIfPossible(shouldKeepVisibleDeadAppWindow());
-                        if (win.mAttrs.type == TYPE_DOCK_DIVIDER) {
-                            // The owner of the docked divider died :( We reset the docked stack,
-                            // just in case they have the divider at an unstable position. Better
-                            // also reset drag resizing state, because the owner can't do it
-                            // anymore.
-                            final ActivityStack stack =
-                                    dc.getRootSplitScreenPrimaryTask();
-                            if (stack != null) {
-                                stack.resetDockedStackToMiddle();
-                            }
-                            resetSplitScreenResizing = true;
-                        }
                     } else if (mHasSurface) {
                         Slog.e(TAG, "!!! LEAK !!! Window removed but surface still valid.");
                         WindowState.this.removeIfPossible();
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index d0eaa48..e1615af 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -645,10 +645,12 @@
     Return<void> gnssSvStatusCb_2_0(const hidl_vec<IGnssCallback_V2_0::GnssSvInfo>& svInfoList) override {
         return gnssSvStatusCbImpl(svInfoList);
     }
+
+    // New in 2.1
     Return<void> gnssSvStatusCb_2_1(const hidl_vec<IGnssCallback_V2_1::GnssSvInfo>& svInfoList) override {
         return gnssSvStatusCbImpl(svInfoList);
     }
-    Return<void> gnssSetCapabilitesCbImpl(uint32_t capabilities, bool hasSubHalCapabilityFlags);
+    Return<void> gnssSetCapabilitiesCb_2_1(uint32_t capabilities) override;
 
     // TODO: Reconsider allocation cost vs threadsafety on these statics
     static const char* sNmeaString;
@@ -851,6 +853,10 @@
     return GnssCallback::gnssSetCapabilitesCb(capabilities);
 }
 
+Return<void> GnssCallback::gnssSetCapabilitiesCb_2_1(uint32_t capabilities) {
+    return GnssCallback::gnssSetCapabilitesCb(capabilities);
+}
+
 Return<void> GnssCallback::gnssAcquireWakelockCb() {
     acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME);
     return Void();
diff --git a/services/core/xsd/vts/Android.bp b/services/core/xsd/vts/Android.bp
index 5545656..636d110 100644
--- a/services/core/xsd/vts/Android.bp
+++ b/services/core/xsd/vts/Android.bp
@@ -31,4 +31,12 @@
         "-Wall",
         "-Werror",
     ],
+    data: [
+        ":default-permissions",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts-core"
+    ],
+    test_config: "vts_defaultPermissions_validate_test.xml",
 }
diff --git a/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
new file mode 100644
index 0000000..1ccacae
--- /dev/null
+++ b/services/core/xsd/vts/vts_defaultPermissions_validate_test.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2020 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.
+-->
+<configuration description="Runs vts_defaultPermissions_validate_test.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="default-permissions.xsd->/data/local/tmp/default-permissions.xsd" />
+        <option name="push" value="vts_defaultPermissions_validate_test->/data/local/tmp/vts_defaultPermissions_validate_test" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="vts_defaultPermissions_validate_test" />
+    </test>
+</configuration>
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index aa5dafc..063ad0f 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -15230,15 +15230,27 @@
             final ActiveAdmin admin =
                     getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
             previousCrossProfilePackages = admin.mCrossProfilePackages;
+            if (packageNames.equals(previousCrossProfilePackages)) {
+                return;
+            }
             admin.mCrossProfilePackages = packageNames;
             saveSettingsLocked(mInjector.userHandleGetCallingUserId());
         }
+        logSetCrossProfilePackages(who, packageNames);
         final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class);
         mInjector.binderWithCleanCallingIdentity(
                 () -> crossProfileApps.resetInteractAcrossProfilesAppOps(
                         previousCrossProfilePackages, new HashSet<>(packageNames)));
     }
 
+    private void logSetCrossProfilePackages(ComponentName who, List<String> packageNames) {
+        DevicePolicyEventLogger
+                .createEvent(DevicePolicyEnums.SET_CROSS_PROFILE_PACKAGES)
+                .setAdmin(who)
+                .setStrings(packageNames.toArray(new String[packageNames.size()]))
+                .write();
+    }
+
     @Override
     public List<String> getCrossProfilePackages(ComponentName who) {
         if (!mHasFeature) {
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 2d18a29..663bf4f 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -17,6 +17,7 @@
 package com.android.server.people;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionSessionId;
 import android.app.prediction.AppTarget;
@@ -24,6 +25,7 @@
 import android.app.prediction.IPredictionCallback;
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
+import android.os.CancellationSignal;
 import android.os.RemoteException;
 import android.util.ArrayMap;
 import android.util.Slog;
@@ -138,6 +140,21 @@
             });
         }
 
+        @Override
+        public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
+            mDataManager.pruneDataForUser(userId, signal);
+        }
+
+        @Override
+        public byte[] backupConversationInfos(@UserIdInt int userId) {
+            return new byte[0];
+        }
+
+        @Override
+        public void restoreConversationInfos(@UserIdInt int userId, @NonNull String key,
+                @NonNull byte[] payload) {
+        }
+
         @VisibleForTesting
         SessionInfo getSessionInfo(AppPredictionSessionId sessionId) {
             return mSessions.get(sessionId);
@@ -160,14 +177,5 @@
                 Slog.e(TAG, "Failed to calling callback" + e);
             }
         }
-
-        @Override
-        public byte[] backupConversationInfos(int userId) {
-            return new byte[0];
-        }
-
-        @Override
-        public void restoreConversationInfos(int userId, String key, byte[] payload) {
-        }
     }
 }
diff --git a/services/people/java/com/android/server/people/data/ConversationInfo.java b/services/people/java/com/android/server/people/data/ConversationInfo.java
index ce35366..859cdf2 100644
--- a/services/people/java/com/android/server/people/data/ConversationInfo.java
+++ b/services/people/java/com/android/server/people/data/ConversationInfo.java
@@ -137,6 +137,11 @@
         return hasShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED);
     }
 
+    /** Whether the shortcut for this conversation is cached in Shortcut Service. */
+    public boolean isShortcutCached() {
+        return hasShortcutFlags(ShortcutInfo.FLAG_CACHED);
+    }
+
     /** Whether this conversation is marked as important by the user. */
     public boolean isImportant() {
         return hasConversationFlags(FLAG_IMPORTANT);
@@ -213,6 +218,9 @@
         if (isShortcutLongLived()) {
             sb.append("Liv");
         }
+        if (isShortcutCached()) {
+            sb.append("Cac");
+        }
         sb.append("]");
         sb.append(", conversationFlags=0x").append(Integer.toHexString(mConversationFlags));
         sb.append(" [");
diff --git a/services/people/java/com/android/server/people/data/ConversationStore.java b/services/people/java/com/android/server/people/data/ConversationStore.java
index ea36d38..3afb209 100644
--- a/services/people/java/com/android/server/people/data/ConversationStore.java
+++ b/services/people/java/com/android/server/people/data/ConversationStore.java
@@ -133,10 +133,11 @@
     }
 
     @MainThread
-    synchronized void deleteConversation(@NonNull String shortcutId) {
+    @Nullable
+    synchronized ConversationInfo deleteConversation(@NonNull String shortcutId) {
         ConversationInfo conversationInfo = mConversationInfoMap.remove(shortcutId);
         if (conversationInfo == null) {
-            return;
+            return null;
         }
 
         LocusId locusId = conversationInfo.getLocusId();
@@ -159,6 +160,7 @@
             mNotifChannelIdToShortcutIdMap.remove(notifChannelId);
         }
         scheduleUpdateConversationsOnDisk();
+        return conversationInfo;
     }
 
     synchronized void forAllConversations(@NonNull Consumer<ConversationInfo> consumer) {
diff --git a/services/people/java/com/android/server/people/data/DataMaintenanceService.java b/services/people/java/com/android/server/people/data/DataMaintenanceService.java
new file mode 100644
index 0000000..58f0654
--- /dev/null
+++ b/services/people/java/com/android/server/people/data/DataMaintenanceService.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 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.people.data;
+
+import android.annotation.UserIdInt;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.CancellationSignal;
+
+import com.android.server.LocalServices;
+import com.android.server.people.PeopleServiceInternal;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This service runs periodically to ensure the data consistency and the retention policy for a
+ * specific user's data.
+ */
+public class DataMaintenanceService extends JobService {
+
+    private static final long JOB_RUN_INTERVAL = TimeUnit.HOURS.toMillis(24);
+
+    /** This job ID must be unique within the system server. */
+    private static final int BASE_JOB_ID = 0xC315BD7;  // 204561367
+
+    static void scheduleJob(Context context, @UserIdInt int userId) {
+        int jobId = getJobId(userId);
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        if (jobScheduler.getPendingJob(jobId) == null) {
+            ComponentName component = new ComponentName(context, DataMaintenanceService.class);
+            JobInfo newJob = new JobInfo.Builder(jobId, component)
+                    .setRequiresDeviceIdle(true)
+                    .setPeriodic(JOB_RUN_INTERVAL)
+                    .build();
+            jobScheduler.schedule(newJob);
+        }
+    }
+
+    static void cancelJob(Context context, @UserIdInt int userId) {
+        JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        jobScheduler.cancel(getJobId(userId));
+    }
+
+    private CancellationSignal mSignal;
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        int userId = getUserId(params.getJobId());
+        mSignal = new CancellationSignal();
+        new Thread(() -> {
+            PeopleServiceInternal peopleServiceInternal =
+                    LocalServices.getService(PeopleServiceInternal.class);
+            peopleServiceInternal.pruneDataForUser(userId, mSignal);
+            jobFinished(params, mSignal.isCanceled());
+        }).start();
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        if (mSignal != null) {
+            mSignal.cancel();
+        }
+        return false;
+    }
+
+    private static int getJobId(@UserIdInt int userId) {
+        return BASE_JOB_ID + userId;
+    }
+
+    private static @UserIdInt int getUserId(int jobId) {
+        return jobId - BASE_JOB_ID;
+    }
+}
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index a904b42..dd9cbd0 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -32,6 +32,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.LauncherApps.ShortcutQuery;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutManager.ShareShortcutInfo;
@@ -40,6 +41,7 @@
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.Process;
 import android.os.RemoteException;
@@ -53,16 +55,20 @@
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
+import android.util.ArraySet;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ChooserActivity;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.telephony.SmsApplication;
 import com.android.server.LocalServices;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
@@ -94,10 +100,12 @@
     private final SparseArray<ScheduledFuture<?>> mUsageStatsQueryFutures = new SparseArray<>();
     private final SparseArray<NotificationListenerService> mNotificationListeners =
             new SparseArray<>();
-    private final ContentObserver mCallLogContentObserver;
-    private final ContentObserver mMmsSmsContentObserver;
+    private final SparseArray<PackageMonitor> mPackageMonitors = new SparseArray<>();
+    private ContentObserver mCallLogContentObserver;
+    private ContentObserver mMmsSmsContentObserver;
 
     private ShortcutServiceInternal mShortcutServiceInternal;
+    private PackageManagerInternal mPackageManagerInternal;
     private ShortcutManager mShortcutManager;
     private UserManager mUserManager;
 
@@ -110,16 +118,13 @@
         mContext = context;
         mInjector = injector;
         mUsageStatsQueryExecutor = mInjector.createScheduledExecutor();
-        mCallLogContentObserver = new CallLogContentObserver(
-                BackgroundThread.getHandler());
-        mMmsSmsContentObserver = new MmsSmsContentObserver(
-                BackgroundThread.getHandler());
         mDiskReadWriterExecutor = mInjector.createScheduledExecutor();
     }
 
     /** Initialization. Called when the system services are up running. */
     public void initialize() {
         mShortcutServiceInternal = LocalServices.getService(ShortcutServiceInternal.class);
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
         mShortcutManager = mContext.getSystemService(ShortcutManager.class);
         mUserManager = mContext.getSystemService(UserManager.class);
 
@@ -172,17 +177,26 @@
             // Should never occur for local calls.
         }
 
+        PackageMonitor packageMonitor = new PerUserPackageMonitor();
+        packageMonitor.register(mContext, null, UserHandle.of(userId), true);
+        mPackageMonitors.put(userId, packageMonitor);
+
         if (userId == UserHandle.USER_SYSTEM) {
             // The call log and MMS/SMS messages are shared across user profiles. So only need to
             // register the content observers once for the primary user.
             // TODO: Register observers after the conversations and events being loaded from disk.
+            mCallLogContentObserver = new CallLogContentObserver(BackgroundThread.getHandler());
             mContext.getContentResolver().registerContentObserver(
                     CallLog.CONTENT_URI, /* notifyForDescendants= */ true,
                     mCallLogContentObserver, UserHandle.USER_SYSTEM);
+
+            mMmsSmsContentObserver = new MmsSmsContentObserver(BackgroundThread.getHandler());
             mContext.getContentResolver().registerContentObserver(
                     MmsSms.CONTENT_URI, /* notifyForDescendants= */ false,
                     mMmsSmsContentObserver, UserHandle.USER_SYSTEM);
         }
+
+        DataMaintenanceService.scheduleJob(mContext, userId);
     }
 
     /** This method is called when a user is stopped. */
@@ -207,10 +221,21 @@
                 // Should never occur for local calls.
             }
         }
-        if (userId == UserHandle.USER_SYSTEM) {
-            mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
-            mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
+        if (mPackageMonitors.indexOfKey(userId) >= 0) {
+            mPackageMonitors.get(userId).unregister();
         }
+        if (userId == UserHandle.USER_SYSTEM) {
+            if (mCallLogContentObserver != null) {
+                mContext.getContentResolver().unregisterContentObserver(mCallLogContentObserver);
+                mCallLogContentObserver = null;
+            }
+            if (mMmsSmsContentObserver != null) {
+                mContext.getContentResolver().unregisterContentObserver(mMmsSmsContentObserver);
+                mCallLogContentObserver = null;
+            }
+        }
+
+        DataMaintenanceService.cancelJob(mContext, userId);
     }
 
     /**
@@ -274,9 +299,8 @@
                     || TextUtils.isEmpty(mimeType)) {
                 return;
             }
-            EventHistoryImpl eventHistory =
-                    packageData.getEventStore().getOrCreateShortcutEventHistory(
-                            shortcutInfo.getId());
+            EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+                    EventStore.CATEGORY_SHORTCUT_BASED, shortcutInfo.getId());
             @Event.EventType int eventType;
             if (mimeType.startsWith("text/")) {
                 eventType = Event.TYPE_SHARE_TEXT;
@@ -291,6 +315,45 @@
         }
     }
 
+    /** Prunes the data for the specified user. */
+    public void pruneDataForUser(@UserIdInt int userId, @NonNull CancellationSignal signal) {
+        UserData userData = getUnlockedUserData(userId);
+        if (userData == null || signal.isCanceled()) {
+            return;
+        }
+        pruneUninstalledPackageData(userData);
+
+        long currentTimeMillis = System.currentTimeMillis();
+        userData.forAllPackages(packageData -> {
+            if (signal.isCanceled()) {
+                return;
+            }
+            packageData.getEventStore().pruneOldEvents(currentTimeMillis);
+            if (!packageData.isDefaultDialer()) {
+                packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_CALL);
+            }
+            if (!packageData.isDefaultSmsApp()) {
+                packageData.getEventStore().deleteEventHistories(EventStore.CATEGORY_SMS);
+            }
+            packageData.pruneOrphanEvents();
+        });
+    }
+
+    private void pruneUninstalledPackageData(@NonNull UserData userData) {
+        Set<String> installApps = new ArraySet<>();
+        mPackageManagerInternal.forEachInstalledPackage(
+                pkg -> installApps.add(pkg.getPackageName()), userData.getUserId());
+        List<String> packagesToDelete = new ArrayList<>();
+        userData.forAllPackages(packageData -> {
+            if (!installApps.contains(packageData.getPackageName())) {
+                packagesToDelete.add(packageData.getPackageName());
+            }
+        });
+        for (String packageName : packagesToDelete) {
+            userData.deletePackageData(packageName);
+        }
+    }
+
     /** Gets a list of {@link ShortcutInfo}s with the given shortcut IDs. */
     private List<ShortcutInfo> getShortcuts(
             @NonNull String packageName, @UserIdInt int userId,
@@ -347,7 +410,8 @@
                 || packageData.getConversationStore().getConversation(shortcutId) == null) {
             return null;
         }
-        return packageData.getEventStore().getOrCreateShortcutEventHistory(shortcutId);
+        return packageData.getEventStore().getOrCreateEventHistory(
+                EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
     }
 
     @VisibleForTesting
@@ -413,6 +477,11 @@
     }
 
     @VisibleForTesting
+    PackageMonitor getPackageMonitorForTesting(@UserIdInt int userId) {
+        return mPackageMonitors.get(userId);
+    }
+
+    @VisibleForTesting
     UserData getUserDataForTesting(@UserIdInt int userId) {
         return mUserDataArray.get(userId);
     }
@@ -499,7 +568,8 @@
                     return;
                 }
                 EventStore eventStore = defaultDialer.getEventStore();
-                eventStore.getOrCreateCallEventHistory(phoneNumber).addEvent(event);
+                eventStore.getOrCreateEventHistory(
+                        EventStore.CATEGORY_CALL, phoneNumber).addEvent(event);
             });
         }
     }
@@ -544,7 +614,8 @@
                     return;
                 }
                 EventStore eventStore = defaultSmsApp.getEventStore();
-                eventStore.getOrCreateSmsEventHistory(phoneNumber).addEvent(event);
+                eventStore.getOrCreateEventHistory(
+                        EventStore.CATEGORY_SMS, phoneNumber).addEvent(event);
             });
         }
     }
@@ -569,6 +640,14 @@
     private class NotificationListener extends NotificationListenerService {
 
         @Override
+        public void onNotificationPosted(StatusBarNotification sbn) {
+            EventHistoryImpl eventHistory = getEventHistoryIfEligible(sbn);
+            if (eventHistory != null) {
+                eventHistory.addEvent(new Event(sbn.getPostTime(), Event.TYPE_NOTIFICATION_POSTED));
+            }
+        }
+
+        @Override
         public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap,
                 int reason) {
             if (reason != REASON_CLICK) {
@@ -617,7 +696,15 @@
                     break;
             }
             conversationStore.addOrUpdate(builder.build());
-            // TODO: Cache the shortcut when a conversation's notification setting is changed.
+
+            if (modificationType == NOTIFICATION_CHANNEL_OR_GROUP_UPDATED
+                    && conversationInfo.isShortcutLongLived()
+                    && !conversationInfo.isShortcutCached()) {
+                mShortcutServiceInternal.cacheShortcuts(user.getIdentifier(),
+                        mContext.getPackageName(), pkg,
+                        Collections.singletonList(conversationInfo.getShortcutId()),
+                        user.getIdentifier());
+            }
         }
     }
 
@@ -670,6 +757,20 @@
         }
     }
 
+    private class PerUserPackageMonitor extends PackageMonitor {
+
+        @Override
+        public void onPackageRemoved(String packageName, int uid) {
+            super.onPackageRemoved(packageName, uid);
+
+            int userId = getChangingUserId();
+            UserData userData = getUnlockedUserData(userId);
+            if (userData != null) {
+                userData.deletePackageData(packageName);
+            }
+        }
+    }
+
     private class ShutdownBroadcastReceiver extends BroadcastReceiver {
 
         @Override
diff --git a/services/people/java/com/android/server/people/data/EventHistoryImpl.java b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
index 6b6bd7e..6bef1db 100644
--- a/services/people/java/com/android/server/people/data/EventHistoryImpl.java
+++ b/services/people/java/com/android/server/people/data/EventHistoryImpl.java
@@ -78,6 +78,17 @@
         mRecentEvents.add(event);
     }
 
+    void onDestroy() {
+        mEventIndexArray.clear();
+        mRecentEvents.clear();
+        // TODO: STOPSHIP: Delete the data files.
+    }
+
+    /** Deletes the events data that exceeds the retention period. */
+    void pruneOldEvents(long currentTimeMillis) {
+        // TODO: STOPSHIP: Delete the old events data files.
+    }
+
     @VisibleForTesting
     static class Injector {
 
diff --git a/services/people/java/com/android/server/people/data/EventList.java b/services/people/java/com/android/server/people/data/EventList.java
index b267d66..d770f91 100644
--- a/services/people/java/com/android/server/people/data/EventList.java
+++ b/services/people/java/com/android/server/people/data/EventList.java
@@ -69,6 +69,10 @@
         return result;
     }
 
+    void clear() {
+        mEvents.clear();
+    }
+
     /** Returns the first index whose timestamp is greater or equal to the provided timestamp. */
     private int firstIndexOnOrAfter(long timestamp) {
         int result = mEvents.size();
diff --git a/services/people/java/com/android/server/people/data/EventStore.java b/services/people/java/com/android/server/people/data/EventStore.java
index d6b7a86..c8d44ac 100644
--- a/services/people/java/com/android/server/people/data/EventStore.java
+++ b/services/people/java/com/android/server/people/data/EventStore.java
@@ -16,99 +16,129 @@
 
 package com.android.server.people.data;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.LocusId;
 import android.util.ArrayMap;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.function.Predicate;
 
 /** The store that stores and accesses the events data for a package. */
 class EventStore {
 
-    private final EventHistoryImpl mPackageEventHistory = new EventHistoryImpl();
+    /** The events that are queryable with a shortcut ID. */
+    static final int CATEGORY_SHORTCUT_BASED = 0;
 
-    // Shortcut ID -> Event History
-    private final Map<String, EventHistoryImpl> mShortcutEventHistoryMap = new ArrayMap<>();
+    /** The events that are queryable with a {@link android.content.LocusId}. */
+    static final int CATEGORY_LOCUS_ID_BASED = 1;
 
-    // Locus ID -> Event History
-    private final Map<LocusId, EventHistoryImpl> mLocusEventHistoryMap = new ArrayMap<>();
+    /** The phone call events that are queryable with a phone number. */
+    static final int CATEGORY_CALL = 2;
 
-    // Phone Number -> Event History
-    private final Map<String, EventHistoryImpl> mCallEventHistoryMap = new ArrayMap<>();
+    /** The SMS or MMS events that are queryable with a phone number. */
+    static final int CATEGORY_SMS = 3;
 
-    // Phone Number -> Event History
-    private final Map<String, EventHistoryImpl> mSmsEventHistoryMap = new ArrayMap<>();
+    /** The events that are queryable with an {@link android.app.Activity} class name. */
+    static final int CATEGORY_CLASS_BASED = 4;
 
-    /** Gets the package level {@link EventHistory}. */
-    @NonNull
-    EventHistory getPackageEventHistory() {
-        return mPackageEventHistory;
-    }
+    @IntDef(prefix = { "CATEGORY_" }, value = {
+            CATEGORY_SHORTCUT_BASED,
+            CATEGORY_LOCUS_ID_BASED,
+            CATEGORY_CALL,
+            CATEGORY_SMS,
+            CATEGORY_CLASS_BASED,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface EventCategory {}
 
-    /** Gets the {@link EventHistory} for the specified {@code shortcutId} if exists. */
-    @Nullable
-    EventHistory getShortcutEventHistory(String shortcutId) {
-        return mShortcutEventHistoryMap.get(shortcutId);
-    }
+    private final List<Map<String, EventHistoryImpl>> mEventHistoryMaps = new ArrayList<>();
 
-    /** Gets the {@link EventHistory} for the specified {@code locusId} if exists. */
-    @Nullable
-    EventHistory getLocusEventHistory(LocusId locusId) {
-        return mLocusEventHistoryMap.get(locusId);
-    }
-
-    /** Gets the phone call {@link EventHistory} for the specified {@code phoneNumber} if exists. */
-    @Nullable
-    EventHistory getCallEventHistory(String phoneNumber) {
-        return mCallEventHistoryMap.get(phoneNumber);
-    }
-
-    /** Gets the SMS {@link EventHistory} for the specified {@code phoneNumber} if exists. */
-    @Nullable
-    EventHistory getSmsEventHistory(String phoneNumber) {
-        return mSmsEventHistoryMap.get(phoneNumber);
+    EventStore() {
+        mEventHistoryMaps.add(CATEGORY_SHORTCUT_BASED, new ArrayMap<>());
+        mEventHistoryMaps.add(CATEGORY_LOCUS_ID_BASED, new ArrayMap<>());
+        mEventHistoryMaps.add(CATEGORY_CALL, new ArrayMap<>());
+        mEventHistoryMaps.add(CATEGORY_SMS, new ArrayMap<>());
+        mEventHistoryMaps.add(CATEGORY_CLASS_BASED, new ArrayMap<>());
     }
 
     /**
-     * Gets the {@link EventHistoryImpl} for the specified {@code shortcutId} or creates a new
-     * instance and put it into the store if not exists. The caller needs to verify if a
-     * conversation with this shortcut ID exists before calling this method.
+     * Gets the {@link EventHistory} for the specified key if exists.
+     *
+     * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+     *            name.
      */
-    @NonNull
-    EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
-        return mShortcutEventHistoryMap.computeIfAbsent(shortcutId, key -> new EventHistoryImpl());
+    @Nullable
+    EventHistory getEventHistory(@EventCategory int category, String key) {
+        return mEventHistoryMaps.get(category).get(key);
     }
 
     /**
-     * Gets the {@link EventHistoryImpl} for the specified {@code locusId} or creates a new
-     * instance and put it into the store if not exists. The caller needs to ensure a conversation
-     * with this locus ID exists before calling this method.
+     * Gets the {@link EventHistoryImpl} for the specified ID or creates a new instance and put it
+     * into the store if not exists. The caller needs to verify if the associated conversation
+     * exists before calling this method.
+     *
+     * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+     *            name.
      */
     @NonNull
-    EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
-        return mLocusEventHistoryMap.computeIfAbsent(locusId, key -> new EventHistoryImpl());
+    EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
+        return mEventHistoryMaps.get(category).computeIfAbsent(key, k -> new EventHistoryImpl());
     }
 
     /**
-     * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for call events
-     * or creates a new instance and put it into the store if not exists. The caller needs to ensure
-     * a conversation with this phone number exists and this package is the default dialer
-     * before calling this method.
+     * Deletes the events and index data for the specified key.
+     *
+     * @param key Category-specific key, it can be shortcut ID, locus ID, phone number, or class
+     *            name.
      */
-    @NonNull
-    EventHistoryImpl getOrCreateCallEventHistory(String phoneNumber) {
-        return mCallEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+    void deleteEventHistory(@EventCategory int category, String key) {
+        EventHistoryImpl eventHistory = mEventHistoryMaps.get(category).remove(key);
+        if (eventHistory != null) {
+            eventHistory.onDestroy();
+        }
+    }
+
+    /** Deletes all the events and index data for the specified category from disk. */
+    void deleteEventHistories(@EventCategory int category) {
+        mEventHistoryMaps.get(category).clear();
+        // TODO: Implement this method to delete the data from disk.
+    }
+
+    /** Deletes the events data that exceeds the retention period. */
+    void pruneOldEvents(long currentTimeMillis) {
+        for (Map<String, EventHistoryImpl> map : mEventHistoryMaps) {
+            for (EventHistoryImpl eventHistory : map.values()) {
+                eventHistory.pruneOldEvents(currentTimeMillis);
+            }
+        }
     }
 
     /**
-     * Gets the {@link EventHistoryImpl} for the specified {@code phoneNumber} for SMS events
-     * or creates a new instance and put it into the store if not exists. The caller needs to ensure
-     * a conversation with this phone number exists and this package is the default SMS app
-     * before calling this method.
+     * Prunes the event histories whose key (shortcut ID, locus ID or phone number) does not match
+     * any conversations.
+     *
+     * @param keyChecker Check whether there exists a conversation contains this key.
      */
-    @NonNull
-    EventHistoryImpl getOrCreateSmsEventHistory(String phoneNumber) {
-        return mSmsEventHistoryMap.computeIfAbsent(phoneNumber, key -> new EventHistoryImpl());
+    void pruneOrphanEventHistories(@EventCategory int category, Predicate<String> keyChecker) {
+        Set<String> keys = mEventHistoryMaps.get(category).keySet();
+        List<String> keysToDelete = new ArrayList<>();
+        for (String key : keys) {
+            if (!keyChecker.test(key)) {
+                keysToDelete.add(key);
+            }
+        }
+        Map<String, EventHistoryImpl> eventHistoryMap = mEventHistoryMaps.get(category);
+        for (String key : keysToDelete) {
+            EventHistoryImpl eventHistory = eventHistoryMap.remove(key);
+            if (eventHistory != null) {
+                eventHistory.onDestroy();
+            }
+        }
     }
 }
diff --git a/services/people/java/com/android/server/people/data/PackageData.java b/services/people/java/com/android/server/people/data/PackageData.java
index f67699c..c55f972 100644
--- a/services/people/java/com/android/server/people/data/PackageData.java
+++ b/services/people/java/com/android/server/people/data/PackageData.java
@@ -16,6 +16,12 @@
 
 package com.android.server.people.data;
 
+import static com.android.server.people.data.EventStore.CATEGORY_CALL;
+import static com.android.server.people.data.EventStore.CATEGORY_CLASS_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_LOCUS_ID_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_SHORTCUT_BASED;
+import static com.android.server.people.data.EventStore.CATEGORY_SMS;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -89,11 +95,6 @@
         mConversationStore.forAllConversations(consumer);
     }
 
-    @NonNull
-    public EventHistory getPackageLevelEventHistory() {
-        return getEventStore().getPackageEventHistory();
-    }
-
     /**
      * Gets the {@link ConversationInfo} for a given shortcut ID. Returns null if such as {@link
      * ConversationInfo} does not exist.
@@ -117,14 +118,16 @@
             return result;
         }
 
-        EventHistory shortcutEventHistory = getEventStore().getShortcutEventHistory(shortcutId);
+        EventHistory shortcutEventHistory = getEventStore().getEventHistory(
+                CATEGORY_SHORTCUT_BASED, shortcutId);
         if (shortcutEventHistory != null) {
             result.addEventHistory(shortcutEventHistory);
         }
 
         LocusId locusId = conversationInfo.getLocusId();
         if (locusId != null) {
-            EventHistory locusEventHistory = getEventStore().getLocusEventHistory(locusId);
+            EventHistory locusEventHistory = getEventStore().getEventHistory(
+                    CATEGORY_LOCUS_ID_BASED, locusId.getId());
             if (locusEventHistory != null) {
                 result.addEventHistory(locusEventHistory);
             }
@@ -135,13 +138,15 @@
             return result;
         }
         if (isDefaultDialer()) {
-            EventHistory callEventHistory = getEventStore().getCallEventHistory(phoneNumber);
+            EventHistory callEventHistory = getEventStore().getEventHistory(
+                    CATEGORY_CALL, phoneNumber);
             if (callEventHistory != null) {
                 result.addEventHistory(callEventHistory);
             }
         }
         if (isDefaultSmsApp()) {
-            EventHistory smsEventHistory = getEventStore().getSmsEventHistory(phoneNumber);
+            EventHistory smsEventHistory = getEventStore().getEventHistory(
+                    CATEGORY_SMS, phoneNumber);
             if (smsEventHistory != null) {
                 result.addEventHistory(smsEventHistory);
             }
@@ -149,6 +154,14 @@
         return result;
     }
 
+    /** Gets the {@link EventHistory} for a given Activity class. */
+    @NonNull
+    public EventHistory getClassLevelEventHistory(String className) {
+        EventHistory eventHistory = getEventStore().getEventHistory(
+                CATEGORY_CLASS_BASED, className);
+        return eventHistory != null ? eventHistory : new AggregateEventHistoryImpl();
+    }
+
     public boolean isDefaultDialer() {
         return mIsDefaultDialerPredicate.test(mPackageName);
     }
@@ -167,6 +180,47 @@
         return mEventStore;
     }
 
+    /**
+     * Deletes all the data (including conversation, events and index) for the specified
+     * conversation shortcut ID.
+     */
+    void deleteDataForConversation(String shortcutId) {
+        ConversationInfo conversationInfo = mConversationStore.deleteConversation(shortcutId);
+        if (conversationInfo == null) {
+            return;
+        }
+        mEventStore.deleteEventHistory(CATEGORY_SHORTCUT_BASED, shortcutId);
+        if (conversationInfo.getLocusId() != null) {
+            mEventStore.deleteEventHistory(
+                    CATEGORY_LOCUS_ID_BASED, conversationInfo.getLocusId().getId());
+        }
+        String phoneNumber = conversationInfo.getContactPhoneNumber();
+        if (!TextUtils.isEmpty(phoneNumber)) {
+            if (isDefaultDialer()) {
+                mEventStore.deleteEventHistory(CATEGORY_CALL, phoneNumber);
+            }
+            if (isDefaultSmsApp()) {
+                mEventStore.deleteEventHistory(CATEGORY_SMS, phoneNumber);
+            }
+        }
+    }
+
+    /** Prunes the events and index data that don't have a associated conversation. */
+    void pruneOrphanEvents() {
+        mEventStore.pruneOrphanEventHistories(CATEGORY_SHORTCUT_BASED,
+                key -> mConversationStore.getConversation(key) != null);
+        mEventStore.pruneOrphanEventHistories(CATEGORY_LOCUS_ID_BASED,
+                key -> mConversationStore.getConversationByLocusId(new LocusId(key)) != null);
+        if (isDefaultDialer()) {
+            mEventStore.pruneOrphanEventHistories(CATEGORY_CALL,
+                    key -> mConversationStore.getConversationByPhoneNumber(key) != null);
+        }
+        if (isDefaultSmsApp()) {
+            mEventStore.pruneOrphanEventHistories(CATEGORY_SMS,
+                    key -> mConversationStore.getConversationByPhoneNumber(key) != null);
+        }
+    }
+
     void onDestroy() {
         // TODO: STOPSHIP: Implements this method for the case of package being uninstalled.
     }
diff --git a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
index fba4f4e..72f1abb 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -80,10 +80,6 @@
                     addEventByShortcutId(packageData, e.getShortcutId(),
                             new Event(e.getTimeStamp(), Event.TYPE_SHORTCUT_INVOCATION));
                     break;
-                case UsageEvents.Event.NOTIFICATION_INTERRUPTION:
-                    addEventByNotificationChannelId(packageData, e.getNotificationChannelId(),
-                            new Event(e.getTimeStamp(), Event.TYPE_NOTIFICATION_POSTED));
-                    break;
                 case UsageEvents.Event.LOCUS_ID_SET:
                     onInAppConversationEnded(packageData, e);
                     LocusId locusId = e.getLocusId() != null ? new LocusId(e.getLocusId()) : null;
@@ -129,8 +125,8 @@
         if (packageData.getConversationStore().getConversation(shortcutId) == null) {
             return;
         }
-        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
-                shortcutId);
+        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+                EventStore.CATEGORY_SHORTCUT_BASED, shortcutId);
         eventHistory.addEvent(event);
     }
 
@@ -138,21 +134,8 @@
         if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
             return;
         }
-        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateLocusEventHistory(
-                locusId);
-        eventHistory.addEvent(event);
-    }
-
-    private void addEventByNotificationChannelId(PackageData packageData,
-            String notificationChannelId, Event event) {
-        ConversationInfo conversationInfo =
-                packageData.getConversationStore().getConversationByNotificationChannelId(
-                        notificationChannelId);
-        if (conversationInfo == null) {
-            return;
-        }
-        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
-                conversationInfo.getShortcutId());
+        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+                EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
         eventHistory.addEvent(event);
     }
 }
diff --git a/services/people/java/com/android/server/people/data/UserData.java b/services/people/java/com/android/server/people/data/UserData.java
index aaa5db8..7ca4b6c 100644
--- a/services/people/java/com/android/server/people/data/UserData.java
+++ b/services/people/java/com/android/server/people/data/UserData.java
@@ -104,6 +104,14 @@
         return mPackageDataMap.get(packageName);
     }
 
+    /** Deletes the specified package data. */
+    void deletePackageData(@NonNull String packageName) {
+        PackageData packageData = mPackageDataMap.remove(packageName);
+        if (packageData != null) {
+            packageData.onDestroy();
+        }
+    }
+
     void setDefaultDialer(@Nullable String packageName) {
         mDefaultDialer = packageName;
     }
diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml
index bbc6bdb..d34f783 100644
--- a/services/tests/servicestests/AndroidTest.xml
+++ b/services/tests/servicestests/AndroidTest.xml
@@ -26,11 +26,6 @@
         <option name="test-file-name" value="SimpleServiceTestApp.apk" />
     </target_preparer>
 
-    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
-        <option name="cleanup" value="true" />
-        <option name="push" value="AppIntegrityManagerServiceTestApp.apk->/data/local/tmp/AppIntegrityManagerServiceTestApp.apk" />
-    </target_preparer>
-
     <option name="test-tag" value="FrameworksServicesTests" />
     <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
         <option name="package" value="com.android.frameworks.servicestests" />
diff --git a/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk
new file mode 100644
index 0000000..cc1f68c
--- /dev/null
+++ b/services/tests/servicestests/assets/AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk
Binary files differ
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 8f70cca..7b2b30b 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2087,7 +2087,7 @@
 
         FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
                 .setFactoryResetProtectionAccounts(new ArrayList<>())
-                .setFactoryResetProtectionDisabled(true)
+                .setFactoryResetProtectionEnabled(false)
                 .build();
         dpm.setFactoryResetProtectionPolicy(admin1, policy);
 
@@ -2105,7 +2105,7 @@
         setupProfileOwner();
 
         FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
-                .setFactoryResetProtectionDisabled(true)
+                .setFactoryResetProtectionEnabled(false)
                 .build();
 
         assertExpectException(SecurityException.class, null,
@@ -2157,7 +2157,7 @@
 
         FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
                 .setFactoryResetProtectionAccounts(new ArrayList<>())
-                .setFactoryResetProtectionDisabled(true)
+                .setFactoryResetProtectionEnabled(false)
                 .build();
 
         dpm.setFactoryResetProtectionPolicy(admin1, policy);
@@ -2177,8 +2177,8 @@
 
     private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
             FactoryResetProtectionPolicy actualPolicy) {
-        assertThat(actualPolicy.isFactoryResetProtectionDisabled()).isEqualTo(
-                expectedPolicy.isFactoryResetProtectionDisabled());
+        assertThat(actualPolicy.isFactoryResetProtectionEnabled()).isEqualTo(
+                expectedPolicy.isFactoryResetProtectionEnabled());
         assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
                 actualPolicy.getFactoryResetProtectionAccounts());
     }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
index bc853c6..e8818a3 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/FactoryResetProtectionPolicyTest.java
@@ -62,7 +62,7 @@
 
         FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
                 .setFactoryResetProtectionAccounts(accounts)
-                .setFactoryResetProtectionDisabled(true)
+                .setFactoryResetProtectionEnabled(false)
                 .build();
 
         testParcelAndUnparcel(policy);
@@ -77,7 +77,7 @@
 
         FactoryResetProtectionPolicy policy = new FactoryResetProtectionPolicy.Builder()
                 .setFactoryResetProtectionAccounts(accounts)
-                .setFactoryResetProtectionDisabled(true)
+                .setFactoryResetProtectionEnabled(false)
                 .build();
 
         testParcelAndUnparcel(policy);
@@ -133,8 +133,8 @@
 
     private void assertPoliciesAreEqual(FactoryResetProtectionPolicy expectedPolicy,
             FactoryResetProtectionPolicy actualPolicy) {
-        assertEquals(expectedPolicy.isFactoryResetProtectionDisabled(),
-                actualPolicy.isFactoryResetProtectionDisabled());
+        assertEquals(expectedPolicy.isFactoryResetProtectionEnabled(),
+                actualPolicy.isFactoryResetProtectionEnabled());
         assertAccountsAreEqual(expectedPolicy.getFactoryResetProtectionAccounts(),
                 actualPolicy.getFactoryResetProtectionAccounts());
     }
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 8d5939a..7027185 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -202,7 +202,7 @@
         assertEquals(1, displayIds.length);
         final int displayId = displayIds[0];
         DisplayInfo info = bs.getDisplayInfo(displayId);
-        assertEquals(info.type, Display.TYPE_BUILT_IN);
+        assertEquals(info.type, Display.TYPE_INTERNAL);
 
         displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class));
 
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
index 0d4c6e8..be873bd 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -90,7 +90,7 @@
 @RunWith(JUnit4.class)
 public class AppIntegrityManagerServiceImplTest {
     private static final String TEST_APP_PATH =
-            "/data/local/tmp/AppIntegrityManagerServiceTestApp.apk";
+            "AppIntegrityManagerServiceImplTest/AppIntegrityManagerServiceTestApp.apk";
 
     private static final String TEST_APP_TWO_CERT_PATH =
             "AppIntegrityManagerServiceImplTest/DummyAppTwoCerts.apk";
@@ -106,9 +106,7 @@
 
     // These are obtained by running the test and checking logcat.
     private static final String APP_CERT =
-            "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
-    private static final String INSTALLER_CERT =
-            "301AA3CB081134501C45F1422ABC66C24224FD5DED5FDC8F17E697176FD866AA";
+            "C8A2E9BCCF597C2FB6DC66BEE293FC13F2FC47EC77BC6B2B0D52C11F51192AB8";
     // We use SHA256 for package names longer than 32 characters.
     private static final String INSTALLER_SHA256 =
             "30F41A7CBF96EE736A54DD6DF759B50ED3CC126ABCEF694E167C324F5976C227";
@@ -149,8 +147,12 @@
 
     @Before
     public void setup() throws Exception {
-        mTestApk = new File(TEST_APP_PATH);
-        mTestApkTwoCerts = File.createTempFile("AppIntegrity", ".apk");
+        mTestApk = File.createTempFile("AppIntegrity", ".apk");
+        try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_PATH)) {
+            Files.copy(inputStream, mTestApk.toPath(), REPLACE_EXISTING);
+        }
+
+        mTestApkTwoCerts = File.createTempFile("AppIntegrityTwoCerts", ".apk");
         try (InputStream inputStream = mRealContext.getAssets().open(TEST_APP_TWO_CERT_PATH)) {
             Files.copy(inputStream, mTestApkTwoCerts.toPath(), REPLACE_EXISTING);
         }
@@ -174,6 +176,7 @@
 
     @After
     public void tearDown() throws Exception {
+        mTestApk.delete();
         mTestApkTwoCerts.delete();
     }
 
@@ -304,7 +307,7 @@
         assertEquals(PACKAGE_NAME, appInstallMetadata.getPackageName());
         assertThat(appInstallMetadata.getAppCertificates()).containsExactly(APP_CERT);
         assertEquals(INSTALLER_SHA256, appInstallMetadata.getInstallerName());
-        assertThat(appInstallMetadata.getInstallerCertificates()).containsExactly(INSTALLER_CERT);
+        // we cannot check installer cert because it seems to be device specific.
         assertEquals(VERSION_CODE, appInstallMetadata.getVersionCode());
         assertFalse(appInstallMetadata.isPreInstalled());
         // These are hardcoded in the test apk android manifest
diff --git a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
index c0e7927..70d6cf8 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/ConversationInfoTest.java
@@ -46,7 +46,7 @@
                 .setContactUri(CONTACT_URI)
                 .setContactPhoneNumber(PHONE_NUMBER)
                 .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
-                .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+                .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED | ShortcutInfo.FLAG_CACHED)
                 .setImportant(true)
                 .setNotificationSilenced(true)
                 .setBubbled(true)
@@ -62,6 +62,7 @@
         assertEquals(PHONE_NUMBER, conversationInfo.getContactPhoneNumber());
         assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
         assertTrue(conversationInfo.isShortcutLongLived());
+        assertTrue(conversationInfo.isShortcutCached());
         assertTrue(conversationInfo.isImportant());
         assertTrue(conversationInfo.isNotificationSilenced());
         assertTrue(conversationInfo.isBubbled());
@@ -83,6 +84,7 @@
         assertNull(conversationInfo.getContactPhoneNumber());
         assertNull(conversationInfo.getNotificationChannelId());
         assertFalse(conversationInfo.isShortcutLongLived());
+        assertFalse(conversationInfo.isShortcutCached());
         assertFalse(conversationInfo.isImportant());
         assertFalse(conversationInfo.isNotificationSilenced());
         assertFalse(conversationInfo.isBubbled());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 6769faa..3ecd319 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -24,12 +24,14 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
@@ -41,6 +43,7 @@
 import android.app.NotificationChannel;
 import android.app.NotificationManager;
 import android.app.Person;
+import android.app.job.JobScheduler;
 import android.app.prediction.AppTarget;
 import android.app.prediction.AppTargetEvent;
 import android.app.prediction.AppTargetId;
@@ -49,12 +52,15 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutManager;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
+import android.content.pm.parsing.AndroidPackage;
 import android.database.ContentObserver;
 import android.net.Uri;
+import android.os.CancellationSignal;
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -66,6 +72,7 @@
 import android.util.Range;
 
 import com.android.internal.app.ChooserActivity;
+import com.android.internal.content.PackageMonitor;
 import com.android.server.LocalServices;
 
 import org.junit.After;
@@ -84,6 +91,7 @@
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
+import java.util.function.Consumer;
 
 @RunWith(JUnit4.class)
 public final class DataManagerTest {
@@ -101,12 +109,14 @@
     @Mock private Context mContext;
     @Mock private ShortcutServiceInternal mShortcutServiceInternal;
     @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
+    @Mock private PackageManagerInternal mPackageManagerInternal;
     @Mock private ShortcutManager mShortcutManager;
     @Mock private UserManager mUserManager;
     @Mock private TelephonyManager mTelephonyManager;
     @Mock private TelecomManager mTelecomManager;
     @Mock private ContentResolver mContentResolver;
     @Mock private ScheduledExecutorService mExecutorService;
+    @Mock private JobScheduler mJobScheduler;
     @Mock private ScheduledFuture mScheduledFuture;
     @Mock private StatusBarNotification mStatusBarNotification;
     @Mock private Notification mNotification;
@@ -114,6 +124,7 @@
     private NotificationChannel mNotificationChannel;
     private DataManager mDataManager;
     private int mCallingUserId;
+    private CancellationSignal mCancellationSignal;
     private TestInjector mInjector;
 
     @Before
@@ -124,6 +135,15 @@
 
         addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
 
+        addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternal);
+        AndroidPackage androidPackage = mock(AndroidPackage.class);
+        when(androidPackage.getPackageName()).thenReturn(TEST_PKG_NAME);
+        doAnswer(ans -> {
+            Consumer<AndroidPackage> callback = (Consumer<AndroidPackage>) ans.getArguments()[0];
+            callback.accept(androidPackage);
+            return null;
+        }).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
+
         when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
 
         Context originalContext = getInstrumentation().getTargetContext();
@@ -145,6 +165,10 @@
         when(mTelecomManager.getDefaultDialerPackage(any(UserHandle.class)))
                 .thenReturn(TEST_PKG_NAME);
 
+        when(mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).thenReturn(mJobScheduler);
+        when(mContext.getSystemServiceName(JobScheduler.class)).thenReturn(
+                Context.JOB_SCHEDULER_SERVICE);
+
         when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
                 TimeUnit.class))).thenReturn(mScheduledFuture);
 
@@ -160,6 +184,7 @@
         when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
         when(mStatusBarNotification.getPackageName()).thenReturn(TEST_PKG_NAME);
         when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
+        when(mStatusBarNotification.getPostTime()).thenReturn(System.currentTimeMillis());
         when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
 
         mNotificationChannel = new NotificationChannel(
@@ -168,6 +193,8 @@
 
         mCallingUserId = USER_ID_PRIMARY;
 
+        mCancellationSignal = new CancellationSignal();
+
         mInjector = new TestInjector();
         mDataManager = new DataManager(mContext, mInjector);
         mDataManager.initialize();
@@ -177,6 +204,7 @@
     public void tearDown() {
         LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
     }
 
     @Test
@@ -298,6 +326,28 @@
     }
 
     @Test
+    public void testNotificationPosted() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+        NotificationListenerService listenerService =
+                mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+        listenerService.onNotificationPosted(mStatusBarNotification);
+
+        List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
+        mDataManager.forAllPackages(packageData ->
+                activeNotificationOpenTimeSlots.addAll(
+                        packageData.getEventHistory(TEST_SHORTCUT_ID)
+                                .getEventIndex(Event.TYPE_NOTIFICATION_POSTED)
+                                .getActiveTimeSlots()));
+        assertEquals(1, activeNotificationOpenTimeSlots.size());
+    }
+
+    @Test
     public void testNotificationOpened() {
         mDataManager.onUserUnlocked(USER_ID_PRIMARY);
 
@@ -366,6 +416,9 @@
         assertTrue(conversationInfo.isImportant());
         assertFalse(conversationInfo.isNotificationSilenced());
         assertFalse(conversationInfo.isDemoted());
+        verify(mShortcutServiceInternal).cacheShortcuts(
+                anyInt(), any(), eq(TEST_PKG_NAME),
+                eq(Collections.singletonList(TEST_SHORTCUT_ID)), eq(USER_ID_PRIMARY));
     }
 
     @Test
@@ -454,6 +507,101 @@
         assertEquals(2, activeTimeSlots.size());
     }
 
+    @Test
+    public void testDeleteUninstalledPackageDataOnPackageRemoved() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+
+        PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY);
+        Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
+        intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_PRIMARY);
+        intent.setData(Uri.parse("package:" + TEST_PKG_NAME));
+        packageMonitor.onReceive(mContext, intent);
+        assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+    }
+
+    @Test
+    public void testPruneUninstalledPackageData() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+
+        doAnswer(ans -> null).when(mPackageManagerInternal)
+                .forEachInstalledPackage(any(Consumer.class), anyInt());
+        mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+        assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
+    }
+
+    @Test
+    public void testPruneCallEventsFromNonDialer() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.onShortcutAddedOrUpdated(shortcut);
+
+        long currentTimestamp = System.currentTimeMillis();
+        mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+                new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_CALL_OUTGOING));
+
+        List<Range<Long>> activeTimeSlots = new ArrayList<>();
+        mDataManager.forAllPackages(packageData ->
+                activeTimeSlots.addAll(
+                        packageData.getEventHistory(TEST_SHORTCUT_ID)
+                                .getEventIndex(Event.CALL_EVENT_TYPES)
+                                .getActiveTimeSlots()));
+        assertEquals(1, activeTimeSlots.size());
+
+        mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultDialer(null);
+        mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+        activeTimeSlots.clear();
+        mDataManager.forAllPackages(packageData ->
+                activeTimeSlots.addAll(
+                        packageData.getEventHistory(TEST_SHORTCUT_ID)
+                                .getEventIndex(Event.CALL_EVENT_TYPES)
+                                .getActiveTimeSlots()));
+        assertTrue(activeTimeSlots.isEmpty());
+    }
+
+    @Test
+    public void testPruneSmsEventsFromNonDefaultSmsApp() {
+        mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+        ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+                buildPerson());
+        mDataManager.onShortcutAddedOrUpdated(shortcut);
+        mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
+
+        long currentTimestamp = System.currentTimeMillis();
+        mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
+                new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_SMS_OUTGOING));
+
+        List<Range<Long>> activeTimeSlots = new ArrayList<>();
+        mDataManager.forAllPackages(packageData ->
+                activeTimeSlots.addAll(
+                        packageData.getEventHistory(TEST_SHORTCUT_ID)
+                                .getEventIndex(Event.SMS_EVENT_TYPES)
+                                .getActiveTimeSlots()));
+        assertEquals(1, activeTimeSlots.size());
+
+        mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(null);
+        mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
+        activeTimeSlots.clear();
+        mDataManager.forAllPackages(packageData ->
+                activeTimeSlots.addAll(
+                        packageData.getEventHistory(TEST_SHORTCUT_ID)
+                                .getEventIndex(Event.SMS_EVENT_TYPES)
+                                .getActiveTimeSlots()));
+        assertTrue(activeTimeSlots.isEmpty());
+    }
+
     private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
         LocalServices.removeServiceForTest(clazz);
         LocalServices.addService(clazz, mock);
@@ -467,6 +615,7 @@
         when(mockContext.getUser()).thenReturn(UserHandle.of(userId));
         ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id)
                 .setShortLabel(id)
+                .setLongLived(true)
                 .setIntent(new Intent("TestIntent"));
         if (person != null) {
             builder.setPersons(new Person[] {person});
diff --git a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
index 1ddc21e..e52cdf5 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/PackageDataTest.java
@@ -81,8 +81,10 @@
     @Test
     public void testGetEventHistory() {
         EventStore eventStore = mPackageData.getEventStore();
-        eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
-        eventStore.getOrCreateLocusEventHistory(LOCUS_ID).addEvent(mE2);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+                .addEvent(mE1);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+                .addEvent(mE2);
 
         EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
         List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
@@ -96,9 +98,10 @@
         mIsDefaultDialer = true;
         mIsDefaultSmsApp = true;
         EventStore eventStore = mPackageData.getEventStore();
-        eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
-        eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
-        eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+                .addEvent(mE1);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
 
         assertTrue(mPackageData.isDefaultDialer());
         assertTrue(mPackageData.isDefaultSmsApp());
@@ -113,9 +116,10 @@
     @Test
     public void testGetEventHistoryNotDefaultDialerOrSmsApp() {
         EventStore eventStore = mPackageData.getEventStore();
-        eventStore.getOrCreateShortcutEventHistory(SHORTCUT_ID).addEvent(mE1);
-        eventStore.getOrCreateCallEventHistory(PHONE_NUMBER).addEvent(mE3);
-        eventStore.getOrCreateSmsEventHistory(PHONE_NUMBER).addEvent(mE4);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+                .addEvent(mE1);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
 
         assertFalse(mPackageData.isDefaultDialer());
         assertFalse(mPackageData.isDefaultSmsApp());
@@ -125,6 +129,61 @@
         assertEventEquals(mE1, events.get(0));
     }
 
+    @Test
+    public void testDeleteConversationData() {
+        mIsDefaultDialer = true;
+        mIsDefaultSmsApp = true;
+        EventStore eventStore = mPackageData.getEventStore();
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+                .addEvent(mE1);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+                .addEvent(mE2);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
+
+        EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+        List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(4, events.size());
+
+        mPackageData.deleteDataForConversation(SHORTCUT_ID);
+
+        eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+        events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertTrue(events.isEmpty());
+    }
+
+    @Test
+    public void testPruneOrphanEvents() {
+        mIsDefaultDialer = true;
+        mIsDefaultSmsApp = true;
+        EventStore eventStore = mPackageData.getEventStore();
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SHORTCUT_BASED, SHORTCUT_ID)
+                .addEvent(mE1);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_LOCUS_ID_BASED, LOCUS_ID.getId())
+                .addEvent(mE2);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_CALL, PHONE_NUMBER).addEvent(mE3);
+        eventStore.getOrCreateEventHistory(EventStore.CATEGORY_SMS, PHONE_NUMBER).addEvent(mE4);
+
+        EventHistory eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+        List<Event> events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(4, events.size());
+
+        ConversationInfo conversationInfo = new ConversationInfo.Builder()
+                .setShortcutId(SHORTCUT_ID)
+                .setLocusId(null)
+                .setContactUri(null)
+                .setContactPhoneNumber(null)
+                .setShortcutFlags(ShortcutInfo.FLAG_LONG_LIVED)
+                .build();
+        mPackageData.getConversationStore().addOrUpdate(conversationInfo);
+        mPackageData.pruneOrphanEvents();
+        eventHistory = mPackageData.getEventHistory(SHORTCUT_ID);
+        events = eventHistory.queryEvents(Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
+        assertEquals(1, events.size());
+        // Only the shortcut based event is kept. All the other events are deleted.
+        assertEventEquals(mE1, events.get(0));
+    }
+
     private void assertEventEquals(Event expected, Event actual) {
         assertEquals(expected.getTimestamp(), actual.getTimestamp());
         assertEquals(expected.getType(), actual.getType());
diff --git a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
index c8543c4..d444466 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/UsageStatsQueryHelperTest.java
@@ -59,7 +59,6 @@
     private static final String PKG_NAME = "pkg";
     private static final String ACTIVITY_NAME = "TestActivity";
     private static final String SHORTCUT_ID = "abc";
-    private static final String NOTIFICATION_CHANNEL_ID = "test : abc";
     private static final LocusId LOCUS_ID_1 = new LocusId("locus_1");
     private static final LocusId LOCUS_ID_2 = new LocusId("locus_2");
 
@@ -83,7 +82,6 @@
                 scheduledExecutorService, testDir, helper);
         mPackageData.mConversationStore.mConversationInfo = new ConversationInfo.Builder()
                 .setShortcutId(SHORTCUT_ID)
-                .setNotificationChannelId(NOTIFICATION_CHANNEL_ID)
                 .setLocusId(LOCUS_ID_1)
                 .build();
 
@@ -114,19 +112,6 @@
     }
 
     @Test
-    public void testQueryNotificationInterruptionEvent() {
-        addUsageEvents(createNotificationInterruptionEvent(100L));
-
-        assertTrue(mHelper.querySince(50L));
-        assertEquals(100L, mHelper.getLastEventTimestamp());
-        Event expectedEvent = new Event(100L, Event.TYPE_NOTIFICATION_POSTED);
-        List<Event> events = mPackageData.mEventStore.mShortcutEventHistory.queryEvents(
-                Event.ALL_EVENT_TYPES, 0L, Long.MAX_VALUE);
-        assertEquals(1, events.size());
-        assertEquals(expectedEvent, events.get(0));
-    }
-
-    @Test
     public void testInAppConversationSwitch() {
         addUsageEvents(
                 createLocusIdSetEvent(100_000L, LOCUS_ID_1.getId()),
@@ -203,13 +188,6 @@
         return e;
     }
 
-    private static UsageEvents.Event createNotificationInterruptionEvent(long timestamp) {
-        UsageEvents.Event e = createUsageEvent(UsageEvents.Event.NOTIFICATION_INTERRUPTION,
-                timestamp);
-        e.mNotificationChannelId = NOTIFICATION_CHANNEL_ID;
-        return e;
-    }
-
     private static UsageEvents.Event createLocusIdSetEvent(long timestamp, String locusId) {
         UsageEvents.Event e = createUsageEvent(UsageEvents.Event.LOCUS_ID_SET, timestamp);
         e.mClass = ACTIVITY_NAME;
@@ -288,14 +266,13 @@
 
         @Override
         @NonNull
-        EventHistoryImpl getOrCreateShortcutEventHistory(String shortcutId) {
-            return mShortcutEventHistory;
-        }
-
-        @Override
-        @NonNull
-        EventHistoryImpl getOrCreateLocusEventHistory(LocusId locusId) {
-            return mLocusEventHistory;
+        EventHistoryImpl getOrCreateEventHistory(@EventCategory int category, String key) {
+            if (category == EventStore.CATEGORY_SHORTCUT_BASED) {
+                return mShortcutEventHistory;
+            } else if (category == EventStore.CATEGORY_LOCUS_ID_BASED) {
+                return mLocusEventHistory;
+            }
+            throw new UnsupportedOperationException();
         }
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index e28d13a..9670658 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -37,6 +37,7 @@
 import android.content.pm.parsing.ParsingPackage;
 import android.os.Build;
 import android.os.Process;
+import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 
@@ -57,6 +58,7 @@
 import java.util.Map;
 import java.util.Set;
 
+@Presubmit
 @RunWith(JUnit4.class)
 public class AppsFilterTest {
 
@@ -314,7 +316,8 @@
                 b -> b.setSigningDetails(frameworkSigningDetails));
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
                 DUMMY_TARGET_UID,
-                b -> b.setSigningDetails(frameworkSigningDetails));
+                b -> b.setSigningDetails(frameworkSigningDetails)
+                        .setPkgFlags(ApplicationInfo.FLAG_SYSTEM));
         PackageSetting calling = simulateAddPackage(appsFilter,
                 pkg("com.some.other.package"), DUMMY_CALLING_UID,
                 b -> b.setSigningDetails(otherSigningDetails));
diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
index 4d0481be..eef9012 100644
--- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertEquals;
 
+import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Log;
@@ -48,8 +49,9 @@
  * Build/Install/Run:
  *  atest FrameworksServicesTests:SystemConfigTest
  */
-@RunWith(AndroidJUnit4.class)
 @SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
 public class SystemConfigTest {
     private static final String LOG_TAG = "SystemConfigTest";
 
diff --git a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp
deleted file mode 100644
index 9aaa37d..0000000
--- a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/Android.bp
+++ /dev/null
@@ -1,21 +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.
-
-android_test_helper_app {
-    name: "AppIntegrityManagerServiceTestApp",
-
-    test_suites: ["device-tests"],
-
-    certificate: "platform",
-}
diff --git a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
deleted file mode 100644
index 98572d4..0000000
--- a/services/tests/servicestests/test-apps/AppIntegrityManagerServiceTestApp/AndroidManifest.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2019 Google Inc.
- *
- * 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.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.google.android.appintegritymanager.test.app"
-    android:versionCode="5000">
-
-    <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="28" />
-
-    <application android:hasCode="false">
-        <meta-data android:name="allowed-installers" android:value="com.android.vending|play_store_cert,adb"/>
-    </application>
-</manifest>
-
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
index 458117d..6eaf546 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryProtoHelperTest.java
@@ -21,6 +21,7 @@
 import android.app.NotificationHistory.HistoricalNotification;
 import android.graphics.drawable.Icon;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
 
 import androidx.test.InstrumentationRegistry;
 import androidx.test.runner.AndroidJUnit4;
@@ -57,6 +58,10 @@
         String expectedText = "text" + index;
         Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
                 index);
+        String conversationId = null;
+        if (index % 2 == 0) {
+            conversationId = "convo" + index;
+        }
 
         return new HistoricalNotification.Builder()
                 .setPackage(packageName)
@@ -68,6 +73,7 @@
                 .setTitle(expectedTitle)
                 .setText(expectedText)
                 .setIcon(expectedIcon)
+                .setConversationId(conversationId)
                 .build();
     }
 
@@ -139,6 +145,9 @@
                 expectedStrings.add(n.getPackage());
                 expectedStrings.add(n.getChannelName());
                 expectedStrings.add(n.getChannelId());
+                if (!TextUtils.isEmpty(n.getConversationId())) {
+                    expectedStrings.add(n.getConversationId());
+                }
                 expectedEntries.add(n);
             }
             history.addNotificationToWrite(n);
@@ -178,6 +187,9 @@
                 expectedStrings.add(n.getPackage());
                 expectedStrings.add(n.getChannelName());
                 expectedStrings.add(n.getChannelId());
+                if (!TextUtils.isEmpty(n.getConversationId())) {
+                    expectedStrings.add(n.getConversationId());
+                }
                 expectedEntries.add(n);
             }
             history.addNotificationToWrite(n);
@@ -227,6 +239,9 @@
                 expectedStrings.add(n.getPackage());
                 expectedStrings.add(n.getChannelName());
                 expectedStrings.add(n.getChannelId());
+                if (n.getConversationId() != null) {
+                    expectedStrings.add(n.getConversationId());
+                }
                 expectedEntries.add(n);
             }
             history.addNotificationToWrite(n);
@@ -264,6 +279,9 @@
             expectedStrings.add(n.getPackage());
             expectedStrings.add(n.getChannelName());
             expectedStrings.add(n.getChannelId());
+            if (n.getConversationId() != null) {
+                expectedStrings.add(n.getConversationId());
+            }
             history.addNotificationToWrite(n);
         }
         history.poolStringsFromNotifications();
@@ -279,6 +297,9 @@
             expectedStrings.add(n.getPackage());
             expectedStrings.add(n.getChannelName());
             expectedStrings.add(n.getChannelId());
+            if (n.getConversationId() != null) {
+                expectedStrings.add(n.getConversationId());
+            }
             actualHistory.addNotificationToWrite(n);
         }
         actualHistory.poolStringsFromNotifications();
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 bc33f08..e5ffb4d 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1284,6 +1284,27 @@
     }
 
     @Test
+    public void testPostCancelPostNotifiesListeners() throws Exception {
+        // WHEN a notification is posted
+        final StatusBarNotification sbn = generateNotificationRecord(null).getSbn();
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
+                sbn.getNotification(), sbn.getUserId());
+        // THEN it is canceled
+        mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
+        // THEN it is posted again (before the cancel has a chance to finish)
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", sbn.getId(),
+                sbn.getNotification(), sbn.getUserId());
+        // THEN the later enqueue isn't swallowed by the cancel. I.e., ordering is respected
+        waitForIdle();
+
+        // The final enqueue made it to the listener instead of being canceled
+        StatusBarNotification[] notifs =
+                mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifs.length);
+        assertEquals(1, mService.getNotificationRecordCount());
+    }
+
+    @Test
     public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
         mBinderService.enqueueNotificationWithTag(PKG, PKG,
                 "testCancelNotificationWhilePostedAndEnqueued", 0,
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
index 9647178..e8c0362 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackSupervisorTests.java
@@ -144,4 +144,33 @@
                 anyInt() /* reason */, anyString() /* packageName */);
         verify(taskChangeNotifier, never()).notifyActivityDismissingDockedStack();
     }
+
+    /**
+     * Ensures that notify focus task changes.
+     */
+    @Test
+    public void testNotifyTaskFocusChanged() {
+        final ActivityRecord fullScreenActivityA = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final Task taskA = fullScreenActivityA.getTask();
+
+        final TaskChangeNotificationController taskChangeNotifier =
+                mService.getTaskChangeNotificationController();
+        spyOn(taskChangeNotifier);
+
+        mService.setResumedActivityUncheckLocked(fullScreenActivityA, "resumeA");
+        verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
+                eq(true) /* focused */);
+        reset(taskChangeNotifier);
+
+        final ActivityRecord fullScreenActivityB = new ActivityBuilder(mService).setCreateTask(true)
+                .setStack(mFullscreenStack).build();
+        final Task taskB = fullScreenActivityB.getTask();
+
+        mService.setResumedActivityUncheckLocked(fullScreenActivityB, "resumeB");
+        verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskA.mTaskId) /* taskId */,
+                eq(false) /* focused */);
+        verify(taskChangeNotifier).notifyTaskFocusChanged(eq(taskB.mTaskId) /* taskId */,
+                eq(true) /* focused */);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index fa182d6..d46975c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -29,6 +29,7 @@
 import static android.app.ActivityManager.START_SWITCHES_CANCELED;
 import static android.app.ActivityManager.START_TASK_TO_FRONT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
@@ -64,8 +65,10 @@
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 
+import android.app.ActivityManager;
 import android.app.ActivityOptions;
 import android.app.IApplicationThread;
+import android.app.WindowConfiguration;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -80,6 +83,9 @@
 import android.platform.test.annotations.Presubmit;
 import android.service.voice.IVoiceInteractionSession;
 import android.view.Gravity;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.SurfaceControl;
 
 import androidx.test.filters.SmallTest;
 
@@ -999,7 +1005,8 @@
         assertThat(outActivity[0].inSplitScreenWindowingMode()).isFalse();
 
         // Move activity to split-screen-primary stack and make sure it has the focus.
-        top.getRootTask().setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        TestSplitOrganizer splitOrg = new TestSplitOrganizer(mService, top.getDisplayId());
+        splitOrg.mPrimary.addChild(top.getRootTask(), 0 /* index */);
         top.getRootTask().moveToFront("testWindowingModeOptionsLaunchAdjacent");
 
         // Activity must landed on split-screen-secondary when launch adjacent.
@@ -1022,4 +1029,58 @@
 
         verify(recentTasks, times(1)).add(any());
     }
+
+    static class TestSplitOrganizer extends ITaskOrganizer.Stub {
+        final ActivityTaskManagerService mService;
+        TaskTile mPrimary;
+        TaskTile mSecondary;
+        boolean mInSplit = false;
+        int mDisplayId;
+        TestSplitOrganizer(ActivityTaskManagerService service, int displayId) {
+            mService = service;
+            mDisplayId = displayId;
+            mService.mTaskOrganizerController.registerTaskOrganizer(this,
+                    WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+            mService.mTaskOrganizerController.registerTaskOrganizer(this,
+                    WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+            IWindowContainer primary = mService.mTaskOrganizerController.createRootTask(
+                    displayId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY).token;
+            mPrimary = TaskTile.forToken(primary.asBinder());
+            IWindowContainer secondary = mService.mTaskOrganizerController.createRootTask(
+                    displayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY).token;
+            mSecondary = TaskTile.forToken(secondary.asBinder());
+        }
+        @Override
+        public void taskAppeared(ActivityManager.RunningTaskInfo info) {
+        }
+        @Override
+        public void taskVanished(IWindowContainer wc) {
+        }
+        @Override
+        public void transactionReady(int id, SurfaceControl.Transaction t) {
+        }
+        @Override
+        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+            if (mInSplit) {
+                return;
+            }
+            if (info.topActivityType != ACTIVITY_TYPE_UNDEFINED) {
+                if (info.configuration.windowConfiguration.getWindowingMode()
+                        == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
+                    mInSplit = true;
+                    mService.mTaskOrganizerController.setLaunchRoot(mDisplayId,
+                            mSecondary.mRemoteToken);
+                    // move everything to secondary because test expects this but usually sysui
+                    // does it.
+                    DisplayContent dc = mService.mRootWindowContainer.getDisplayContent(mDisplayId);
+                    for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+                        if (!WindowConfiguration.isSplitScreenWindowingMode(
+                                dc.getStackAt(i).getWindowingMode())) {
+                            mSecondary.addChild(dc.getStackAt(i), 0);
+                        }
+                    }
+                }
+            }
+        }
+    };
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 2ea00ce..3b6816a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -1033,6 +1033,54 @@
         assertTrue(continued[0]);
     }
 
+    @Test
+    public void testGetOrCreateRootHomeTask_defaultDisplay() {
+        DisplayContent defaultDisplay = mWm.mRoot.getDisplayContent(DEFAULT_DISPLAY);
+
+        // Remove the current home stack if it exists so a new one can be created below.
+        ActivityStack homeTask = defaultDisplay.getRootHomeTask();
+        if (homeTask != null) {
+            defaultDisplay.removeStack(homeTask);
+        }
+        assertNull(defaultDisplay.getRootHomeTask());
+
+        assertNotNull(defaultDisplay.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_supportedSecondaryDisplay() {
+        DisplayContent display = createNewDisplay();
+        doReturn(true).when(display).supportsSystemDecorations();
+        doReturn(false).when(display).isUntrustedVirtualDisplay();
+
+        // Remove the current home stack if it exists so a new one can be created below.
+        ActivityStack homeTask = display.getRootHomeTask();
+        if (homeTask != null) {
+            display.removeStack(homeTask);
+        }
+        assertNull(display.getRootHomeTask());
+
+        assertNotNull(display.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_unsupportedSystemDecorations() {
+        DisplayContent display = createNewDisplay();
+        doReturn(false).when(display).supportsSystemDecorations();
+
+        assertNull(display.getRootHomeTask());
+        assertNull(display.getOrCreateRootHomeTask());
+    }
+
+    @Test
+    public void testGetOrCreateRootHomeTask_untrustedVirtualDisplay() {
+        DisplayContent display = createNewDisplay();
+        doReturn(true).when(display).isUntrustedVirtualDisplay();
+
+        assertNull(display.getRootHomeTask());
+        assertNull(display.getOrCreateRootHomeTask());
+    }
+
     private boolean isOptionsPanelAtRight(int displayId) {
         return (mWm.getPreferredOptionsPanelGravity(displayId) & Gravity.RIGHT) == Gravity.RIGHT;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
index 2a20c2e..7ba3fd8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTestsBase.java
@@ -102,10 +102,11 @@
     }
 
     void addWindow(WindowState win) {
-        mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, Binder.getCallingPid(),
-                Binder.getCallingUid());
+        final int callingPid = Binder.getCallingPid();
+        final int callingUid = Binder.getCallingUid();
+        mDisplayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
         assertEquals(WindowManagerGlobal.ADD_OKAY,
-                mDisplayPolicy.validateAddingWindowLw(win.mAttrs));
+                mDisplayPolicy.validateAddingWindowLw(win.mAttrs, callingPid, callingUid));
         mDisplayPolicy.addWindowLw(win, win.mAttrs);
         win.mHasSurface = true;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index a672a95..4449069 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1098,7 +1098,6 @@
         assertSecurityException(expectCallable,
                 () -> mService.setTaskWindowingModeSplitScreenPrimary(0,
                         SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, true, true, new Rect(), true));
-        assertSecurityException(expectCallable, () -> mService.dismissSplitScreenMode(true));
         assertSecurityException(expectCallable, () -> mService.dismissPip(true, 0));
         assertSecurityException(expectCallable,
                 () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 79db927..dd46673 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -860,6 +860,26 @@
                 secondaryDisplay.mDisplayId, result.getDisplayId());
     }
 
+    @Test
+    public void testSwitchUser_missingHomeRootTask() {
+        doReturn(mFullscreenStack).when(mRootWindowContainer).getTopDisplayFocusedStack();
+
+        DisplayContent defaultDisplay = mRootWindowContainer.getDefaultDisplay();
+        ActivityStack homeStack = defaultDisplay.getRootHomeTask();
+        if (homeStack != null) {
+            homeStack.removeImmediately();
+        }
+        assertNull(defaultDisplay.getRootHomeTask());
+
+        int currentUser = mRootWindowContainer.mCurrentUser;
+        int otherUser = currentUser + 1;
+
+        mRootWindowContainer.switchUser(otherUser, null);
+
+        assertNotNull(defaultDisplay.getRootHomeTask());
+        assertEquals(defaultDisplay.getTopStack(), defaultDisplay.getRootHomeTask());
+    }
+
     /**
      * Mock {@link RootWindowContainer#resolveHomeActivity} for returning consistent activity
      * info for test cases (the original implementation will resolve from the real package manager).
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 0312df6..cd53ece 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -28,19 +28,17 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
+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.never;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -49,6 +47,7 @@
 import android.app.ActivityManager.RunningTaskInfo;
 import android.app.ActivityManager.StackInfo;
 import android.app.PictureInPictureParams;
+import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Binder;
@@ -56,7 +55,6 @@
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
-import android.content.pm.ActivityInfo;
 import android.util.Rational;
 import android.view.Display;
 import android.view.ITaskOrganizer;
@@ -458,9 +456,9 @@
     private List<TaskTile> getTaskTiles(DisplayContent dc) {
         ArrayList<TaskTile> out = new ArrayList<>();
         for (int i = dc.getStackCount() - 1; i >= 0; --i) {
-            final Task t = dc.getStackAt(i);
-            if (t instanceof TaskTile) {
-                out.add((TaskTile) t);
+            final TaskTile t = dc.getStackAt(i).asTile();
+            if (t != null) {
+                out.add(t);
             }
         }
         return out;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index 3a68924..7a075a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -75,7 +75,8 @@
     }
 
     @Override
-    public int checkAddPermission(WindowManager.LayoutParams attrs, int[] outAppOp) {
+    public int checkAddPermission(int type, boolean isRoundedCornerOverlay, String packageName,
+            int[] outAppOp) {
         return 0;
     }
 
diff --git a/telephony/java/android/telephony/CellIdentityGsm.java b/telephony/java/android/telephony/CellIdentityGsm.java
index 9f2537c..203047f 100644
--- a/telephony/java/android/telephony/CellIdentityGsm.java
+++ b/telephony/java/android/telephony/CellIdentityGsm.java
@@ -22,11 +22,12 @@
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity to represent a unique GSM cell
@@ -50,7 +51,7 @@
     private final int mBsic;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     /**
      * @hide
@@ -62,7 +63,7 @@
         mCid = CellInfo.UNAVAILABLE;
         mArfcn = CellInfo.UNAVAILABLE;
         mBsic = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
     }
 
     /**
@@ -81,13 +82,13 @@
      */
     public CellIdentityGsm(int lac, int cid, int arfcn, int bsic, @Nullable String mccStr,
             @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
-            @NonNull List<String> additionalPlmns) {
+            @NonNull Collection<String> additionalPlmns) {
         super(TAG, CellInfo.TYPE_GSM, mccStr, mncStr, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mArfcn = inRangeOrUnavailable(arfcn, 0, MAX_ARFCN);
         mBsic = inRangeOrUnavailable(bsic, 0, MAX_BSIC);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
@@ -99,7 +100,7 @@
     public CellIdentityGsm(@NonNull android.hardware.radio.V1_0.CellIdentityGsm cid) {
         this(cid.lac, cid.cid, cid.arfcn,
                 cid.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.bsic,
-                cid.mcc, cid.mnc, "", "", Collections.emptyList());
+                cid.mcc, cid.mnc, "", "", new ArraySet<>());
     }
 
     /** @hide */
@@ -107,7 +108,7 @@
         this(cid.base.lac, cid.base.cid, cid.base.arfcn,
                 cid.base.bsic == (byte) 0xFF ? CellInfo.UNAVAILABLE : cid.base.bsic, cid.base.mcc,
                 cid.base.mnc, cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
-                Collections.emptyList());
+                new ArraySet<>());
     }
 
     /** @hide */
@@ -221,8 +222,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -296,7 +297,7 @@
         dest.writeInt(mCid);
         dest.writeInt(mArfcn);
         dest.writeInt(mBsic);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -306,7 +307,7 @@
         mCid = in.readInt();
         mArfcn = in.readInt();
         mBsic = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
 
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityLte.java b/telephony/java/android/telephony/CellIdentityLte.java
index a194ae3..e4198d1 100644
--- a/telephony/java/android/telephony/CellIdentityLte.java
+++ b/telephony/java/android/telephony/CellIdentityLte.java
@@ -23,11 +23,13 @@
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity is to represent a unique LTE cell
@@ -54,7 +56,7 @@
     private final int mBandwidth;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     private ClosedSubscriberGroupInfo mCsgInfo;
 
@@ -69,7 +71,7 @@
         mTac = CellInfo.UNAVAILABLE;
         mEarfcn = CellInfo.UNAVAILABLE;
         mBandwidth = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
     }
 
@@ -86,7 +88,7 @@
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public CellIdentityLte(int mcc, int mnc, int ci, int pci, int tac) {
         this(ci, pci, tac, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE, String.valueOf(mcc),
-                String.valueOf(mnc), null, null, Collections.emptyList(), null);
+                String.valueOf(mnc), null, null, new ArraySet<>(), null);
     }
 
     /**
@@ -107,7 +109,7 @@
      */
     public CellIdentityLte(int ci, int pci, int tac, int earfcn, int bandwidth,
             @Nullable String mccStr, @Nullable String mncStr, @Nullable String alphal,
-            @Nullable String alphas, @NonNull List<String> additionalPlmns,
+            @Nullable String alphas, @NonNull Collection<String> additionalPlmns,
             @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_LTE, mccStr, mncStr, alphal, alphas);
         mCi = inRangeOrUnavailable(ci, 0, MAX_CI);
@@ -115,7 +117,7 @@
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mEarfcn = inRangeOrUnavailable(earfcn, 0, MAX_EARFCN);
         mBandwidth = inRangeOrUnavailable(bandwidth, 0, MAX_BANDWIDTH);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
@@ -127,14 +129,14 @@
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_0.CellIdentityLte cid) {
         this(cid.ci, cid.pci, cid.tac, cid.earfcn,
-                CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", Collections.emptyList(), null);
+                CellInfo.UNAVAILABLE, cid.mcc, cid.mnc, "", "", new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityLte(@NonNull android.hardware.radio.V1_2.CellIdentityLte cid) {
         this(cid.base.ci, cid.base.pci, cid.base.tac, cid.base.earfcn, cid.bandwidth,
                 cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
-                cid.operatorNames.alphaShort, Collections.emptyList(), null);
+                cid.operatorNames.alphaShort, new ArraySet<>(), null);
     }
 
     /** @hide */
@@ -270,8 +272,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -361,7 +363,7 @@
         dest.writeInt(mTac);
         dest.writeInt(mEarfcn);
         dest.writeInt(mBandwidth);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
     }
 
@@ -373,7 +375,7 @@
         mTac = in.readInt();
         mEarfcn = in.readInt();
         mBandwidth = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index a0ef5aa..cba500a 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -22,11 +22,14 @@
 import android.os.Parcel;
 import android.telephony.AccessNetworkConstants.NgranBands.NgranBand;
 import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * Information to represent a unique NR(New Radio 5G) cell.
@@ -46,7 +49,7 @@
     private final List<Integer> mBands;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     /**
      *
@@ -66,14 +69,14 @@
     public CellIdentityNr(int pci, int tac, int nrArfcn, @NgranBand List<Integer> bands,
                           @Nullable String mccStr, @Nullable String mncStr, long nci,
                           @Nullable String alphal, @Nullable String alphas,
-                          @NonNull List<String> additionalPlmns) {
+                          @NonNull Collection<String> additionalPlmns) {
         super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
         mPci = inRangeOrUnavailable(pci, 0, MAX_PCI);
         mTac = inRangeOrUnavailable(tac, 0, MAX_TAC);
         mNrArfcn = inRangeOrUnavailable(nrArfcn, 0, MAX_NRARFCN);
         mBands = new ArrayList<>(bands);
         mNci = inRangeOrUnavailable(nci, 0, MAX_NCI);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
@@ -85,7 +88,7 @@
     public CellIdentityNr(@NonNull android.hardware.radio.V1_4.CellIdentityNr cid) {
         this(cid.pci, cid.tac, cid.nrarfcn, Collections.emptyList(), cid.mcc, cid.mnc, cid.nci,
                 cid.operatorNames.alphaLong, cid.operatorNames.alphaShort,
-                Collections.emptyList());
+                new ArraySet<>());
     }
 
     /** @hide */
@@ -212,8 +215,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return Collections.unmodifiableList(mAdditionalPlmns);
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     @Override
@@ -241,7 +244,7 @@
         dest.writeInt(mNrArfcn);
         dest.writeList(mBands);
         dest.writeLong(mNci);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
     }
 
     /** Construct from Parcel, type has already been processed */
@@ -252,7 +255,7 @@
         mNrArfcn = in.readInt();
         mBands = in.readArrayList(null);
         mNci = in.readLong();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
     }
 
     /** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 531487a..30f98bc 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -20,11 +20,12 @@
 import android.annotation.Nullable;
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity is to represent a unique TD-SCDMA cell
@@ -50,7 +51,7 @@
     private final int mUarfcn;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     private ClosedSubscriberGroupInfo mCsgInfo;
 
@@ -63,7 +64,7 @@
         mCid = CellInfo.UNAVAILABLE;
         mCpid = CellInfo.UNAVAILABLE;
         mUarfcn = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
     }
 
@@ -85,13 +86,14 @@
      */
     public CellIdentityTdscdma(@Nullable String mcc, @Nullable String mnc, int lac, int cid,
             int cpid, int uarfcn, @Nullable String alphal, @Nullable String alphas,
-            @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
+            @NonNull Collection<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_TDSCDMA, mcc, mnc, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mCpid = inRangeOrUnavailable(cpid, 0, MAX_CPID);
         mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
@@ -208,8 +210,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -289,7 +291,7 @@
         dest.writeInt(mCid);
         dest.writeInt(mCpid);
         dest.writeInt(mUarfcn);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
     }
 
@@ -300,7 +302,7 @@
         mCid = in.readInt();
         mCpid = in.readInt();
         mUarfcn = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/CellIdentityWcdma.java b/telephony/java/android/telephony/CellIdentityWcdma.java
index 15e491b..9d2cb74 100644
--- a/telephony/java/android/telephony/CellIdentityWcdma.java
+++ b/telephony/java/android/telephony/CellIdentityWcdma.java
@@ -22,11 +22,12 @@
 import android.os.Parcel;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
+import android.util.ArraySet;
 
-import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.Objects;
+import java.util.Set;
 
 /**
  * CellIdentity to represent a unique UMTS cell
@@ -51,7 +52,7 @@
     private final int mUarfcn;
 
     // a list of additional PLMN-IDs reported for this cell
-    private final List<String> mAdditionalPlmns;
+    private final ArraySet<String> mAdditionalPlmns;
 
     @Nullable
     private final ClosedSubscriberGroupInfo mCsgInfo;
@@ -65,7 +66,7 @@
         mCid = CellInfo.UNAVAILABLE;
         mPsc = CellInfo.UNAVAILABLE;
         mUarfcn = CellInfo.UNAVAILABLE;
-        mAdditionalPlmns = Collections.emptyList();
+        mAdditionalPlmns = new ArraySet<>();
         mCsgInfo = null;
     }
 
@@ -86,13 +87,14 @@
      */
     public CellIdentityWcdma(int lac, int cid, int psc, int uarfcn, @Nullable String mccStr,
             @Nullable String mncStr, @Nullable String alphal, @Nullable String alphas,
-            @NonNull List<String> additionalPlmns, @Nullable ClosedSubscriberGroupInfo csgInfo) {
+            @NonNull Collection<String> additionalPlmns,
+            @Nullable ClosedSubscriberGroupInfo csgInfo) {
         super(TAG, CellInfo.TYPE_WCDMA, mccStr, mncStr, alphal, alphas);
         mLac = inRangeOrUnavailable(lac, 0, MAX_LAC);
         mCid = inRangeOrUnavailable(cid, 0, MAX_CID);
         mPsc = inRangeOrUnavailable(psc, 0, MAX_PSC);
         mUarfcn = inRangeOrUnavailable(uarfcn, 0, MAX_UARFCN);
-        mAdditionalPlmns = new ArrayList<>(additionalPlmns.size());
+        mAdditionalPlmns = new ArraySet<>(additionalPlmns.size());
         for (String plmn : additionalPlmns) {
             if (isValidPlmn(plmn)) {
                 mAdditionalPlmns.add(plmn);
@@ -104,14 +106,14 @@
     /** @hide */
     public CellIdentityWcdma(@NonNull android.hardware.radio.V1_0.CellIdentityWcdma cid) {
         this(cid.lac, cid.cid, cid.psc, cid.uarfcn, cid.mcc, cid.mnc, "", "",
-                Collections.emptyList(), null);
+                new ArraySet<>(), null);
     }
 
     /** @hide */
     public CellIdentityWcdma(@NonNull android.hardware.radio.V1_2.CellIdentityWcdma cid) {
         this(cid.base.lac, cid.base.cid, cid.base.psc, cid.base.uarfcn,
                 cid.base.mcc, cid.base.mnc, cid.operatorNames.alphaLong,
-                cid.operatorNames.alphaShort, Collections.emptyList(), null);
+                cid.operatorNames.alphaShort, new ArraySet<>(), null);
     }
 
     /** @hide */
@@ -232,8 +234,8 @@
      * @return a list of additional PLMN IDs supported by this cell.
      */
     @NonNull
-    public List<String> getAdditionalPlmns() {
-        return mAdditionalPlmns;
+    public Set<String> getAdditionalPlmns() {
+        return Collections.unmodifiableSet(mAdditionalPlmns);
     }
 
     /**
@@ -305,7 +307,7 @@
         dest.writeInt(mCid);
         dest.writeInt(mPsc);
         dest.writeInt(mUarfcn);
-        dest.writeList(mAdditionalPlmns);
+        dest.writeArraySet(mAdditionalPlmns);
         dest.writeParcelable(mCsgInfo, flags);
     }
 
@@ -316,7 +318,7 @@
         mCid = in.readInt();
         mPsc = in.readInt();
         mUarfcn = in.readInt();
-        mAdditionalPlmns = in.readArrayList(null);
+        mAdditionalPlmns = (ArraySet<String>) in.readArraySet(null);
         mCsgInfo = in.readParcelable(null);
         if (DBG) log(toString());
     }
diff --git a/telephony/java/android/telephony/NetworkRegistrationInfo.java b/telephony/java/android/telephony/NetworkRegistrationInfo.java
index f9de47d..c74e17f 100644
--- a/telephony/java/android/telephony/NetworkRegistrationInfo.java
+++ b/telephony/java/android/telephony/NetworkRegistrationInfo.java
@@ -258,7 +258,7 @@
         mCellIdentity = cellIdentity;
         mEmergencyOnly = emergencyOnly;
         mNrState = NR_STATE_NONE;
-        mRplmn = (rplmn == null) ? "" : rplmn;
+        mRplmn = rplmn;
     }
 
     /**
@@ -408,13 +408,13 @@
      * <p>If the device is registered, this will return the registered PLMN-ID. If registration
      * has failed, then this will return the PLMN ID of the last attempted registration. If the
      * device is not registered, or if is registered to a non-3GPP radio technology, then this
-     * will return an empty string.
+     * will return null.
      *
      * <p>See 3GPP TS 23.122 for further information about the Registered PLMN.
      *
-     * @return the registered PLMN-ID or an empty string.
+     * @return the registered PLMN-ID or null.
      */
-    @NonNull public String getRegisteredPlmn() {
+    @Nullable public String getRegisteredPlmn() {
         return mRplmn;
     }
 
@@ -892,7 +892,7 @@
          * @return The same instance of the builder.
          */
         public @NonNull Builder setRegisteredPlmn(@Nullable String rplmn) {
-            mRplmn = (rplmn == null) ? "" : rplmn;
+            mRplmn = rplmn;
             return this;
         }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 15103bf..68b6683 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -13135,7 +13135,7 @@
     */
     static IPhoneSubInfo getSubscriberInfoService() {
         // Keeps cache disabled until test fixes are checked into AOSP.
-        if (true) {
+        if (!sServiceHandleCacheEnabled) {
             return IPhoneSubInfo.Stub.asInterface(
                 TelephonyFrameworkInitializer
                         .getTelephonyServiceManager()
@@ -13169,7 +13169,7 @@
     */
     static ISub getSubscriptionService() {
         // Keeps cache disabled until test fixes are checked into AOSP.
-        if (true) {
+        if (!sServiceHandleCacheEnabled) {
             return ISub.Stub.asInterface(
                     TelephonyFrameworkInitializer
                             .getTelephonyServiceManager()
@@ -13203,7 +13203,7 @@
     */
     static ISms getSmsService() {
         // Keeps cache disabled until test fixes are checked into AOSP.
-        if (true) {
+        if (!sServiceHandleCacheEnabled) {
             return ISms.Stub.asInterface(
                     TelephonyFrameworkInitializer
                             .getTelephonyServiceManager()
diff --git a/tests/RollbackTest/RollbackTest/AndroidManifest.xml b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
index 2b8c964..9274da2 100644
--- a/tests/RollbackTest/RollbackTest/AndroidManifest.xml
+++ b/tests/RollbackTest/RollbackTest/AndroidManifest.xml
@@ -17,6 +17,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
     package="com.android.tests.rollback" >
 
+    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
     <application>
         <receiver android:name="com.android.cts.install.lib.LocalIntentSender"
                   android:exported="true" />
diff --git a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
index 37842de..957216e 100644
--- a/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
+++ b/tests/utils/testutils/java/com/android/server/wm/test/filters/FrameworksTestsFilter.java
@@ -45,7 +45,6 @@
             "android.view.InsetsSourceTest",
             "android.view.InsetsSourceConsumerTest",
             "android.view.InsetsStateTest",
-            "android.view.WindowMetricsTest"
     };
 
     public FrameworksTestsFilter(Bundle testArgs) {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index dc5ac1e..e1450cb 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -486,7 +486,7 @@
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.SAE);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
-                requirePMF = true;
+                requirePmf = true;
                 break;
             case SECURITY_TYPE_EAP_SUITE_B:
                 allowedProtocols.set(WifiConfiguration.Protocol.RSN);
@@ -496,14 +496,14 @@
                 allowedGroupManagementCiphers.set(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256);
                 // Note: allowedSuiteBCiphers bitset will be set by the service once the
                 // certificates are attached to this profile
-                requirePMF = true;
+                requirePmf = true;
                 break;
             case SECURITY_TYPE_OWE:
                 allowedProtocols.set(WifiConfiguration.Protocol.RSN);
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.OWE);
                 allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
                 allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
-                requirePMF = true;
+                requirePmf = true;
                 break;
             case SECURITY_TYPE_WAPI_PSK:
                 allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WAPI_PSK);
@@ -662,7 +662,7 @@
      * @hide
      */
     @SystemApi
-    public boolean requirePMF;
+    public boolean requirePmf;
 
     /**
      * Update identifier, for Passpoint network.
@@ -2169,7 +2169,7 @@
                 append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN)
                 .append(" PRIO: ").append(this.priority)
                 .append(" HIDDEN: ").append(this.hiddenSSID)
-                .append(" PMF: ").append(this.requirePMF)
+                .append(" PMF: ").append(this.requirePmf)
                 .append("CarrierId: ").append(this.carrierId)
                 .append('\n');
 
@@ -2761,7 +2761,7 @@
             mRandomizedMacAddress = source.mRandomizedMacAddress;
             macRandomizationSetting = source.macRandomizationSetting;
             randomizedMacExpirationTimeMs = source.randomizedMacExpirationTimeMs;
-            requirePMF = source.requirePMF;
+            requirePmf = source.requirePmf;
             updateIdentifier = source.updateIdentifier;
             carrierId = source.carrierId;
             mPasspointUniqueId = source.mPasspointUniqueId;
@@ -2793,7 +2793,7 @@
         dest.writeInt(wepTxKeyIndex);
         dest.writeInt(priority);
         dest.writeInt(hiddenSSID ? 1 : 0);
-        dest.writeInt(requirePMF ? 1 : 0);
+        dest.writeInt(requirePmf ? 1 : 0);
         dest.writeString(updateIdentifier);
 
         writeBitSet(dest, allowedKeyManagement);
@@ -2869,7 +2869,7 @@
                 config.wepTxKeyIndex = in.readInt();
                 config.priority = in.readInt();
                 config.hiddenSSID = in.readInt() != 0;
-                config.requirePMF = in.readInt() != 0;
+                config.requirePmf = in.readInt() != 0;
                 config.updateIdentifier = in.readString();
 
                 config.allowedKeyManagement   = readBitSet(in);
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 05a3dce..00790d5 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -363,7 +363,7 @@
         assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE));
         assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
         assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
-        assertTrue(config.requirePMF);
+        assertTrue(config.requirePmf);
     }
 
     /**
@@ -380,7 +380,7 @@
         assertTrue(config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE));
         assertTrue(config.allowedPairwiseCiphers.get(WifiConfiguration.PairwiseCipher.CCMP));
         assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.CCMP));
-        assertTrue(config.requirePMF);
+        assertTrue(config.requirePmf);
     }
 
     /**
@@ -399,7 +399,7 @@
         assertTrue(config.allowedGroupCiphers.get(WifiConfiguration.GroupCipher.GCMP_256));
         assertTrue(config.allowedGroupManagementCiphers
                 .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
-        assertTrue(config.requirePMF);
+        assertTrue(config.requirePmf);
     }
 
     /**
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index af6fb5c..51bf738 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -140,7 +140,7 @@
         assertTrue(suggestion.wifiConfiguration.allowedKeyManagement
                 .get(WifiConfiguration.KeyMgmt.OWE));
         assertNull(suggestion.wifiConfiguration.preSharedKey);
-        assertTrue(suggestion.wifiConfiguration.requirePMF);
+        assertTrue(suggestion.wifiConfiguration.requirePmf);
         assertFalse(suggestion.isUserAllowedToManuallyConnect);
         assertTrue(suggestion.isInitialAutoJoinEnabled);
     }
@@ -163,7 +163,7 @@
                 .get(WifiConfiguration.KeyMgmt.SAE));
         assertEquals("\"" + TEST_PRESHARED_KEY + "\"",
                 suggestion.wifiConfiguration.preSharedKey);
-        assertTrue(suggestion.wifiConfiguration.requirePMF);
+        assertTrue(suggestion.wifiConfiguration.requirePmf);
         assertTrue(suggestion.isUserAllowedToManuallyConnect);
         assertFalse(suggestion.isInitialAutoJoinEnabled);
     }
@@ -191,7 +191,7 @@
                 .get(WifiConfiguration.GroupCipher.GCMP_256));
         assertTrue(suggestion.wifiConfiguration.allowedGroupManagementCiphers
                 .get(WifiConfiguration.GroupMgmtCipher.BIP_GMAC_256));
-        assertTrue(suggestion.wifiConfiguration.requirePMF);
+        assertTrue(suggestion.wifiConfiguration.requirePmf);
         assertNull(suggestion.wifiConfiguration.preSharedKey);
         // allowedSuiteBCiphers are set according to the loaded certificate and cannot be tested
         // here.