Merge "SystemUI Split via TaskOrganizer"
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 088cadb..8a9c774 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -896,7 +896,7 @@
          * @param flags Flags for the observer.
          */
         public TriggerContentUri(@NonNull Uri uri, @Flags int flags) {
-            mUri = uri;
+            mUri = Objects.requireNonNull(uri);
             mFlags = flags;
         }
 
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 1072406..7833a03 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -16,6 +16,7 @@
 
 package com.android.server.job;
 
+import android.annotation.NonNull;
 import android.app.job.JobInfo;
 import android.util.proto.ProtoOutputStream;
 
@@ -44,6 +45,10 @@
     void removeBackingUpUid(int uid);
     void clearAllBackingUpUids();
 
+    /** Returns the package responsible for backing up media on the device. */
+    @NonNull
+    String getMediaBackupPackage();
+
     /**
      * The user has started interacting with the app.  Take any appropriate action.
      */
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index e4c6b52..ff7944d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -77,6 +77,7 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.ArrayUtils;
@@ -248,6 +249,9 @@
      */
     private final List<JobRestriction> mJobRestrictions;
 
+    @NonNull
+    private final String mSystemGalleryPackage;
+
     private final CountQuotaTracker mQuotaTracker;
     private static final String QUOTA_TRACKER_SCHEDULE_PERSISTED_TAG = ".schedulePersisted()";
 
@@ -1394,6 +1398,9 @@
         mJobRestrictions = new ArrayList<>();
         mJobRestrictions.add(new ThermalStatusRestriction(this));
 
+        mSystemGalleryPackage = Objects.requireNonNull(
+                context.getString(R.string.config_systemGallery));
+
         // If the job store determined that it can't yet reschedule persisted jobs,
         // we need to start watching the clock.
         if (!mJobs.jobTimesInflatedValid()) {
@@ -2359,6 +2366,11 @@
         }
 
         @Override
+        public String getMediaBackupPackage() {
+            return mSystemGalleryPackage;
+        }
+
+        @Override
         public void reportAppUsage(String packageName, int userId) {
             JobSchedulerService.this.reportAppUsage(packageName, userId);
         }
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
index a775cf5..5fcd774 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ContentObserverController.java
@@ -344,7 +344,7 @@
                     mContext.getContentResolver().unregisterContentObserver(obs);
                     ArrayMap<JobInfo.TriggerContentUri, ObserverInstance> observerOfUser =
                             mObservers.get(obs.mUserId);
-                    if (observerOfUser !=  null) {
+                    if (observerOfUser != null) {
                         observerOfUser.remove(obs.mUri);
                     }
                 }
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 1e89158..cf7f380 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
@@ -19,6 +19,7 @@
 import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
 import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
 import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
 import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
 
 import android.app.AppGlobals;
@@ -30,6 +31,7 @@
 import android.net.Uri;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.provider.MediaStore;
 import android.text.format.DateFormat;
 import android.util.ArraySet;
 import android.util.Pair;
@@ -207,6 +209,18 @@
      */
     private int mDynamicConstraints = 0;
 
+    /**
+     * Indicates whether the job is responsible for backing up media, so we can be lenient in
+     * applying standby throttling.
+     *
+     * Doesn't exempt jobs with a deadline constraint, as they can be started without any content or
+     * network changes, in which case this exemption does not make sense.
+     *
+     * TODO(b/149519887): Use a more explicit signal, maybe an API flag, that the scheduling package
+     * needs to provide at the time of scheduling a job.
+     */
+    private final boolean mHasMediaBackupExemption;
+
     // Set to true if doze constraint was satisfied due to app being whitelisted.
     public boolean dozeWhitelisted;
 
@@ -415,9 +429,11 @@
         this.mOriginalLatestRunTimeElapsedMillis = latestRunTimeElapsedMillis;
         this.numFailures = numFailures;
 
+        boolean requiresNetwork = false;
         int requiredConstraints = job.getConstraintFlags();
         if (job.getRequiredNetwork() != null) {
             requiredConstraints |= CONSTRAINT_CONNECTIVITY;
+            requiresNetwork = true;
         }
         if (earliestRunTimeElapsedMillis != NO_EARLIEST_RUNTIME) {
             requiredConstraints |= CONSTRAINT_TIMING_DELAY;
@@ -425,8 +441,16 @@
         if (latestRunTimeElapsedMillis != NO_LATEST_RUNTIME) {
             requiredConstraints |= CONSTRAINT_DEADLINE;
         }
+        boolean mediaOnly = false;
         if (job.getTriggerContentUris() != null) {
             requiredConstraints |= CONSTRAINT_CONTENT_TRIGGER;
+            mediaOnly = true;
+            for (JobInfo.TriggerContentUri uri : job.getTriggerContentUris()) {
+                if (!MediaStore.AUTHORITY.equals(uri.getUri().getAuthority())) {
+                    mediaOnly = false;
+                    break;
+                }
+            }
         }
         this.requiredConstraints = requiredConstraints;
         mRequiredConstraintsOfInterest = requiredConstraints & CONSTRAINTS_OF_INTEREST;
@@ -450,6 +474,9 @@
             // our source UID into place.
             job.getRequiredNetwork().networkCapabilities.setSingleUid(this.sourceUid);
         }
+        final JobSchedulerInternal jsi = LocalServices.getService(JobSchedulerInternal.class);
+        mHasMediaBackupExemption = !job.hasLateConstraint() && mediaOnly && requiresNetwork
+                && this.sourcePackageName.equals(jsi.getMediaBackupPackage());
     }
 
     /** Copy constructor: used specifically when cloning JobStatus objects for persistence,
@@ -545,7 +572,6 @@
 
         int standbyBucket = JobSchedulerService.standbyBucketForPackage(jobPackage,
                 sourceUserId, elapsedNow);
-        JobSchedulerInternal js = LocalServices.getService(JobSchedulerInternal.class);
         return new JobStatus(job, callingUid, sourcePkg, sourceUserId,
                 standbyBucket, tag, 0,
                 earliestRunTimeElapsedMillis, latestRunTimeElapsedMillis,
@@ -734,7 +760,14 @@
             // like other ACTIVE apps.
             return ACTIVE_INDEX;
         }
-        return getStandbyBucket();
+        final int actualBucket = getStandbyBucket();
+        if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX
+                && mHasMediaBackupExemption) {
+            // Cap it at WORKING_INDEX as media back up jobs are important to the user, and the
+            // source package may not have been used directly in a while.
+            return Math.min(WORKING_INDEX, actualBucket);
+        }
+        return actualBucket;
     }
 
     /** Returns the real standby bucket of the job. */
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index c0f84a0..baa1c25 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -20,6 +20,7 @@
 
 apex_defaults {
     native_shared_libs: [
+        "libstatssocket",
         "libstatspull",
         "libstats_jni",
     ],
@@ -76,4 +77,4 @@
         //TODO (b/148620413): remove platform.
          "//apex_available:platform",
     ],
-}
\ No newline at end of file
+}
diff --git a/api/current.txt b/api/current.txt
index 0290586..6a918d9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2958,7 +2958,7 @@
   }
 
   public static final class AccessibilityService.ScreenshotResult {
-    method @Nullable public android.graphics.ColorSpace getColorSpace();
+    method @NonNull public android.graphics.ColorSpace getColorSpace();
     method @NonNull public android.hardware.HardwareBuffer getHardwareBuffer();
     method public long getTimestamp();
   }
@@ -7242,7 +7242,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 +7251,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 {
@@ -12071,7 +12071,7 @@
     field public static final String FEATURE_COMPANION_DEVICE_SETUP = "android.software.companion_device_setup";
     field public static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
     field public static final String FEATURE_CONSUMER_IR = "android.hardware.consumerir";
-    field public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub";
+    field public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
     field public static final String FEATURE_DEVICE_ADMIN = "android.software.device_admin";
     field public static final String FEATURE_EMBEDDED = "android.hardware.type.embedded";
     field public static final String FEATURE_ETHERNET = "android.hardware.ethernet";
@@ -27044,7 +27044,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 +27424,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 +27433,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 +27445,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 +27459,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);
@@ -37314,6 +37314,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";
@@ -52155,6 +52156,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..55460bb 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7598,7 +7598,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 +9680,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 {
@@ -14063,6 +14064,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/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 956fd29..1bcf44e 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -120,13 +120,14 @@
         "libstatslog",
         "libstatsmetadata",
         "libsysutils",
+	// TODO(b/145923087): move to shared when statsd is moved to the apex
+        "libstatssocket",
         "libutils",
     ],
     shared_libs: [
         "libbinder",
         "libincident",
         "liblog",
-        "libstatssocket",
         "statsd-aidl-cpp",
     ],
 }
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/storage/StorageManager.cpp b/cmds/statsd/src/storage/StorageManager.cpp
index 1bac19e..4899b4a 100644
--- a/cmds/statsd/src/storage/StorageManager.cpp
+++ b/cmds/statsd/src/storage/StorageManager.cpp
@@ -690,14 +690,16 @@
         if (name[0] == '.') continue;
 
         FileName output;
+        string file_name;
         if (parseTimestampOnly) {
+            file_name = StringPrintf("%s/%s", path, name);
             output.mTimestampSec = StrToInt64(strtok(name, "_"));
             output.mIsHistory = false;
         } else {
             parseFileName(name, &output);
+            file_name = output.getFullFileName(path);
         }
         if (output.mTimestampSec == -1) continue;
-        string file_name = output.getFullFileName(path);
 
         // Check for timestamp and delete if it's too old.
         long fileAge = nowSec - output.mTimestampSec;
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 0ed6b1f..b7a35f7 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -29,6 +29,7 @@
 import android.content.pm.ParceledListSlice;
 import android.graphics.Bitmap;
 import android.graphics.ColorSpace;
+import android.graphics.ParcelableColorSpace;
 import android.graphics.Region;
 import android.hardware.HardwareBuffer;
 import android.os.Binder;
@@ -39,6 +40,7 @@
 import android.os.Message;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.Slog;
@@ -600,8 +602,12 @@
             "screenshot_hardwareBuffer";
 
     /** @hide */
-    public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID =
-            "screenshot_colorSpaceId";
+    public static final String KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE =
+            "screenshot_colorSpace";
+
+    /** @hide */
+    public static final String KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP =
+            "screenshot_timestamp";
 
     /**
      * Callback for {@link android.view.accessibility.AccessibilityEvent}s.
@@ -1920,6 +1926,8 @@
      *                  default display.
      * @param executor Executor on which to run the callback.
      * @param callback The callback invoked when the taking screenshot is done.
+     *                 The {@link AccessibilityService.ScreenshotResult} will be null for an
+     *                 invalid display.
      *
      * @return {@code true} if the taking screenshot accepted, {@code false} if not.
      */
@@ -1941,14 +1949,11 @@
                 }
                 final HardwareBuffer hardwareBuffer =
                         result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER);
-                final int colorSpaceId =
-                        result.getInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID);
-                ColorSpace colorSpace = null;
-                if (colorSpaceId >= 0 && colorSpaceId < ColorSpace.Named.values().length) {
-                    colorSpace = ColorSpace.get(ColorSpace.Named.values()[colorSpaceId]);
-                }
+                final ParcelableColorSpace colorSpace =
+                        result.getParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE);
                 ScreenshotResult screenshot = new ScreenshotResult(hardwareBuffer,
-                        colorSpace, System.currentTimeMillis());
+                        colorSpace.getColorSpace(),
+                        result.getLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP));
                 sendScreenshotResult(executor, callback, screenshot);
             }));
         } catch (RemoteException re) {
@@ -2361,41 +2366,38 @@
     }
 
     /**
-     * Class including hardwareBuffer, colorSpace, and timestamp to be the result for
+     * Can be used to construct a bitmap of the screenshot or any other operations for
      * {@link AccessibilityService#takeScreenshot} API.
-     * <p>
-     * <strong>Note:</strong> colorSpace would be null if the name of this colorSpace isn't at
-     * {@link ColorSpace.Named}.
-     * </p>
      */
     public static final class ScreenshotResult {
         private final @NonNull HardwareBuffer mHardwareBuffer;
-        private final @Nullable ColorSpace mColorSpace;
+        private final @NonNull ColorSpace mColorSpace;
         private final long mTimestamp;
 
         private ScreenshotResult(@NonNull HardwareBuffer hardwareBuffer,
-                @Nullable ColorSpace colorSpace, long timestamp) {
+                @NonNull ColorSpace colorSpace, long timestamp) {
             Preconditions.checkNotNull(hardwareBuffer, "hardwareBuffer cannot be null");
+            Preconditions.checkNotNull(colorSpace, "colorSpace cannot be null");
             mHardwareBuffer = hardwareBuffer;
             mColorSpace = colorSpace;
             mTimestamp = timestamp;
         }
 
         /**
-         * Gets the colorSpace identifying a specific organization of colors of the screenshot.
+         * Gets the {@link ColorSpace} identifying a specific organization of colors of the
+         * screenshot.
          *
-         * @return the colorSpace or {@code null} if the name of colorSpace isn't at
-         * {@link ColorSpace.Named}
+         * @return the color space
          */
-        @Nullable
+        @NonNull
         public ColorSpace getColorSpace() {
             return mColorSpace;
         }
 
         /**
-         * Gets the hardwareBuffer representing a memory buffer of the screenshot.
+         * Gets the {@link HardwareBuffer} representing a memory buffer of the screenshot.
          *
-         * @return the hardwareBuffer
+         * @return the hardware buffer
          */
         @NonNull
         public HardwareBuffer getHardwareBuffer() {
@@ -2405,7 +2407,8 @@
         /**
          * Gets the timestamp of taking the screenshot.
          *
-         * @return the timestamp from {@link System#currentTimeMillis()}
+         * @return milliseconds of non-sleep uptime before screenshot since boot and it's from
+         * {@link SystemClock#uptimeMillis()}
          */
         public long getTimestamp() {
             return mTimestamp;
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..395a196 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);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index 9aa6b87..526c0b3 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -162,6 +162,7 @@
     void applyAdjustmentFromAssistant(in INotificationListener token, in Adjustment adjustment);
     void applyAdjustmentsFromAssistant(in INotificationListener token, in List<Adjustment> adjustments);
     void unsnoozeNotificationFromAssistant(in INotificationListener token, String key);
+    void unsnoozeNotificationFromSystemListener(in INotificationListener token, String key);
 
     ComponentName getEffectsSuppressor();
     boolean matchesCallFilter(in Bundle extras);
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/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/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/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ae14748..7b484b7 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1988,10 +1988,11 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
-     * {@link #hasSystemFeature}: The device supports a Context Hub.
+     * {@link #hasSystemFeature}: The device supports a Context Hub, used to expose the
+     * functionalities in {@link android.hardware.location.ContextHubManager}.
      */
     @SdkConstant(SdkConstantType.FEATURE)
-    public static final String FEATURE_CONTEXTHUB = "android.hardware.context_hub";
+    public static final String FEATURE_CONTEXT_HUB = "android.hardware.context_hub";
 
     /** {@hide} */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index db16d24..1ed791d 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -54,7 +54,7 @@
  */
 @SystemApi
 @SystemService(Context.CONTEXTHUB_SERVICE)
-@RequiresFeature(PackageManager.FEATURE_CONTEXTHUB)
+@RequiresFeature(PackageManager.FEATURE_CONTEXT_HUB)
 public final class ContextHubManager {
     private static final String TAG = "ContextHubManager";
 
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 49e1d5e..a95938b 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -789,6 +789,7 @@
                 Log.w(TAG, "onCreateInlineSuggestionsRequest() returned null request");
                 requestCallback.onInlineSuggestionsUnsupported();
             } else {
+                request.setHostInputToken(getHostInputToken());
                 final IInlineSuggestionsResponseCallback inlineSuggestionsResponseCallback =
                         new InlineSuggestionsResponseCallbackImpl(this,
                                 mInlineSuggestionsRequestInfo.mComponentName,
@@ -833,6 +834,18 @@
         onInlineSuggestionsResponse(response);
     }
 
+    /**
+     * 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() {
         setImeWindowStatus(IME_ACTIVE | IME_INVISIBLE, mBackDisposition);
         onPreRenderedWindowVisibilityChanged(false /* setVisible */);
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 37efec3..a8f88d4 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}.
      *
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/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index afeb6c3..44e7ae6 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -79,6 +79,7 @@
 
         DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
         DEFAULT_FLAGS.put(SETTINGS_SCHEDULES_FLAG, "false");
+        DEFAULT_FLAGS.put("settings_contextual_home2", "false");
     }
 
     /**
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 37b9eb3..c62e934 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/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index bf84819..680a878 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -24,6 +24,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
 
 /**
  * Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy
@@ -40,6 +41,7 @@
     private WindowlessWindowManager mWm;
 
     private SurfaceControl mSurfaceControl;
+    private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
     /**
      * Package encapsulating a Surface hierarchy which contains interactive view
@@ -49,15 +51,18 @@
      */
     public static final class SurfacePackage implements Parcelable {
         private final SurfaceControl mSurfaceControl;
-        // TODO: Accessibility ID goes here
+        private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
-        SurfacePackage(SurfaceControl sc) {
+        SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) {
             mSurfaceControl = sc;
+            mAccessibilityEmbeddedConnection = connection;
         }
 
         private SurfacePackage(Parcel in) {
             mSurfaceControl = new SurfaceControl();
             mSurfaceControl.readFromParcel(in);
+            mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface(
+                    in.readStrongBinder());
         }
 
         /**
@@ -69,6 +74,16 @@
             return mSurfaceControl;
         }
 
+        /**
+         * Gets an accessibility embedded connection interface for this SurfaceControlViewHost.
+         *
+         * @return {@link IAccessibilityEmbeddedConnection} interface.
+         * @hide
+         */
+        public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
+            return mAccessibilityEmbeddedConnection;
+        }
+
         @Override
         public int describeContents() {
             return 0;
@@ -77,6 +92,7 @@
         @Override
         public void writeToParcel(@NonNull Parcel out, int flags) {
             mSurfaceControl.writeToParcel(out, flags);
+            out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder());
         }
 
         public static final @NonNull Creator<SurfacePackage> CREATOR
@@ -95,6 +111,7 @@
             @NonNull WindowlessWindowManager wwm) {
         mWm = wwm;
         mViewRoot = new ViewRootImpl(c, d, mWm);
+        mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
     /**
@@ -118,6 +135,7 @@
         mWm = new WindowlessWindowManager(context.getResources().getConfiguration(),
                 mSurfaceControl, hostToken);
         mViewRoot = new ViewRootImpl(context, display, mWm);
+        mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
     }
 
     /**
@@ -128,8 +146,8 @@
      * are linked.
      */
     public @Nullable SurfacePackage getSurfacePackage() {
-        if (mSurfaceControl != null) {
-            return new SurfacePackage(mSurfaceControl);
+        if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
+            return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection);
         } else {
             return null;
         }
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index d5ed36b..47ffd3e 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -28,6 +28,7 @@
 import android.graphics.BlendMode;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.PorterDuff;
@@ -38,12 +39,14 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
+import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.SurfaceControl.Transaction;
 import android.view.SurfaceControlViewHost;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityEmbeddedConnection;
 
 import com.android.internal.view.SurfaceCallbackHelper;
 
@@ -203,8 +206,12 @@
     private SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
     private int mParentSurfaceGenerationId;
 
-    // The token of embedded windowless view hierarchy.
-    private IBinder mEmbeddedViewHierarchy;
+    private RemoteAccessibilityEmbeddedConnection mRemoteAccessibilityEmbeddedConnection;
+
+    private final Matrix mScreenMatrixForEmbeddedHierarchy = new Matrix();
+    private final Matrix mTmpMatrix = new Matrix();
+    private final float[] mMatrixValues = new float[9];
+
     SurfaceControlViewHost.SurfacePackage mSurfacePackage;
 
     public SurfaceView(Context context) {
@@ -923,6 +930,7 @@
                     }
 
                     mTmpTransaction.apply();
+                    updateScreenMatrixForEmbeddedHierarchy();
 
                     if (sizeChanged || creating) {
                         redrawNeeded = true;
@@ -1510,6 +1518,7 @@
     @Override
     public void surfaceDestroyed() {
         setWindowStopped(true);
+        setRemoteAccessibilityEmbeddedConnection(null, null);
     }
 
     /**
@@ -1568,31 +1577,133 @@
 
     private void reparentSurfacePackage(SurfaceControl.Transaction t,
             SurfaceControlViewHost.SurfacePackage p) {
-        // TODO: Link accessibility IDs here.
+        initEmbeddedHierarchyForAccessibility(p);
         final SurfaceControl sc = p.getSurfaceControl();
         t.reparent(sc, mSurfaceControl).show(sc);
     }
 
-    /**
-     * Add the token of embedded view hierarchy. Set {@code null} to clear the embedded view
-     * hierarchy.
-     *
-     * @param token IBinder token.
-     * @hide
-     */
-    public void setEmbeddedViewHierarchy(IBinder token) {
-        mEmbeddedViewHierarchy = token;
-    }
-
     /** @hide */
     @Override
     public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) {
         super.onInitializeAccessibilityNodeInfoInternal(info);
-        if (mEmbeddedViewHierarchy == null) {
+        final RemoteAccessibilityEmbeddedConnection wrapper =
+                getRemoteAccessibilityEmbeddedConnection();
+        if (wrapper == null) {
             return;
         }
         // Add a leashed child when this SurfaceView embeds another view hierarchy. Getting this
         // leashed child would return the root node in the embedded hierarchy
-        info.addChild(mEmbeddedViewHierarchy);
+        info.addChild(wrapper.getLeashToken());
+    }
+
+    private void initEmbeddedHierarchyForAccessibility(SurfaceControlViewHost.SurfacePackage p) {
+        final IAccessibilityEmbeddedConnection connection = p.getAccessibilityEmbeddedConnection();
+        final RemoteAccessibilityEmbeddedConnection wrapper =
+                getRemoteAccessibilityEmbeddedConnection();
+
+        // Do nothing if package is embedding the same view hierarchy.
+        if (wrapper != null && wrapper.getConnection().equals(connection)) {
+            return;
+        }
+
+        // If this SurfaceView embeds a different view hierarchy, unlink the previous one first.
+        setRemoteAccessibilityEmbeddedConnection(null, null);
+
+        try {
+            final IBinder leashToken = connection.associateEmbeddedHierarchy(
+                    getViewRootImpl().mLeashToken, getAccessibilityViewId());
+            setRemoteAccessibilityEmbeddedConnection(connection, leashToken);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while associateEmbeddedHierarchy " + e);
+        }
+        updateScreenMatrixForEmbeddedHierarchy();
+    }
+
+    private void setRemoteAccessibilityEmbeddedConnection(
+            IAccessibilityEmbeddedConnection connection, IBinder leashToken) {
+        try {
+            if (mRemoteAccessibilityEmbeddedConnection != null) {
+                mRemoteAccessibilityEmbeddedConnection.getConnection()
+                        .disassociateEmbeddedHierarchy();
+                mRemoteAccessibilityEmbeddedConnection.unlinkToDeath();
+                mRemoteAccessibilityEmbeddedConnection = null;
+            }
+            if (connection != null && leashToken != null) {
+                mRemoteAccessibilityEmbeddedConnection =
+                        new RemoteAccessibilityEmbeddedConnection(connection, leashToken);
+                mRemoteAccessibilityEmbeddedConnection.linkToDeath();
+            }
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while setRemoteEmbeddedConnection " + e);
+        }
+    }
+
+    private RemoteAccessibilityEmbeddedConnection getRemoteAccessibilityEmbeddedConnection() {
+        return mRemoteAccessibilityEmbeddedConnection;
+    }
+
+    private void updateScreenMatrixForEmbeddedHierarchy() {
+        mTmpMatrix.reset();
+        mTmpMatrix.setTranslate(mScreenRect.left, mScreenRect.top);
+        mTmpMatrix.postScale(mScreenRect.width() / (float) mSurfaceWidth,
+                mScreenRect.height() / (float) mSurfaceHeight);
+
+        // If the screen matrix is identity or doesn't change, do nothing.
+        if (mTmpMatrix.isIdentity() || mTmpMatrix.equals(mScreenMatrixForEmbeddedHierarchy)) {
+            return;
+        }
+
+        try {
+            final RemoteAccessibilityEmbeddedConnection wrapper =
+                    getRemoteAccessibilityEmbeddedConnection();
+            if (wrapper == null) {
+                return;
+            }
+            mTmpMatrix.getValues(mMatrixValues);
+            wrapper.getConnection().setScreenMatrix(mMatrixValues);
+            mScreenMatrixForEmbeddedHierarchy.set(mTmpMatrix);
+        } catch (RemoteException e) {
+            Log.d(TAG, "Error while setScreenMatrix " + e);
+        }
+    }
+
+    /**
+     * Wrapper of accessibility embedded connection for embedded view hierarchy.
+     */
+    private final class RemoteAccessibilityEmbeddedConnection implements IBinder.DeathRecipient {
+        private final IAccessibilityEmbeddedConnection mConnection;
+        private final IBinder mLeashToken;
+
+        RemoteAccessibilityEmbeddedConnection(IAccessibilityEmbeddedConnection connection,
+                IBinder leashToken) {
+            mConnection = connection;
+            mLeashToken = leashToken;
+        }
+
+        IAccessibilityEmbeddedConnection getConnection() {
+            return mConnection;
+        }
+
+        IBinder getLeashToken() {
+            return mLeashToken;
+        }
+
+        void linkToDeath() throws RemoteException {
+            mConnection.asBinder().linkToDeath(this, 0);
+        }
+
+        void unlinkToDeath() {
+            mConnection.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            unlinkToDeath();
+            runOnUiThread(() -> {
+                if (mRemoteAccessibilityEmbeddedConnection == this) {
+                    mRemoteAccessibilityEmbeddedConnection = null;
+                }
+            });
+        }
     }
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 857bc50..b971a20 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -655,7 +655,7 @@
 
     private final GestureExclusionTracker mGestureExclusionTracker = new GestureExclusionTracker();
 
-    private IAccessibilityEmbeddedConnection mEmbeddedConnection;
+    private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
 
     static final class SystemUiVisibilityInfo {
         int seq;
@@ -9370,11 +9370,12 @@
      * Gets an accessibility embedded connection interface for this ViewRootImpl.
      * @hide
      */
-    public IAccessibilityEmbeddedConnection getEmbeddedConnection() {
-        if (mEmbeddedConnection == null) {
-            mEmbeddedConnection = new AccessibilityEmbeddedConnection(ViewRootImpl.this);
+    public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() {
+        if (mAccessibilityEmbeddedConnection == null) {
+            mAccessibilityEmbeddedConnection = new AccessibilityEmbeddedConnection(
+                    ViewRootImpl.this);
         }
-        return mEmbeddedConnection;
+        return mAccessibilityEmbeddedConnection;
     }
 
     private class SendWindowContentChangedAccessibilityEvent implements Runnable {
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/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/Editor.java b/core/java/android/widget/Editor.java
index 46b8b77..12f245e 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -5271,6 +5271,8 @@
                 if (opacity < 10 || opacity > 100) {
                     opacity = 50;
                 }
+                // Converts the opacity value from range {0..100} to {0..255}.
+                opacity = opacity * 255 / 100;
             }
             mDeltaHeight = deltaHeight;
             mDrawableOpacity = opacity;
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/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/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..d1a7d24 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: [
@@ -351,6 +349,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",
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..d8f30e9 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/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/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/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60b3a19..9dc40c8 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5344,6 +5344,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/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..7fc041c 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
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/symbols.xml b/core/res/res/values/symbols.xml
index 5aefe11..500bfe9 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" />
@@ -3902,4 +3903,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/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%
rename from core/java/com/android/internal/view/animation/NativeInterpolatorFactory.java
rename 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/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 91f9447..1df3336 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -204,8 +204,7 @@
 
 void CanvasContext::allocateBuffers() {
     if (mNativeSurface) {
-        ANativeWindow* anw = mNativeSurface->getNativeWindow();
-        ANativeWindow_allocateBuffers(anw);
+        ANativeWindow_tryAllocateBuffers(mNativeSurface->getNativeWindow());
     }
 }
 
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/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/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
similarity index 99%
rename from media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
rename to media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
index 3ffb951..615dc48 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java
@@ -70,8 +70,8 @@
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
-public class MediaRouterManagerTest {
-    private static final String TAG = "MediaRouterManagerTest";
+public class MediaRouter2ManagerTest {
+    private static final String TAG = "MediaRouter2ManagerTest";
     private static final int TIMEOUT_MS = 5000;
 
     private Context mContext;
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/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 6212493..3f42ad4 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -27,6 +27,7 @@
         "SettingsLibRadioButtonPreference",
         "WifiTrackerLib",
         "SettingsLibDisplayDensityUtils",
+        "SettingsLibSchedulesProvider",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
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/SchedulesProvider/Android.bp b/packages/SettingsLib/SchedulesProvider/Android.bp
new file mode 100644
index 0000000..ef59252
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/Android.bp
@@ -0,0 +1,12 @@
+android_library {
+    name: "SettingsLibSchedulesProvider",
+
+    srcs: ["src/**/*.java"],
+
+    static_libs: [
+          "androidx.annotation_annotation",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml b/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml
new file mode 100644
index 0000000..1b0e4bf
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?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
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.schedulesprovider">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.java
new file mode 100644
index 0000000..7d2b8e2
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/ScheduleInfo.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.settingslib.schedulesprovider;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import androidx.annotation.NonNull;
+
+/**
+ * This is a schedule data item. It contains the schedule title text, the summary text which
+ * displays on the summary of the Settings preference and an {@link Intent}. Intent is able to
+ * launch the editing page of the schedule data when user clicks this item (preference).
+ */
+public class ScheduleInfo implements Parcelable {
+    private static final String TAG = "ScheduleInfo";
+    private final String mTitle;
+    private final String mSummary;
+    private final Intent mIntent;
+
+    public ScheduleInfo(Builder builder) {
+        mTitle = builder.mTitle;
+        mSummary = builder.mSummary;
+        mIntent = builder.mIntent;
+    }
+
+    protected ScheduleInfo(Parcel in) {
+        mTitle = in.readString();
+        mSummary = in.readString();
+        mIntent = in.readParcelable(Intent.class.getClassLoader());
+    }
+
+    /**
+     * Returns the title text.
+     *
+     * @return The title.
+     */
+    public String getTitle() {
+        return mTitle;
+    }
+
+    /**
+     * Returns the summary text.
+     *
+     * @return The summary.
+     */
+    public String getSummary() {
+        return mSummary;
+    }
+
+    /**
+     * Returns an {@link Intent}.
+     */
+    public Intent getIntent() {
+        return mIntent;
+    }
+
+    /**
+     * Verify the member variables are valid.
+     *
+     * @return {@code true} if all member variables are valid.
+     */
+    public boolean isValid() {
+        return !TextUtils.isEmpty(mTitle) && !TextUtils.isEmpty(mSummary) && (mIntent != null);
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mTitle);
+        dest.writeString(mSummary);
+        dest.writeParcelable(mIntent, flags);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public static final Creator<ScheduleInfo> CREATOR = new Creator<ScheduleInfo>() {
+        @Override
+        public ScheduleInfo createFromParcel(Parcel in) {
+            return new ScheduleInfo(in);
+        }
+
+        @Override
+        public ScheduleInfo[] newArray(int size) {
+            return new ScheduleInfo[size];
+        }
+    };
+
+    @NonNull
+    @Override
+    public String toString() {
+        return "title : " + mTitle + " summary : " + mSummary + (mIntent == null
+                ? " and intent is null." : ".");
+    }
+
+    /**
+     * A simple builder for {@link ScheduleInfo}.
+     */
+    public static class Builder {
+        @NonNull
+        private String mTitle;
+        @NonNull
+        private String mSummary;
+        @NonNull
+        private Intent mIntent;
+
+        /**
+         * Sets the title.
+         *
+         * @param title The title of the preference item.
+         * @return This instance.
+         */
+        public Builder setTitle(@NonNull String title) {
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * Sets the summary.
+         *
+         * @param summary The summary of the preference summary.
+         * @return This instance.
+         */
+        public Builder setSummary(@NonNull String summary) {
+            mSummary = summary;
+            return this;
+        }
+
+        /**
+         * Sets the {@link Intent}.
+         *
+         * @param intent The action when user clicks the preference.
+         * @return This instance.
+         */
+        public Builder setIntent(@NonNull Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        /**
+         * Creates an instance of {@link ScheduleInfo}.
+         *
+         * @return The instance of {@link ScheduleInfo}.
+         */
+        public ScheduleInfo build() {
+            return new ScheduleInfo(this);
+        }
+    }
+}
diff --git a/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
new file mode 100644
index 0000000..a423e47
--- /dev/null
+++ b/packages/SettingsLib/SchedulesProvider/src/com/android/settingslib/schedulesprovider/SchedulesProvider.java
@@ -0,0 +1,133 @@
+/*
+ * 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.schedulesprovider;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * This provider is a bridge for client apps to provide the schedule data.
+ * Client provider needs to implement their {@link #getScheduleInfoList()} and returns a list of
+ * {@link ScheduleInfo}.
+ */
+public abstract class SchedulesProvider extends ContentProvider {
+    public static final String METHOD_GENERATE_SCHEDULE_INFO_LIST = "generateScheduleInfoList";
+    public static final String BUNDLE_SCHEDULE_INFO_LIST = "scheduleInfoList";
+    private static final String TAG = "SchedulesProvider";
+
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public final Cursor query(
+            Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        throw new UnsupportedOperationException("Query operation is not supported currently.");
+    }
+
+    @Override
+    public final String getType(Uri uri) {
+        throw new UnsupportedOperationException("GetType operation is not supported currently.");
+    }
+
+    @Override
+    public final Uri insert(Uri uri, ContentValues values) {
+        throw new UnsupportedOperationException("Insert operation is not supported currently.");
+    }
+
+    @Override
+    public final int delete(Uri uri, String selection, String[] selectionArgs) {
+        throw new UnsupportedOperationException("Delete operation not supported currently.");
+    }
+
+    @Override
+    public final int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        throw new UnsupportedOperationException("Update operation is not supported currently.");
+    }
+
+    /**
+     * Return the list of the schedule information.
+     *
+     * @return a list of the {@link ScheduleInfo}.
+     */
+    public abstract ArrayList<ScheduleInfo> getScheduleInfoList();
+
+    /**
+     * Returns a bundle which contains a list of {@link ScheduleInfo} and data types:
+     * scheduleInfoList : ArrayList<ScheduleInfo>
+     */
+    @Override
+    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
+        final Bundle bundle = new Bundle();
+        if (METHOD_GENERATE_SCHEDULE_INFO_LIST.equals(method)) {
+            final ArrayList<ScheduleInfo> scheduleInfoList = filterInvalidData(
+                    getScheduleInfoList());
+            if (scheduleInfoList != null) {
+                bundle.putParcelableArrayList(BUNDLE_SCHEDULE_INFO_LIST, scheduleInfoList);
+            }
+        }
+        return bundle;
+    }
+
+    /**
+     * To filter the invalid schedule info.
+     *
+     * @param scheduleInfoList The list of the {@link ScheduleInfo}.
+     * @return The valid list of the {@link ScheduleInfo}.
+     */
+    private ArrayList<ScheduleInfo> filterInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
+        if (scheduleInfoList == null) {
+            Log.d(TAG, "package : " + getContext().getPackageName() + " has no scheduling data.");
+            return null;
+        }
+        // Dump invalid data in debug mode.
+        if (SystemProperties.getInt("ro.debuggable", 0) == 1) {
+            new Thread(() -> {
+                dumpInvalidData(scheduleInfoList);
+            }).start();
+        }
+        final List<ScheduleInfo> filteredList = scheduleInfoList
+                .stream()
+                .filter(scheduleInfo -> scheduleInfo.isValid())
+                .collect(Collectors.toList());
+
+        return new ArrayList<>(filteredList);
+    }
+
+    private void dumpInvalidData(ArrayList<ScheduleInfo> scheduleInfoList) {
+        Log.d(TAG, "package : " + getContext().getPackageName()
+                + " provided some scheduling data are invalid.");
+        scheduleInfoList
+                .stream()
+                .filter(scheduleInfo -> !scheduleInfo.isValid())
+                .forEach(scheduleInfo -> Log.d(TAG, scheduleInfo.toString()));
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e910967..8e4a982 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -74,13 +74,6 @@
         refreshDevices();
     }
 
-    @VisibleForTesting
-    String getControlCategoryByPackageName(String packageName) {
-        //TODO(b/117129183): Use package name to get ControlCategory.
-        //Since api not ready, return fixed ControlCategory for prototype.
-        return "com.google.android.gms.cast.CATEGORY_CAST";
-    }
-
     @Override
     public void stopScan() {
         mRouterManager.unregisterCallback(mMediaRouterCallback);
@@ -192,5 +185,10 @@
         public void onRoutesChanged(List<MediaRoute2Info> routes) {
             refreshDevices();
         }
+
+        @Override
+        public void onRoutesRemoved(List<MediaRoute2Info> routes) {
+            refreshDevices();
+        }
     }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 984ab11..f8db70a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -16,6 +16,8 @@
 package com.android.settingslib.media;
 
 import android.app.Notification;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 import android.text.TextUtils;
@@ -26,13 +28,13 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.bluetooth.BluetoothCallback;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
@@ -66,9 +68,16 @@
     @VisibleForTesting
     List<MediaDevice> mMediaDevices = new ArrayList<>();
     @VisibleForTesting
+    List<MediaDevice> mDisconnectedMediaDevices = new ArrayList<>();
+    @VisibleForTesting
     MediaDevice mPhoneDevice;
     @VisibleForTesting
     MediaDevice mCurrentConnectedDevice;
+    @VisibleForTesting
+    DeviceAttributeChangeCallback mDeviceAttributeChangeCallback =
+            new DeviceAttributeChangeCallback();
+    @VisibleForTesting
+    BluetoothAdapter mBluetoothAdapter;
 
     /**
      * Register to start receiving callbacks for MediaDevice events.
@@ -89,6 +98,7 @@
         mPackageName = packageName;
         mLocalBluetoothManager =
                 LocalBluetoothManager.getInstance(context, /* onInitCallback= */ null);
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         if (mLocalBluetoothManager == null) {
             Log.e(TAG, "Bluetooth is not supported on this device");
             return;
@@ -164,7 +174,7 @@
     }
 
     void dispatchDeviceListUpdate() {
-        Collections.sort(mMediaDevices, COMPARATOR);
+        //TODO(b/149260820): Use new rule to rank device once device type api is ready.
         for (DeviceCallback callback : getCallbacks()) {
             callback.onDeviceListUpdate(new ArrayList<>(mMediaDevices));
         }
@@ -278,12 +288,44 @@
         public void onDeviceListAdded(List<MediaDevice> devices) {
             mMediaDevices.clear();
             mMediaDevices.addAll(devices);
+            mMediaDevices.addAll(buildDisconnectedBluetoothDevice());
+
             final MediaDevice infoMediaDevice = mInfoMediaManager.getCurrentConnectedDevice();
             mCurrentConnectedDevice = infoMediaDevice != null
                     ? infoMediaDevice : updateCurrentConnectedDevice();
             dispatchDeviceListUpdate();
         }
 
+        private List<MediaDevice> buildDisconnectedBluetoothDevice() {
+            for (MediaDevice device : mDisconnectedMediaDevices) {
+                ((BluetoothMediaDevice) device).getCachedDevice()
+                        .unregisterCallback(mDeviceAttributeChangeCallback);
+            }
+            mDisconnectedMediaDevices.clear();
+            final List<BluetoothDevice> bluetoothDevices =
+                    mBluetoothAdapter.getMostRecentlyConnectedDevices();
+            final CachedBluetoothDeviceManager cachedDeviceManager =
+                    mLocalBluetoothManager.getCachedDeviceManager();
+
+            for (BluetoothDevice device : bluetoothDevices) {
+                final CachedBluetoothDevice cachedDevice =
+                        cachedDeviceManager.findDevice(device);
+                if (cachedDevice != null) {
+                    if (cachedDevice.getBondState() == BluetoothDevice.BOND_BONDED
+                            && !cachedDevice.isConnected()) {
+                        final MediaDevice mediaDevice = new BluetoothMediaDevice(mContext,
+                                cachedDevice,
+                                null, null, mPackageName);
+                        if (!mMediaDevices.contains(mediaDevice)) {
+                            cachedDevice.registerCallback(mDeviceAttributeChangeCallback);
+                            mDisconnectedMediaDevices.add(mediaDevice);
+                        }
+                    }
+                }
+            }
+            return new ArrayList<>(mDisconnectedMediaDevices);
+        }
+
         @Override
         public void onDeviceRemoved(MediaDevice device) {
             if (mMediaDevices.contains(device)) {
@@ -345,4 +387,17 @@
          */
         default void onDeviceAttributesChanged() {};
     }
+
+    /**
+     * This callback is for update {@link BluetoothMediaDevice} summary when
+     * {@link CachedBluetoothDevice} connection state is changed.
+     */
+    @VisibleForTesting
+    class DeviceAttributeChangeCallback implements CachedBluetoothDevice.Callback {
+
+        @Override
+        public void onDeviceAttributesChanged() {
+            dispatchDeviceAttributesChanged();
+        }
+    }
 }
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 05f5fa0..8b815bf 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
@@ -203,4 +203,46 @@
 
         assertThat(mInfoMediaManager.connectDeviceWithoutPackageName(device)).isFalse();
     }
+
+    @Test
+    public void onRoutesRemoved_getAvailableRoutes_shouldAddMediaDevice() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        when(info.getId()).thenReturn(TEST_ID);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        final List<MediaRoute2Info> routes = new ArrayList<>();
+        routes.add(info);
+        when(mRouterManager.getAvailableRoutes(TEST_PACKAGE_NAME)).thenReturn(routes);
+
+        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+        assertThat(mediaDevice).isNull();
+
+        mInfoMediaManager.mMediaRouterCallback.onRoutesRemoved(routes);
+
+        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+        assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice);
+        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+    }
+
+    @Test
+    public void onRoutesRemoved_buildAllRoutes_shouldAddMediaDevice() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        when(info.getId()).thenReturn(TEST_ID);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        final List<MediaRoute2Info> routes = new ArrayList<>();
+        routes.add(info);
+        when(mRouterManager.getAllRoutes()).thenReturn(routes);
+
+        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+        assertThat(mediaDevice).isNull();
+
+        mInfoMediaManager.mPackageName = "";
+        mInfoMediaManager.mMediaRouterCallback.onRoutesChanged(routes);
+
+        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
index 3d67ba0..3611dfe 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java
@@ -25,13 +25,17 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.content.Context;
 
 import com.android.settingslib.bluetooth.A2dpProfile;
 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
+import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
 import com.android.settingslib.bluetooth.HearingAidProfile;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
+import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -40,11 +44,14 @@
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
 import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+import org.robolectric.shadow.api.Shadow;
 
 import java.util.ArrayList;
 import java.util.List;
 
 @RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowBluetoothAdapter.class})
 public class LocalMediaManagerTest {
 
     private static final String TEST_DEVICE_ID_1 = "device_id_1";
@@ -69,11 +76,15 @@
 
     private Context mContext;
     private LocalMediaManager mLocalMediaManager;
+    private ShadowBluetoothAdapter mShadowBluetoothAdapter;
 
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
         mContext = RuntimeEnvironment.application;
+        final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
+        mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
+        mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
 
         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalProfileManager);
         when(mLocalProfileManager.getA2dpProfile()).thenReturn(mA2dpProfile);
@@ -81,6 +92,7 @@
 
         mLocalMediaManager = new LocalMediaManager(mContext, mLocalBluetoothManager,
                 mInfoMediaManager, "com.test.packagename");
+        mLocalMediaManager.mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
     }
 
     @Test
@@ -419,4 +431,50 @@
                 MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
         assertThat(activeDevices).containsExactly(device3);
     }
+
+    @Test
+    public void onDeviceAttributesChanged_shouldBeCalled() {
+        mLocalMediaManager.registerCallback(mCallback);
+
+        mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged();
+
+        verify(mCallback).onDeviceAttributesChanged();
+    }
+
+    @Test
+    public void onDeviceListAdded_haveDisconnectedDevice_addDisconnectedDevice() {
+        final List<MediaDevice> devices = new ArrayList<>();
+        final MediaDevice device1 = mock(MediaDevice.class);
+        final MediaDevice device2 = mock(MediaDevice.class);
+        final MediaDevice device3 = mock(MediaDevice.class);
+        mLocalMediaManager.mPhoneDevice = mock(PhoneMediaDevice.class);
+        devices.add(device1);
+        devices.add(device2);
+        mLocalMediaManager.mMediaDevices.add(device3);
+        mLocalMediaManager.mMediaDevices.add(mLocalMediaManager.mPhoneDevice);
+
+        final List<BluetoothDevice> bluetoothDevices = new ArrayList<>();
+        final BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class);
+        final CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
+        final CachedBluetoothDeviceManager cachedManager = mock(CachedBluetoothDeviceManager.class);
+        bluetoothDevices.add(bluetoothDevice);
+        mShadowBluetoothAdapter.setMostRecentlyConnectedDevices(bluetoothDevices);
+
+        when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(cachedManager);
+        when(cachedManager.findDevice(bluetoothDevice)).thenReturn(cachedDevice);
+        when(cachedDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(cachedDevice.isConnected()).thenReturn(false);
+
+        when(device1.getId()).thenReturn(TEST_DEVICE_ID_1);
+        when(device2.getId()).thenReturn(TEST_DEVICE_ID_2);
+        when(device3.getId()).thenReturn(TEST_DEVICE_ID_3);
+        when(mLocalMediaManager.mPhoneDevice.getId()).thenReturn("test_phone_id");
+
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(2);
+        mLocalMediaManager.registerCallback(mCallback);
+        mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices);
+
+        assertThat(mLocalMediaManager.mMediaDevices).hasSize(3);
+        verify(mCallback).onDeviceListUpdate(any());
+    }
 }
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
index 906dba4..015ce149 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowBluetoothAdapter.java
@@ -17,6 +17,7 @@
 package com.android.settingslib.testutils.shadow;
 
 import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
 
@@ -29,6 +30,7 @@
 public class ShadowBluetoothAdapter extends org.robolectric.shadows.ShadowBluetoothAdapter {
 
     private List<Integer> mSupportedProfiles;
+    private List<BluetoothDevice> mMostRecentlyConnectedDevices;
     private BluetoothProfile.ServiceListener mServiceListener;
 
     @Implementation
@@ -50,4 +52,13 @@
     public void setSupportedProfiles(List<Integer> supportedProfiles) {
         mSupportedProfiles = supportedProfiles;
     }
+
+    @Implementation
+    protected List<BluetoothDevice> getMostRecentlyConnectedDevices() {
+        return mMostRecentlyConnectedDevices;
+    }
+
+    public void setMostRecentlyConnectedDevices(List<BluetoothDevice> list) {
+        mMostRecentlyConnectedDevices = list;
+    }
 }
diff --git a/packages/SettingsProvider/res/values/strings.xml b/packages/SettingsProvider/res/values/strings.xml
index 3787727..76bea31 100644
--- a/packages/SettingsProvider/res/values/strings.xml
+++ b/packages/SettingsProvider/res/values/strings.xml
@@ -23,15 +23,10 @@
     <!-- A notification is shown when the user's softap config has been changed due to underlying
      hardware restrictions. This is the notifications's title.
      [CHAR_LIMIT=NONE] -->
-    <string name="wifi_softap_config_change">Changes to your hotspot settings</string>
+    <string name="wifi_softap_config_change">Hotspot settings have changed</string>
 
     <!-- A notification is shown when the user's softap config has been changed due to underlying
          hardware restrictions. This is the notification's summary message.
          [CHAR_LIMIT=NONE] -->
-    <string name="wifi_softap_config_change_summary">Your hotspot band has changed.</string>
-
-    <!-- A notification is shown when the user's softap config has been changed due to underlying
-         hardware restrictions. This is the notification's full message.
-         [CHAR_LIMIT=NONE] -->
-    <string name="wifi_softap_config_change_detailed">This device doesn\u2019t support your preference for 5GHz only. Instead, this device will use the 5GHz band when available.</string>
+    <string name="wifi_softap_config_change_summary">Tap to see details</string>
 </resources>
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/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
index 1ee5f90..ca841a5 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WifiSoftApConfigChangedNotifier.java
@@ -57,7 +57,6 @@
         Resources resources = context.getResources();
         CharSequence title = resources.getText(R.string.wifi_softap_config_change);
         CharSequence contentSummary = resources.getText(R.string.wifi_softap_config_change_summary);
-        CharSequence content = resources.getText(R.string.wifi_softap_config_change_detailed);
         int color = resources.getColor(
                 android.R.color.system_notification_accent_color, context.getTheme());
 
@@ -73,7 +72,6 @@
                 .setLocalOnly(true)
                 .setColor(color)
                 .setStyle(new Notification.BigTextStyle()
-                        .bigText(content)
                         .setBigContentTitle(title)
                         .setSummaryText(contentSummary))
                 .setAutoCancel(true)
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/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/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt
index 55c1b6a..aec20751 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
@@ -41,7 +42,8 @@
 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,7 +52,6 @@
     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
 
@@ -58,7 +59,6 @@
         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) {
@@ -121,6 +121,7 @@
             template is ToggleTemplate -> ToggleBehavior()
             template is ToggleRangeTemplate -> ToggleRangeBehavior()
             template is TemperatureControlTemplate -> TemperatureControlBehavior()
+            template is ThumbnailTemplate -> StaticBehavior(uiExecutor, bgExecutor)
             else -> {
                 object : Behavior {
                     override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
@@ -141,9 +142,9 @@
         icon.setImageIcon(Icon.createWithResource(context, ri.iconResourceId))
         icon.setImageTintList(fg)
 
-        gd.setColor(bg)
+        clipLayer.getDrawable().setTintBlendMode(BlendMode.HUE)
+        clipLayer.getDrawable().setTintList(bg)
     }
-
     fun setEnabled(enabled: Boolean) {
         status.setEnabled(enabled)
         icon.setEnabled(enabled)
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/StaticBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/StaticBehavior.kt
new file mode 100644
index 0000000..340cdc0
--- /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
+
+import java.util.concurrent.Executor
+
+/**
+ * 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(
+    val uiExecutor: Executor,
+    val bgExecutor: Executor
+) : Behavior {
+    lateinit var control: Control
+
+    override fun apply(cvh: ControlViewHolder, 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) {
+            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())
+                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/UnknownBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
index 5a6e5b4..27f1b33 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/UnknownBehavior.kt
@@ -18,7 +18,7 @@
 
 class UnknownBehavior : Behavior {
     override fun apply(cvh: ControlViewHolder, cws: ControlWithState) {
-        cvh.status.setText("Loading...")
+        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/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/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/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/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/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/util/FloatingContentCoordinator.kt b/packages/SystemUI/src/com/android/systemui/util/FloatingContentCoordinator.kt
index 70bcc21..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/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 7d47f6bd..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/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/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index 75ec4b0..6c8aaf4 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -16,8 +16,9 @@
 
 package com.android.server.accessibility;
 
-import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE;
 import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER;
+import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP;
 import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS;
@@ -39,6 +40,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.graphics.GraphicBuffer;
+import android.graphics.ParcelableColorSpace;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -1021,13 +1023,15 @@
                 final GraphicBuffer graphicBuffer = screenshotBuffer.getGraphicBuffer();
                 final HardwareBuffer hardwareBuffer =
                         HardwareBuffer.createFromGraphicBuffer(graphicBuffer);
-                final int colorSpaceId = screenshotBuffer.getColorSpace().getId();
+                final ParcelableColorSpace colorSpace =
+                        new ParcelableColorSpace(screenshotBuffer.getColorSpace());
 
                 // Send back the result.
                 final Bundle payload = new Bundle();
                 payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_HARDWAREBUFFER,
                         hardwareBuffer);
-                payload.putInt(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE_ID, colorSpaceId);
+                payload.putParcelable(KEY_ACCESSIBILITY_SCREENSHOT_COLORSPACE, colorSpace);
+                payload.putLong(KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP, SystemClock.uptimeMillis());
                 callback.sendResult(payload);
             }, null).recycleOnUse());
         } finally {
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..b3ef134 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2677,7 +2677,7 @@
                             synchronized (mLock) {
                                 requestHideFillUi(mCurrentViewId);
                             }
-                        });
+                        }, mService.getRemoteInlineSuggestionRenderServiceLocked());
         try {
             imeResponse.getCallback().onInlineSuggestionsResponse(inlineSuggestionsResponse);
         } catch (RemoteException e) {
@@ -2981,7 +2981,7 @@
                     synchronized (mLock) {
                         cancelAugmentedAutofillLocked();
                     }
-                });
+                }, mService.getRemoteInlineSuggestionRenderServiceLocked());
 
         if (mAugmentedAutofillDestroyer == null) {
             mAugmentedAutofillDestroyer = () -> remoteService.onDestroyAutofillWindowsRequest();
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..54d0433 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineSuggestionFactory.java
@@ -22,13 +22,14 @@
 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.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.AutofillValue;
 import android.view.inline.InlinePresentationSpec;
@@ -40,11 +41,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,24 +65,6 @@
     }
 
     /**
-     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
-     * augmented autofill service.
-     */
-    public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
-            @NonNull InlineSuggestionsRequest request,
-            @NonNull Dataset[] datasets,
-            @NonNull AutofillId autofillId,
-            @NonNull Context context,
-            @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
-            @NonNull Runnable onErrorCallback) {
-        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.
      */
@@ -90,11 +76,33 @@
             @NonNull AutofillId autofillId,
             @NonNull Context context,
             @NonNull AutoFillUI.AutoFillUiCallback client,
-            @NonNull Runnable onErrorCallback) {
+            @NonNull Runnable onErrorCallback,
+            @Nullable RemoteInlineSuggestionRenderService remoteRenderService) {
         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)));
+                (dataset, datasetIndex) -> client.fill(requestId, datasetIndex, dataset),
+                remoteRenderService);
+    }
+
+    /**
+     * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by augmented
+     * autofill service.
+     */
+    public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
+            @NonNull InlineSuggestionsRequest request,
+            @NonNull Dataset[] datasets,
+            @NonNull AutofillId autofillId,
+            @NonNull Context context,
+            @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback,
+            @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, fieldIndex) -> inlineSuggestionUiCallback.autofill(dataset),
+                remoteRenderService);
     }
 
     private static InlineSuggestionsResponse createInlineSuggestionsResponseInternal(
@@ -102,10 +110,9 @@
             @NonNull 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];
             final int fieldIndex = dataset.getFieldIds().indexOf(autofillId);
@@ -124,14 +131,15 @@
             }
             InlineSuggestion inlineSuggestion = createInlineSuggestion(isAugmented, dataset,
                     fieldIndex, mergedInlinePresentation(request, i, inlinePresentation),
-                    inlineSuggestionUi, onClickListenerFactory);
+                    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,40 +181,40 @@
     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 fieldIndex,
+            @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, fieldIndex), onErrorCallback,
+                        remoteRenderService, hostInputToken));
+
         return inlineSuggestion;
     }
 
@@ -231,27 +239,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() {
     }
 }
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/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/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 3ffe1be..0edc160 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -56,6 +56,7 @@
 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 com.android.server.appop.AppOpsService.ModeCallback.ALL_OPS;
 
@@ -3365,7 +3366,8 @@
             return false;
         }
 
-        return permInfo.getProtection() == PROTECTION_DANGEROUS;
+        return permInfo.getProtection() == PROTECTION_DANGEROUS
+                || (permInfo.getProtectionFlags() & PROTECTION_FLAG_APPOP) != 0;
     }
 
     private void verifyIncomingUid(int uid) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 67d7530..f41eeeb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1001,11 +1001,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 ?
@@ -3425,14 +3428,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 +3464,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 +3489,10 @@
         public IBinder getBinder() {
             return mCb;
         }
+
+        public int getUid() {
+            return mUid;
+        }
     }
 
     /** @see AudioManager#setMode(int) */
@@ -3518,7 +3541,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 +3554,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 +3595,7 @@
                 }
             } else {
                 if (hdlr == null) {
-                    hdlr = new SetModeDeathHandler(cb, pid);
+                    hdlr = new SetModeDeathHandler(cb, pid, uid);
                 }
                 // Register for client death notification
                 try {
@@ -3587,7 +3613,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 +6660,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
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/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 18383c4..ae11c70 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -257,12 +257,12 @@
         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());
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index d8cab82..d0d0f5a 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3979,6 +3979,29 @@
         }
 
         /**
+         * Allows the notification assistant to un-snooze a single notification.
+         *
+         * @param token The binder for the listener, to check that the caller is allowed
+         */
+        @Override
+        public void unsnoozeNotificationFromSystemListener(INotificationListener token,
+                String key) {
+            long identity = Binder.clearCallingIdentity();
+            try {
+                synchronized (mNotificationLock) {
+                    final ManagedServiceInfo info =
+                            mListeners.checkServiceTokenLocked(token);
+                    if (!info.isSystem) {
+                        throw new SecurityException("Not allowed to unsnooze before deadline");
+                    }
+                    unsnoozeNotificationInt(key, info);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+
+        /**
          * Allow an INotificationListener to simulate clearing (dismissing) a single notification.
          *
          * {@see com.android.server.StatusBarManagerService.NotificationCallbacks#onNotificationClear}
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 661297a..bae1dd3 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -16,6 +16,7 @@
 package com.android.server.notification;
 
 import android.annotation.NonNull;
+import android.annotation.UserIdInt;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -42,7 +43,9 @@
 
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.sql.Array;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -77,26 +80,26 @@
     private static final String REPOST_ACTION = SnoozeHelper.class.getSimpleName() + ".EVALUATE";
     private static final int REQUEST_CODE_REPOST = 1;
     private static final String REPOST_SCHEME = "repost";
-    private static final String EXTRA_KEY = "key";
+    static final String EXTRA_KEY = "key";
     private static final String EXTRA_USER_ID = "userId";
 
     private final Context mContext;
     private AlarmManager mAm;
     private final ManagedServices.UserProfiles mUserProfiles;
 
-    // User id : package name : notification key : record.
-    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, NotificationRecord>>>
+    // User id | package name : notification key : record.
+    private ArrayMap<String, ArrayMap<String, NotificationRecord>>
             mSnoozedNotifications = new ArrayMap<>();
-    // User id : package name : notification key : time-milliseconds .
+    // User id | package name : notification key : time-milliseconds .
     // This member stores persisted snoozed notification trigger times. it persists through reboots
     // It should have the notifications that haven't expired or re-posted yet
-    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, Long>>>
+    private final ArrayMap<String, ArrayMap<String, Long>>
             mPersistedSnoozedNotifications = new ArrayMap<>();
-    // User id : package name : notification key : creation ID .
+    // User id | package name : notification key : creation ID .
     // This member stores persisted snoozed notification trigger context for the assistant
     // it persists through reboots.
     // It should have the notifications that haven't expired or re-posted yet
-    private ArrayMap<Integer, ArrayMap<String, ArrayMap<String, String>>>
+    private final ArrayMap<String, ArrayMap<String, String>>
             mPersistedSnoozedNotificationsWithContext = new ArrayMap<>();
     // notification key : package.
     private ArrayMap<String, String> mPackages = new ArrayMap<>();
@@ -115,6 +118,10 @@
         mUserProfiles = userProfiles;
     }
 
+    private String getPkgKey(@UserIdInt int userId, String pkg) {
+        return userId + "|" + pkg;
+    }
+
     void cleanupPersistedContext(String key){
         int userId = mUsers.get(key);
         String pkg = mPackages.get(key);
@@ -144,15 +151,13 @@
     }
 
     protected boolean isSnoozed(int userId, String pkg, String key) {
-        return mSnoozedNotifications.containsKey(userId)
-                && mSnoozedNotifications.get(userId).containsKey(pkg)
-                && mSnoozedNotifications.get(userId).get(pkg).containsKey(key);
+        return mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))
+                && mSnoozedNotifications.get(getPkgKey(userId, pkg)).containsKey(key);
     }
 
     protected Collection<NotificationRecord> getSnoozed(int userId, String pkg) {
-        if (mSnoozedNotifications.containsKey(userId)
-                && mSnoozedNotifications.get(userId).containsKey(pkg)) {
-            return mSnoozedNotifications.get(userId).get(pkg).values();
+        if (mSnoozedNotifications.containsKey(getPkgKey(userId, pkg))) {
+            return mSnoozedNotifications.get(getPkgKey(userId, pkg)).values();
         }
         return Collections.EMPTY_LIST;
     }
@@ -161,14 +166,14 @@
     ArrayList<NotificationRecord> getNotifications(String pkg,
             String groupKey, Integer userId) {
         ArrayList<NotificationRecord> records =  new ArrayList<>();
-        if (mSnoozedNotifications.containsKey(userId)
-                && mSnoozedNotifications.get(userId).containsKey(pkg)) {
-            ArrayMap<String, NotificationRecord> packages =
-                    mSnoozedNotifications.get(userId).get(pkg);
-            for (int i = 0; i < packages.size(); i++) {
-                String currentGroupKey = packages.valueAt(i).getSbn().getGroup();
+        ArrayMap<String, NotificationRecord> allRecords =
+                mSnoozedNotifications.get(getPkgKey(userId, pkg));
+        if (allRecords != null) {
+            for (int i = 0; i < allRecords.size(); i++) {
+                NotificationRecord r = allRecords.valueAt(i);
+                String currentGroupKey = r.getSbn().getGroup();
                 if (currentGroupKey.equals(groupKey)) {
-                    records.add(packages.valueAt(i));
+                    records.add(r);
                 }
             }
         }
@@ -176,47 +181,30 @@
     }
 
     protected NotificationRecord getNotification(String key) {
-        List<NotificationRecord> snoozedForUser = new ArrayList<>();
-        IntArray userIds = mUserProfiles.getCurrentProfileIds();
-        if (userIds != null) {
-            final int userIdsSize = userIds.size();
-            for (int i = 0; i < userIdsSize; i++) {
-                final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
-                        mSnoozedNotifications.get(userIds.get(i));
-                if (snoozedPkgs != null) {
-                    final int snoozedPkgsSize = snoozedPkgs.size();
-                    for (int j = 0; j < snoozedPkgsSize; j++) {
-                        final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
-                        if (records != null) {
-                            return records.get(key);
-                        }
-                    }
-                }
-            }
+        if (!mUsers.containsKey(key) || !mPackages.containsKey(key)) {
+            Slog.w(TAG, "Snoozed data sets no longer agree for " + key);
+            return null;
         }
-        return null;
+        int userId = mUsers.get(key);
+        String pkg = mPackages.get(key);
+        ArrayMap<String, NotificationRecord> snoozed =
+                mSnoozedNotifications.get(getPkgKey(userId, pkg));
+        if (snoozed == null) {
+            return null;
+        }
+        return snoozed.get(key);
     }
 
     protected @NonNull List<NotificationRecord> getSnoozed() {
-        List<NotificationRecord> snoozedForUser = new ArrayList<>();
-        IntArray userIds = mUserProfiles.getCurrentProfileIds();
-        if (userIds != null) {
-            final int N = userIds.size();
-            for (int i = 0; i < N; i++) {
-                final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
-                        mSnoozedNotifications.get(userIds.get(i));
-                if (snoozedPkgs != null) {
-                    final int M = snoozedPkgs.size();
-                    for (int j = 0; j < M; j++) {
-                        final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
-                        if (records != null) {
-                            snoozedForUser.addAll(records.values());
-                        }
-                    }
-                }
-            }
+        // caller filters records based on the current user profiles and listener access, so just
+        // return everything
+        List<NotificationRecord> snoozed= new ArrayList<>();
+        for (String userPkgKey : mSnoozedNotifications.keySet()) {
+            ArrayMap<String, NotificationRecord> snoozedRecords =
+                    mSnoozedNotifications.get(userPkgKey);
+            snoozed.addAll(snoozedRecords.values());
         }
-        return snoozedForUser;
+        return snoozed;
     }
 
     /**
@@ -261,120 +249,88 @@
     }
 
     private <T> void storeRecord(String pkg, String key, Integer userId,
-            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, T object) {
+            ArrayMap<String, ArrayMap<String, T>> targets, T object) {
 
-        ArrayMap<String, ArrayMap<String, T>> records =
-                targets.get(userId);
-        if (records == null) {
-            records = new ArrayMap<>();
+        ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
+        if (keyToValue == null) {
+            keyToValue = new ArrayMap<>();
         }
-        ArrayMap<String, T> pkgRecords = records.get(pkg);
-        if (pkgRecords == null) {
-            pkgRecords = new ArrayMap<>();
-        }
-        pkgRecords.put(key, object);
-        records.put(pkg, pkgRecords);
-        targets.put(userId, records);
+        keyToValue.put(key, object);
+        targets.put(getPkgKey(userId, pkg), keyToValue);
 
     }
 
     private <T> T removeRecord(String pkg, String key, Integer userId,
-            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets) {
+            ArrayMap<String, ArrayMap<String, T>> targets) {
         T object = null;
-
-        ArrayMap<String, ArrayMap<String, T>> records =
-                targets.get(userId);
-        if (records == null) {
+        ArrayMap<String, T> keyToValue = targets.get(getPkgKey(userId, pkg));
+        if (keyToValue == null) {
             return null;
         }
-        ArrayMap<String, T> pkgRecords = records.get(pkg);
-        if (pkgRecords == null) {
-            return null;
-        }
-        object = pkgRecords.remove(key);
-        if (pkgRecords.size() == 0) {
-            records.remove(pkg);
-        }
-        if (records.size() == 0) {
-            targets.remove(userId);
+        object = keyToValue.remove(key);
+        if (keyToValue.size() == 0) {
+            targets.remove(getPkgKey(userId, pkg));
         }
         return object;
     }
 
     protected boolean cancel(int userId, String pkg, String tag, int id) {
-        if (mSnoozedNotifications.containsKey(userId)) {
-            ArrayMap<String, NotificationRecord> recordsForPkg =
-                    mSnoozedNotifications.get(userId).get(pkg);
-            if (recordsForPkg != null) {
-                final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
-                for (Map.Entry<String, NotificationRecord> record : records) {
-                    final StatusBarNotification sbn = record.getValue().getSbn();
-                    if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
-                        record.getValue().isCanceled = true;
-                        return true;
-                    }
+        ArrayMap<String, NotificationRecord> recordsForPkg =
+                mSnoozedNotifications.get(getPkgKey(userId, pkg));
+        if (recordsForPkg != null) {
+            final Set<Map.Entry<String, NotificationRecord>> records = recordsForPkg.entrySet();
+            for (Map.Entry<String, NotificationRecord> record : records) {
+                final StatusBarNotification sbn = record.getValue().getSbn();
+                if (Objects.equals(sbn.getTag(), tag) && sbn.getId() == id) {
+                    record.getValue().isCanceled = true;
+                    return true;
                 }
             }
         }
         return false;
     }
 
-    protected boolean cancel(int userId, boolean includeCurrentProfiles) {
-        int[] userIds = {userId};
-        if (includeCurrentProfiles) {
-            userIds = mUserProfiles.getCurrentProfileIds().toArray();
+    protected void cancel(int userId, boolean includeCurrentProfiles) {
+        if (mSnoozedNotifications.size() == 0) {
+            return;
         }
-        final int N = userIds.length;
-        for (int i = 0; i < N; i++) {
-            final ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
-                    mSnoozedNotifications.get(userIds[i]);
-            if (snoozedPkgs != null) {
-                final int M = snoozedPkgs.size();
-                for (int j = 0; j < M; j++) {
-                    final ArrayMap<String, NotificationRecord> records = snoozedPkgs.valueAt(j);
-                    if (records != null) {
-                        int P = records.size();
-                        for (int k = 0; k < P; k++) {
-                            records.valueAt(k).isCanceled = true;
-                        }
-                    }
+        IntArray userIds = new IntArray();
+        userIds.add(userId);
+        if (includeCurrentProfiles) {
+            userIds = mUserProfiles.getCurrentProfileIds();
+        }
+        for (ArrayMap<String, NotificationRecord> snoozedRecords : mSnoozedNotifications.values()) {
+            for (NotificationRecord r : snoozedRecords.values()) {
+                if (userIds.binarySearch(r.getUserId()) >= 0) {
+                    r.isCanceled = true;
                 }
-                return true;
             }
         }
-        return false;
     }
 
     protected boolean cancel(int userId, String pkg) {
-        if (mSnoozedNotifications.containsKey(userId)) {
-            if (mSnoozedNotifications.get(userId).containsKey(pkg)) {
-                ArrayMap<String, NotificationRecord> records =
-                        mSnoozedNotifications.get(userId).get(pkg);
-                int N = records.size();
-                for (int i = 0; i < N; i++) {
-                    records.valueAt(i).isCanceled = true;
-                }
-                return true;
-            }
+        ArrayMap<String, NotificationRecord> records =
+                mSnoozedNotifications.get(getPkgKey(userId, pkg));
+        if (records == null) {
+            return false;
         }
-        return false;
+        int N = records.size();
+        for (int i = 0; i < N; i++) {
+            records.valueAt(i).isCanceled = true;
+        }
+        return true;
     }
 
     /**
      * Updates the notification record so the most up to date information is shown on re-post.
      */
     protected void update(int userId, NotificationRecord record) {
-        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
-                mSnoozedNotifications.get(userId);
+        ArrayMap<String, NotificationRecord> records =
+                mSnoozedNotifications.get(getPkgKey(userId, record.getSbn().getPackageName()));
         if (records == null) {
             return;
         }
-        ArrayMap<String, NotificationRecord> pkgRecords = records.get(record.getSbn().getPackageName());
-        if (pkgRecords == null) {
-            return;
-        }
-        NotificationRecord existing = pkgRecords.get(record.getKey());
-        pkgRecords.put(record.getKey(), record);
+        records.put(record.getKey(), record);
     }
 
     protected void repost(String key) {
@@ -386,20 +342,18 @@
 
     protected void repost(String key, int userId) {
         final String pkg = mPackages.remove(key);
-        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
-                mSnoozedNotifications.get(userId);
+        ArrayMap<String, NotificationRecord> records =
+                mSnoozedNotifications.get(getPkgKey(userId, pkg));
         if (records == null) {
             return;
         }
-        ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
-        if (pkgRecords == null) {
-            return;
-        }
-        final NotificationRecord record = pkgRecords.remove(key);
+        final NotificationRecord record = records.remove(key);
         mPackages.remove(key);
         mUsers.remove(key);
 
         if (record != null && !record.isCanceled) {
+            final PendingIntent pi = createPendingIntent(pkg, record.getKey(), userId);
+            mAm.cancel(pi);
             MetricsLogger.action(record.getLogMaker()
                     .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
                     .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
@@ -408,55 +362,46 @@
     }
 
     protected void repostGroupSummary(String pkg, int userId, String groupKey) {
-        if (mSnoozedNotifications.containsKey(userId)) {
-            ArrayMap<String, ArrayMap<String, NotificationRecord>> keysByPackage
-                    = mSnoozedNotifications.get(userId);
+        ArrayMap<String, NotificationRecord> recordsByKey
+                = mSnoozedNotifications.get(getPkgKey(userId, pkg));
+        if (recordsByKey == null) {
+            return;
+        }
 
-            if (keysByPackage != null && keysByPackage.containsKey(pkg)) {
-                ArrayMap<String, NotificationRecord> recordsByKey = keysByPackage.get(pkg);
+        String groupSummaryKey = null;
+        int N = recordsByKey.size();
+        for (int i = 0; i < N; i++) {
+            final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
+            if (potentialGroupSummary.getSbn().isGroup()
+                    && potentialGroupSummary.getNotification().isGroupSummary()
+                    && groupKey.equals(potentialGroupSummary.getGroupKey())) {
+                groupSummaryKey = potentialGroupSummary.getKey();
+                break;
+            }
+        }
 
-                if (recordsByKey != null) {
-                    String groupSummaryKey = null;
-                    int N = recordsByKey.size();
-                    for (int i = 0; i < N; i++) {
-                        final NotificationRecord potentialGroupSummary = recordsByKey.valueAt(i);
-                        if (potentialGroupSummary.getSbn().isGroup()
-                                && potentialGroupSummary.getNotification().isGroupSummary()
-                                && groupKey.equals(potentialGroupSummary.getGroupKey())) {
-                            groupSummaryKey = potentialGroupSummary.getKey();
-                            break;
-                        }
-                    }
+        if (groupSummaryKey != null) {
+            NotificationRecord record = recordsByKey.remove(groupSummaryKey);
+            mPackages.remove(groupSummaryKey);
+            mUsers.remove(groupSummaryKey);
 
-                    if (groupSummaryKey != null) {
-                        NotificationRecord record = recordsByKey.remove(groupSummaryKey);
-                        mPackages.remove(groupSummaryKey);
-                        mUsers.remove(groupSummaryKey);
-
-                        if (record != null && !record.isCanceled) {
-                            MetricsLogger.action(record.getLogMaker()
-                                    .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
-                                    .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
-                            mCallback.repost(userId, record);
-                        }
-                    }
-                }
+            if (record != null && !record.isCanceled) {
+                MetricsLogger.action(record.getLogMaker()
+                        .setCategory(MetricsProto.MetricsEvent.NOTIFICATION_SNOOZED)
+                        .setType(MetricsProto.MetricsEvent.TYPE_OPEN));
+                mCallback.repost(userId, record);
             }
         }
     }
 
     protected void clearData(int userId, String pkg) {
-        ArrayMap<String, ArrayMap<String, NotificationRecord>> records =
-                mSnoozedNotifications.get(userId);
+        ArrayMap<String, NotificationRecord> records =
+                mSnoozedNotifications.get(getPkgKey(userId, pkg));
         if (records == null) {
             return;
         }
-        ArrayMap<String, NotificationRecord> pkgRecords = records.get(pkg);
-        if (pkgRecords == null) {
-            return;
-        }
-        for (int i = pkgRecords.size() - 1; i >= 0; i--) {
-            final NotificationRecord r = pkgRecords.removeAt(i);
+        for (int i = records.size() - 1; i >= 0; i--) {
+            final NotificationRecord r = records.removeAt(i);
             if (r != null) {
                 mPackages.remove(r.getKey());
                 mUsers.remove(r.getKey());
@@ -495,22 +440,36 @@
 
     public void dump(PrintWriter pw, NotificationManagerService.DumpFilter filter) {
         pw.println("\n  Snoozed notifications:");
-        for (int userId : mSnoozedNotifications.keySet()) {
+        for (String userPkgKey : mSnoozedNotifications.keySet()) {
             pw.print(INDENT);
-            pw.println("user: " + userId);
-            ArrayMap<String, ArrayMap<String, NotificationRecord>> snoozedPkgs =
-                    mSnoozedNotifications.get(userId);
-            for (String pkg : snoozedPkgs.keySet()) {
+            pw.println("key: " + userPkgKey);
+            ArrayMap<String, NotificationRecord> snoozedRecords =
+                    mSnoozedNotifications.get(userPkgKey);
+            Set<String> snoozedKeys = snoozedRecords.keySet();
+            for (String key : snoozedKeys) {
                 pw.print(INDENT);
                 pw.print(INDENT);
-                pw.println("package: " + pkg);
-                Set<String> snoozedKeys = snoozedPkgs.get(pkg).keySet();
-                for (String key : snoozedKeys) {
-                    pw.print(INDENT);
-                    pw.print(INDENT);
-                    pw.print(INDENT);
-                    pw.println(key);
-                }
+                pw.print(INDENT);
+                pw.println(key);
+            }
+        }
+        pw.println("\n Pending snoozed notifications");
+        for (String userPkgKey : mPersistedSnoozedNotifications.keySet()) {
+            pw.print(INDENT);
+            pw.println("key: " + userPkgKey);
+            ArrayMap<String, Long> snoozedRecords =
+                    mPersistedSnoozedNotifications.get(userPkgKey);
+            if (snoozedRecords == null) {
+                continue;
+            }
+            Set<String> snoozedKeys = snoozedRecords.keySet();
+            for (String key : snoozedKeys) {
+                pw.print(INDENT);
+                pw.print(INDENT);
+                pw.print(INDENT);
+                pw.print(key);
+                pw.print(INDENT);
+                pw.println(snoozedRecords.get(key));
             }
         }
     }
@@ -538,41 +497,34 @@
         void insert(T t) throws IOException;
     }
     private <T> void writeXml(XmlSerializer out,
-            ArrayMap<Integer, ArrayMap<String, ArrayMap<String, T>>> targets, String tag,
+            ArrayMap<String, ArrayMap<String, T>> targets, String tag,
             Inserter<T> attributeInserter)
             throws IOException {
         synchronized (targets) {
             final int M = targets.size();
             for (int i = 0; i < M; i++) {
-                final ArrayMap<String, ArrayMap<String, T>> packages =
-                        targets.valueAt(i);
-                if (packages == null) {
-                    continue;
-                }
-                final int N = packages.size();
-                for (int j = 0; j < N; j++) {
-                    final ArrayMap<String, T> keyToValue = packages.valueAt(j);
-                    if (keyToValue == null) {
-                        continue;
-                    }
-                    final int O = keyToValue.size();
-                    for (int k = 0; k < O; k++) {
-                        T value = keyToValue.valueAt(k);
+                String userIdPkgKey = targets.keyAt(i);
+                // T is a String (snoozed until context) or Long (snoozed until time)
+                ArrayMap<String, T> keyToValue = targets.valueAt(i);
+                for (int j = 0; j < keyToValue.size(); j++) {
+                    String key = keyToValue.keyAt(j);
+                    T value = keyToValue.valueAt(j);
 
-                        out.startTag(null, tag);
+                    out.startTag(null, tag);
 
-                        attributeInserter.insert(value);
+                    attributeInserter.insert(value);
 
-                        out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
-                                XML_SNOOZED_NOTIFICATION_VERSION);
-                        out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, keyToValue.keyAt(k));
-                        out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, packages.keyAt(j));
-                        out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
-                                targets.keyAt(i).toString());
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_VERSION_LABEL,
+                            XML_SNOOZED_NOTIFICATION_VERSION);
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_KEY, key);
 
-                        out.endTag(null, tag);
+                    String pkg = mPackages.get(key);
+                    int userId = mUsers.get(key);
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_PKG, pkg);
+                    out.attribute(null, XML_SNOOZED_NOTIFICATION_USER_ID,
+                            String.valueOf(userId));
 
-                    }
+                    out.endTag(null, tag);
                 }
             }
         }
@@ -606,7 +558,6 @@
                             }
                             scheduleRepost(pkg, key, userId, time - System.currentTimeMillis());
                         }
-                        continue;
                     }
                     if (tag.equals(XML_SNOOZED_NOTIFICATION_CONTEXT)) {
                         final String creationId = parser.getAttributeValue(
@@ -615,18 +566,9 @@
                             storeRecord(pkg, key, userId, mPersistedSnoozedNotificationsWithContext,
                                     creationId);
                         }
-                        continue;
                     }
-
-
                 } catch (Exception e) {
-                    //we dont cre if it is a number format exception or a null pointer exception.
-                    //we just want to debug it and continue with our lives
-                    if (DEBUG) {
-                        Slog.d(TAG,
-                                "Exception in reading snooze data from policy xml: "
-                                        + e.getMessage());
-                    }
+                    Slog.e(TAG,  "Exception in reading snooze data from policy xml", e);
                 }
             }
         }
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/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/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/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e26dac6..084800c 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,
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 314e866..f777e90 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5552,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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index cafe29e..55a41ab 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1934,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();
@@ -3206,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 */);
     }
 
     /**
@@ -3766,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,
@@ -4204,9 +4222,6 @@
         }
 
         ActivityStack getRootHomeTask() {
-            if (mRootHomeTask == null && mDisplayId == DEFAULT_DISPLAY) {
-                Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
-            }
             return mRootHomeTask;
         }
 
@@ -5983,7 +5998,7 @@
         } 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.
@@ -6528,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/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 3ff1b95..370d125 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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 2196d89..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);
@@ -2008,16 +2006,13 @@
             for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
                 final ActivityStack stack = display.getStackAt(stackNdx);
                 stack.switchUser(userId);
-                Task task = stack.getTopMostTask();
-                if (task != null && task != stack) {
-                    stack.positionChildAtTop(task);
-                }
             }
         }
 
+        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()) {
@@ -2039,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/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/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index f68f8b6..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;
     }
 
     /**
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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 93662c9..1936f13 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1741,7 +1741,7 @@
             mSystemServiceManager.startService(SensorNotificationService.class);
             t.traceEnd();
 
-            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXTHUB)) {
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CONTEXT_HUB)) {
                 t.traceBegin("StartContextHubSystemService");
                 mSystemServiceManager.startService(ContextHubSystemService.class);
                 t.traceEnd();
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/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..2d7c937 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);
             });
         }
     }
@@ -670,6 +741,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..6dcfaa0 100644
--- a/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
+++ b/services/people/java/com/android/server/people/data/UsageStatsQueryHelper.java
@@ -129,8 +129,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,8 +138,8 @@
         if (packageData.getConversationStore().getConversationByLocusId(locusId) == null) {
             return;
         }
-        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateLocusEventHistory(
-                locusId);
+        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+                EventStore.CATEGORY_LOCUS_ID_BASED, locusId.getId());
         eventHistory.addEvent(event);
     }
 
@@ -151,8 +151,8 @@
         if (conversationInfo == null) {
             return;
         }
-        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateShortcutEventHistory(
-                conversationInfo.getShortcutId());
+        EventHistoryImpl eventHistory = packageData.getEventStore().getOrCreateEventHistory(
+                EventStore.CATEGORY_SHORTCUT_BASED, conversationInfo.getShortcutId());
         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/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
index 64da6f6..d7a3cfd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/JobStatusTest.java
@@ -19,6 +19,12 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
+import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
+import static com.android.server.job.JobSchedulerService.RARE_INDEX;
+import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
+import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BACKGROUND_NOT_RESTRICTED;
 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_BATTERY_NOT_LOW;
 import static com.android.server.job.controllers.JobStatus.CONSTRAINT_CHARGING;
@@ -34,13 +40,16 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
 
 import android.app.job.JobInfo;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.ComponentName;
 import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
 import android.os.SystemClock;
 import android.provider.MediaStore;
+import android.util.SparseIntArray;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -52,6 +61,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
 import org.mockito.MockitoSession;
 import org.mockito.quality.Strictness;
 
@@ -61,7 +71,12 @@
 @RunWith(AndroidJUnit4.class)
 public class JobStatusTest {
     private static final double DELTA = 0.00001;
+    private static final String TEST_PACKAGE = "job.test.package";
+    private static final ComponentName TEST_JOB_COMPONENT = new ComponentName(TEST_PACKAGE, "test");
+    private static final Uri TEST_MEDIA_URI = Uri.parse("content://media/path/to/media");
 
+    @Mock
+    private JobSchedulerInternal mJobSchedulerInternal;
     private MockitoSession mMockingSession;
 
     @Before
@@ -71,7 +86,7 @@
                 .strictness(Strictness.LENIENT)
                 .mockStatic(LocalServices.class)
                 .startMocking();
-        doReturn(mock(JobSchedulerInternal.class))
+        doReturn(mJobSchedulerInternal)
                 .when(() -> LocalServices.getService(JobSchedulerInternal.class));
         doReturn(mock(PackageManagerInternal.class))
                 .when(() -> LocalServices.getService(PackageManagerInternal.class));
@@ -94,6 +109,82 @@
         }
     }
 
+    private static void assertEffectiveBucketForMediaExemption(JobStatus jobStatus,
+            boolean exemptionGranted) {
+        final SparseIntArray effectiveBucket = new SparseIntArray();
+        effectiveBucket.put(ACTIVE_INDEX, ACTIVE_INDEX);
+        effectiveBucket.put(WORKING_INDEX, WORKING_INDEX);
+        effectiveBucket.put(FREQUENT_INDEX, exemptionGranted ? WORKING_INDEX : FREQUENT_INDEX);
+        effectiveBucket.put(RARE_INDEX, exemptionGranted ? WORKING_INDEX : RARE_INDEX);
+        effectiveBucket.put(NEVER_INDEX, NEVER_INDEX);
+        effectiveBucket.put(RESTRICTED_INDEX, RESTRICTED_INDEX);
+        for (int i = 0; i < effectiveBucket.size(); i++) {
+            jobStatus.setStandbyBucket(effectiveBucket.keyAt(i));
+            assertEquals(effectiveBucket.valueAt(i), jobStatus.getEffectiveStandbyBucket());
+        }
+    }
+
+    @Test
+    public void testMediaBackupExemption_lateConstraint() {
+        final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+                .setOverrideDeadline(12)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                .build();
+        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
+    }
+
+    @Test
+    public void testMediaBackupExemption_noConnectivityConstraint() {
+        final JobInfo triggerContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+                .build();
+        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        assertEffectiveBucketForMediaExemption(createJobStatus(triggerContentJob), false);
+    }
+
+    @Test
+    public void testMediaBackupExemption_noContentTriggerConstraint() {
+        final JobInfo networkJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                .build();
+        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        assertEffectiveBucketForMediaExemption(createJobStatus(networkJob), false);
+    }
+
+    @Test
+    public void testMediaBackupExemption_wrongSourcePackage() {
+        final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                .build();
+        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn("not.test.package");
+        assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
+    }
+
+    @Test
+    public void testMediaBackupExemption_nonMediaUri() {
+        final Uri nonMediaUri = Uri.parse("content://not-media/any/path");
+        final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(nonMediaUri, 0))
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                .build();
+        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), false);
+    }
+
+    @Test
+    public void testMediaBackupExemptionGranted() {
+        final JobInfo networkContentJob = new JobInfo.Builder(42, TEST_JOB_COMPONENT)
+                .addTriggerContentUri(new JobInfo.TriggerContentUri(TEST_MEDIA_URI, 0))
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                .build();
+        when(mJobSchedulerInternal.getMediaBackupPackage()).thenReturn(TEST_PACKAGE);
+        assertEffectiveBucketForMediaExemption(createJobStatus(networkContentJob), true);
+    }
+
     @Test
     public void testFraction() throws Exception {
         final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
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/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/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 6769faa..0bb984e 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);
 
@@ -168,6 +192,8 @@
 
         mCallingUserId = USER_ID_PRIMARY;
 
+        mCancellationSignal = new CancellationSignal();
+
         mInjector = new TestInjector();
         mDataManager = new DataManager(mContext, mInjector);
         mDataManager.initialize();
@@ -177,6 +203,7 @@
     public void tearDown() {
         LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
         LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
     }
 
     @Test
@@ -454,6 +481,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);
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..dc4876b 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
@@ -288,14 +288,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/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/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 92d47c3..bc33f08 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -2622,6 +2622,33 @@
     }
 
     @Test
+    public void testSystemNotificationListenerCanUnsnooze() throws Exception {
+        final NotificationRecord nr = generateNotificationRecord(
+                mTestNotificationChannel, 2, "group", false);
+
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testSystemNotificationListenerCanUnsnooze",
+                nr.getSbn().getId(), nr.getSbn().getNotification(),
+                nr.getSbn().getUserId());
+        waitForIdle();
+        NotificationManagerService.SnoozeNotificationRunnable snoozeNotificationRunnable =
+                mService.new SnoozeNotificationRunnable(
+                        nr.getKey(), 100, null);
+        snoozeNotificationRunnable.run();
+
+        ManagedServices.ManagedServiceInfo listener = mListeners.new ManagedServiceInfo(
+                null, new ComponentName(PKG, "test_class"), mUid, true, null, 0);
+        listener.isSystem = true;
+        when(mListeners.checkServiceTokenLocked(any())).thenReturn(listener);
+
+        mBinderService.unsnoozeNotificationFromSystemListener(null, nr.getKey());
+        waitForIdle();
+        StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifs.length);
+        assertNotNull(notifs[0].getKey());//mService.getNotificationRecord(nr.getSbn().getKey()));
+    }
+
+    @Test
     public void testSetListenerAccessForUser() throws Exception {
         UserHandle user = UserHandle.of(10);
         ComponentName c = ComponentName.unflattenFromString("package/Component");
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 3186d53..1dd0b1a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -15,15 +15,18 @@
  */
 package com.android.server.notification;
 
+import static com.android.server.notification.SnoozeHelper.EXTRA_KEY;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
 import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
 
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -322,8 +325,12 @@
         mSnoozeHelper.snooze(r, 1000);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
         mSnoozeHelper.snooze(r2, 1000);
+        reset(mAm);
         mSnoozeHelper.repost(r.getKey(), UserHandle.USER_SYSTEM);
         verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+        ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+        verify(mAm).cancel(captor.capture());
+        assertEquals(r.getKey(), captor.getValue().getIntent().getStringExtra(EXTRA_KEY));
     }
 
     @Test
@@ -332,8 +339,10 @@
         mSnoozeHelper.snooze(r, 1000);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "one", UserHandle.ALL);
         mSnoozeHelper.snooze(r2, 1000);
+        reset(mAm);
         mSnoozeHelper.repost(r.getKey());
         verify(mCallback, times(1)).repost(UserHandle.USER_SYSTEM, r);
+        verify(mAm).cancel(any(PendingIntent.class));
     }
 
     @Test
@@ -370,31 +379,7 @@
     }
 
     @Test
-    public void testGetSnoozedByUser() throws Exception {
-        NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
-        NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
-        NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
-        NotificationRecord r4 = getNotificationRecord("pkg2", 3, "three", UserHandle.CURRENT);
-        mSnoozeHelper.snooze(r, 1000);
-        mSnoozeHelper.snooze(r2, 1000);
-        mSnoozeHelper.snooze(r3, 1000);
-        mSnoozeHelper.snooze(r4, 1000);
-        IntArray profileIds = new IntArray();
-        profileIds.add(UserHandle.USER_SYSTEM);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
-        assertEquals(3, mSnoozeHelper.getSnoozed().size());
-        profileIds = new IntArray();
-        profileIds.add(UserHandle.USER_CURRENT);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
-        assertEquals(1, mSnoozeHelper.getSnoozed().size());
-    }
-
-    @Test
-    public void testGetSnoozedByUser_managedProfiles() throws Exception {
-        IntArray profileIds = new IntArray();
-        profileIds.add(UserHandle.USER_CURRENT);
-        profileIds.add(UserHandle.USER_SYSTEM);
-        when(mUserProfiles.getCurrentProfileIds()).thenReturn(profileIds);
+    public void testGetSnoozedBy() throws Exception {
         NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
         NotificationRecord r2 = getNotificationRecord("pkg", 2, "two", UserHandle.SYSTEM);
         NotificationRecord r3 = getNotificationRecord("pkg2", 3, "three", UserHandle.SYSTEM);
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/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/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/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 51b4a31..c51a852 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2377,7 +2377,7 @@
      *  {@link CellSignalStrengthLte#USE_RSRQ}, {@link CellSignalStrengthLte#USE_RSSNR}.
      *
      * For example, if both RSRP and RSRQ are used, the value of key is 3 (1 << 0 | 1 << 1).
-     * If the key is invalid or not configured, a default value (RSRP | RSSNR = 1 << 0 | 1 << 2)
+     * If the key is invalid or not configured, a default value (RSRP = 1 << 0)
      * will apply.
      *
      * @hide
@@ -4334,7 +4334,7 @@
         sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
         sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
         sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
-                CellSignalStrengthLte.USE_RSRP | CellSignalStrengthLte.USE_RSSNR);
+                CellSignalStrengthLte.USE_RSRP);
         // Default wifi configurations.
         sDefaults.putAll(Wifi.getDefaults());
         sDefaults.putBoolean(ENABLE_EAP_METHOD_PREFIX_BOOL, false);
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 1cd45e9..2529387 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -181,7 +181,7 @@
         mCqi = CellInfo.UNAVAILABLE;
         mTimingAdvance = CellInfo.UNAVAILABLE;
         mLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-        mParametersUseForLevel = USE_RSRP | USE_RSSNR;
+        mParametersUseForLevel = USE_RSRP;
     }
 
     /** {@inheritDoc} */
@@ -236,7 +236,7 @@
         int[] rsrpThresholds, rsrqThresholds, rssnrThresholds;
         boolean rsrpOnly;
         if (cc == null) {
-            mParametersUseForLevel = USE_RSRP | USE_RSSNR;
+            mParametersUseForLevel = USE_RSRP;
             rsrpThresholds = sRsrpThresholds;
             rsrqThresholds = sRsrqThresholds;
             rssnrThresholds = sRssnrThresholds;
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index c40573b..6fdc13e 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -492,6 +492,7 @@
     int RIL_REQUEST_ENABLE_UICC_APPLICATIONS = 208;
     int RIL_REQUEST_GET_UICC_APPLICATIONS_ENABLEMENT = 209;
     int RIL_REQUEST_SET_SYSTEM_SELECTION_CHANNELS = 210;
+    int RIL_REQUEST_GET_BARRING_INFO = 211;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 95b8f67..da45d9a 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -296,6 +296,8 @@
                     AppLaunchResult launchResults = null;
                     if (hasFailureOnFirstLaunch(launch)) {
                         // skip if the app has failures while launched first
+                        Log.w(TAG, "Has failures on first launch: " + launch.getApp());
+                        forceStopApp(launch.getApp());
                         continue;
                     }
                     AtraceLogger atraceLogger = null;
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.