Merge "Update boot image and system server profiles [M5C5P89S0PP]" into sc-dev
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index b3c33b6..144536e 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1229,7 +1229,11 @@
          * </ul>
          * Note that the system may choose to delay jobs with large network
          * usage estimates when the device has a poor network connection, in
-         * order to save battery.
+         * order to save battery and possible network costs.
+         * Starting from Android version {@link Build.VERSION_CODES#S}, JobScheduler may attempt
+         * to run large jobs when the device is charging and on an unmetered network, even if the
+         * network is slow. This gives large jobs an opportunity to make forward progress, even if
+         * they risk timing out.
          * <p>
          * The values provided here only reflect the traffic that will be
          * performed by the base job; if you're using {@link JobWorkItem} then
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 60f5769..cef065d 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -547,7 +547,7 @@
     private int[] mPowerSaveWhitelistUserAppIdArray = new int[0];
 
     /**
-     * List of end times for UIDs that are temporarily marked as being allowed to access
+     * List of end times for app-IDs that are temporarily marked as being allowed to access
      * the network and acquire wakelocks. Times are in milliseconds.
      */
     private final SparseArray<Pair<MutableLong, String>> mTempWhitelistAppIdEndTimes
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 5365218..1169391 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -121,7 +121,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
-import com.android.internal.os.BinderDeathDispatcher;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.LocalLog;
@@ -184,8 +183,7 @@
     static final boolean DEBUG_BG_LIMIT = localLOGV || false;
     static final boolean DEBUG_STANDBY = localLOGV || false;
     static final boolean RECORD_ALARMS_IN_HISTORY = true;
-    // TODO(b/178484639) : Turn off once alarms and reminders work is complete.
-    static final boolean RECORD_DEVICE_IDLE_ALARMS = true;
+    static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
     static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
 
     static final int TICK_HISTORY_DEPTH = 10;
@@ -206,8 +204,6 @@
                     .addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
                             | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
 
-    private static final BinderDeathDispatcher<IAlarmListener> sListenerDeathDispatcher =
-            new BinderDeathDispatcher<>();
     final LocalLog mLog = new LocalLog(TAG);
 
     AppOpsManager mAppOps;
@@ -1837,8 +1833,9 @@
         }
 
         if (directReceiver != null) {
-            if (sListenerDeathDispatcher.linkToDeath(directReceiver, mListenerDeathRecipient)
-                    <= 0) {
+            try {
+                directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0);
+            } catch (RemoteException e) {
                 Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag);
                 return;
             }
@@ -2851,12 +2848,6 @@
                 pw.println();
             }
 
-            pw.println("Listener death dispatcher state:");
-            pw.increaseIndent();
-            sListenerDeathDispatcher.dump(pw);
-            pw.println();
-            pw.decreaseIndent();
-
             if (mLog.dump(pw, "Recent problems:")) {
                 pw.println();
             }
@@ -3448,6 +3439,9 @@
 
         for (final Alarm removed : removedAlarms) {
             decrementAlarmCount(removed.uid, 1);
+            if (removed.listener != null) {
+                removed.listener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0);
+            }
             if (!RemovedAlarm.isLoggable(reason)) {
                 continue;
             }
@@ -4701,6 +4695,8 @@
                     // Direct listener callback alarm
                     mListenerCount++;
 
+                    alarm.listener.asBinder().unlinkToDeath(mListenerDeathRecipient, 0);
+
                     if (RECORD_ALARMS_IN_HISTORY) {
                         if (alarm.listener == mTimeTickTrigger) {
                             mTickHistory[mNextTickHistory++] = nowELAPSED;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
index 7b947fd..e8065aa 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/ConnectivityController.java
@@ -25,12 +25,18 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -101,6 +107,8 @@
     private final ConnectivityManager mConnManager;
     private final NetworkPolicyManagerInternal mNetPolicyManagerInternal;
 
+    private final ChargingTracker mChargingTracker;
+
     /** List of tracked jobs keyed by source UID. */
     @GuardedBy("mLock")
     private final SparseArray<ArraySet<JobStatus>> mTrackedJobs = new SparseArray<>();
@@ -229,6 +237,9 @@
         // network changes against the active network for each UID with jobs.
         final NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
         mConnManager.registerNetworkCallback(request, mNetworkCallback);
+
+        mChargingTracker = new ChargingTracker();
+        mChargingTracker.startTracking();
     }
 
     @GuardedBy("mLock")
@@ -538,6 +549,14 @@
      */
     private boolean isInsane(JobStatus jobStatus, Network network,
             NetworkCapabilities capabilities, Constants constants) {
+        if (capabilities.hasCapability(NET_CAPABILITY_NOT_METERED)
+                && mChargingTracker.isCharging()) {
+            // We're charging and on an unmetered network. We don't have to be as conservative about
+            // making sure the job will run within its max execution time. Let's just hope the app
+            // supports interruptible work.
+            return false;
+        }
+
         // Use the maximum possible time since it gives us an upper bound, even though the job
         // could end up stopping earlier.
         final long maxJobExecutionTimeMs = mService.getMaxJobExecutionTimeMs(jobStatus);
@@ -922,7 +941,7 @@
      *                      {@link Network}, or {@code null} to update all tracked jobs.
      */
     @GuardedBy("mLock")
-    private void updateTrackedJobsLocked(int filterUid, Network filterNetwork) {
+    private void updateTrackedJobsLocked(int filterUid, @Nullable Network filterNetwork) {
         boolean changed = false;
         if (filterUid == -1) {
             for (int i = mTrackedJobs.size() - 1; i >= 0; i--) {
@@ -937,7 +956,8 @@
     }
 
     @GuardedBy("mLock")
-    private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs, Network filterNetwork) {
+    private boolean updateTrackedJobsLocked(ArraySet<JobStatus> jobs,
+            @Nullable Network filterNetwork) {
         if (jobs == null || jobs.size() == 0) {
             return false;
         }
@@ -993,6 +1013,51 @@
         }
     }
 
+    private final class ChargingTracker extends BroadcastReceiver {
+        /**
+         * Track whether we're "charging", where charging means that we're ready to commit to
+         * doing work.
+         */
+        private boolean mCharging;
+
+        ChargingTracker() {}
+
+        public void startTracking() {
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(BatteryManager.ACTION_CHARGING);
+            filter.addAction(BatteryManager.ACTION_DISCHARGING);
+            mContext.registerReceiver(this, filter);
+
+            // Initialise tracker state.
+            final BatteryManagerInternal batteryManagerInternal =
+                    LocalServices.getService(BatteryManagerInternal.class);
+            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+        }
+
+        public boolean isCharging() {
+            return mCharging;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            synchronized (mLock) {
+                final String action = intent.getAction();
+                if (BatteryManager.ACTION_CHARGING.equals(action)) {
+                    if (mCharging) {
+                        return;
+                    }
+                    mCharging = true;
+                } else if (BatteryManager.ACTION_DISCHARGING.equals(action)) {
+                    if (!mCharging) {
+                        return;
+                    }
+                    mCharging = false;
+                }
+                updateTrackedJobsLocked(-1, null);
+            }
+        }
+    }
+
     private final NetworkCallback mNetworkCallback = new NetworkCallback() {
         @Override
         public void onAvailable(Network network) {
diff --git a/core/api/current.txt b/core/api/current.txt
index 002b333..4c61afe 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -31964,7 +31964,6 @@
     method public long getUserCreationTime(android.os.UserHandle);
     method public android.os.UserHandle getUserForSerialNumber(long);
     method @NonNull @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", android.Manifest.permission.GET_ACCOUNTS_PRIVILEGED, "android.permission.CREATE_USERS"}, conditional=true) public String getUserName();
-    method public int getUserPrivacySensitivity();
     method public java.util.List<android.os.UserHandle> getUserProfiles();
     method public android.os.Bundle getUserRestrictions();
     method @RequiresPermission(anyOf={"android.permission.MANAGE_USERS", "android.permission.INTERACT_ACROSS_USERS"}, conditional=true) public android.os.Bundle getUserRestrictions(android.os.UserHandle);
@@ -32045,8 +32044,6 @@
     field public static final String DISALLOW_USER_SWITCH = "no_user_switch";
     field public static final String ENSURE_VERIFY_APPS = "ensure_verify_apps";
     field public static final String KEY_RESTRICTIONS_PENDING = "restrictions_pending";
-    field public static final int PRIVACY_SENSITIVITY_DEFAULT = 0; // 0x0
-    field public static final int PRIVACY_SENSITIVITY_LOCATION = 1; // 0x1
     field public static final int QUIET_MODE_DISABLE_ONLY_IF_CREDENTIAL_NOT_REQUIRED = 1; // 0x1
     field public static final int USER_CREATION_FAILED_NOT_PERMITTED = 1; // 0x1
     field public static final int USER_CREATION_FAILED_NO_MORE_USERS = 2; // 0x2
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f23966e..7072bbe 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -4930,10 +4930,6 @@
     field @Deprecated public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
 
-  public final class LocationDeviceConfig {
-    field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist";
-  }
-
   public class LocationManager {
     method @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void addProviderRequestChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.provider.ProviderRequest.ChangedListener);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void flushGnssBatch();
@@ -4946,7 +4942,6 @@
     method public boolean isLocationEnabledForUser(@NonNull android.os.UserHandle);
     method public boolean isProviderEnabledForUser(@NonNull String, @NonNull android.os.UserHandle);
     method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@NonNull String);
-    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String);
     method @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String, @Nullable String);
     method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.UPDATE_APP_OPS_STATS}) public boolean registerGnssBatchedLocationCallback(long, boolean, @NonNull android.location.BatchedLocationCallback, @Nullable android.os.Handler);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssRequest, @NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
@@ -12843,7 +12838,6 @@
     method public void onCreated(@NonNull android.telephony.ims.stub.SipDelegate, @Nullable java.util.Set<android.telephony.ims.FeatureTagState>);
     method public void onDestroyed(int);
     method public void onFeatureTagRegistrationChanged(@NonNull android.telephony.ims.DelegateRegistrationState);
-    method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
   }
 
   public final class FeatureTagState implements android.os.Parcelable {
@@ -13664,67 +13658,6 @@
     method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
   }
 
-  @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable {
-    method @Deprecated public boolean containsKey(@NonNull String);
-    method @Deprecated @NonNull public android.os.PersistableBundle copyBundle();
-    method @Deprecated public int describeContents();
-    method @Deprecated public boolean getBoolean(@NonNull String, boolean);
-    method @Deprecated public int getInt(@NonNull String, int);
-    method @Deprecated @Nullable public String getString(@NonNull String);
-    method @Deprecated public long getVersion();
-    method @Deprecated public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @Deprecated @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
-    field @Deprecated public static final String IPTYPE_IPV4 = "IPV4";
-    field @Deprecated public static final String IPTYPE_IPV6 = "IPV6";
-    field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
-    field @Deprecated public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
-    field @Deprecated public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
-    field @Deprecated public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
-    field @Deprecated public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
-    field @Deprecated public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
-    field @Deprecated public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
-    field @Deprecated public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
-    field @Deprecated public static final String SIP_TRANSPORT_TCP = "TCP";
-    field @Deprecated public static final String SIP_TRANSPORT_UDP = "UDP";
-  }
-
-  @Deprecated public static final class SipDelegateImsConfiguration.Builder {
-    ctor @Deprecated public SipDelegateImsConfiguration.Builder(int);
-    ctor @Deprecated public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
-    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
-    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
-    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
-    method @Deprecated @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
-  }
-
   public class SipDelegateManager {
     method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void createSipDelegate(@NonNull android.telephony.ims.DelegateRequest, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.stub.DelegateConnectionStateCallback, @NonNull android.telephony.ims.stub.DelegateConnectionMessageCallback) throws android.telephony.ims.ImsException;
     method @RequiresPermission(android.Manifest.permission.PERFORM_IMS_SINGLE_REGISTRATION) public void destroySipDelegate(@NonNull android.telephony.ims.SipDelegateConnection, int);
@@ -13892,11 +13825,10 @@
   }
 
   public interface DelegateConnectionStateCallback {
-    method public default void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration);
+    method public void onConfigurationChanged(@NonNull android.telephony.ims.SipDelegateConfiguration);
     method public void onCreated(@NonNull android.telephony.ims.SipDelegateConnection);
     method public void onDestroyed(int);
     method public void onFeatureTagStatusChanged(@NonNull android.telephony.ims.DelegateRegistrationState, @NonNull java.util.Set<android.telephony.ims.FeatureTagState>);
-    method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
   }
 
   public class ImsCallSessionImplBase implements java.lang.AutoCloseable {
diff --git a/core/api/system-removed.txt b/core/api/system-removed.txt
index 7cf0076..9a8a493 100644
--- a/core/api/system-removed.txt
+++ b/core/api/system-removed.txt
@@ -135,6 +135,7 @@
   public class LocationManager {
     method @Deprecated public boolean addGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
     method @Deprecated public boolean addGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG) public boolean isProviderPackage(@Nullable String, @NonNull String);
     method @Deprecated public void removeGpsMeasurementListener(android.location.GpsMeasurementsEvent.Listener);
     method @Deprecated public void removeGpsNavigationMessageListener(android.location.GpsNavigationMessageEvent.Listener);
     method @Deprecated @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) public void setLocationControllerExtraPackage(String);
@@ -231,6 +232,83 @@
 
 }
 
+package android.telephony.ims {
+
+  public interface DelegateStateCallback {
+    method @Deprecated public void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+  }
+
+  @Deprecated public final class SipDelegateImsConfiguration implements android.os.Parcelable {
+    method public boolean containsKey(@NonNull String);
+    method @NonNull public android.os.PersistableBundle copyBundle();
+    method public int describeContents();
+    method public boolean getBoolean(@NonNull String, boolean);
+    method public int getInt(@NonNull String, int);
+    method @Nullable public String getString(@NonNull String);
+    method public long getVersion();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipDelegateImsConfiguration> CREATOR;
+    field public static final String IPTYPE_IPV4 = "IPV4";
+    field public static final String IPTYPE_IPV6 = "IPV6";
+    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_HEADER_STRING = "sip_config_auhentication_header_string";
+    field public static final String KEY_SIP_CONFIG_AUTHENTICATION_NONCE_STRING = "sip_config_authentication_nonce_string";
+    field public static final String KEY_SIP_CONFIG_CELLULAR_NETWORK_INFO_HEADER_STRING = "sip_config_cellular_network_info_header_string";
+    field public static final String KEY_SIP_CONFIG_HOME_DOMAIN_STRING = "sip_config_home_domain_string";
+    field public static final String KEY_SIP_CONFIG_IMEI_STRING = "sip_config_imei_string";
+    field public static final String KEY_SIP_CONFIG_IPTYPE_STRING = "sip_config_iptype_string";
+    field public static final String KEY_SIP_CONFIG_IS_COMPACT_FORM_ENABLED_BOOL = "sip_config_is_compact_form_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_GRUU_ENABLED_BOOL = "sip_config_is_gruu_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_IPSEC_ENABLED_BOOL = "sip_config_is_ipsec_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_KEEPALIVE_ENABLED_BOOL = "sip_config_is_keepalive_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_IS_NAT_ENABLED_BOOL = "sip_config_is_nat_enabled_bool";
+    field public static final String KEY_SIP_CONFIG_MAX_PAYLOAD_SIZE_ON_UDP_INT = "sip_config_udp_max_payload_size_int";
+    field public static final String KEY_SIP_CONFIG_PATH_HEADER_STRING = "sip_config_path_header_string";
+    field public static final String KEY_SIP_CONFIG_P_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_access_network_info_header_string";
+    field public static final String KEY_SIP_CONFIG_P_ASSOCIATED_URI_HEADER_STRING = "sip_config_p_associated_uri_header_string";
+    field public static final String KEY_SIP_CONFIG_P_LAST_ACCESS_NETWORK_INFO_HEADER_STRING = "sip_config_p_last_access_network_info_header_string";
+    field public static final String KEY_SIP_CONFIG_SECURITY_VERIFY_HEADER_STRING = "sip_config_security_verify_header_string";
+    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_IPADDRESS_STRING = "sip_config_server_default_ipaddress_string";
+    field public static final String KEY_SIP_CONFIG_SERVER_DEFAULT_PORT_INT = "sip_config_server_default_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_CLIENT_PORT_INT = "sip_config_server_ipsec_client_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_server_ipsec_old_client_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVER_IPSEC_SERVER_PORT_INT = "sip_config_server_ipsec_server_port_int";
+    field public static final String KEY_SIP_CONFIG_SERVICE_ROUTE_HEADER_STRING = "sip_config_service_route_header_string";
+    field public static final String KEY_SIP_CONFIG_TRANSPORT_TYPE_STRING = "sip_config_protocol_type_string";
+    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_IPADDRESS_STRING = "sip_config_ue_default_ipaddress_string";
+    field public static final String KEY_SIP_CONFIG_UE_DEFAULT_PORT_INT = "sip_config_ue_default_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_CLIENT_PORT_INT = "sip_config_ue_ipsec_client_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_OLD_CLIENT_PORT_INT = "sip_config_ue_ipsec_old_client_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_IPSEC_SERVER_PORT_INT = "sip_config_ue_ipsec_server_port_int";
+    field public static final String KEY_SIP_CONFIG_UE_PRIVATE_USER_ID_STRING = "sip_config_ue_private_user_id_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_GRUU_STRING = "sip_config_ue_public_gruu_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_IPADDRESS_WITH_NAT_STRING = "sip_config_ue_public_ipaddress_with_nat_string";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_PORT_WITH_NAT_INT = "sip_config_ue_public_port_with_nat_int";
+    field public static final String KEY_SIP_CONFIG_UE_PUBLIC_USER_ID_STRING = "sip_config_ue_public_user_id_string";
+    field public static final String KEY_SIP_CONFIG_URI_USER_PART_STRING = "sip_config_uri_user_part_string";
+    field public static final String KEY_SIP_CONFIG_USER_AGENT_HEADER_STRING = "sip_config_sip_user_agent_header_string";
+    field public static final String SIP_TRANSPORT_TCP = "TCP";
+    field public static final String SIP_TRANSPORT_UDP = "UDP";
+  }
+
+  public static final class SipDelegateImsConfiguration.Builder {
+    ctor public SipDelegateImsConfiguration.Builder(int);
+    ctor public SipDelegateImsConfiguration.Builder(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addBoolean(@NonNull String, boolean);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addInt(@NonNull String, int);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration.Builder addString(@NonNull String, @NonNull String);
+    method @NonNull public android.telephony.ims.SipDelegateImsConfiguration build();
+  }
+
+}
+
+package android.telephony.ims.stub {
+
+  public interface DelegateConnectionStateCallback {
+    method @Deprecated public default void onImsConfigurationChanged(@NonNull android.telephony.ims.SipDelegateImsConfiguration);
+  }
+
+}
+
 package android.view.translation {
 
   public final class UiTranslationManager {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 8499b37..2f795f0 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1360,10 +1360,6 @@
     method public void setType(int);
   }
 
-  public final class LocationDeviceConfig {
-    field public static final String IGNORE_SETTINGS_ALLOWLIST = "ignore_settings_allowlist";
-  }
-
   public class LocationManager {
     method @NonNull public String[] getBackgroundThrottlingWhitelist();
     method @NonNull public android.os.PackageTagsList getIgnoreSettingsAllowlist();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 8d39d8a..7149096 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -237,7 +237,6 @@
 import java.util.TimeZone;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
 
 /**
@@ -338,6 +337,11 @@
      */
     @UnsupportedAppUsage
     final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
+    /**
+     * Maps from activity token to local record of the activities that are preparing to be launched.
+     */
+    final Map<IBinder, ActivityClientRecord> mLaunchingActivities =
+            Collections.synchronizedMap(new ArrayMap<IBinder, ActivityClientRecord>());
     /** The activities to be truly destroyed (not include relaunch). */
     final Map<IBinder, ClientTransactionItem> mActivitiesToBeDestroyed =
             Collections.synchronizedMap(new ArrayMap<IBinder, ClientTransactionItem>());
@@ -347,7 +351,6 @@
     // Number of activities that are currently visible on-screen.
     @UnsupportedAppUsage
     int mNumVisibleActivities = 0;
-    private final AtomicInteger mNumLaunchingActivities = new AtomicInteger();
     @GuardedBy("mAppThread")
     private int mLastProcessState = PROCESS_STATE_UNKNOWN;
     @GuardedBy("mAppThread")
@@ -3256,6 +3259,21 @@
     }
 
     @Override
+    public void addLaunchingActivity(IBinder token, ActivityClientRecord activity) {
+        mLaunchingActivities.put(token, activity);
+    }
+
+    @Override
+    public ActivityClientRecord getLaunchingActivity(IBinder token) {
+        return mLaunchingActivities.get(token);
+    }
+
+    @Override
+    public void removeLaunchingActivity(IBinder token) {
+        mLaunchingActivities.remove(token);
+    }
+
+    @Override
     public ActivityClientRecord getActivityClient(IBinder token) {
         return mActivities.get(token);
     }
@@ -3299,7 +3317,7 @@
             // Defer the top state for VM to avoid aggressive JIT compilation affecting activity
             // launch time.
             if (processState == ActivityManager.PROCESS_STATE_TOP
-                    && mNumLaunchingActivities.get() > 0) {
+                    && !mLaunchingActivities.isEmpty()) {
                 mPendingProcessState = processState;
                 mH.postDelayed(this::applyPendingProcessState, PENDING_TOP_PROCESS_STATE_TIMEOUT);
             } else {
@@ -3315,7 +3333,7 @@
         // Handle the pending configuration if the process state is changed from cached to
         // non-cached. Except the case where there is a launching activity because the
         // LaunchActivityItem will handle it.
-        if (wasCached && !isCachedProcessState() && mNumLaunchingActivities.get() == 0) {
+        if (wasCached && !isCachedProcessState() && mLaunchingActivities.isEmpty()) {
             final Configuration pendingConfig =
                     mConfigurationController.getPendingConfiguration(false /* clearPending */);
             if (pendingConfig == null) {
@@ -3353,11 +3371,6 @@
         }
     }
 
-    @Override
-    public void countLaunchingActivities(int num) {
-        mNumLaunchingActivities.getAndAdd(num);
-    }
-
     @UnsupportedAppUsage
     public final void sendActivityResult(
             IBinder token, String id, int requestCode,
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index c752f34..115101c 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -82,9 +82,6 @@
     /** Set current process state. */
     public abstract void updateProcessState(int processState, boolean fromIpc);
 
-    /** Count how many activities are launching. */
-    public abstract void countLaunchingActivities(int num);
-
     // Execute phase related logic and handlers. Methods here execute actual lifecycle transactions
     // and deliver callbacks.
 
@@ -193,6 +190,26 @@
             FixedRotationAdjustments fixedRotationAdjustments);
 
     /**
+     * Add {@link ActivityClientRecord} that is preparing to be launched.
+     * @param token Activity token.
+     * @param activity An initialized instance of {@link ActivityClientRecord} to use during launch.
+     */
+    public abstract void addLaunchingActivity(IBinder token, ActivityClientRecord activity);
+
+    /**
+     * Get {@link ActivityClientRecord} that is preparing to be launched.
+     * @param token Activity token.
+     * @return An initialized instance of {@link ActivityClientRecord} to use during launch.
+     */
+    public abstract ActivityClientRecord getLaunchingActivity(IBinder token);
+
+    /**
+     * Remove {@link ActivityClientRecord} from the launching activity list.
+     * @param token Activity token.
+     */
+    public abstract void removeLaunchingActivity(IBinder token);
+
+    /**
      * Get {@link android.app.ActivityThread.ActivityClientRecord} instance that corresponds to the
      * provided token.
      */
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 47ababf..432d99d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3314,6 +3314,19 @@
     }
 
     /**
+     * Sets the token used for background operations for the pending intents associated with this
+     * notification.
+     *
+     * This token is automatically set during deserialization for you, you usually won't need to
+     * call this unless you want to change the existing token, if any.
+     *
+     * @hide
+     */
+    public void setAllowlistToken(@Nullable IBinder token) {
+        mAllowlistToken = token;
+    }
+
+    /**
      * @hide
      */
     public static void addFieldsFromContext(Context context, Notification notification) {
diff --git a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
index d5585db..032b57e 100644
--- a/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
+++ b/core/java/android/app/servertransaction/ActivityConfigurationChangeItem.java
@@ -40,7 +40,8 @@
 
     @Override
     public void preExecute(android.app.ClientTransactionHandler client, IBinder token) {
-        final ActivityClientRecord r = getActivityClientRecord(client, token);
+        final ActivityClientRecord r = getActivityClientRecord(client, token,
+                true /* includeLaunching */);
         // Notify the client of an upcoming change in the token configuration. This ensures that
         // batches of config change items only process the newest configuration.
         client.updatePendingActivityConfiguration(r, mConfiguration);
diff --git a/core/java/android/app/servertransaction/ActivityTransactionItem.java b/core/java/android/app/servertransaction/ActivityTransactionItem.java
index f7d7e9d..a539812 100644
--- a/core/java/android/app/servertransaction/ActivityTransactionItem.java
+++ b/core/java/android/app/servertransaction/ActivityTransactionItem.java
@@ -55,15 +55,40 @@
 
     @NonNull ActivityClientRecord getActivityClientRecord(
             @NonNull ClientTransactionHandler client, IBinder token) {
-        final ActivityClientRecord r = client.getActivityClient(token);
+        return getActivityClientRecord(client, token, false /* includeLaunching */);
+    }
+
+    /**
+     * Get the {@link ActivityClientRecord} instance that corresponds to the provided token.
+     * @param client Target client handler.
+     * @param token Target activity token.
+     * @param includeLaunching Indicate to also find the {@link ActivityClientRecord} in launching
+     *                         activity list. It should be noted that there is no activity in
+     *                         {@link ActivityClientRecord} from the launching activity list.
+     * @return The {@link ActivityClientRecord} instance that corresponds to the provided token.
+     */
+    @NonNull ActivityClientRecord getActivityClientRecord(
+            @NonNull ClientTransactionHandler client, IBinder token, boolean includeLaunching) {
+        ActivityClientRecord r = client.getActivityClient(token);
+        if (r != null) {
+            if (client.getActivity(token) == null) {
+                throw new IllegalArgumentException("Activity must not be null to execute "
+                        + "transaction item");
+            }
+            return r;
+        }
+        // The activity may not be launched yet. Fallback to check launching activity.
+        if (includeLaunching) {
+            r = client.getLaunchingActivity(token);
+        }
         if (r == null) {
             throw new IllegalArgumentException("Activity client record must not be null to execute "
                     + "transaction item");
         }
-        if (client.getActivity(token) == null) {
-            throw new IllegalArgumentException("Activity must not be null to execute "
-                    + "transaction item");
-        }
+
+        // We don't need to check the activity of launching activity client records because they
+        // have not been launched yet.
+
         return r;
     }
 }
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index e281a02..34e4fcd 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -82,7 +82,12 @@
 
     @Override
     public void preExecute(ClientTransactionHandler client, IBinder token) {
-        client.countLaunchingActivities(1);
+        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
+                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
+                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
+                client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
+                mLaunchedFromBubble);
+        client.addLaunchingActivity(token, r);
         client.updateProcessState(mProcState, false);
         client.updatePendingConfiguration(mCurConfig);
         if (mActivityClientController != null) {
@@ -94,11 +99,7 @@
     public void execute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
-        ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
-                mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
-                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
-                client, mAssistToken, mFixedRotationAdjustments, mShareableActivityToken,
-                mLaunchedFromBubble);
+        ActivityClientRecord r = client.getLaunchingActivity(token);
         client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -106,7 +107,7 @@
     @Override
     public void postExecute(ClientTransactionHandler client, IBinder token,
             PendingTransactionActions pendingActions) {
-        client.countLaunchingActivities(-1);
+        client.removeLaunchingActivity(token);
     }
 
 
diff --git a/core/java/android/app/servertransaction/MoveToDisplayItem.java b/core/java/android/app/servertransaction/MoveToDisplayItem.java
index 944367e..4b8a347 100644
--- a/core/java/android/app/servertransaction/MoveToDisplayItem.java
+++ b/core/java/android/app/servertransaction/MoveToDisplayItem.java
@@ -40,7 +40,8 @@
 
     @Override
     public void preExecute(ClientTransactionHandler client, IBinder token) {
-        final ActivityClientRecord r = getActivityClientRecord(client, token);
+        final ActivityClientRecord r = getActivityClientRecord(client, token,
+                true /* includeLaunching */);
         // Notify the client of an upcoming change in the token configuration. This ensures that
         // batches of config change items only process the newest configuration.
         client.updatePendingActivityConfiguration(r, mConfiguration);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fc676cf..886cd7f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -400,10 +400,7 @@
     public static final int BIND_BYPASS_POWER_NETWORK_RESTRICTIONS = 0x00020000;
 
     /**
-     * Flag for {@link #bindService}: allow background foreground service starts from the bound
-     * service's process.
-     * This flag is only respected if the caller is holding
-     * {@link android.Manifest.permission#START_FOREGROUND_SERVICES_FROM_BACKGROUND}.
+     * Do not use. This flag is no longer needed nor used.
      * @hide
      */
     @SystemApi
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 33606e8..33a34be 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4672,27 +4672,27 @@
             @PermissionGroupInfoFlags int flags);
 
     /**
-     * Get the platform permissions which belong to a particular permission group.
+     * Get the platform-defined permissions which belong to a particular permission group.
      *
-     * @param permissionGroupName The permission group whose permissions are desired
-     * @param executor Executor on which to invoke the callback
-     * @param callback A callback which will receive a list of the platform permissions in the
-     *                 group, or empty if the group is not a valid platform group, or there
-     *                 was an exception.
+     * @param permissionGroupName the permission group whose permissions are desired
+     * @param executor the {@link Executor} on which to invoke the callback
+     * @param callback the callback which will receive a list of the platform-defined permissions in
+     *                 the group, or empty if the group is not a valid platform-defined permission
+     *                 group, or there was an exception
      */
     public void getPlatformPermissionsForGroup(@NonNull String permissionGroupName,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull Consumer<List<String>> callback) {}
 
     /**
-     * Get the platform group of a particular permission, if the permission is a platform
-     * permission.
+     * Get the platform-defined permission group of a particular permission, if the permission is a
+     * platform-defined permission.
      *
-     * @param permissionName The permission name whose group is desired
-     * @param executor Executor on which to invoke the callback
-     * @param callback A callback which will receive the name of the permission group this
-     *                 permission belongs to, or null if it has no group, is not a platform
-     *                 permission, or there was an exception.
+     * @param permissionName the permission whose group is desired
+     * @param executor the {@link Executor} on which to invoke the callback
+     * @param callback the callback which will receive the name of the permission group this
+     *                 permission belongs to, or {@code null} if it has no group, is not a
+     *                 platform-defined permission, or there was an exception
      */
     public void getGroupOfPlatformPermission(@NonNull String permissionName,
             @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<String> callback) {}
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index f0d410f..881e0cf 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -481,6 +481,7 @@
 
     boolean mFullscreenApplied;
     boolean mIsFullscreen;
+    private boolean mLastWasInFullscreenMode;
     @UnsupportedAppUsage
     View mExtractView;
     boolean mExtractViewHidden;
@@ -920,8 +921,17 @@
         if (mHandler == null) {
             mHandler = new Handler(getMainLooper());
         }
-        mImeSurfaceScheduledForRemoval = true;
-        mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS);
+
+        if (mLastWasInFullscreenMode) {
+            // Caching surface / delaying surface removal can cause mServedView to detach in certain
+            // cases in RecyclerView (b/187772544).
+            // TODO(b/188818557): Re-enable IME surface caching for fullscreen mode once detaching
+            //  view issues is resolved in RecyclerView.
+            removeImeSurface();
+        } else {
+            mImeSurfaceScheduledForRemoval = true;
+            mHandler.postDelayed(() -> removeImeSurface(), TIMEOUT_SURFACE_REMOVAL_MILLIS);
+        }
     }
 
     private void removeImeSurface() {
@@ -2350,6 +2360,7 @@
             onWindowHidden();
             mDecorViewWasVisible = false;
         }
+        mLastWasInFullscreenMode = mIsFullscreen;
         updateFullscreenMode();
     }
 
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java
index 370052d..f2857ce 100644
--- a/core/java/android/os/BatteryUsageStats.java
+++ b/core/java/android/os/BatteryUsageStats.java
@@ -368,27 +368,19 @@
     };
 
     /** Returns a proto (as used for atoms.proto) corresponding to this BatteryUsageStats. */
-    public byte[] getStatsProto(long sessionEndTimestampMs) {
-
-        final long sessionStartMillis = getStatsStartTimestamp();
-        // TODO(b/187223764): Use the getStatsEndTimestamp() instead, once that is added.
-        final long sessionEndMillis = sessionEndTimestampMs;
-        final long sessionDurationMillis = sessionEndTimestampMs - getStatsStartTimestamp();
-
+    public byte[] getStatsProto() {
         final BatteryConsumer deviceBatteryConsumer = getAggregateBatteryConsumer(
                 AGGREGATE_BATTERY_CONSUMER_SCOPE_DEVICE);
 
-        final int sessionDischargePercentage = getDischargePercentage();
-
         final ProtoOutputStream proto = new ProtoOutputStream();
-        proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, sessionStartMillis);
-        proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, sessionEndMillis);
-        proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, sessionDurationMillis);
+        proto.write(BatteryUsageStatsAtomsProto.SESSION_START_MILLIS, getStatsStartTimestamp());
+        proto.write(BatteryUsageStatsAtomsProto.SESSION_END_MILLIS, getStatsEndTimestamp());
+        proto.write(BatteryUsageStatsAtomsProto.SESSION_DURATION_MILLIS, getStatsDuration());
         deviceBatteryConsumer.writeStatsProto(proto,
                 BatteryUsageStatsAtomsProto.DEVICE_BATTERY_CONSUMER);
         writeUidBatteryConsumersProto(proto);
         proto.write(BatteryUsageStatsAtomsProto.SESSION_DISCHARGE_PERCENTAGE,
-                sessionDischargePercentage);
+                getDischargePercentage());
         return proto.getBytes();
     }
 
@@ -399,8 +391,8 @@
     private void writeUidBatteryConsumersProto(ProtoOutputStream proto) {
         final List<UidBatteryConsumer> consumers = getUidBatteryConsumers();
 
-        // TODO: Sort the list by power consumption. If during the for, proto.getRawSize() > 45kb,
-        //       truncate the remainder of the list.
+        // TODO(b/189225426): Sort the list by power consumption. If during the for,
+        //                    proto.getRawSize() > 45kb, truncate the remainder of the list.
         final int size = consumers.size();
         for (int i = 0; i < size; i++) {
             final UidBatteryConsumer consumer = consumers.get(i);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 5848d2f..224cd84 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -189,28 +189,6 @@
     public @interface QuietModeFlag {}
 
     /**
-     * Flag returned by {@link #getUserPrivacySensitivity} to indicate that the user isn't
-     * particularly sensitive about a certain aspect of privacy.
-     */
-    public static final int PRIVACY_SENSITIVITY_DEFAULT = 0x0;
-
-    /**
-     * Flag returned by {@link #getUserPrivacySensitivity} to indicate that the user is sensitive
-     * about location privacy.
-     */
-    public static final int PRIVACY_SENSITIVITY_LOCATION = 0x1;
-
-    /**
-     * List of flags available for the {@link #getUserPrivacySensitivity} method.
-     * @hide
-     */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(flag = true, prefix = { "PRIVACY_SENSITIVITY_" }, value = {
-            PRIVACY_SENSITIVITY_DEFAULT,
-            PRIVACY_SENSITIVITY_LOCATION})
-    public @interface PrivacySensitivityFlag {}
-
-    /**
      * @hide
      * No user restriction.
      */
@@ -3960,17 +3938,6 @@
     }
 
     /**
-     * Get the privacy sensitivity of the user.
-     *
-     * @return the privacy sensitivity of the user
-     */
-    @PrivacySensitivityFlag
-    public int getUserPrivacySensitivity() {
-        // TODO: Add actual implementation.
-        return PRIVACY_SENSITIVITY_DEFAULT;
-    }
-
-    /**
      * Returns whether the given user has a badge (generally to put on profiles' icons).
      *
      * @param userId userId of the user in question
diff --git a/core/java/android/os/VibratorManager.java b/core/java/android/os/VibratorManager.java
index 01cece3..c82a516 100644
--- a/core/java/android/os/VibratorManager.java
+++ b/core/java/android/os/VibratorManager.java
@@ -94,6 +94,8 @@
      * VibrationEffect VibrationEffects} to be played on one or more vibrators.
      * </p>
      *
+     * <p>The app should be in foreground for the vibration to happen.</p>
+     *
      * @param effect a combination of effects to be performed by one or more vibrators.
      */
     @RequiresPermission(android.Manifest.permission.VIBRATE)
@@ -109,6 +111,9 @@
      * VibrationEffect} to be played on one or more vibrators.
      * </p>
      *
+     * <p>The app should be in foreground for the vibration to happen. Background apps should
+     * specify a ringtone, notification or alarm usage in order to vibrate.</p>
+     *
      * @param effect a combination of effects to be performed by one or more vibrators.
      * @param attributes {@link VibrationAttributes} corresponding to the vibration. For example,
      *                   specify {@link VibrationAttributes#USAGE_ALARM} for alarm vibrations or
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5cfb665..e3302d1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9850,12 +9850,10 @@
         public static final String MEDIA_CONTROLS_RESUME = "qs_media_resumption";
 
         /**
-         * Controls which packages are blocked from persisting in media controls when resumption is
-         * enabled. The list of packages is set by the user in the Settings app.
-         * @see Settings.Secure#MEDIA_CONTROLS_RESUME
+         * Controls whether contextual suggestions can be shown in the media controls.
          * @hide
          */
-        public static final String MEDIA_CONTROLS_RESUME_BLOCKED = "qs_media_resumption_blocked";
+        public static final String MEDIA_CONTROLS_RECOMMENDATION = "qs_media_recommend";
 
         /**
          * Controls magnification mode when magnification is enabled via a system-wide triple tap
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 0acacb6..908d236 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9828,30 +9828,37 @@
         if (mContext.getContentCaptureOptions() == null) return;
 
         if (appeared) {
-            if (!isLaidOut() || getVisibility() != VISIBLE
-                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0) {
+            // The appeared event stops sending to AiAi.
+            // 1. The view is hidden.
+            // 2. The same event was sent.
+            // 3. The view is not laid out, and it will be laid out in the future.
+            //    Some recycled views cached its layout and a relayout is unnecessary. In this case,
+            // system still needs to notify content capture the view appeared. When a view is
+            // recycled, it will set the flag PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED.
+            final boolean isRecycledWithoutRelayout = getNotifiedContentCaptureDisappeared()
+                    && getVisibility() == VISIBLE
+                    && !isLayoutRequested();
+            if (getVisibility() != VISIBLE || getNotifiedContentCaptureAppeared()
+                    || !(isLaidOut() || isRecycledWithoutRelayout)) {
                 if (DEBUG_CONTENT_CAPTURE) {
                     Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'appeared' on " + this + ": laid="
                             + isLaidOut() + ", visibleToUser=" + isVisibleToUser()
                             + ", visible=" + (getVisibility() == VISIBLE)
-                            + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4
-                            & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
-                            + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
-                            & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+                            + ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared()
+                            + ", alreadyNotifiedDisappeared="
+                            + getNotifiedContentCaptureDisappeared());
                 }
                 return;
             }
         } else {
-            if ((mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) == 0
-                    || (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0) {
+            if (!getNotifiedContentCaptureAppeared() || getNotifiedContentCaptureDisappeared()) {
                 if (DEBUG_CONTENT_CAPTURE) {
                     Log.v(CONTENT_CAPTURE_LOG_TAG, "Ignoring 'disappeared' on " + this + ": laid="
                             + isLaidOut() + ", visibleToUser=" + isVisibleToUser()
                             + ", visible=" + (getVisibility() == VISIBLE)
-                            + ": alreadyNotifiedAppeared=" + ((mPrivateFlags4
-                            & PFLAG4_NOTIFIED_CONTENT_CAPTURE_APPEARED) != 0)
-                            + ", alreadyNotifiedDisappeared=" + ((mPrivateFlags4
-                            & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0));
+                            + ": alreadyNotifiedAppeared=" + getNotifiedContentCaptureAppeared()
+                            + ", alreadyNotifiedDisappeared="
+                            + getNotifiedContentCaptureDisappeared());
                 }
                 return;
             }
@@ -9899,6 +9906,10 @@
     }
 
 
+    private boolean getNotifiedContentCaptureDisappeared() {
+        return (mPrivateFlags4 & PFLAG4_NOTIFIED_CONTENT_CAPTURE_DISAPPEARED) != 0;
+    }
+
     /**
      * Sets the (optional) {@link ContentCaptureSession} associated with this view.
      *
diff --git a/core/java/com/android/internal/os/CpuPowerCalculator.java b/core/java/com/android/internal/os/CpuPowerCalculator.java
index 0d041c4..e693d9d 100644
--- a/core/java/com/android/internal/os/CpuPowerCalculator.java
+++ b/core/java/com/android/internal/os/CpuPowerCalculator.java
@@ -201,7 +201,10 @@
         result.packageWithHighestDrain = packageWithHighestDrain;
     }
 
-    private double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
+    /**
+     * Calculates CPU power consumed by the specified app, using the PowerProfile model.
+     */
+    public double calculateUidModeledPowerMah(BatteryStats.Uid u, int statsType) {
         // Constant battery drain when CPU is active
         double powerMah = calculateActiveCpuPowerMah(u.getCpuActiveTime());
 
diff --git a/core/java/com/android/internal/os/SystemServicePowerCalculator.java b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
index a26abc2..b4a2b63 100644
--- a/core/java/com/android/internal/os/SystemServicePowerCalculator.java
+++ b/core/java/com/android/internal/os/SystemServicePowerCalculator.java
@@ -40,8 +40,10 @@
     // to this layout:
     // {cluster1-speed1, cluster1-speed2, ..., cluster2-speed1, cluster2-speed2, ...}
     private final UsageBasedPowerEstimator[] mPowerEstimators;
+    private final CpuPowerCalculator mCpuPowerCalculator;
 
     public SystemServicePowerCalculator(PowerProfile powerProfile) {
+        mCpuPowerCalculator = new CpuPowerCalculator(powerProfile);
         int numFreqs = 0;
         final int numCpuClusters = powerProfile.getNumCpuClusters();
         for (int cluster = 0; cluster < numCpuClusters; cluster++) {
@@ -62,7 +64,22 @@
     @Override
     public void calculate(BatteryUsageStats.Builder builder, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
-        double systemServicePowerMah = calculateSystemServicePower(batteryStats);
+        final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
+        if (systemUid == null) {
+            return;
+        }
+
+        final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
+        final int powerModel = getPowerModel(consumptionUC, query);
+
+        double systemServicePowerMah;
+        if (powerModel == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+            systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
+                    systemUid, consumptionUC);
+        } else {
+            systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
+        }
+
         final SparseArray<UidBatteryConsumer.Builder> uidBatteryConsumerBuilders =
                 builder.getUidBatteryConsumerBuilders();
         final UidBatteryConsumer.Builder systemServerConsumer = uidBatteryConsumerBuilders.get(
@@ -76,7 +93,7 @@
             // distributed to applications
             systemServerConsumer.setConsumedPower(
                     BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS,
-                    -systemServicePowerMah);
+                    -systemServicePowerMah, powerModel);
         }
 
         for (int i = uidBatteryConsumerBuilders.size() - 1; i >= 0; i--) {
@@ -84,7 +101,8 @@
             if (app != systemServerConsumer) {
                 final BatteryStats.Uid uid = app.getBatteryStatsUid();
                 app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES,
-                        systemServicePowerMah * uid.getProportionalSystemServiceUsage());
+                        systemServicePowerMah * uid.getProportionalSystemServiceUsage(),
+                        powerModel);
             }
         }
 
@@ -102,7 +120,20 @@
     public void calculate(List<BatterySipper> sippers, BatteryStats batteryStats,
             long rawRealtimeUs, long rawUptimeUs, int statsType,
             SparseArray<UserHandle> asUsers) {
-        double systemServicePowerMah = calculateSystemServicePower(batteryStats);
+        final BatteryStats.Uid systemUid = batteryStats.getUidStats().get(Process.SYSTEM_UID);
+        if (systemUid == null) {
+            return;
+        }
+
+        final long consumptionUC = systemUid.getCpuMeasuredBatteryConsumptionUC();
+        double systemServicePowerMah;
+        if (getPowerModel(consumptionUC) == BatteryConsumer.POWER_MODEL_MEASURED_ENERGY) {
+            systemServicePowerMah = calculatePowerUsingMeasuredConsumption(batteryStats,
+                    systemUid, consumptionUC);
+        } else {
+            systemServicePowerMah = calculatePowerUsingPowerProfile(batteryStats);
+        }
+
         BatterySipper systemServerSipper = null;
         for (int i = sippers.size() - 1; i >= 0; i--) {
             final BatterySipper app = sippers.get(i);
@@ -134,7 +165,21 @@
         }
     }
 
-    private double calculateSystemServicePower(BatteryStats batteryStats) {
+    private double calculatePowerUsingMeasuredConsumption(BatteryStats batteryStats,
+            BatteryStats.Uid systemUid, long consumptionUC) {
+        // Use the PowerProfile based model to estimate the ratio between the power consumed
+        // while handling incoming binder calls and the entire System UID power consumption.
+        // Apply that ratio to the _measured_ system UID power consumption to get a more
+        // accurate estimate of the power consumed by incoming binder calls.
+        final double systemServiceModeledPowerMah = calculatePowerUsingPowerProfile(batteryStats);
+        final double systemUidModeledPowerMah = mCpuPowerCalculator.calculateUidModeledPowerMah(
+                systemUid, BatteryStats.STATS_SINCE_CHARGED);
+
+        return uCtoMah(consumptionUC) * systemServiceModeledPowerMah
+                / systemUidModeledPowerMah;
+    }
+
+    private double calculatePowerUsingPowerProfile(BatteryStats batteryStats) {
         final long[] systemServiceTimeAtCpuSpeeds = batteryStats.getSystemServiceTimeAtCpuSpeeds();
         if (systemServiceTimeAtCpuSpeeds == null) {
             return 0;
@@ -145,13 +190,12 @@
         double powerMah = 0;
         final int size = Math.min(mPowerEstimators.length, systemServiceTimeAtCpuSpeeds.length);
         for (int i = 0; i < size; i++) {
-            powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i]);
+            powerMah += mPowerEstimators[i].calculatePower(systemServiceTimeAtCpuSpeeds[i] / 1000);
         }
 
         if (DEBUG) {
             Log.d(TAG, "System service power:" + powerMah);
         }
-
         return powerMah;
     }
 }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 084d4d1..f457c56 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2890,7 +2890,13 @@
     <permission android:name="android.permission.SET_DISPLAY_OFFSET"
         android:protectionLevel="signature|privileged" />
 
-    <!-- Allows a companion app to run in the background.
+    <!-- Allows a companion app to run in the background. This permission implies
+         {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND},
+         and allows to start a foreground service from the background.
+         If an app does not have to run in the background, but only needs to start a foreground
+         service from the background, consider using
+         {@link android.Manifest.permission#REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND},
+         which is less powerful.
          <p>Protection level: normal
     -->
     <permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND"
@@ -2900,6 +2906,7 @@
 
     <!-- Allows a companion app to start a foreground service from the background.
          {@see android.Manifest.permission#REQUEST_COMPANION_RUN_IN_BACKGROUND}
+         <p>Protection level: normal
          -->
     <permission android:name="android.permission.REQUEST_COMPANION_START_FOREGROUND_SERVICES_FROM_BACKGROUND"
         android:protectionLevel="normal"/>
diff --git a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
index bee0a0b..333eebb 100644
--- a/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
+++ b/core/tests/batterystatstests/BatteryUsageStatsProtoTests/src/com/android/internal/os/BatteryUsageStatsPulledTest.java
@@ -48,9 +48,8 @@
 
     @Test
     public void testGetStatsProto() {
-        final long sessionEndTimestampMs = 1050;
         final BatteryUsageStats bus = buildBatteryUsageStats();
-        final byte[] bytes = bus.getStatsProto(sessionEndTimestampMs);
+        final byte[] bytes = bus.getStatsProto();
         BatteryUsageStatsAtomsProto proto;
         try {
             proto = BatteryUsageStatsAtomsProto.parseFrom(bytes);
@@ -60,9 +59,9 @@
         }
 
         assertEquals(bus.getStatsStartTimestamp(), proto.sessionStartMillis);
-        assertEquals(sessionEndTimestampMs, proto.sessionEndMillis);
+        assertEquals(bus.getStatsEndTimestamp(), proto.sessionEndMillis);
         assertEquals(
-                sessionEndTimestampMs - bus.getStatsStartTimestamp(),
+                bus.getStatsEndTimestamp() - bus.getStatsStartTimestamp(),
                 proto.sessionDurationMillis);
         assertEquals(bus.getDischargePercentage(), proto.sessionDischargePercentage);
 
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index cd45060..a36d9fe 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -18,25 +18,28 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.os.BatteryConsumer;
-import android.os.BatteryStats;
-import android.os.BatteryUsageStatsQuery;
 import android.os.Binder;
 import android.os.Process;
-import android.os.UidBatteryConsumer;
 
-import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.internal.power.MeasuredEnergyStats;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -47,6 +50,8 @@
 public class SystemServicePowerCalculatorTest {
 
     private static final double PRECISION = 0.000001;
+    private static final int APP_UID1 = 100;
+    private static final int APP_UID2 = 200;
 
     @Rule
     public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule()
@@ -61,73 +66,52 @@
             .setAveragePowerForCpuCore(1, 0, 500)
             .setAveragePowerForCpuCore(1, 1, 600);
 
+    @Mock
     private BatteryStatsImpl.UserInfoProvider mMockUserInfoProvider;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader mMockKernelCpuUidClusterTimeReader;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidUserSysTimeReader mMockKernelCpuUidUserSysTimeReader;
+    @Mock
+    private KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader mMockKerneCpuUidActiveTimeReader;
+    @Mock
+    private SystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
+
+    private final KernelCpuSpeedReader[] mMockKernelCpuSpeedReaders = new KernelCpuSpeedReader[]{
+            mock(KernelCpuSpeedReader.class),
+            mock(KernelCpuSpeedReader.class),
+    };
+
     private MockBatteryStatsImpl mMockBatteryStats;
-    private MockKernelCpuUidFreqTimeReader mMockCpuUidFreqTimeReader;
-    private MockSystemServerCpuThreadReader mMockSystemServerCpuThreadReader;
 
     @Before
     public void setUp() throws IOException {
-        mMockUserInfoProvider = mock(BatteryStatsImpl.UserInfoProvider.class);
-        mMockSystemServerCpuThreadReader = new MockSystemServerCpuThreadReader();
-        mMockCpuUidFreqTimeReader = new MockKernelCpuUidFreqTimeReader();
+        MockitoAnnotations.initMocks(this);
         mMockBatteryStats = mStatsRule.getBatteryStats()
-                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader)
+                .setUserInfoProvider(mMockUserInfoProvider)
+                .setKernelCpuSpeedReaders(mMockKernelCpuSpeedReaders)
                 .setKernelCpuUidFreqTimeReader(mMockCpuUidFreqTimeReader)
-                .setUserInfoProvider(mMockUserInfoProvider);
+                .setKernelCpuUidClusterTimeReader(mMockKernelCpuUidClusterTimeReader)
+                .setKernelCpuUidUserSysTimeReader(mMockKernelCpuUidUserSysTimeReader)
+                .setKernelCpuUidActiveTimeReader(mMockKerneCpuUidActiveTimeReader)
+                .setSystemServerCpuThreadReader(mMockSystemServerCpuThreadReader);
     }
 
     @Test
     public void testPowerProfileBasedModel() {
-        when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
-
-        // Test Power Profile has two CPU clusters with 2 speeds each, thus 4 freq times total
-        mMockSystemServerCpuThreadReader.setCpuTimes(
-                new long[] {30000, 40000, 50000, 60000},
-                new long[] {20000, 30000, 40000, 50000});
-
-        mMockCpuUidFreqTimeReader.setSystemServerCpuTimes(
-                new long[] {10000, 20000, 30000, 40000}
-        );
-
-        mMockBatteryStats.readKernelUidCpuFreqTimesLocked(null, true, false, null);
-
-        int workSourceUid1 = 100;
-        int workSourceUid2 = 200;
-        int transactionCode = 42;
-
-        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
-        BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(workSourceUid1,
-                Binder.class, transactionCode, true /*screenInteractive */);
-        stat1.incrementalCallCount = 100;
-        stat1.recordedCallCount = 100;
-        stat1.cpuTimeMicros = 1000000;
-        callStats.add(stat1);
-
-        mMockBatteryStats.noteBinderCallStats(workSourceUid1, 100, callStats);
-
-        callStats.clear();
-        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(workSourceUid2,
-                Binder.class, transactionCode, true /*screenInteractive */);
-        stat2.incrementalCallCount = 100;
-        stat2.recordedCallCount = 100;
-        stat2.cpuTimeMicros = 9000000;
-        callStats.add(stat2);
-
-        mMockBatteryStats.noteBinderCallStats(workSourceUid2, 100, callStats);
-
-        mMockBatteryStats.updateSystemServiceCallStats();
-        mMockBatteryStats.updateSystemServerThreadStats();
+        prepareBatteryStats(null);
 
         SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
                 mStatsRule.getPowerProfile());
 
-        mStatsRule.apply(new FakeCpuPowerCalculator(), calculator);
+        mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);
 
-        assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid1)
+        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
                 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                 .isWithin(PRECISION).of(1.888888);
-        assertThat(mStatsRule.getUidBatteryConsumer(workSourceUid2)
+        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
                 .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
                 .isWithin(PRECISION).of(17.0);
         assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
@@ -141,58 +125,105 @@
                 .isWithin(PRECISION).of(18.888888);
     }
 
-    private static class MockKernelCpuUidFreqTimeReader extends
-            KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader {
-        private long[] mSystemServerCpuTimes;
+    @Test
+    public void testMeasuredEnergyBasedModel() {
+        final boolean[] supportedPowerBuckets =
+                new boolean[MeasuredEnergyStats.NUMBER_STANDARD_POWER_BUCKETS];
+        supportedPowerBuckets[MeasuredEnergyStats.POWER_BUCKET_CPU] = true;
+        mStatsRule.getBatteryStats()
+                .initMeasuredEnergyStatsLocked(supportedPowerBuckets, new String[0]);
 
-        MockKernelCpuUidFreqTimeReader() {
-            super(/*throttle */false);
-        }
+        prepareBatteryStats(new long[]{50000000, 100000000});
 
-        void setSystemServerCpuTimes(long[] systemServerCpuTimes) {
-            mSystemServerCpuTimes = systemServerCpuTimes;
-        }
+        SystemServicePowerCalculator calculator = new SystemServicePowerCalculator(
+                mStatsRule.getPowerProfile());
 
-        @Override
-        public boolean perClusterTimesAvailable() {
-            return true;
-        }
+        mStatsRule.apply(new CpuPowerCalculator(mStatsRule.getPowerProfile()), calculator);
 
-        @Override
-        public void readDelta(boolean forcedRead, @Nullable Callback<long[]> cb) {
-            if (cb != null) {
-                cb.onUidCpuTime(Process.SYSTEM_UID, mSystemServerCpuTimes);
-            }
-        }
+        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID1)
+                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
+                .isWithin(PRECISION).of(1.979351);
+        assertThat(mStatsRule.getUidBatteryConsumer(APP_UID2)
+                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
+                .isWithin(PRECISION).of(17.814165);
+        assertThat(mStatsRule.getUidBatteryConsumer(Process.SYSTEM_UID)
+                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_REATTRIBUTED_TO_OTHER_CONSUMERS))
+                .isWithin(PRECISION).of(-19.793517);
+        assertThat(mStatsRule.getDeviceBatteryConsumer()
+                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
+                .isWithin(PRECISION).of(19.793517);
+        assertThat(mStatsRule.getAppsBatteryConsumer()
+                .getConsumedPower(BatteryConsumer.POWER_COMPONENT_SYSTEM_SERVICES))
+                .isWithin(PRECISION).of(19.793517);
     }
 
-    private static class MockSystemServerCpuThreadReader extends SystemServerCpuThreadReader {
-        private final SystemServiceCpuThreadTimes mThreadTimes = new SystemServiceCpuThreadTimes();
+    private void prepareBatteryStats(long[] clusterChargesUc) {
+        when(mMockUserInfoProvider.exists(anyInt())).thenReturn(true);
 
-        MockSystemServerCpuThreadReader() {
-            super(null);
-        }
+        when(mMockKernelCpuSpeedReaders[0].readDelta()).thenReturn(new long[]{1000, 2000});
+        when(mMockKernelCpuSpeedReaders[1].readDelta()).thenReturn(new long[]{3000, 4000});
 
-        public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
-            mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
-            mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
-        }
+        when(mMockCpuUidFreqTimeReader.perClusterTimesAvailable()).thenReturn(false);
 
-        @Override
-        public SystemServiceCpuThreadTimes readDelta() {
-            return mThreadTimes;
-        }
-    }
+        // User/System CPU time in microseconds
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+            callback.onUidCpuTime(APP_UID1, new long[]{1_000_000, 2_000_000});
+            callback.onUidCpuTime(APP_UID2, new long[]{3_000_000, 4_000_000});
+            callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{60_000_000, 80_000_000});
+            return null;
+        }).when(mMockKernelCpuUidUserSysTimeReader).readDelta(anyBoolean(), any());
 
-    private static class FakeCpuPowerCalculator extends PowerCalculator {
-        @Override
-        protected void calculateApp(UidBatteryConsumer.Builder app, BatteryStats.Uid u,
-                long rawRealtimeUs, long rawUptimeUs, BatteryUsageStatsQuery query) {
-            if (u.getUid() == Process.SYSTEM_UID) {
-                // SystemServer must be attributed at least as much power as the total
-                // of all system services requested by apps.
-                app.setConsumedPower(BatteryConsumer.POWER_COMPONENT_CPU, 1000000);
-            }
-        }
+        // Active CPU time in milliseconds
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<Long> callback = invocation.getArgument(1);
+            callback.onUidCpuTime(APP_UID1, 1111L);
+            callback.onUidCpuTime(APP_UID2, 3333L);
+            callback.onUidCpuTime(Process.SYSTEM_UID, 10000L);
+            return null;
+        }).when(mMockKerneCpuUidActiveTimeReader).readDelta(anyBoolean(), any());
+
+        // Per-cluster CPU time in milliseconds
+        doAnswer(invocation -> {
+            final KernelCpuUidTimeReader.Callback<long[]> callback = invocation.getArgument(1);
+            callback.onUidCpuTime(APP_UID1, new long[]{1111, 2222});
+            callback.onUidCpuTime(APP_UID2, new long[]{3333, 4444});
+            callback.onUidCpuTime(Process.SYSTEM_UID, new long[]{50_000, 80_000});
+            return null;
+        }).when(mMockKernelCpuUidClusterTimeReader).readDelta(anyBoolean(), any());
+
+        // System service CPU time
+        final SystemServerCpuThreadReader.SystemServiceCpuThreadTimes threadTimes =
+                new SystemServerCpuThreadReader.SystemServiceCpuThreadTimes();
+        threadTimes.binderThreadCpuTimesUs =
+                new long[]{20_000_000, 30_000_000, 40_000_000, 50_000_000};
+
+        when(mMockSystemServerCpuThreadReader.readDelta()).thenReturn(threadTimes);
+
+        int transactionCode = 42;
+
+        Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>();
+        BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(APP_UID1,
+                Binder.class, transactionCode, true /*screenInteractive */);
+        stat1.incrementalCallCount = 100;
+        stat1.recordedCallCount = 100;
+        stat1.cpuTimeMicros = 1_000_000;
+        callStats.add(stat1);
+
+        mMockBatteryStats.noteBinderCallStats(APP_UID1, 100, callStats);
+
+        callStats.clear();
+        BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(APP_UID2,
+                Binder.class, transactionCode, true /*screenInteractive */);
+        stat2.incrementalCallCount = 100;
+        stat2.recordedCallCount = 100;
+        stat2.cpuTimeMicros = 9_000_000;
+        callStats.add(stat2);
+
+        mMockBatteryStats.noteBinderCallStats(APP_UID2, 100, callStats);
+
+        mMockBatteryStats.updateCpuTimeLocked(true, true, clusterChargesUc);
+
+        mMockBatteryStats.prepareForDumpLocked();
     }
 }
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index be45f18..7ad46a1 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -975,7 +975,7 @@
         shader.setColor(color, effectColor);
         shader.setOrigin(cx, cy);
         shader.setTouch(x, y);
-        shader.setResolution(w, h, mState.mDensity);
+        shader.setResolution(w, h);
         shader.setNoisePhase(noisePhase);
         shader.setRadius(radius);
         shader.setProgress(.0f);
@@ -1193,6 +1193,9 @@
             mRipplePaint = new Paint();
             mRipplePaint.setAntiAlias(true);
             mRipplePaint.setStyle(Paint.Style.FILL);
+            if (mState.mRippleStyle == STYLE_PATTERNED) {
+                mRipplePaint.setDither(true);
+            }
         }
 
         final float x = mHotspotBounds.exactCenterX();
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index eb726c1..57b3223 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -20,7 +20,6 @@
 import android.graphics.Color;
 import android.graphics.RuntimeShader;
 import android.graphics.Shader;
-import android.util.DisplayMetrics;
 
 final class RippleShader extends RuntimeShader {
     private static final String SHADER_UNIFORMS =  "uniform vec2 in_origin;\n"
@@ -204,8 +203,8 @@
                 sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()});
     }
 
-    public void setResolution(float w, float h, int density) {
-        final float densityScale = density * DisplayMetrics.DENSITY_DEFAULT_SCALE * 0.8f;
+    public void setResolution(float w, float h) {
+        final float densityScale = 2.1f;
         setUniform("in_resolutionScale", new float[] {1f / w, 1f / h});
         setUniform("in_noiseScale", new float[] {densityScale / w, densityScale / h});
     }
diff --git a/location/java/android/location/LocationDeviceConfig.java b/location/java/android/location/LocationDeviceConfig.java
index 9284574..c55eed9 100644
--- a/location/java/android/location/LocationDeviceConfig.java
+++ b/location/java/android/location/LocationDeviceConfig.java
@@ -16,16 +16,11 @@
 
 package android.location;
 
-import android.annotation.SystemApi;
-import android.annotation.TestApi;
-
 /**
  * DeviceConfig keys within the location namespace.
  *
  * @hide
  */
-@SystemApi
-@TestApi
 public final class LocationDeviceConfig {
 
     /**
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index f83dc40..ae44c5e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1823,6 +1823,7 @@
      * @deprecated Use {@link #isProviderPackage(String, String, String)} instead.
      *
      * @hide
+     * @removed
      */
     @Deprecated
     @SystemApi
diff --git a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
index 5bedd7a..9e39355 100644
--- a/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
+++ b/packages/SettingsLib/BannerMessagePreference/src/com/android/settingslib/widget/BannerMessagePreference.java
@@ -73,11 +73,11 @@
             throw new IllegalArgumentException();
         }
 
-        @ColorRes int getAccentColorResId() {
+        public @ColorRes int getAccentColorResId() {
             return mAccentColorResId;
         }
 
-        @ColorRes int getBackgroundColorResId() {
+        public @ColorRes int getBackgroundColorResId() {
             return mBackgroundColorResId;
         }
     }
diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
index 4c45c5e..8c2621d 100644
--- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
+++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/SettingsTransitionActivity.java
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.collapsingtoolbar;
 
+import static com.android.settingslib.transition.SettingsTransitionHelper.EXTRA_PAGE_TRANSITION_TYPE;
+
 import android.app.ActivityOptions;
 import android.content.Intent;
 import android.os.Bundle;
@@ -28,6 +30,7 @@
 import androidx.fragment.app.FragmentActivity;
 
 import com.android.settingslib.transition.SettingsTransitionHelper;
+import com.android.settingslib.transition.SettingsTransitionHelper.TransitionType;
 
 /**
  * A base Activity for Settings-specific page transition. Activities extending it will get
@@ -35,7 +38,6 @@
  */
 public abstract class SettingsTransitionActivity extends FragmentActivity {
     private static final String TAG = "SettingsTransitionActivity";
-    private static final int DEFAULT_REQUEST = -1;
 
     private Toolbar mToolbar;
 
@@ -58,45 +60,17 @@
     }
 
     @Override
-    public void startActivity(Intent intent) {
-        if (!isSettingsTransitionEnabled()) {
-            super.startActivity(intent);
-            return;
-        }
-
-        super.startActivity(intent, createActivityOptionsBundleForTransition(null));
-    }
-
-    @Override
-    public void startActivity(Intent intent, @Nullable Bundle options) {
-        if (!isSettingsTransitionEnabled()) {
-            super.startActivity(intent, options);
-            return;
-        }
-
-        super.startActivity(intent, createActivityOptionsBundleForTransition(options));
-    }
-
-    @Override
-    public void startActivityForResult(Intent intent, int requestCode) {
-        if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) {
-            super.startActivityForResult(intent, requestCode);
-            return;
-        }
-
-        super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition(
-                null));
-    }
-
-    @Override
     public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
-        if (!isSettingsTransitionEnabled() || requestCode == DEFAULT_REQUEST) {
+        final int transitionType = intent.getIntExtra(EXTRA_PAGE_TRANSITION_TYPE,
+                TransitionType.TRANSITION_SHARED_AXIS);
+        if (!isSettingsTransitionEnabled() ||
+                transitionType == TransitionType.TRANSITION_NONE) {
             super.startActivityForResult(intent, requestCode, options);
             return;
         }
 
-        super.startActivityForResult(intent, requestCode, createActivityOptionsBundleForTransition(
-                options));
+        super.startActivityForResult(intent, requestCode,
+                createActivityOptionsBundleForTransition(options));
     }
 
     protected boolean isSettingsTransitionEnabled() {
diff --git a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
index 3d93964..4612861 100644
--- a/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
+++ b/packages/SettingsLib/SettingsTransition/src/com/android/settingslib/transition/SettingsTransitionHelper.java
@@ -55,6 +55,8 @@
         int TRANSITION_FADE = 2;
     }
 
+    public static final String EXTRA_PAGE_TRANSITION_TYPE = "page_transition_type";
+
     private static final String TAG = "SettingsTransitionHelper";
     private static final long DURATION = 450L;
     private static final float FADE_THROUGH_THRESHOLD = 0.22F;
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 404299a..af8cd1e 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1133,7 +1133,7 @@
     <!-- [CHAR_LIMIT=40] Label for battery level chart when charging with duration -->
     <string name="power_charging_duration"><xliff:g id="level">%1$s</xliff:g> - <xliff:g id="time">%2$s</xliff:g> left until full</string>
     <!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
-    <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Optimizing for battery health</string>
+    <string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging temporarily limited</string>
 
     <!-- Battery Info screen. Value for a status item.  Used for diagnostic info screens, precise translation isn't needed -->
     <string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index cf54083..ae6165b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -169,7 +169,7 @@
         Settings.Secure.AWARE_TAP_PAUSE_TOUCH_COUNT,
         Settings.Secure.PEOPLE_STRIP,
         Settings.Secure.MEDIA_CONTROLS_RESUME,
-        Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED,
+        Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
         Settings.Secure.ACCESSIBILITY_BUTTON_TARGETS,
         Settings.Secure.ACCESSIBILITY_MAGNIFICATION_CAPABILITY,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 50fab4f..e09d420 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -251,8 +251,7 @@
         VALIDATORS.put(Secure.TAP_GESTURE, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.PEOPLE_STRIP, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.MEDIA_CONTROLS_RESUME_BLOCKED,
-                COLON_SEPARATED_PACKAGE_LIST_VALIDATOR);
+        VALIDATORS.put(Secure.MEDIA_CONTROLS_RECOMMENDATION, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_MAGNIFICATION_MODE,
                 new InclusiveIntegerRangeValidator(
                         Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN,
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index b3c4374..989010e 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -114,6 +114,11 @@
          * Set or clear next alarm information
          */
         void setNextAlarm(@Nullable Drawable image, @Nullable String description);
+
+        /**
+         * Set or clear device media playing
+         */
+        void setMediaTarget(@Nullable SmartspaceTarget target);
     }
 
     /** Interface for launching Intents, which can differ on the lockscreen */
diff --git a/packages/SystemUI/res-keyguard/values/strings.xml b/packages/SystemUI/res-keyguard/values/strings.xml
index 4b662137..caa2166 100644
--- a/packages/SystemUI/res-keyguard/values/strings.xml
+++ b/packages/SystemUI/res-keyguard/values/strings.xml
@@ -79,8 +79,8 @@
          is not fully charged, and it's plugged into a slow charger, say that it's charging slowly.  -->
     <string name="keyguard_plugged_in_charging_slowly"><xliff:g id="percentage">%s</xliff:g> • Charging slowly</string>
 
-    <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's optimizing for battery health.  -->
-    <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Optimizing for battery health</string>
+    <!-- When the lock screen is showing and the phone plugged in, and the defend mode is triggered, say that it's charging temporarily limited.  -->
+    <string name="keyguard_plugged_in_charging_limited"><xliff:g id="percentage">%s</xliff:g> • Charging temporarily limited</string>
 
     <!-- When the lock screen is showing and the battery is low, warn user to plug
          in the phone soon. -->
diff --git a/packages/SystemUI/res/layout-land/volume_dialog.xml b/packages/SystemUI/res/layout-land/volume_dialog.xml
index b08e513..f1cda27 100644
--- a/packages/SystemUI/res/layout-land/volume_dialog.xml
+++ b/packages/SystemUI/res/layout-land/volume_dialog.xml
@@ -126,6 +126,7 @@
             android:gravity="right"
             android:layout_gravity="right"
             android:clipToPadding="false"
+            android:clipToOutline="true"
             android:background="@drawable/volume_row_rounded_background">
             <com.android.systemui.volume.CaptionsToggleImageButton
                 android:id="@+id/odi_captions_icon"
@@ -146,6 +147,7 @@
         android:layout="@layout/volume_tool_tip_view"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_gravity="bottom | right"/>
+        android:layout_gravity="bottom | right"
+        android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
 
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/people_tile_large_with_content.xml b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
index e294dad..b7e86a3 100644
--- a/packages/SystemUI/res/layout/people_tile_large_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_large_with_content.xml
@@ -14,6 +14,7 @@
 ~ limitations under the License.
 -->
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:layout_gravity="center"
@@ -52,7 +53,7 @@
             android:paddingStart="8dp"
             android:paddingEnd="8dp"
             android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-            android:textColor="?android:attr/textColorPrimary"
+            android:textColor="?androidprv:attr/textColorOnAccent"
             android:background="@drawable/people_space_messages_count_background"
             android:textSize="14sp"
             android:maxLines="1"
diff --git a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
index 47cab42..1086a13 100644
--- a/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
+++ b/packages/SystemUI/res/layout/people_tile_medium_with_content.xml
@@ -16,6 +16,7 @@
  -->
 <LinearLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:theme="@android:style/Theme.DeviceDefault.DayNight"
     android:id="@+id/item"
     android:background="@drawable/people_space_tile_view_card"
@@ -128,7 +129,7 @@
                     android:paddingStart="8dp"
                     android:paddingEnd="8dp"
                     android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-                    android:textColor="?android:attr/textColorPrimary"
+                    android:textColor="?androidprv:attr/textColorOnAccent"
                     android:background="@drawable/people_space_messages_count_background"
                     android:textSize="14sp"
                     android:maxLines="1"
diff --git a/packages/SystemUI/res/layout/people_tile_small.xml b/packages/SystemUI/res/layout/people_tile_small.xml
index 7a1371d..22fcd3b 100644
--- a/packages/SystemUI/res/layout/people_tile_small.xml
+++ b/packages/SystemUI/res/layout/people_tile_small.xml
@@ -14,6 +14,7 @@
   ~ limitations under the License.
   -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:theme="@android:style/Theme.DeviceDefault.DayNight"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
@@ -50,7 +51,7 @@
             android:gravity="center"
             android:paddingHorizontal="8dp"
             android:textAppearance="@*android:style/TextAppearance.DeviceDefault.ListItem"
-            android:textColor="?android:attr/textColorPrimary"
+            android:textColor="?androidprv:attr/textColorOnAccent"
             android:background="@drawable/people_space_messages_count_background"
             android:textSize="@dimen/name_text_size_for_small"
             android:maxLines="1"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 09d4685..fb39d3e 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -137,11 +137,12 @@
             systemui:layout_constraintRight_toRightOf="parent"
             systemui:layout_constraintBottom_toBottomOf="parent"
             android:layout_marginBottom="20dp"
-            android:paddingLeft="20dp"
-            android:paddingRight="20dp"
-            android:paddingTop="10dp"
-            android:paddingBottom="10dp"
-            android:elevation="10dp"
+            android:paddingHorizontal="16dp"
+            android:minHeight="44dp"
+            android:elevation="4dp"
+            android:background="@drawable/rounded_bg_full"
+            android:gravity="center"
+            android:text="@string/tap_again"
             android:visibility="gone"
         />
     </com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index beac057..51718d9 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -125,6 +125,7 @@
             android:gravity="right"
             android:layout_gravity="right"
             android:clipToPadding="false"
+            android:clipToOutline="true"
             android:background="@drawable/volume_row_rounded_background">
             <com.android.systemui.volume.CaptionsToggleImageButton
                 android:id="@+id/odi_captions_icon"
@@ -146,7 +147,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="bottom | right"
-        android:layout_marginRight="@dimen/volume_tool_tip_right_margin"
-        android:layout_marginBottom="@dimen/volume_tool_tip_bottom_margin"/>
+        android:layout_marginRight="@dimen/volume_tool_tip_right_margin"/>
 
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_tool_tip_view.xml b/packages/SystemUI/res/layout/volume_tool_tip_view.xml
index 9fe885e..ee24969 100644
--- a/packages/SystemUI/res/layout/volume_tool_tip_view.xml
+++ b/packages/SystemUI/res/layout/volume_tool_tip_view.xml
@@ -17,6 +17,7 @@
 
 <com.android.systemui.volume.VolumeToolTipView
     xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:id="@+id/tooltip_view"
     android:layout_height="wrap_content"
     android:layout_width="wrap_content"
@@ -35,7 +36,7 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_gravity="center_vertical"
-            android:textColor="@android:color/white"
+            android:textColor="?android:attr/textColorPrimaryInverse"
             android:text="@string/volume_odi_captions_tip"
             android:textSize="14sp"/>
         <ImageView
@@ -48,7 +49,7 @@
             android:layout_marginEnd="2dp"
             android:alpha="0.7"
             android:src="@drawable/ic_remove_no_shadow"
-            android:tint="@android:color/white"
+            android:tint="?android:attr/textColorPrimaryInverse"
             android:background="?android:attr/selectableItemBackgroundBorderless"
             android:contentDescription="@string/accessibility_volume_close_odi_captions_tip"/>
     </LinearLayout>
diff --git a/packages/SystemUI/res/values-land-television/dimens.xml b/packages/SystemUI/res/values-land-television/dimens.xml
index 220ed5c..a9bc9e5 100644
--- a/packages/SystemUI/res/values-land-television/dimens.xml
+++ b/packages/SystemUI/res/values-land-television/dimens.xml
@@ -23,6 +23,7 @@
   <dimen name="volume_dialog_slider_width">4dp</dimen>
   <dimen name="volume_dialog_slider_corner_radius">@dimen/volume_dialog_slider_width</dimen>
   <dimen name="volume_dialog_background_blur_radius">100dp</dimen>
+  <dimen name="volume_tool_tip_right_margin">136dp</dimen>
 
   <dimen name="tv_volume_dialog_bubble_size">36dp</dimen>
   <dimen name="tv_volume_dialog_row_padding">6dp</dimen>
diff --git a/packages/SystemUI/res/values-land/dimens.xml b/packages/SystemUI/res/values-land/dimens.xml
index 215698d..9df9db6 100644
--- a/packages/SystemUI/res/values-land/dimens.xml
+++ b/packages/SystemUI/res/values-land/dimens.xml
@@ -43,7 +43,6 @@
 
     <dimen name="qs_detail_margin_top">14dp</dimen>
 
-    <dimen name="volume_tool_tip_right_margin">136dp</dimen>
     <dimen name="volume_tool_tip_top_margin">12dp</dimen>
     <dimen name="volume_row_slider_height">128dp</dimen>
 
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 6393147..f4086ed 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -38,8 +38,6 @@
     <!-- The new animations to/from lockscreen and AOD! -->
     <bool name="flag_lockscreen_animations">false</bool>
 
-    <bool name="flag_toast_style">false</bool>
-
     <bool name="flag_pm_lite">false</bool>
 
     <bool name="flag_alarm_tile">false</bool>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 01d0dde..db3fae6 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1062,6 +1062,9 @@
     <!-- Shows to explain the double tap interaction with notifications: After tapping a notification on Keyguard, this will explain users to tap again to launch a notification. [CHAR LIMIT=60] -->
     <string name="notification_tap_again">Tap again to open</string>
 
+    <!-- Asks for a second tap as confirmation on an item that normally requires one tap. [CHAR LIMIT=60] -->
+    <string name="tap_again">Tap again</string>
+
     <!-- Message shown when lock screen is tapped or face authentication fails. [CHAR LIMIT=60] -->
     <string name="keyguard_unlock">Swipe up to open</string>
 
diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp
index f98a959..b2ae2a0 100644
--- a/packages/SystemUI/shared/Android.bp
+++ b/packages/SystemUI/shared/Android.bp
@@ -40,7 +40,8 @@
     name: "SystemUISharedLib",
     srcs: [
         "src/**/*.java",
-        "src/**/I*.aidl",
+        "src/**/*.kt",
+        "src/**/*.aidl",
         ":wm_shell-aidls",
     ],
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 2cf3ad2..c468e41 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -36,6 +36,9 @@
  * Various shared constants between Launcher and SysUI as part of quickstep
  */
 public class QuickStepContract {
+    // Fully qualified name of the Launcher activity.
+    public static final String LAUNCHER_ACTIVITY_CLASS_NAME =
+            "com.google.android.apps.nexuslauncher.NexusLauncherActivity";
 
     public static final String KEY_EXTRA_SYSUI_PROXY = "extra_sysui_proxy";
     public static final String KEY_EXTRA_WINDOW_CORNER_RADIUS = "extra_window_corner_radius";
@@ -52,6 +55,8 @@
     // See IStartingWindow.aidl
     public static final String KEY_EXTRA_SHELL_STARTING_WINDOW =
             "extra_shell_starting_window";
+    // See ISmartspaceTransitionController.aidl
+    public static final String KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER = "smartspace_transition";
 
     public static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
             WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl
new file mode 100644
index 0000000..511df4c
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2021 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.shared.system.smartspace;
+
+import com.android.systemui.shared.system.smartspace.SmartspaceState;
+
+// Methods for getting and setting the state of a SmartSpace. This is used to allow a remote process
+// (such as System UI) to sync with and control a SmartSpace view hosted in another process (such as
+// Launcher).
+interface ISmartspaceCallback {
+
+    // Return information about the state of the SmartSpace, including location on-screen and
+    // currently selected page.
+    SmartspaceState getSmartspaceState();
+
+    // Set the currently selected page of this SmartSpace.
+    oneway void setSelectedPage(int selectedPage);
+
+    oneway void setVisibility(int visibility);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl
new file mode 100644
index 0000000..2b3e961
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/ISmartspaceTransitionController.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2021 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.shared.system.smartspace;
+
+import com.android.systemui.shared.system.smartspace.ISmartspaceCallback;
+
+// Controller that keeps track of SmartSpace instances in remote processes (such as Launcher).
+interface ISmartspaceTransitionController {
+    oneway void setSmartspace(ISmartspaceCallback callback);
+}
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
new file mode 100644
index 0000000..2d01d6a
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2021 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.shared.system.smartspace;
+
+import com.android.systemui.shared.system.smartspace.SmartspaceState;
+
+parcelable SmartspaceState;
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
new file mode 100644
index 0000000..2d51c4d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/smartspace/SmartspaceState.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2021 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.shared.system.smartspace
+
+import android.graphics.Rect
+import android.os.Parcel
+import android.os.Parcelable
+
+/**
+ * Represents the state of a SmartSpace, including its location on screen and the index of the
+ * currently selected page. This object contains all of the information needed to synchronize two
+ * SmartSpace instances so that we can perform shared-element transitions between them.
+ */
+class SmartspaceState() : Parcelable {
+    var boundsOnScreen: Rect = Rect()
+    var selectedPage = 0
+
+    constructor(parcel: Parcel) : this() {
+        this.boundsOnScreen = parcel.readParcelable(Rect::javaClass.javaClass.classLoader)
+        this.selectedPage = parcel.readInt()
+    }
+
+    override fun writeToParcel(dest: Parcel?, flags: Int) {
+        dest?.writeParcelable(boundsOnScreen, 0)
+        dest?.writeInt(selectedPage)
+    }
+
+    override fun describeContents(): Int {
+        return 0
+    }
+
+    override fun toString(): String {
+        return "boundsOnScreen: $boundsOnScreen, selectedPage: $selectedPage"
+    }
+
+    companion object CREATOR : Parcelable.Creator<SmartspaceState> {
+        override fun createFromParcel(parcel: Parcel): SmartspaceState {
+            return SmartspaceState(parcel)
+        }
+
+        override fun newArray(size: Int): Array<SmartspaceState?> {
+            return arrayOfNulls(size)
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index aa7f9a2..28534d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -32,8 +32,10 @@
 import com.android.systemui.R;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
@@ -44,7 +46,9 @@
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.ViewController;
 
+import java.util.HashSet;
 import java.util.Locale;
+import java.util.Set;
 import java.util.TimeZone;
 
 import javax.inject.Inject;
@@ -92,6 +96,9 @@
     // If set, will replace keyguard_status_area
     private View mSmartspaceView;
 
+    private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+    private SmartspaceTransitionController mSmartspaceTransitionController;
+
     @Inject
     public KeyguardClockSwitchController(
             KeyguardClockSwitch keyguardClockSwitch,
@@ -104,7 +111,9 @@
             BatteryController batteryController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             KeyguardBypassController bypassController,
-            LockscreenSmartspaceController smartspaceController) {
+            LockscreenSmartspaceController smartspaceController,
+            KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+            SmartspaceTransitionController smartspaceTransitionController) {
         super(keyguardClockSwitch);
         mStatusBarStateController = statusBarStateController;
         mColorExtractor = colorExtractor;
@@ -116,6 +125,9 @@
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mBypassController = bypassController;
         mSmartspaceController = smartspaceController;
+
+        mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+        mSmartspaceTransitionController = smartspaceTransitionController;
     }
 
     /**
@@ -187,6 +199,7 @@
             nic.setLayoutParams(lp);
 
             mView.setSmartspaceView(mSmartspaceView);
+            mSmartspaceTransitionController.setLockscreenSmartspace(mSmartspaceView);
         }
     }
 
@@ -285,12 +298,44 @@
         if (mSmartspaceView != null) {
             PropertyAnimator.setProperty(mSmartspaceView, AnimatableProperty.TRANSLATION_X,
                     x, props, animate);
+
+            // If we're unlocking with the SmartSpace shared element transition, let the controller
+            // know that it should re-position our SmartSpace.
+            if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
+                mKeyguardUnlockAnimationController.updateLockscreenSmartSpacePosition();
+            } else {
+                // Otherwise, reset Y translation in case it's still offset from a previous shared
+                // element transition.
+                ((View) mSmartspaceView).setTranslationY(0f);
+            }
         }
 
         mKeyguardSliceViewController.updatePosition(x, props, animate);
         mNotificationIconAreaController.updatePosition(x, props, animate);
     }
 
+    /** Sets an alpha value on every child view except for the smartspace. */
+    public void setChildrenAlphaExcludingSmartspace(float alpha) {
+        final Set<View> excludedViews = new HashSet<>();
+
+        if (mSmartspaceView != null) {
+            excludedViews.add(mSmartspaceView);
+        }
+
+        setChildrenAlphaExcluding(alpha, excludedViews);
+    }
+
+    /** Sets an alpha value on every child view except for the views in the provided set. */
+    public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) {
+        for (int i = 0; i < mView.getChildCount(); i++) {
+            final View child = mView.getChildAt(i);
+
+            if (!excludedViews.contains(child)) {
+                child.setAlpha(alpha);
+            }
+        }
+    }
+
     void updateTimeZone(TimeZone timeZone) {
         mView.onTimeZoneChanged(timeZone);
         if (mClockViewController != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
index 299eceb..96eda3d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusView.java
@@ -28,6 +28,7 @@
 import android.util.Log;
 import android.util.TypedValue;
 import android.view.View;
+import android.view.ViewGroup;
 import android.widget.GridLayout;
 import android.widget.TextView;
 
@@ -39,6 +40,7 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.Set;
 
 /**
  * View consisting of:
@@ -55,6 +57,7 @@
     private final LockPatternUtils mLockPatternUtils;
     private final IActivityManager mIActivityManager;
 
+    private ViewGroup mStatusViewContainer;
     private TextView mLogoutView;
     private KeyguardClockSwitch mClockView;
     private TextView mOwnerInfo;
@@ -66,6 +69,7 @@
 
     private float mDarkAmount = 0;
     private int mTextColor;
+    private float mChildrenAlphaExcludingSmartSpace = 1f;
 
     /**
      * Bottom margin that defines the margin between bottom of smart space and top of notification
@@ -132,6 +136,7 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
+        mStatusViewContainer = findViewById(R.id.status_view_container);
         mLogoutView = findViewById(R.id.logout);
         if (mLogoutView != null) {
             mLogoutView.setOnClickListener(this::onLogoutClicked);
@@ -249,6 +254,27 @@
         mClockView.setTextColor(blendedTextColor);
     }
 
+    public void setChildrenAlphaExcludingClockView(float alpha) {
+        setChildrenAlphaExcluding(alpha, Set.of(mClockView));
+    }
+
+    /** Sets an alpha value on every view except for the views in the provided set. */
+    public void setChildrenAlphaExcluding(float alpha, Set<View> excludedViews) {
+        mChildrenAlphaExcludingSmartSpace = alpha;
+
+        for (int i = 0; i < mStatusViewContainer.getChildCount(); i++) {
+            final View child = mStatusViewContainer.getChildAt(i);
+
+            if (!excludedViews.contains(child)) {
+                child.setAlpha(alpha);
+            }
+        }
+    }
+
+    public float getChildrenAlphaExcludingSmartSpace() {
+        return mChildrenAlphaExcludingSmartSpace;
+    }
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("KeyguardStatusView:");
         pw.println("  mOwnerInfo: " + (mOwnerInfo == null
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 388c085..7b6514a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -20,6 +20,8 @@
 import android.os.UserHandle;
 import android.util.Slog;
 
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -49,6 +51,9 @@
     private final ConfigurationController mConfigurationController;
     private final DozeParameters mDozeParameters;
     private final KeyguardVisibilityHelper mKeyguardVisibilityHelper;
+    private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+    private final KeyguardStateController mKeyguardStateController;
+    private SmartspaceTransitionController mSmartspaceTransitionController;
     private final Rect mClipBounds = new Rect();
 
     @Inject
@@ -59,15 +64,33 @@
             KeyguardStateController keyguardStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             ConfigurationController configurationController,
-            DozeParameters dozeParameters) {
+            DozeParameters dozeParameters,
+            KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+            SmartspaceTransitionController smartspaceTransitionController) {
         super(keyguardStatusView);
         mKeyguardSliceViewController = keyguardSliceViewController;
         mKeyguardClockSwitchController = keyguardClockSwitchController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mConfigurationController = configurationController;
         mDozeParameters = dozeParameters;
+        mKeyguardStateController = keyguardStateController;
         mKeyguardVisibilityHelper = new KeyguardVisibilityHelper(mView, keyguardStateController,
                 dozeParameters);
+        mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+        mSmartspaceTransitionController = smartspaceTransitionController;
+
+        mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
+            @Override
+            public void onKeyguardShowingChanged() {
+                // If we explicitly re-show the keyguard, make sure that all the child views are
+                // visible. They might have been animating out as part of the SmartSpace shared
+                // element transition.
+                if (keyguardStateController.isShowing()) {
+                    mView.setChildrenAlphaExcludingClockView(1f);
+                    mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f);
+                }
+            }
+        });
     }
 
     @Override
@@ -137,7 +160,24 @@
      */
     public void setAlpha(float alpha) {
         if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
-            mView.setAlpha(alpha);
+            // If we're capable of performing the SmartSpace shared element transition, and we are
+            // going to (we're swiping to dismiss vs. bringing up the PIN screen), then fade out
+            // everything except for the SmartSpace.
+            if (mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
+                mView.setChildrenAlphaExcludingClockView(alpha);
+                mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(alpha);
+            } else if (!mKeyguardVisibilityHelper.isVisibilityAnimating()) {
+                // Otherwise, we can just set the alpha for the entire container.
+                mView.setAlpha(alpha);
+
+                // If we previously unlocked with the shared element transition, some child views
+                // might still have alpha = 0f. Set them back to 1f since we're just using the
+                // parent container's alpha.
+                if (mView.getChildrenAlphaExcludingSmartSpace() < 1f) {
+                    mView.setChildrenAlphaExcludingClockView(1f);
+                    mKeyguardClockSwitchController.setChildrenAlphaExcludingSmartspace(1f);
+                }
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index cc167b9..e6fe060 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -133,6 +133,7 @@
                     .setTaskViewFactory(Optional.ofNullable(null))
                     .setTransitions(Transitions.createEmptyForTesting())
                     .setStartingSurface(Optional.ofNullable(null));
+
         }
         mSysUIComponent = builder.build();
         if (mInitializeComponents) {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 1396099..f422e9e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -45,6 +45,7 @@
 import com.android.systemui.recents.Recents;
 import com.android.systemui.screenshot.dagger.ScreenshotModule;
 import com.android.systemui.settings.dagger.SettingsModule;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -173,6 +174,12 @@
         return SystemUIFactory.getInstance();
     }
 
+    @SysUISingleton
+    @Provides
+    static SmartspaceTransitionController provideSmartspaceTransitionController() {
+        return new SmartspaceTransitionController();
+    }
+
     // TODO: This should provided by the WM component
     /** Provides Optional of BubbleManager */
     @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
index 411c328..665376a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardUnlockAnimationController.kt
@@ -23,11 +23,14 @@
 import android.graphics.Matrix
 import android.view.RemoteAnimationTarget
 import android.view.SyncRtSurfaceTransactionApplier
+import android.view.View
 import androidx.core.math.MathUtils
 import com.android.internal.R
+import com.android.keyguard.KeyguardClockSwitchController
 import com.android.keyguard.KeyguardViewController
 import com.android.systemui.animation.Interpolators
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController
 import com.android.systemui.statusbar.policy.KeyguardStateController
 import dagger.Lazy
 import javax.inject.Inject
@@ -85,7 +88,8 @@
     context: Context,
     private val keyguardStateController: KeyguardStateController,
     private val keyguardViewMediator: Lazy<KeyguardViewMediator>,
-    private val keyguardViewController: KeyguardViewController
+    private val keyguardViewController: KeyguardViewController,
+    private val smartspaceTransitionController: SmartspaceTransitionController
 ) : KeyguardStateController.Callback {
 
     /**
@@ -131,6 +135,21 @@
     /** Rounded corner radius to apply to the surface behind the keyguard. */
     private var roundedCornerRadius = 0f
 
+    /** The SmartSpace view on the lockscreen, provided by [KeyguardClockSwitchController]. */
+    public var lockscreenSmartSpace: View? = null
+
+    /**
+     * Whether we are currently in the process of unlocking the keyguard, and we are performing the
+     * shared element SmartSpace transition.
+     */
+    private var unlockingWithSmartSpaceTransition: Boolean = false
+
+    /**
+     * Whether we tried to start the SmartSpace shared element transition for this unlock swipe.
+     * It's possible we're unable to do so (if the Launcher SmartSpace is not available).
+     */
+    private var attemptedSmartSpaceTransitionForThisSwipe = false
+
     init {
         surfaceBehindAlphaAnimator.duration = 150
         surfaceBehindAlphaAnimator.interpolator = Interpolators.ALPHA_IN
@@ -214,6 +233,24 @@
     }
 
     /**
+     * Whether we are currently in the process of unlocking the keyguard, and we are performing the
+     * shared element SmartSpace transition.
+     */
+    fun isUnlockingWithSmartSpaceTransition(): Boolean {
+        return unlockingWithSmartSpaceTransition
+    }
+
+    /**
+     * Update the lockscreen SmartSpace to be positioned according to the current dismiss amount. As
+     * the dismiss amount increases, we will increase our SmartSpace's progress to the destination
+     * bounds (the location of the Launcher SmartSpace).
+     */
+    fun updateLockscreenSmartSpacePosition() {
+        smartspaceTransitionController.setProgressToDestinationBounds(
+                keyguardStateController.dismissAmount / DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD)
+    }
+
+    /**
      * Scales in and translates up the surface behind the keyguard. This is used during unlock
      * animations and swipe gestures to animate the surface's entry (and exit, if the swipe is
      * cancelled).
@@ -284,36 +321,85 @@
             return
         }
 
+        if (keyguardViewController.isShowing) {
+            updateKeyguardViewMediatorIfThresholdsReached()
+
+            // If the surface is visible or it's about to be, start updating its appearance to
+            // reflect the new dismiss amount.
+            if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
+                    keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
+                updateSurfaceBehindAppearAmount()
+            }
+        }
+
+        // The end of the SmartSpace transition can occur after the keyguard is hidden (when we tell
+        // Launcher's SmartSpace to become visible again), so update it even if the keyguard view is
+        // no longer showing.
+        updateSmartSpaceTransition()
+    }
+
+    /**
+     * Lets the KeyguardViewMediator know if the dismiss amount has crossed a threshold of interest,
+     * such as reaching the point in the dismiss swipe where we need to make the surface behind the
+     * keyguard visible.
+     */
+    private fun updateKeyguardViewMediatorIfThresholdsReached() {
         val dismissAmount = keyguardStateController.dismissAmount
 
         // Hide the keyguard if we're fully dismissed, or if we're swiping to dismiss and have
         // crossed the threshold to finish the dismissal.
         val reachedHideKeyguardThreshold = (dismissAmount >= 1f ||
                 (keyguardStateController.isDismissingFromSwipe &&
-                // Don't hide if we're flinging during a swipe, since we need to finish
-                // animating it out. This will be called again after the fling ends.
-                !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
-                dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD))
+                        // Don't hide if we're flinging during a swipe, since we need to finish
+                        // animating it out. This will be called again after the fling ends.
+                        !keyguardStateController.isFlingingToDismissKeyguardDuringSwipeGesture &&
+                        dismissAmount >= DISMISS_AMOUNT_EXIT_KEYGUARD_THRESHOLD))
 
         if (dismissAmount >= DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
                 !keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
-            // We passed the threshold, and we're not yet showing the surface behind the keyguard.
-            // Animate it in.
+            // We passed the threshold, and we're not yet showing the surface behind the
+            // keyguard. Animate it in.
             keyguardViewMediator.get().showSurfaceBehindKeyguard()
             fadeInSurfaceBehind()
         } else if (dismissAmount < DISMISS_AMOUNT_SHOW_SURFACE_THRESHOLD &&
                 keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard()) {
-            // We're no longer past the threshold but we are showing the surface. Animate it out.
+            // We're no longer past the threshold but we are showing the surface. Animate it
+            // out.
             keyguardViewMediator.get().hideSurfaceBehindKeyguard()
             fadeOutSurfaceBehind()
-        } else if (keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe &&
+        } else if (keyguardViewMediator.get()
+                        .isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe &&
                 reachedHideKeyguardThreshold) {
             keyguardViewMediator.get().onKeyguardExitRemoteAnimationFinished()
         }
+    }
 
-        if (keyguardViewMediator.get().requestedShowSurfaceBehindKeyguard() ||
-                keyguardViewMediator.get().isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe) {
-            updateSurfaceBehindAppearAmount()
+    /**
+     * Updates flags related to the SmartSpace transition in response to a change in keyguard
+     * dismiss amount, and also updates the SmartSpaceTransitionController, which will let Launcher
+     * know if it needs to do something as a result.
+     */
+    private fun updateSmartSpaceTransition() {
+        val dismissAmount = keyguardStateController.dismissAmount
+
+        // If we've begun a swipe, and are capable of doing the SmartSpace transition, start it!
+        if (!attemptedSmartSpaceTransitionForThisSwipe &&
+                dismissAmount > 0f &&
+                dismissAmount < 1f &&
+                keyguardViewController.isShowing) {
+            attemptedSmartSpaceTransitionForThisSwipe = true
+
+            smartspaceTransitionController.prepareForUnlockTransition()
+            if (keyguardStateController.canPerformSmartSpaceTransition()) {
+                unlockingWithSmartSpaceTransition = true
+                smartspaceTransitionController.launcherSmartspace?.setVisibility(
+                        View.INVISIBLE)
+            }
+        } else if (attemptedSmartSpaceTransitionForThisSwipe &&
+                (dismissAmount == 0f || dismissAmount == 1f)) {
+            attemptedSmartSpaceTransitionForThisSwipe = false
+            unlockingWithSmartSpaceTransition = false
+            smartspaceTransitionController.launcherSmartspace?.setVisibility(View.VISIBLE)
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 8532a46..27a4e93 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -447,11 +447,8 @@
 
             if (mKey != null) {
                 closeGuts();
-                mKeyguardDismissUtil.executeWhenUnlocked(() -> {
-                    mMediaDataManagerLazy.get().dismissMediaData(mKey,
-                            MediaViewController.GUTS_ANIMATION_DURATION + 100);
-                    return true;
-                }, /* requiresShadeOpen */ true, false);
+                mMediaDataManagerLazy.get().dismissMediaData(mKey,
+                        MediaViewController.GUTS_ANIMATION_DURATION + 100);
             } else {
                 Log.w(TAG, "Dismiss media with null notification. Token uid="
                         + data.getToken().getUid());
@@ -576,11 +573,8 @@
             logSmartspaceCardReported(761, // SMARTSPACE_CARD_DISMISS
                     /* isRecommendationCard */ true);
             closeGuts();
-            mKeyguardDismissUtil.executeWhenUnlocked(() -> {
-                mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
-                        MediaViewController.GUTS_ANIMATION_DURATION + 100L);
-                return true;
-            }, true /* requiresShadeOpen */, false);
+            mMediaDataManagerLazy.get().dismissSmartspaceRecommendation(
+                    MediaViewController.GUTS_ANIMATION_DURATION + 100L);
         });
 
         mController = null;
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index ffcec29..c74f2fe 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -138,17 +138,6 @@
     private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
     // There should ONLY be at most one Smartspace media recommendation.
     private var smartspaceMediaTarget: SmartspaceTarget? = null
-    internal var appsBlockedFromResume: MutableSet<String> = Utils.getBlockedMediaApps(context)
-        set(value) {
-            // Update list
-            appsBlockedFromResume.clear()
-            appsBlockedFromResume.addAll(value)
-
-            // Remove any existing resume players that are now blocked
-            appsBlockedFromResume.forEach {
-                removeAllForPackage(it)
-            }
-        }
     private var smartspaceSession: SmartspaceSession? = null
 
     @Inject
@@ -690,7 +679,9 @@
     }
 
     override fun onSmartspaceTargetsUpdated(targets: List<Parcelable>) {
-        Log.d(TAG, "My Smartspace media updates are here")
+        if (!Utils.allowMediaRecommendations(context)) {
+            return
+        }
         val mediaTargets = targets.filterIsInstance<SmartspaceTarget>()
         when (mediaTargets.size) {
             0 -> {
@@ -736,8 +727,7 @@
     fun onNotificationRemoved(key: String) {
         Assert.isMainThread()
         val removed = mediaEntries.remove(key)
-        if (useMediaResumption && removed?.resumeAction != null &&
-                !isBlockedFromResume(removed.packageName) && removed?.isLocalSession == true) {
+        if (useMediaResumption && removed?.resumeAction != null && removed?.isLocalSession) {
             Log.d(TAG, "Not removing $key because resumable")
             // Move to resume key (aka package name) if that key doesn't already exist.
             val resumeAction = getResumeMediaAction(removed.resumeAction!!)
@@ -765,13 +755,6 @@
         }
     }
 
-    private fun isBlockedFromResume(packageName: String?): Boolean {
-        if (packageName == null) {
-            return true
-        }
-        return appsBlockedFromResume.contains(packageName)
-    }
-
     fun setMediaResumptionEnabled(isEnabled: Boolean) {
         if (useMediaResumption == isEnabled) {
             return
@@ -844,7 +827,6 @@
             println("externalListeners: ${mediaDataFilter.listeners}")
             println("mediaEntries: $mediaEntries")
             println("useMediaResumption: $useMediaResumption")
-            println("appsBlockedFromResume: $appsBlockedFromResume")
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index b0be576..7fe408f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -23,7 +23,6 @@
 import android.content.IntentFilter
 import android.content.pm.PackageManager
 import android.media.MediaDescription
-import android.media.session.MediaController
 import android.os.UserHandle
 import android.provider.Settings
 import android.service.media.MediaBrowserService
@@ -59,7 +58,6 @@
 
     private var useMediaResumption: Boolean = Utils.useMediaResumption(context)
     private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue()
-    private var blockedApps: MutableSet<String> = Utils.getBlockedMediaApps(context)
 
     private lateinit var mediaDataManager: MediaDataManager
 
@@ -124,14 +122,6 @@
                 mediaDataManager.setMediaResumptionEnabled(useMediaResumption)
             }
         }, Settings.Secure.MEDIA_CONTROLS_RESUME)
-
-        // Listen to changes in which apps are allowed to persist
-        tunerService.addTunable(object : TunerService.Tunable {
-            override fun onTuningChanged(key: String?, newValue: String?) {
-                blockedApps = Utils.getBlockedMediaApps(context)
-                mediaDataManager.appsBlockedFromResume = blockedApps
-            }
-        }, Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED)
     }
 
     private fun loadSavedComponents() {
@@ -160,10 +150,8 @@
         }
 
         resumeComponents.forEach {
-            if (!blockedApps.contains(it.packageName)) {
-                val browser = mediaBrowserFactory.create(mediaBrowserCallback, it)
-                browser.findRecentMedia()
-            }
+            val browser = mediaBrowserFactory.create(mediaBrowserCallback, it)
+            browser.findRecentMedia()
         }
     }
 
@@ -171,9 +159,9 @@
         if (useMediaResumption) {
             // If this had been started from a resume state, disconnect now that it's live
             mediaBrowser?.disconnect()
+            mediaBrowser = null
             // If we don't have a resume action, check if we haven't already
-            if (data.resumeAction == null && !data.hasCheckedForResume &&
-                    !blockedApps.contains(data.packageName) && data.isLocalSession) {
+            if (data.resumeAction == null && !data.hasCheckedForResume && data.isLocalSession) {
                 // TODO also check for a media button receiver intended for restarting (b/154127084)
                 Log.d(TAG, "Checking for service component for " + data.packageName)
                 val pm = context.packageManager
@@ -201,7 +189,6 @@
      */
     private fun tryUpdateResumptionList(key: String, componentName: ComponentName) {
         Log.d(TAG, "Testing if we can connect to $componentName")
-        mediaBrowser?.disconnect()
         mediaBrowser = mediaBrowserFactory.create(
                 object : ResumeMediaBrowser.Callback() {
                     override fun onConnected() {
@@ -211,7 +198,6 @@
                     override fun onError() {
                         Log.e(TAG, "Cannot resume with $componentName")
                         mediaDataManager.setResumeAction(key, null)
-                        mediaBrowser?.disconnect()
                         mediaBrowser = null
                     }
 
@@ -224,7 +210,6 @@
                         Log.d(TAG, "Can get resumable media from $componentName")
                         mediaDataManager.setResumeAction(key, getResumeAction(componentName))
                         updateResumptionList(componentName)
-                        mediaBrowser?.disconnect()
                         mediaBrowser = null
                     }
                 },
@@ -262,30 +247,7 @@
      */
     private fun getResumeAction(componentName: ComponentName): Runnable {
         return Runnable {
-            mediaBrowser?.disconnect()
-            mediaBrowser = mediaBrowserFactory.create(
-                object : ResumeMediaBrowser.Callback() {
-                    override fun onConnected() {
-                        if (mediaBrowser?.token == null) {
-                            Log.e(TAG, "Error after connect")
-                            mediaBrowser?.disconnect()
-                            mediaBrowser = null
-                            return
-                        }
-                        Log.d(TAG, "Connected for restart $componentName")
-                        val controller = MediaController(context, mediaBrowser!!.token)
-                        val controls = controller.transportControls
-                        controls.prepare()
-                        controls.play()
-                    }
-
-                    override fun onError() {
-                        Log.e(TAG, "Resume failed for $componentName")
-                        mediaBrowser?.disconnect()
-                        mediaBrowser = null
-                    }
-                },
-                componentName)
+            mediaBrowser = mediaBrowserFactory.create(null, componentName)
             mediaBrowser?.restart()
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index e453653..fecc903 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.media;
 
+import android.annotation.Nullable;
 import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
@@ -47,7 +48,7 @@
 
     private static final String TAG = "ResumeMediaBrowser";
     private final Context mContext;
-    private final Callback mCallback;
+    @Nullable private final Callback mCallback;
     private MediaBrowserFactory mBrowserFactory;
     private MediaBrowser mMediaBrowser;
     private ComponentName mComponentName;
@@ -58,8 +59,8 @@
      * @param callback used to report media items found
      * @param componentName Component name of the MediaBrowserService this browser will connect to
      */
-    public ResumeMediaBrowser(Context context, Callback callback, ComponentName componentName,
-            MediaBrowserFactory browserFactory) {
+    public ResumeMediaBrowser(Context context, @Nullable Callback callback,
+            ComponentName componentName, MediaBrowserFactory browserFactory) {
         mContext = context;
         mCallback = callback;
         mComponentName = componentName;
@@ -93,18 +94,24 @@
                 List<MediaBrowser.MediaItem> children) {
             if (children.size() == 0) {
                 Log.d(TAG, "No children found for " + mComponentName);
-                mCallback.onError();
+                if (mCallback != null) {
+                    mCallback.onError();
+                }
             } else {
                 // We ask apps to return a playable item as the first child when sending
                 // a request with EXTRA_RECENT; if they don't, no resume controls
                 MediaBrowser.MediaItem child = children.get(0);
                 MediaDescription desc = child.getDescription();
                 if (child.isPlayable() && mMediaBrowser != null) {
-                    mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
-                            ResumeMediaBrowser.this);
+                    if (mCallback != null) {
+                        mCallback.addTrack(desc, mMediaBrowser.getServiceComponent(),
+                                ResumeMediaBrowser.this);
+                    }
                 } else {
                     Log.d(TAG, "Child found but not playable for " + mComponentName);
-                    mCallback.onError();
+                    if (mCallback != null) {
+                        mCallback.onError();
+                    }
                 }
             }
             disconnect();
@@ -113,7 +120,9 @@
         @Override
         public void onError(String parentId) {
             Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId);
-            mCallback.onError();
+            if (mCallback != null) {
+                mCallback.onError();
+            }
             disconnect();
         }
 
@@ -121,7 +130,9 @@
         public void onError(String parentId, Bundle options) {
             Log.d(TAG, "Subscribe error for " + mComponentName + ": " + parentId
                     + ", options: " + options);
-            mCallback.onError();
+            if (mCallback != null) {
+                mCallback.onError();
+            }
             disconnect();
         }
     };
@@ -138,13 +149,20 @@
             Log.d(TAG, "Service connected for " + mComponentName);
             if (mMediaBrowser != null && mMediaBrowser.isConnected()) {
                 String root = mMediaBrowser.getRoot();
-                if (!TextUtils.isEmpty(root) && mMediaBrowser != null) {
-                    mCallback.onConnected();
-                    mMediaBrowser.subscribe(root, mSubscriptionCallback);
+                if (!TextUtils.isEmpty(root)) {
+                    if (mCallback != null) {
+                        mCallback.onConnected();
+                    }
+                    if (mMediaBrowser != null) {
+                        mMediaBrowser.subscribe(root, mSubscriptionCallback);
+                    }
                     return;
                 }
             }
-            mCallback.onError();
+            if (mCallback != null) {
+                mCallback.onError();
+            }
+            disconnect();
         }
 
         /**
@@ -153,7 +171,9 @@
         @Override
         public void onConnectionSuspended() {
             Log.d(TAG, "Connection suspended for " + mComponentName);
-            mCallback.onError();
+            if (mCallback != null) {
+                mCallback.onError();
+            }
             disconnect();
         }
 
@@ -163,16 +183,18 @@
         @Override
         public void onConnectionFailed() {
             Log.d(TAG, "Connection failed for " + mComponentName);
-            mCallback.onError();
+            if (mCallback != null) {
+                mCallback.onError();
+            }
             disconnect();
         }
     };
 
     /**
-     * Disconnect the media browser. This should be called after restart or testConnection have
-     * completed to close the connection.
+     * Disconnect the media browser. This should be done after callbacks have completed to
+     * disconnect from the media browser service.
      */
-    public void disconnect() {
+    protected void disconnect() {
         if (mMediaBrowser != null) {
             mMediaBrowser.disconnect();
         }
@@ -183,7 +205,8 @@
      * Connects to the MediaBrowserService and starts playback.
      * ResumeMediaBrowser.Callback#onError or ResumeMediaBrowser.Callback#onConnected will be called
      * depending on whether it was successful.
-     * ResumeMediaBrowser#disconnect should be called after this to ensure the connection is closed.
+     * If the connection is successful, the listener should call ResumeMediaBrowser#disconnect after
+     * getting a media update from the app
      */
     public void restart() {
         disconnect();
@@ -195,7 +218,10 @@
                     public void onConnected() {
                         Log.d(TAG, "Connected for restart " + mMediaBrowser.isConnected());
                         if (mMediaBrowser == null || !mMediaBrowser.isConnected()) {
-                            mCallback.onError();
+                            if (mCallback != null) {
+                                mCallback.onError();
+                            }
+                            disconnect();
                             return;
                         }
                         MediaSession.Token token = mMediaBrowser.getSessionToken();
@@ -203,17 +229,26 @@
                         controller.getTransportControls();
                         controller.getTransportControls().prepare();
                         controller.getTransportControls().play();
-                        mCallback.onConnected();
+                        if (mCallback != null) {
+                            mCallback.onConnected();
+                        }
+                        // listener should disconnect after media player update
                     }
 
                     @Override
                     public void onConnectionFailed() {
-                        mCallback.onError();
+                        if (mCallback != null) {
+                            mCallback.onError();
+                        }
+                        disconnect();
                     }
 
                     @Override
                     public void onConnectionSuspended() {
-                        mCallback.onError();
+                        if (mCallback != null) {
+                            mCallback.onError();
+                        }
+                        disconnect();
                     }
                 }, rootHints);
         mMediaBrowser.connect();
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 9e77b60..d8b342a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -31,6 +31,7 @@
 import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_NAVIGATION_BARS;
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
@@ -200,7 +201,7 @@
     private final UiEventLogger mUiEventLogger;
 
     private Bundle mSavedState;
-    private NavigationBarView mNavigationBarView = null;
+    private NavigationBarView mNavigationBarView;
 
     private @WindowVisibleState int mNavigationBarWindowState = WINDOW_STATE_SHOWING;
 
@@ -392,7 +393,8 @@
             };
 
     private final Runnable mAutoDim = () -> getBarTransitions().setAutoDim(true);
-
+    private final Runnable mEnableLayoutTransitions = () ->
+            mNavigationBarView.setLayoutTransitionsEnabled(true);
     private final Runnable mOnVariableDurationHomeLongClick = () -> {
         if (onHomeLongClick(mNavigationBarView.getHomeButton().getCurrentView())) {
             mNavigationBarView.getHomeButton().getCurrentView().performHapticFeedback(
@@ -488,7 +490,7 @@
         mA11yBtnMode = mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode();
     }
 
-    public View getView() {
+    public NavigationBarView getView() {
         return mNavigationBarView;
     }
 
@@ -504,16 +506,19 @@
                         | WindowManager.LayoutParams.FLAG_SLIPPERY,
                 PixelFormat.TRANSLUCENT);
         lp.token = new Binder();
-        lp.setTitle("NavigationBar" + mContext.getDisplayId());
         lp.accessibilityTitle = mContext.getString(R.string.nav_bar);
-        lp.windowAnimations = 0;
         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
+        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        lp.windowAnimations = 0;
+        lp.setTitle("NavigationBar" + mContext.getDisplayId());
+        lp.setFitInsetsTypes(0 /* types */);
 
         NavigationBarFrame frame = (NavigationBarFrame) LayoutInflater.from(mContext).inflate(
                 R.layout.navigation_bar_window, null);
         View barView = LayoutInflater.from(frame.getContext()).inflate(
                 R.layout.navigation_bar, frame);
         barView.addOnAttachStateChangeListener(this);
+        mNavigationBarView = barView.findViewById(R.id.navigation_bar_view);
 
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + barView);
         mContext.getSystemService(WindowManager.class).addView(frame, lp);
@@ -533,6 +538,19 @@
         mContentResolver.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
                 false, mAssistContentObserver, UserHandle.USER_ALL);
+        mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
+                R.bool.allow_force_nav_bar_handle_opaque);
+        mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                NAV_BAR_HANDLE_FORCE_OPAQUE,
+                /* defaultValue = */ true);
+        mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong(
+                DeviceConfig.NAMESPACE_SYSTEMUI,
+                HOME_BUTTON_LONG_PRESS_DURATION_MS,
+                /* defaultValue = */ 0
+        )).filter(duration -> duration != 0);
+        DeviceConfig.addOnPropertiesChangedListener(
+                DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
         updateAssistantEntrypoints();
 
         if (savedState != null) {
@@ -543,25 +561,10 @@
             mTransientShown = savedState.getBoolean(EXTRA_TRANSIENT_STATE, false);
         }
         mSavedState = savedState;
-        mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
 
         // Respect the latest disabled-flags.
         mCommandQueue.recomputeDisableFlags(mDisplayId, false);
 
-        mAllowForceNavBarHandleOpaque = mContext.getResources().getBoolean(
-                R.bool.allow_force_nav_bar_handle_opaque);
-        mForceNavBarHandleOpaque = DeviceConfig.getBoolean(
-                DeviceConfig.NAMESPACE_SYSTEMUI,
-                NAV_BAR_HANDLE_FORCE_OPAQUE,
-                /* defaultValue = */ true);
-        mHomeButtonLongPressDurationMs = Optional.of(DeviceConfig.getLong(
-            DeviceConfig.NAMESPACE_SYSTEMUI,
-            HOME_BUTTON_LONG_PRESS_DURATION_MS,
-            /* defaultValue = */ 0
-        )).filter(duration -> duration != 0);
-        DeviceConfig.addOnPropertiesChangedListener(
-                DeviceConfig.NAMESPACE_SYSTEMUI, mHandler::post, mOnPropertiesChangedListener);
-
         mIsCurrentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
         mDeviceProvisionedController.addCallback(mUserSetupListener);
 
@@ -587,7 +590,6 @@
     @Override
     public void onViewAttachedToWindow(View v) {
         final Display display = v.getDisplay();
-        mNavigationBarView = v.findViewById(R.id.navigation_bar_view);
         mNavigationBarView.setComponents(mStatusBarLazy.get().getPanelController());
         mNavigationBarView.setDisabledFlags(mDisabledFlags1);
         mNavigationBarView.setOnVerticalChangedListener(this::onVerticalChanged);
@@ -598,6 +600,9 @@
         mNavigationBarView.setNavigationIconHints(mNavigationIconHints);
         mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
         mNavigationBarView.setBehavior(mBehavior);
+
+        mAccessibilityManagerWrapper.addCallback(mAccessibilityListener);
+
         mSplitScreenOptional.ifPresent(mNavigationBarView::registerDockedListener);
         mPipOptional.ifPresent(mNavigationBarView::registerPipExclusionBoundsChangeListener);
 
@@ -657,10 +662,8 @@
 
     @Override
     public void onViewDetachedFromWindow(View v) {
-        if (mNavigationBarView != null) {
-            mNavigationBarView.getBarTransitions().destroy();
-            mNavigationBarView.getLightTransitionsController().destroy(mContext);
-        }
+        mNavigationBarView.getBarTransitions().destroy();
+        mNavigationBarView.getLightTransitionsController().destroy(mContext);
         mOverviewProxyService.removeCallback(mOverviewProxyListener);
         mBroadcastDispatcher.unregisterReceiver(mBroadcastReceiver);
         if (mOrientationHandle != null) {
@@ -671,6 +674,8 @@
                     mOrientationHandleGlobalLayoutListener);
         }
         mHandler.removeCallbacks(mAutoDim);
+        mHandler.removeCallbacks(mOnVariableDurationHomeLongClick);
+        mHandler.removeCallbacks(mEnableLayoutTransitions);
         mNavigationBarView = null;
         mOrientationHandle = null;
     }
@@ -682,9 +687,7 @@
         outState.putInt(EXTRA_APPEARANCE, mAppearance);
         outState.putInt(EXTRA_BEHAVIOR, mBehavior);
         outState.putBoolean(EXTRA_TRANSIENT_STATE, mTransientShown);
-        if (mNavigationBarView != null) {
-            mNavigationBarView.getLightTransitionsController().saveState(outState);
-        }
+        mNavigationBarView.getLightTransitionsController().saveState(outState);
     }
 
     /**
@@ -809,15 +812,12 @@
             // mOrientedHandle is initialized lazily
             mOrientationHandle.setVisibility(View.GONE);
         }
-        if (mNavigationBarView != null) {
-            mNavigationBarView.setVisibility(View.VISIBLE);
-            mNavigationBarView.setOrientedHandleSamplingRegion(null);
-        }
+        mNavigationBarView.setVisibility(View.VISIBLE);
+        mNavigationBarView.setOrientedHandleSamplingRegion(null);
     }
 
     private void reconfigureHomeLongClick() {
-        if (mNavigationBarView == null
-                || mNavigationBarView.getHomeButton().getCurrentView() == null) {
+        if (mNavigationBarView.getHomeButton().getCurrentView() == null) {
             return;
         }
         if (mHomeButtonLongPressDurationMs.isPresent() || !mLongPressHomeEnabled) {
@@ -844,17 +844,12 @@
         pw.println("  mHomeButtonLongPressDurationMs=" + mHomeButtonLongPressDurationMs);
         pw.println("  mLongPressHomeEnabled=" + mLongPressHomeEnabled);
         pw.println("  mAssistantTouchGestureEnabled=" + mAssistantTouchGestureEnabled);
-
-        if (mNavigationBarView != null) {
-            pw.println("  mNavigationBarWindowState="
-                    + windowStateToString(mNavigationBarWindowState));
-            pw.println("  mNavigationBarMode="
-                    + BarTransitions.modeToString(mNavigationBarMode));
-            dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
-            mNavigationBarView.dump(pw);
-        } else {
-            pw.print("  mNavigationBarView=null");
-        }
+        pw.println("  mNavigationBarWindowState="
+                + windowStateToString(mNavigationBarWindowState));
+        pw.println("  mNavigationBarMode="
+                + BarTransitions.modeToString(mNavigationBarMode));
+        dumpBarTransitions(pw, "mNavigationBarView", mNavigationBarView.getBarTransitions());
+        mNavigationBarView.dump(pw);
     }
 
     // ----- CommandQueue Callbacks -----
@@ -889,10 +884,7 @@
         if (hints == mNavigationIconHints) return;
 
         mNavigationIconHints = hints;
-
-        if (mNavigationBarView != null) {
-            mNavigationBarView.setNavigationIconHints(hints);
-        }
+        mNavigationBarView.setNavigationIconHints(hints);
         checkBarModes();
         updateSystemUiStateFlags(-1);
     }
@@ -911,23 +903,12 @@
                 orientSecondaryHomeHandle();
             }
             if (DEBUG_WINDOW_STATE) Log.d(TAG, "Navigation bar " + windowStateToString(state));
-
-            if (mNavigationBarView != null) {
-                mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
-            }
+            mNavigationBarView.setWindowVisible(isNavBarWindowVisible());
         }
     }
 
     @Override
     public void onRotationProposal(final int rotation, boolean isValid) {
-        if (mNavigationBarView == null) {
-            if (RotationContextButton.DEBUG_ROTATION) {
-                Log.v(TAG, "onRotationProposal proposedRotation=" +
-                        Surface.rotationToString(rotation) + ", mNavigationBarView is null");
-            }
-            return;
-        }
-
         final int winRotation = mNavigationBarView.getDisplay().getRotation();
         final boolean rotateSuggestionsDisabled = RotationButtonController
                 .hasDisable2RotateSuggestionFlag(mDisabledFlags2);
@@ -941,8 +922,7 @@
                     + ", isValid=" + isValid + ", mNavBarWindowState="
                     + StatusBarManager.windowStateToString(mNavigationBarWindowState)
                     + ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
-                    + ", isRotateButtonVisible=" + (mNavigationBarView == null ? "null"
-                    : rotationButton.isVisible()));
+                    + ", isRotateButtonVisible=" + rotationButton.isVisible());
         }
 
         // Respect the disabled flag, no need for action as flag change callback will handle hiding
@@ -983,9 +963,6 @@
         boolean nbModeChanged = false;
         if (mAppearance != appearance) {
             mAppearance = appearance;
-            if (getView() == null) {
-                return;
-            }
             nbModeChanged = updateBarMode(barMode(mTransientShown, appearance));
         }
         if (mLightBarController != null) {
@@ -994,9 +971,7 @@
         }
         if (mBehavior != behavior) {
             mBehavior = behavior;
-            if (mNavigationBarView != null) {
-                mNavigationBarView.setBehavior(behavior);
-            }
+            mNavigationBarView.setBehavior(behavior);
             updateSystemUiStateFlags(-1);
         }
     }
@@ -1034,12 +1009,7 @@
     }
 
     private void handleTransientChanged() {
-        if (getView() == null) {
-            return;
-        }
-        if (mNavigationBarView != null) {
-            mNavigationBarView.onTransientStateChanged(mTransientShown);
-        }
+        mNavigationBarView.onTransientStateChanged(mTransientShown);
         final int barMode = barMode(mTransientShown, mAppearance);
         if (updateBarMode(barMode) && mLightBarController != null) {
             mLightBarController.onNavigationBarModeChanged(barMode);
@@ -1092,9 +1062,7 @@
                 | StatusBarManager.DISABLE_SEARCH);
         if (masked != mDisabledFlags1) {
             mDisabledFlags1 = masked;
-            if (mNavigationBarView != null) {
-                mNavigationBarView.setDisabledFlags(state1);
-            }
+            mNavigationBarView.setDisabledFlags(state1);
             updateScreenPinningGestures();
         }
 
@@ -1110,17 +1078,13 @@
 
     private void setDisabled2Flags(int state2) {
         // Method only called on change of disable2 flags
-        if (mNavigationBarView != null) {
-            mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2);
-        }
+        mNavigationBarView.getRotationButtonController().onDisable2FlagChanged(state2);
     }
 
     // ----- Internal stuff -----
 
     private void refreshLayout(int layoutDirection) {
-        if (mNavigationBarView != null) {
-            mNavigationBarView.setLayoutDirection(layoutDirection);
-        }
+        mNavigationBarView.setLayoutDirection(layoutDirection);
     }
 
     private boolean shouldDisableNavbarGestures() {
@@ -1129,7 +1093,7 @@
     }
 
     private void repositionNavigationBar() {
-        if (mNavigationBarView == null || !mNavigationBarView.isAttachedToWindow()) return;
+        if (!mNavigationBarView.isAttachedToWindow()) return;
 
         prepareNavigationBarView();
 
@@ -1138,10 +1102,6 @@
     }
 
     private void updateScreenPinningGestures() {
-        if (mNavigationBarView == null) {
-            return;
-        }
-
         // Change the cancel pin gesture to home and back if recents button is invisible
         boolean pinningActive = ActivityManagerWrapper.getInstance().isScreenPinningActive();
         ButtonDispatcher backButton = mNavigationBarView.getBackButton();
@@ -1252,10 +1212,7 @@
                 AssistManager.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
         mAssistManagerLazy.get().startAssist(args);
         mStatusBarLazy.get().awakenDreams();
-
-        if (mNavigationBarView != null) {
-            mNavigationBarView.abortCurrentGesture();
-        }
+        mNavigationBarView.abortCurrentGesture();
         return true;
     }
 
@@ -1405,9 +1362,6 @@
     }
 
     void updateAccessibilityServicesState(AccessibilityManager accessibilityManager) {
-        if (mNavigationBarView == null) {
-            return;
-        }
         boolean[] feedbackEnabled = new boolean[1];
         int a11yFlags = getA11yButtonState(feedbackEnabled);
 
@@ -1593,7 +1547,7 @@
 
     public void disableAnimationsDuringHide(long delay) {
         mNavigationBarView.setLayoutTransitionsEnabled(false);
-        mNavigationBarView.postDelayed(() -> mNavigationBarView.setLayoutTransitionsEnabled(true),
+        mHandler.postDelayed(mEnableLayoutTransitions,
                 delay + StackStateAnimator.ANIMATION_DURATION_GO_TO_FULL_SHADE);
     }
 
@@ -1608,10 +1562,7 @@
     }
 
     public NavigationBarTransitions getBarTransitions() {
-        if (mNavigationBarView != null) {
-            return mNavigationBarView.getBarTransitions();
-        }
-        return null;
+        return mNavigationBarView.getBarTransitions();
     }
 
     public void finishBarAnimations() {
@@ -1626,8 +1577,7 @@
     }
 
     private final Consumer<Integer> mRotationWatcher = rotation -> {
-        if (mNavigationBarView != null
-                && mNavigationBarView.needsReorient(rotation)) {
+        if (mNavigationBarView.needsReorient(rotation)) {
             repositionNavigationBar();
         }
     };
@@ -1635,9 +1585,6 @@
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (mNavigationBarView == null) {
-                return;
-            }
             String action = intent.getAction();
             if (Intent.ACTION_SCREEN_OFF.equals(action)
                     || Intent.ACTION_SCREEN_ON.equals(action)) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
index ae9b598..8b5a537 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarController.java
@@ -123,8 +123,7 @@
 
     // Tracks config changes that will actually recreate the nav bar
     private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges(
-            ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE
-                    | ActivityInfo.CONFIG_SCREEN_LAYOUT
+            ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_SCREEN_LAYOUT
                     | ActivityInfo.CONFIG_UI_MODE);
 
     @Inject
@@ -225,10 +224,7 @@
                 if (navBar == null) {
                     continue;
                 }
-                NavigationBarView view = (NavigationBarView) navBar.getView();
-                if (view != null) {
-                    view.updateStates();
-                }
+                navBar.getView().updateStates();
             }
         });
     }
@@ -366,13 +362,12 @@
                 mHandler,
                 mNavBarOverlayController,
                 mUiEventLogger);
+        mNavigationBars.put(displayId, navBar);
 
         View navigationBarView = navBar.createView(savedState);
         navigationBarView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
-                mNavigationBars.put(displayId, navBar);
-
                 if (result != null) {
                     navBar.setImeWindowStatus(display.getDisplayId(), result.mImeToken,
                             result.mImeWindowVis, result.mImeBackDisposition,
@@ -448,7 +443,7 @@
      */
     public @Nullable NavigationBarView getNavigationBarView(int displayId) {
         NavigationBar navBar = mNavigationBars.get(displayId);
-        return (navBar == null) ? null : (NavigationBarView) navBar.getView();
+        return (navBar == null) ? null : navBar.getView();
     }
 
     /** @return {@link NavigationBar} on the default display. */
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
index 4d25079..0218016 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/ButtonDispatcher.java
@@ -40,7 +40,6 @@
     private final ArrayList<View> mViews = new ArrayList<>();
 
     private final int mId;
-    private final AssistManager mAssistManager;
 
     private View.OnClickListener mClickListener;
     private View.OnTouchListener mTouchListener;
@@ -73,7 +72,6 @@
 
     public ButtonDispatcher(int id) {
         mId = id;
-        mAssistManager = Dependency.get(AssistManager.class);
     }
 
     public void clear() {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index be7adbc..74d3425 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -430,12 +430,14 @@
             } else {
                 state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
             }
-        } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
-            state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
-        } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) {
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
-            state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+        } else if (cb.mNoDefaultNetwork) {
+            if (cb.mNoNetworksAvailable) {
+                state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
+                state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
+            } else {
+                state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
+                state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+            }
         } else if (cb.mIsTransient) {
             state.icon = ResourceIcon.get(
                 com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
@@ -488,12 +490,14 @@
             state.state = Tile.STATE_INACTIVE;
             state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
             state.secondaryLabel = r.getString(R.string.status_bar_airplane);
-        } else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
-            state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
-        } else if (cb.mNoValidatedNetwork && !cb.mNoNetworksAvailable) {
-            state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
-            state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+        } else if (cb.mNoDefaultNetwork) {
+            if (cb.mNoNetworksAvailable) {
+                state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_unavailable);
+                state.secondaryLabel = r.getString(R.string.quick_settings_networks_unavailable);
+            } else {
+                state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_available);
+                state.secondaryLabel = r.getString(R.string.quick_settings_networks_available);
+            }
         } else {
             state.icon = new SignalIcon(cb.mMobileSignalIconId);
             state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
index e467925..64aec5e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -18,13 +18,14 @@
 
 import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
 
+import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
+
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.graphics.drawable.Drawable;
 import android.os.Handler;
 import android.os.Looper;
 import android.service.quickaccesswallet.GetWalletCardsError;
-import android.service.quickaccesswallet.GetWalletCardsRequest;
 import android.service.quickaccesswallet.GetWalletCardsResponse;
 import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.service.quickaccesswallet.WalletCard;
@@ -51,6 +52,7 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 import com.android.systemui.wallet.ui.WalletActivity;
 
 import java.util.List;
@@ -66,16 +68,15 @@
 
     private final CharSequence mLabel = mContext.getString(R.string.wallet_title);
     private final WalletCardRetriever mCardRetriever = new WalletCardRetriever();
-    // TODO(b/180959290): Re-create the QAW Client when the default NFC payment app changes.
-    private final QuickAccessWalletClient mQuickAccessWalletClient;
     private final KeyguardStateController mKeyguardStateController;
     private final PackageManager mPackageManager;
     private final SecureSettings mSecureSettings;
     private final Executor mExecutor;
+    private final QuickAccessWalletController mController;
     private final FeatureFlags mFeatureFlags;
 
-    @VisibleForTesting Drawable mCardViewDrawable;
     private WalletCard mSelectedCard;
+    @VisibleForTesting Drawable mCardViewDrawable;
 
     @Inject
     public QuickAccessWalletTile(
@@ -87,15 +88,15 @@
             StatusBarStateController statusBarStateController,
             ActivityStarter activityStarter,
             QSLogger qsLogger,
-            QuickAccessWalletClient quickAccessWalletClient,
             KeyguardStateController keyguardStateController,
             PackageManager packageManager,
             SecureSettings secureSettings,
-            @Background Executor executor,
+            @Main Executor executor,
+            QuickAccessWalletController quickAccessWalletController,
             FeatureFlags featureFlags) {
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
-        mQuickAccessWalletClient = quickAccessWalletClient;
+        mController = quickAccessWalletController;
         mKeyguardStateController = keyguardStateController;
         mPackageManager = packageManager;
         mSecureSettings = secureSettings;
@@ -115,7 +116,11 @@
     protected void handleSetListening(boolean listening) {
         super.handleSetListening(listening);
         if (listening) {
-            queryWalletCards();
+            mController.setupWalletChangeObservers(mCardRetriever, DEFAULT_PAYMENT_APP_CHANGE);
+            if (!mController.getWalletClient().isWalletServiceAvailable()) {
+                mController.reCreateWalletClient();
+            }
+            mController.queryWalletCards(mCardRetriever);
         }
     }
 
@@ -139,12 +144,13 @@
                     mContext.startActivity(intent);
                 }
             } else {
-                if (mQuickAccessWalletClient.createWalletIntent() == null) {
+                if (mController.getWalletClient().createWalletIntent() == null) {
                     Log.w(TAG, "Could not get intent of the wallet app.");
                     return;
                 }
                 mActivityStarter.postStartActivityDismissingKeyguard(
-                        mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0,
+                        mController.getWalletClient().createWalletIntent(),
+                        /* delay= */ 0,
                         animationController);
             }
         });
@@ -152,30 +158,34 @@
 
     @Override
     protected void handleUpdateState(State state, Object arg) {
-        CharSequence label = mQuickAccessWalletClient.getServiceLabel();
+        CharSequence label = mController.getWalletClient().getServiceLabel();
         state.label = label == null ? mLabel : label;
         state.contentDescription = state.label;
         state.icon = ResourceIcon.get(R.drawable.ic_wallet_lockscreen);
         boolean isDeviceLocked = !mKeyguardStateController.isUnlocked();
-        if (mQuickAccessWalletClient.isWalletServiceAvailable()) {
+        if (mController.getWalletClient().isWalletServiceAvailable()) {
             if (mSelectedCard != null) {
                 if (isDeviceLocked) {
                     state.state = Tile.STATE_INACTIVE;
                     state.secondaryLabel =
                             mContext.getString(R.string.wallet_secondary_label_device_locked);
+                    state.sideViewCustomDrawable = null;
                 } else {
                     state.state = Tile.STATE_ACTIVE;
                     state.secondaryLabel = mSelectedCard.getContentDescription();
+                    state.sideViewCustomDrawable = mCardViewDrawable;
                 }
             } else {
                 state.state = Tile.STATE_INACTIVE;
                 state.secondaryLabel = mContext.getString(R.string.wallet_secondary_label_no_card);
+                state.sideViewCustomDrawable = null;
             }
             state.stateDescription = state.secondaryLabel;
         } else {
             state.state = Tile.STATE_UNAVAILABLE;
+            state.secondaryLabel = null;
+            state.sideViewCustomDrawable = null;
         }
-        state.sideViewCustomDrawable = isDeviceLocked ? null : mCardViewDrawable;
     }
 
     @Override
@@ -198,19 +208,14 @@
 
     @Override
     public CharSequence getTileLabel() {
-        CharSequence label = mQuickAccessWalletClient.getServiceLabel();
+        CharSequence label = mController.getWalletClient().getServiceLabel();
         return label == null ? mLabel : label;
     }
 
-    private void queryWalletCards() {
-        int cardWidth =
-                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width);
-        int cardHeight =
-                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height);
-        int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
-        GetWalletCardsRequest request =
-                new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1);
-        mQuickAccessWalletClient.getWalletCards(mExecutor, request, mCardRetriever);
+    @Override
+    protected void handleDestroy() {
+        super.handleDestroy();
+        mController.unregisterWalletChangeObservers(DEFAULT_PAYMENT_APP_CHANGE);
     }
 
     private class WalletCardRetriever implements
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 89cb5af..f4c15fb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -29,6 +29,7 @@
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SPLIT_SCREEN;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_STARTING_WINDOW;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SUPPORTS_WINDOW_CORNERS;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
 import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_WINDOW_CORNER_RADIUS;
@@ -88,6 +89,7 @@
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.ActivityManagerWrapper;
 import com.android.systemui.shared.system.QuickStepContract;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -148,6 +150,7 @@
     private final CommandQueue mCommandQueue;
     private final ShellTransitions mShellTransitions;
     private final Optional<StartingSurface> mStartingSurface;
+    private final SmartspaceTransitionController mSmartspaceTransitionController;
 
     private Region mActiveNavBarRegion;
 
@@ -552,6 +555,9 @@
             mStartingSurface.ifPresent((startingwindow) -> params.putBinder(
                     KEY_EXTRA_SHELL_STARTING_WINDOW,
                     startingwindow.createExternalInterface().asBinder()));
+            params.putBinder(
+                    KEY_EXTRA_SMARTSPACE_TRANSITION_CONTROLLER,
+                    mSmartspaceTransitionController.createExternalInterface().asBinder());
 
             try {
                 Log.d(TAG_OPS + " b/182478748", "OverviewProxyService.onInitialize: curUser="
@@ -613,7 +619,8 @@
             Optional<OneHanded> oneHandedOptional,
             BroadcastDispatcher broadcastDispatcher,
             ShellTransitions shellTransitions,
-            Optional<StartingSurface> startingSurface) {
+            Optional<StartingSurface> startingSurface,
+            SmartspaceTransitionController smartspaceTransitionController) {
         super(broadcastDispatcher);
         mContext = context;
         mPipOptional = pipOptional;
@@ -675,6 +682,7 @@
         updateEnabledState();
         startConnectionToCurrentUser();
         mStartingSurface = startingSurface;
+        mSmartspaceTransitionController = smartspaceTransitionController;
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt b/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt
new file mode 100644
index 0000000..89b3df0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shared/system/smartspace/SmartspaceTransitionController.kt
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2021 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.shared.system.smartspace
+
+import android.graphics.Rect
+import android.view.View
+import com.android.systemui.shared.system.ActivityManagerWrapper
+import com.android.systemui.shared.system.QuickStepContract
+import kotlin.math.min
+
+/**
+ * Controller that keeps track of SmartSpace instances in remote processes (such as Launcher),
+ * allowing System UI to query or update their state during shared-element transitions.
+ */
+class SmartspaceTransitionController {
+
+    /**
+     * Implementation of [ISmartspaceTransitionController] that we provide to Launcher, allowing it
+     * to provide us with a callback to query and update the state of its Smartspace.
+     */
+    private val ISmartspaceTransitionController = object : ISmartspaceTransitionController.Stub() {
+        override fun setSmartspace(callback: ISmartspaceCallback?) {
+            this@SmartspaceTransitionController.launcherSmartspace = callback
+            updateLauncherSmartSpaceState()
+        }
+    }
+
+    /**
+     * Callback provided by Launcher to allow us to query and update the state of its SmartSpace.
+     */
+    public var launcherSmartspace: ISmartspaceCallback? = null
+
+    public var lockscreenSmartspace: View? = null
+
+    /**
+     * Cached state of the Launcher SmartSpace. Retrieving the state is an IPC, so we should avoid
+     * unnecessary
+     */
+    public var mLauncherSmartspaceState: SmartspaceState? = null
+
+    /**
+     * The bounds of our SmartSpace when the shared element transition began. We'll interpolate
+     * between this and [smartspaceDestinationBounds] as the dismiss amount changes.
+     */
+    private val smartspaceOriginBounds = Rect()
+
+    /** The bounds of the Launcher's SmartSpace, which is where we are animating our SmartSpace. */
+
+    private val smartspaceDestinationBounds = Rect()
+
+    fun createExternalInterface(): ISmartspaceTransitionController {
+        return ISmartspaceTransitionController
+    }
+
+    /**
+     * Updates [mLauncherSmartspaceState] and returns it. This will trigger a binder call, so use the
+     * cached [mLauncherSmartspaceState] if possible.
+     */
+    fun updateLauncherSmartSpaceState(): SmartspaceState? {
+        return launcherSmartspace?.smartspaceState.also {
+            mLauncherSmartspaceState = it
+        }
+    }
+
+    fun prepareForUnlockTransition() {
+        updateLauncherSmartSpaceState().also { state ->
+            if (state?.boundsOnScreen != null && lockscreenSmartspace != null) {
+                lockscreenSmartspace!!.getBoundsOnScreen(smartspaceOriginBounds)
+                with(smartspaceDestinationBounds) {
+                    set(state.boundsOnScreen)
+                    offset(-lockscreenSmartspace!!.paddingLeft,
+                            -lockscreenSmartspace!!.paddingTop)
+                }
+            }
+        }
+    }
+
+    fun setProgressToDestinationBounds(progress: Float) {
+        if (!isSmartspaceTransitionPossible()) {
+            return
+        }
+
+        val progressClamped = min(1f, progress)
+
+        // Calculate the distance (relative to the origin) that we need to be for the current
+        // progress value.
+        val progressX =
+                (smartspaceDestinationBounds.left - smartspaceOriginBounds.left) * progressClamped
+        val progressY =
+                (smartspaceDestinationBounds.top - smartspaceOriginBounds.top) * progressClamped
+
+        val lockscreenSmartspaceCurrentBounds = Rect().also {
+            lockscreenSmartspace!!.getBoundsOnScreen(it)
+        }
+
+        // Figure out how far that is from our present location on the screen. This approach
+        // compensates for the fact that our parent container is also translating to animate out.
+        val dx = smartspaceOriginBounds.left + progressX -
+                lockscreenSmartspaceCurrentBounds.left
+        var dy = smartspaceOriginBounds.top + progressY -
+                lockscreenSmartspaceCurrentBounds.top
+
+        with(lockscreenSmartspace!!) {
+            translationX = translationX + dx
+            translationY = translationY + dy
+        }
+    }
+
+    /**
+     * Whether we're capable of performing the Smartspace shared element transition when we unlock.
+     * This is true if:
+     *
+     * - The Launcher registered a Smartspace with us, it's reporting non-empty bounds on screen.
+     * - Launcher is behind the keyguard, and the Smartspace is visible on the currently selected
+     *   page.
+     */
+    public fun isSmartspaceTransitionPossible(): Boolean {
+        val smartSpaceNullOrBoundsEmpty = mLauncherSmartspaceState?.boundsOnScreen?.isEmpty ?: true
+        return isLauncherUnderneath() && !smartSpaceNullOrBoundsEmpty
+    }
+
+    companion object {
+        fun isLauncherUnderneath(): Boolean {
+            return ActivityManagerWrapper.getInstance()
+                    .runningTask?.topActivity?.className?.equals(
+                            QuickStepContract.LAUNCHER_ACTIVITY_CLASS_NAME) ?: false
+        }
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index ac6d9e8..c7b6e67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -61,10 +61,6 @@
         return mFlagReader.isEnabled(R.bool.flag_conversations);
     }
 
-    public boolean isToastStyleEnabled() {
-        return mFlagReader.isEnabled(R.bool.flag_toast_style);
-    }
-
     public boolean isMonetEnabled() {
         return mFlagReader.isEnabled(R.bool.flag_monet);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
index b33424c..acfd998 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/HeadsUpStatusBarView.java
@@ -35,7 +35,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry.OnSensitivityChangedListener;
 
-import java.util.List;
 
 /**
  * The view in the statusBar that contains part of the heads-up information
@@ -55,10 +54,8 @@
     private int[] mTmpPosition = new int[2];
     private boolean mFirstLayout = true;
     private int mMaxWidth;
-    private View mRootView;
     private int mSysWinInset;
     private int mCutOutInset;
-    private List<Rect> mCutOutBounds;
     private Rect mIconDrawingRect = new Rect();
     private Point mDisplaySize;
     private Runnable mOnDrawingRectChangedListener;
@@ -197,19 +194,7 @@
         int targetPadding = mAbsoluteStartPadding + mSysWinInset + mCutOutInset;
         boolean isRtl = isLayoutRtl();
         int start = isRtl ? (mDisplaySize.x - right) : left;
-
         if (start != targetPadding) {
-            if (mCutOutBounds != null) {
-                for (Rect cutOutRect : mCutOutBounds) {
-                    int cutOutStart = (isRtl)
-                            ? (mDisplaySize.x - cutOutRect.right) : cutOutRect.left;
-                    if (start > cutOutStart) {
-                        start -= cutOutRect.width();
-                        break;
-                    }
-                }
-            }
-
             int newPadding = targetPadding - start + getPaddingStart();
             setPaddingRelative(newPadding, 0, mEndMargin, 0);
         }
@@ -252,12 +237,6 @@
 
         getDisplaySize();
 
-        mCutOutBounds = null;
-        if (displayCutout != null && displayCutout.getSafeInsetRight() == 0
-                && displayCutout.getSafeInsetLeft() == 0) {
-            mCutOutBounds = displayCutout.getBoundingRects();
-        }
-
         // For Double Cut Out mode, the System window navigation bar is at the right
         // side of the left cut out. In this condition, mSysWinInset include the left cut
         // out width so we set mCutOutInset to be 0. For RTL, the condition is the same.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
index b6357b8..045a197 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeWindowController.java
@@ -36,6 +36,12 @@
      */
     default void registerCallback(StatusBarWindowCallback callback) {}
 
+    /**
+     * Unregisters a {@link StatusBarWindowCallback previous registered with
+     * {@link #registerCallback(StatusBarWindowCallback)}}
+     */
+    default void unregisterCallback(StatusBarWindowCallback callback) {}
+
     /** Notifies the registered {@link StatusBarWindowCallback} instances. */
     default void notifyStateChangedCallbacks() {}
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 889dfde..9dc4ac9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -358,9 +358,13 @@
                 && anv instanceof ExpandableNotificationRow
                 && ((ExpandableNotificationRow) anv).isHeadsUp();
 
+        final boolean isHunGoingToShade = mAmbientState.isShadeExpanded()
+                && anv == mAmbientState.getTrackedHeadsUpRow();
+
         final boolean shouldUpdateCornerRoundness = viewStart < shelfStart
                 && !mHostLayoutController.isViewAffectedBySwipe(anv)
                 && !isUnlockedHeadsUp
+                && !isHunGoingToShade
                 && !mAmbientState.isPulsing()
                 && !mAmbientState.isDozing();
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
index 655ed41..33aa7c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationScheduler.kt
@@ -26,14 +26,18 @@
 import android.provider.DeviceConfig
 import android.util.Log
 import android.view.View
+import com.android.systemui.Dumpable
 
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.phone.StatusBarWindowController
 import com.android.systemui.statusbar.policy.CallbackController
 import com.android.systemui.util.Assert
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.time.SystemClock
+import java.io.FileDescriptor
+import java.io.PrintWriter
 
 import javax.inject.Inject
 
@@ -59,9 +63,10 @@
     private val coordinator: SystemEventCoordinator,
     private val chipAnimationController: SystemEventChipAnimationController,
     private val statusBarWindowController: StatusBarWindowController,
+    private val dumpManager: DumpManager,
     private val systemClock: SystemClock,
     @Main private val executor: DelayableExecutor
-) : CallbackController<SystemStatusAnimationCallback> {
+) : CallbackController<SystemStatusAnimationCallback>, Dumpable {
 
     companion object {
         private const val PROPERTY_ENABLE_IMMERSIVE_INDICATOR = "enable_immersive_indicator"
@@ -71,10 +76,6 @@
                 PROPERTY_ENABLE_IMMERSIVE_INDICATOR, true)
     }
 
-    /** True from the time a scheduled event starts until it's animation finishes */
-    var isActive = false
-        private set
-
     @SystemAnimationState var animationState: Int = IDLE
         private set
 
@@ -88,6 +89,7 @@
 
     init {
         coordinator.attachScheduler(this)
+        dumpManager.registerDumpable(TAG, this)
     }
 
     fun onStatusEvent(event: StatusEvent) {
@@ -293,6 +295,20 @@
         anim -> chipAnimationController.onChipAnimationUpdate(anim, animationState)
     }
 
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.println("Scheduled event: $scheduledEvent")
+        pw.println("Has persistent privacy dot: $hasPersistentDot")
+        pw.println("Animation state: $animationState")
+        pw.println("Listeners:")
+        if (listeners.isEmpty()) {
+            pw.println("(none)")
+        } else {
+            listeners.forEach {
+                pw.println("  $it")
+            }
+        }
+    }
+
     inner class ChipAnimatorAdapter(
         @SystemAnimationState val endState: Int,
         val viewCreator: (context: Context) -> View
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index d9cbfda..86465b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -380,11 +380,15 @@
         ExpandableViewState viewState = view.getViewState();
         viewState.location = ExpandableViewState.LOCATION_UNKNOWN;
 
-        if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) {
-            viewState.alpha = Interpolators.getNotificationScrimAlpha(
-                    ambientState.getExpansionFraction(), true /* notification */);
-        } else {
-            viewState.alpha = 1f - ambientState.getHideAmount();
+        final boolean isHunGoingToShade = ambientState.isShadeExpanded()
+                && view == ambientState.getTrackedHeadsUpRow();
+        if (!isHunGoingToShade) {
+            if (ambientState.isExpansionChanging() && !ambientState.isOnKeyguard()) {
+                viewState.alpha = Interpolators.getNotificationScrimAlpha(
+                        ambientState.getExpansionFraction(), true /* notification */);
+            } else {
+                viewState.alpha = 1f - ambientState.getHideAmount();
+            }
         }
 
         if (view.mustStayOnScreen() && viewState.yTranslation >= 0) {
@@ -414,13 +418,15 @@
         }
 
         if (view instanceof FooterView) {
+            final boolean shadeClosed = !ambientState.isShadeExpanded();
             final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
 
             final float footerEnd = algorithmState.mCurrentExpandedYPosition
                     + view.getIntrinsicHeight();
             final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
 
-            viewState.hidden = isShelfShowing || noSpaceForFooter;
+            viewState.hidden = shadeClosed || isShelfShowing || noSpaceForFooter;
+
         } else if (view != ambientState.getTrackedHeadsUpRow()) {
             if (ambientState.isExpansionChanging()) {
                 // Show all views. Views below the shelf will later be clipped (essentially hidden)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 7f919b5..4d8e7de3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -24,6 +24,8 @@
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_LEFT_UNLOCK;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_BUTTON;
 import static com.android.systemui.tuner.LockscreenFragment.LOCKSCREEN_RIGHT_UNLOCK;
+import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
+import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
 
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
@@ -39,7 +41,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.Configuration;
-import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
 import android.os.Bundle;
@@ -49,13 +50,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.provider.MediaStore;
-import android.provider.Settings;
 import android.service.media.CameraPrewarmService;
 import android.service.quickaccesswallet.GetWalletCardsError;
-import android.service.quickaccesswallet.GetWalletCardsRequest;
 import android.service.quickaccesswallet.GetWalletCardsResponse;
 import android.service.quickaccesswallet.QuickAccessWalletClient;
-import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
 import android.telecom.TelecomManager;
 import android.text.TextUtils;
 import android.util.AttributeSet;
@@ -71,7 +69,6 @@
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.widget.LockPatternUtils;
@@ -97,11 +94,9 @@
 import com.android.systemui.statusbar.policy.PreviewInflater;
 import com.android.systemui.tuner.LockscreenFragment.LockButtonFactory;
 import com.android.systemui.tuner.TunerService;
-import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 import com.android.systemui.wallet.ui.WalletActivity;
 
-import java.util.concurrent.Executor;
-
 /**
  * Implementation for the bottom area of the Keyguard, including camera/phone affordance and status
  * text.
@@ -137,10 +132,9 @@
     private KeyguardAffordanceView mLeftAffordanceView;
 
     private ImageView mWalletButton;
-    private boolean mWalletEnabled = false;
     private boolean mHasCard = false;
     private WalletCardRetriever mCardRetriever = new WalletCardRetriever();
-    private QuickAccessWalletClient mQuickAccessWalletClient;
+    private QuickAccessWalletController mQuickAccessWalletController;
 
     private ViewGroup mIndicationArea;
     private TextView mIndicationText;
@@ -159,7 +153,6 @@
     private StatusBar mStatusBar;
     private KeyguardAffordanceHelper mAffordanceHelper;
     private FalsingManager mFalsingManager;
-    @Nullable private Executor mUiExecutor;
     private boolean mUserSetupComplete;
     private boolean mPrewarmBound;
     private Messenger mPrewarmMessenger;
@@ -193,8 +186,6 @@
     private int mBurnInYOffset;
     private ActivityIntentHelper mActivityIntentHelper;
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
-    private ContentObserver mWalletPreferenceObserver;
-    private SecureSettings mSecureSettings;
 
     public KeyguardBottomAreaView(Context context) {
         this(context, null);
@@ -332,8 +323,9 @@
         getContext().unregisterReceiver(mDevicePolicyReceiver);
         mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback);
 
-        if (mWalletPreferenceObserver != null) {
-            mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver);
+        if (mQuickAccessWalletController != null) {
+            mQuickAccessWalletController.unregisterWalletChangeObservers(
+                    WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
         }
     }
 
@@ -456,7 +448,10 @@
     }
 
     private void updateWalletVisibility() {
-        if (mDozing || !mWalletEnabled || !mHasCard) {
+        if (mDozing
+                || mQuickAccessWalletController == null
+                || !mQuickAccessWalletController.isWalletEnabled()
+                || !mHasCard) {
             mWalletButton.setVisibility(GONE);
             mIndicationArea.setPadding(0, 0, 0, 0);
         } else {
@@ -690,7 +685,9 @@
     @Override
     public void onKeyguardShowingChanged() {
         if (mKeyguardStateController.isShowing()) {
-            queryWalletCards();
+            if (mQuickAccessWalletController != null) {
+                mQuickAccessWalletController.queryWalletCards(mCardRetriever);
+            }
         }
     }
 
@@ -935,50 +932,17 @@
     /**
      * Initialize the wallet feature, only enabling if the feature is enabled within the platform.
      */
-    public void initWallet(QuickAccessWalletClient client, Executor uiExecutor,
-            SecureSettings secureSettings) {
-        mQuickAccessWalletClient = client;
-        mSecureSettings = secureSettings;
-        setupWalletPreferenceObserver();
-        updateWalletPreference();
-
-        mUiExecutor = uiExecutor;
-        queryWalletCards();
+    public void initWallet(
+            QuickAccessWalletController controller) {
+        mQuickAccessWalletController = controller;
+        mQuickAccessWalletController.setupWalletChangeObservers(
+                mCardRetriever, WALLET_PREFERENCE_CHANGE, DEFAULT_PAYMENT_APP_CHANGE);
+        mQuickAccessWalletController.updateWalletPreference();
+        mQuickAccessWalletController.queryWalletCards(mCardRetriever);
 
         updateWalletVisibility();
     }
 
-    private void setupWalletPreferenceObserver() {
-        if (mWalletPreferenceObserver == null) {
-            mWalletPreferenceObserver = new ContentObserver(null /* handler */) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    mUiExecutor.execute(() -> updateWalletPreference());
-                }
-            };
-
-            mSecureSettings.registerContentObserver(
-                    Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY),
-                    false /* notifyForDescendants */,
-                    mWalletPreferenceObserver);
-        }
-    }
-
-    private void updateWalletPreference() {
-        mWalletEnabled = mQuickAccessWalletClient.isWalletFeatureAvailable()
-                && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked();
-    }
-
-    private void queryWalletCards() {
-        if (!mWalletEnabled || mUiExecutor == null) {
-            return;
-        }
-        GetWalletCardsRequest request =
-                new GetWalletCardsRequest(1 /* cardWidth */, 1 /* cardHeight */,
-                        1 /* iconSizePx */, 1 /* maxCards */);
-        mQuickAccessWalletClient.getWalletCards(mUiExecutor, request, mCardRetriever);
-    }
-
     private void onWalletClick(View v) {
         // More coming here; need to inform the user about how to proceed
         if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
@@ -991,12 +955,13 @@
                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
             mContext.startActivity(intent);
         } else {
-            if (mQuickAccessWalletClient.createWalletIntent() == null) {
+            if (mQuickAccessWalletController.getWalletClient().createWalletIntent() == null) {
                 Log.w(TAG, "Could not get intent of the wallet app.");
                 return;
             }
             mActivityStarter.postStartActivityDismissingKeyguard(
-                    mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
+                    mQuickAccessWalletController.getWalletClient().createWalletIntent(),
+                    /* delay= */ 0);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 0c4bec2c..eef2420 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -116,6 +116,12 @@
     // right and left padding applied to this view to account for cutouts and rounded corners
     private Pair<Integer, Integer> mPadding = new Pair(0, 0);
 
+    /**
+     * The clipping on the top
+     */
+    private int mTopClipping;
+    private final Rect mClipRect = new Rect(0, 0, 0, 0);
+
     public KeyguardStatusBarView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mUserManager = UserManager.get(getContext());
@@ -549,4 +555,25 @@
     public void onSystemChromeAnimationUpdate(ValueAnimator anim) {
         mSystemIconsContainer.setAlpha((float) anim.getAnimatedValue());
     }
+
+    @Override
+    protected void onLayout(boolean changed, int l, int t, int r, int b) {
+        super.onLayout(changed, l, t, r, b);
+        updateClipping();
+    }
+
+    /**
+     * Set the clipping on the top of the view.
+     */
+    public void setTopClipping(int topClipping) {
+        if (topClipping != mTopClipping) {
+            mTopClipping = topClipping;
+            updateClipping();
+        }
+    }
+
+    private void updateClipping() {
+        mClipRect.set(0, mTopClipping, getWidth(), getHeight());
+        setClipBounds(mClipRect);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 7407143..7737420 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -27,7 +27,6 @@
 import static com.android.systemui.classifier.Classifier.QUICK_SETTINGS;
 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
-import static com.android.systemui.util.Utils.shouldUseSplitNotificationShade;
 
 import static java.lang.Float.isNaN;
 
@@ -54,7 +53,6 @@
 import android.os.SystemClock;
 import android.os.UserManager;
 import android.os.VibrationEffect;
-import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.util.Log;
 import android.util.MathUtils;
 import android.view.DisplayCutout;
@@ -153,6 +151,7 @@
 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
 import com.android.systemui.util.Utils;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import java.io.FileDescriptor;
@@ -310,6 +309,7 @@
     private final FeatureFlags mFeatureFlags;
     private final ScrimController mScrimController;
     private final PrivacyDotViewController mPrivacyDotViewController;
+    private final QuickAccessWalletController mQuickAccessWalletController;
 
     // Maximum # notifications to show on Keyguard; extras will be collapsed in an overflow card.
     // If there are exactly 1 + mMaxKeyguardNotifications, then still shows all notifications
@@ -582,8 +582,8 @@
     private int mScrimCornerRadius;
     private int mScreenCornerRadius;
     private int mNotificationScrimPadding;
+    private boolean mQSAnimatingHiddenFromCollapsed;
 
-    private final QuickAccessWalletClient mQuickAccessWalletClient;
     private final Executor mUiExecutor;
     private final SecureSettings mSecureSettings;
 
@@ -664,11 +664,11 @@
             AmbientState ambientState,
             LockIconViewController lockIconViewController,
             FeatureFlags featureFlags,
-            QuickAccessWalletClient quickAccessWalletClient,
             KeyguardMediaController keyguardMediaController,
             PrivacyDotViewController privacyDotViewController,
             TapAgainViewController tapAgainViewController,
             FragmentService fragmentService,
+            QuickAccessWalletController quickAccessWalletController,
             @Main Executor uiExecutor,
             SecureSettings secureSettings) {
         super(view, falsingManager, dozeLog, keyguardStateController,
@@ -679,6 +679,7 @@
         mVibratorHelper = vibratorHelper;
         mKeyguardMediaController = keyguardMediaController;
         mPrivacyDotViewController = privacyDotViewController;
+        mQuickAccessWalletController = quickAccessWalletController;
         mMetricsLogger = metricsLogger;
         mActivityManager = activityManager;
         mConfigurationController = configurationController;
@@ -721,7 +722,6 @@
         mScrimController.setClipsQsScrim(!mShouldUseSplitNotificationShade);
         mUserManager = userManager;
         mMediaDataManager = mediaDataManager;
-        mQuickAccessWalletClient = quickAccessWalletClient;
         mTapAgainViewController = tapAgainViewController;
         mUiExecutor = uiExecutor;
         mSecureSettings = secureSettings;
@@ -1122,7 +1122,7 @@
         mKeyguardBottomArea.setFalsingManager(mFalsingManager);
 
         if (mFeatureFlags.isQuickAccessWalletEnabled()) {
-            mKeyguardBottomArea.initWallet(mQuickAccessWalletClient, mUiExecutor, mSecureSettings);
+            mKeyguardBottomArea.initWallet(mQuickAccessWalletController);
         }
     }
 
@@ -1259,7 +1259,7 @@
                 bypassEnabled, getUnlockedStackScrollerPadding(),
                 computeQsExpansionFraction(),
                 mDisplayCutoutTopInset,
-                shouldUseSplitNotificationShade(mFeatureFlags, mResources));
+                mShouldUseSplitNotificationShade);
         mClockPositionAlgorithm.run(mClockPositionResult);
         boolean animate = mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
         boolean animateClock = animate || mAnimateNextPositionUpdate;
@@ -1443,7 +1443,7 @@
         }
         mStatusBar.getGutsManager().closeAndSaveGuts(true /* leavebehind */, true /* force */,
                 true /* controls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
-        if (animate) {
+        if (animate && !isFullyCollapsed()) {
             animateCloseQs(true /* animateAway */);
         } else {
             closeQs();
@@ -1727,6 +1727,11 @@
     }
 
     private float computeQsExpansionFraction() {
+        if (mQSAnimatingHiddenFromCollapsed) {
+            // When hiding QS from collapsed state, the expansion can sometimes temporarily
+            // be larger than 0 because of the timing, leading to flickers.
+            return 0.0f;
+        }
         return Math.min(
                 1f, (mQsExpansionHeight - mQsMinExpansionHeight) / (mQsMaxExpansionHeight
                         - mQsMinExpansionHeight));
@@ -2260,19 +2265,24 @@
             boolean visible) {
         // Fancy clipping for quick settings
         int radius = mScrimCornerRadius;
+        int statusBarClipTop = 0;
+        boolean clipStatusView = false;
         if (!mShouldUseSplitNotificationShade) {
             // The padding on this area is large enough that we can use a cheaper clipping strategy
             mKeyguardStatusAreaClipBounds.set(left, top, right, bottom);
-            mKeyguardStatusViewController.setClipBounds(visible
-                    ? mKeyguardStatusAreaClipBounds : null);
+            clipStatusView = visible;
             radius = (int) MathUtils.lerp(mScreenCornerRadius, mScrimCornerRadius,
                     Math.min(top / (float) mScrimCornerRadius, 1f));
+            statusBarClipTop = top - mKeyguardStatusBar.getTop();
         }
         if (mQs != null) {
             mQs.setFancyClipping(top, bottom, radius, visible);
         }
+        mKeyguardStatusViewController.setClipBounds(
+                clipStatusView ? mKeyguardStatusAreaClipBounds : null);
         mScrimController.setNotificationsBounds(left, top, right, bottom);
         mScrimController.setScrimCornerRadius(radius);
+        mKeyguardStatusBar.setTopClipping(statusBarClipTop);
     }
 
     private float getQSEdgePosition() {
@@ -2527,6 +2537,7 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
+                mQSAnimatingHiddenFromCollapsed = false;
                 mAnimatingQS = false;
                 notifyExpandingFinished();
                 mNotificationStackScrollLayoutController.resetCheckSnoozeLeavebehind();
@@ -2543,6 +2554,7 @@
         animator.start();
         mQsExpansionAnimator = animator;
         mQsAnimatorExpand = expanding;
+        mQSAnimatingHiddenFromCollapsed = computeQsExpansionFraction() == 0.0f && target == 0;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index ae018ba..52f9aca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -152,6 +152,16 @@
         mCallbacks.add(new WeakReference<StatusBarWindowCallback>(callback));
     }
 
+    @Override
+    public void unregisterCallback(StatusBarWindowCallback callback) {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            if (mCallbacks.get(i).get() == callback) {
+                mCallbacks.remove(i);
+                return;
+            }
+        }
+    }
+
     /**
      * Register a listener to monitor scrims visibility
      * @param listener A listener to monitor scrims visibility
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index ba2340e..c27497e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -464,6 +464,7 @@
     protected final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
     private final BrightnessSlider.Factory mBrightnessSliderFactory;
     private final FeatureFlags mFeatureFlags;
+    private final KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
 
     private final List<ExpansionChangedListener> mExpansionChangedListeners;
 
@@ -885,6 +886,8 @@
         mAnimationScheduler = animationScheduler;
         mStatusBarLocationPublisher = locationPublisher;
         mFeatureFlags = featureFlags;
+        mKeyguardUnlockAnimationController = keyguardUnlockAnimationController;
+
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         lockscreenShadeTransitionController.setStatusbar(this);
 
@@ -1369,7 +1372,8 @@
         // are already animating the keyguard dismiss (since we will need to either finish or cancel
         // the animation).
         if (trackingTouch
-                || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()) {
+                || mKeyguardViewMediator.isAnimatingBetweenKeyguardAndSurfaceBehindOrWillBe()
+                || mKeyguardUnlockAnimationController.isUnlockingWithSmartSpaceTransition()) {
             mKeyguardStateController.notifyKeyguardDismissAmountChanged(
                     1f - expansion, trackingTouch);
         }
@@ -4386,6 +4390,7 @@
         } else {
             mScrimController.transitionTo(ScrimState.UNLOCKED, mUnlockScrimCallback);
         }
+        updateLightRevealScrimVisibility();
         Trace.endSection();
     }
 
@@ -4818,6 +4823,11 @@
             return;
         }
 
+        if (mDozeServiceHost.isPulsing()) {
+            mLightRevealScrim.setVisibility(View.GONE);
+            return;
+        }
+
         if (mFeatureFlags.useNewLockscreenAnimations()
                 && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
             mLightRevealScrim.setVisibility(View.VISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index 8befe80..edcf261 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -23,6 +23,8 @@
 import android.view.DisplayCutout
 import android.view.View.LAYOUT_DIRECTION_RTL
 import android.view.WindowManager
+import android.view.WindowMetrics
+import androidx.annotation.VisibleForTesting
 import com.android.systemui.Dumpable
 import com.android.systemui.R
 import com.android.systemui.dagger.SysUISingleton
@@ -118,17 +120,8 @@
         val chipWidth = rotatedResources.getDimensionPixelSize(
                 R.dimen.ongoing_appops_chip_max_width)
 
-        return if (context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL) {
-            Rect(insets.left - dotWidth,
-                    insets.top,
-                    insets.left + chipWidth,
-                    insets.bottom)
-        } else {
-            Rect(insets.right - chipWidth,
-                    insets.top,
-                    insets.right + dotWidth,
-                    insets.bottom)
-        }
+        val isRtl = context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+        return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
     }
 
     /**
@@ -139,8 +132,7 @@
         var insets = insetsByCorner[rotation]
         if (insets == null) {
             val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
-            insets = getCalculatedInsetsForRotation(rotation, rotatedResources)
-            insetsByCorner[rotation] = insets
+            insets = getAndSetInsetsForRotation(rotation, rotatedResources)
         }
 
         return insets
@@ -157,13 +149,19 @@
     }
 
     private fun getCalculatedInsetsForRotation(
-        @Rotation rotation: Int,
+        @Rotation targetRotation: Int,
         rotatedResources: Resources
     ): Rect {
         val dc = context.display.cutout
+        val currentRotation = RotationUtils.getExactRotation(context)
 
         return calculateInsetsForRotationWithRotatedResources(
-                rotation, rotatedResources, dc, windowManager, context)
+                currentRotation,
+                targetRotation,
+                dc,
+                windowManager.maximumWindowMetrics,
+                rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height),
+                rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding))
     }
 
     override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
@@ -179,8 +177,8 @@
 
 private const val TAG = "StatusBarInsetsProvider"
 
-private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotation: Int): Rect {
-    val bounds = wm.maximumWindowMetrics.bounds
+private fun getRotationZeroDisplayBounds(wm: WindowMetrics, @Rotation exactRotation: Int): Rect {
+    val bounds = wm.bounds
 
     if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) {
         return bounds
@@ -190,9 +188,24 @@
     return Rect(0, 0, bounds.bottom, bounds.right)
 }
 
-private fun getCurrentDisplayBounds(wm: WindowManager): Rect {
-    val bounds = wm.maximumWindowMetrics.bounds
-    return bounds
+@VisibleForTesting
+fun getPrivacyChipBoundingRectForInsets(
+    contentRect: Rect,
+    dotWidth: Int,
+    chipWidth: Int,
+    isRtl: Boolean
+): Rect {
+    return if (isRtl) {
+        Rect(contentRect.left - dotWidth,
+                contentRect.top,
+                contentRect.left + chipWidth,
+                contentRect.bottom)
+    } else {
+        Rect(contentRect.right - chipWidth,
+                contentRect.top,
+                contentRect.right + dotWidth,
+                contentRect.bottom)
+    }
 }
 
 /**
@@ -206,41 +219,32 @@
  * @see [RotationUtils#getResourcesForRotation]
  */
 fun calculateInsetsForRotationWithRotatedResources(
+    @Rotation currentRotation: Int,
     @Rotation targetRotation: Int,
-    rotatedResources: Resources,
     displayCutout: DisplayCutout?,
-    windowmanager: WindowManager,
-    context: Context
+    windowMetrics: WindowMetrics,
+    statusBarHeight: Int,
+    roundedCornerPadding: Int
 ): Rect {
-    val rtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
-
-    val exactRotation = RotationUtils.getExactRotation(context)
-    val height = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height)
-
     /*
     TODO: Check if this is ever used for devices with no rounded corners
-    val paddingStart = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_start)
-    val paddingEnd = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_end)
-    val left = if (rtl) paddingEnd else paddingStart
-    val right = if(rtl) paddingStart else paddingEnd
+    val left = if (isRtl) paddingEnd else paddingStart
+    val right = if (isRtl) paddingStart else paddingEnd
      */
 
-    val roundedCornerPadding = rotatedResources.getDimensionPixelSize(
-            R.dimen.rounded_corner_content_padding)
-
-    val rotZeroBounds = getRotationZeroDisplayBounds(windowmanager, exactRotation)
-    val currentBounds = getCurrentDisplayBounds(windowmanager)
+    val rotZeroBounds = getRotationZeroDisplayBounds(windowMetrics, currentRotation)
+    val currentBounds = windowMetrics.bounds
 
     val sbLeftRight = getStatusBarLeftRight(
             displayCutout,
-            height,
+            statusBarHeight,
             rotZeroBounds.right,
             rotZeroBounds.bottom,
             currentBounds.width(),
             currentBounds.height(),
             roundedCornerPadding,
             targetRotation,
-            exactRotation)
+            currentRotation)
 
     return sbLeftRight
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index b10e841..d3953df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -175,7 +175,7 @@
                 && !mIsAirplaneMode) {
             newState.visible = true;
             newState.resId = R.drawable.ic_qs_no_internet_unavailable;
-        } else if (mWifiIconState.noValidatedNetwork && !mWifiIconState.noNetworksAvailable
+        } else if (mWifiIconState.noDefaultNetwork && !mWifiIconState.noNetworksAvailable
                 && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
             newState.visible = true;
             newState.resId = R.drawable.ic_qs_no_internet_available;
@@ -380,7 +380,7 @@
         if (noDefaultNetwork && noNetworksAvailable && !mIsAirplaneMode) {
             newState.visible = true;
             newState.resId = R.drawable.ic_qs_no_internet_unavailable;
-        } else if (noValidatedNetwork && !noNetworksAvailable
+        } else if (noDefaultNetwork && !noNetworksAvailable
                 && (!mIsAirplaneMode || (mIsAirplaneMode && mIsWifiEnabled))) {
             newState.visible = true;
             newState.resId = R.drawable.ic_qs_no_internet_available;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
index 9856795..52e0e8a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainView.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.FrameLayout;
 import android.widget.TextView;
 
 import androidx.annotation.NonNull;
@@ -35,24 +34,24 @@
 /**
  * View to show a toast-like popup on the notification shade and quick settings.
  */
-public class TapAgainView extends FrameLayout {
+public class TapAgainView extends TextView {
+    private TextView mTextView;
+
     public TapAgainView(
             @NonNull Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
-        updateBgColor();
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
-        TextView text = new TextView(mContext);
-        text.setText(R.string.notification_tap_again);
-        addView(text);
+        updateColor();
     }
 
-    void updateBgColor() {
-        setBackgroundResource(R.drawable.rounded_bg_full);
+    void updateColor() {
+        int textColor = getResources().getColor(R.color.notif_pill_text, mContext.getTheme());
+        setTextColor(textColor);
+        setBackground(getResources().getDrawable(R.drawable.rounded_bg_full, mContext.getTheme()));
     }
 
     /** Make the view visible. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
index bb53bad..0c5502b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/TapAgainViewController.java
@@ -44,17 +44,17 @@
     final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
         @Override
         public void onOverlayChanged() {
-            mView.updateBgColor();
+            mView.updateColor();
         }
 
         @Override
         public void onUiModeChanged() {
-            mView.updateBgColor();
+            mView.updateColor();
         }
 
         @Override
         public void onThemeChanged() {
-            mView.updateBgColor();
+            mView.updateColor();
         }
     };
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
index e7201f8..af7bf95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateController.java
@@ -50,6 +50,13 @@
     boolean canDismissLockScreen();
 
     /**
+     * Whether we can currently perform the shared element SmartSpace transition. This is true if
+     * we're on the lockscreen, it can be dismissed with a swipe, and the Launcher is underneath the
+     * keyguard and displaying a SmartSpace that it has registered with System UI.
+     */
+    boolean canPerformSmartSpaceTransition();
+
+    /**
      * If the device has PIN/pattern/password or a lock screen at all.
      */
     boolean isMethodSecure();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index e69c1f2..0945a3f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -32,6 +32,7 @@
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -53,6 +54,7 @@
     private final LockPatternUtils mLockPatternUtils;
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
             new UpdateMonitorCallback();
+    private final SmartspaceTransitionController mSmartspaceTransitionController;
 
     private boolean mCanDismissLockScreen;
     private boolean mShowing;
@@ -96,10 +98,12 @@
      */
     @Inject
     public KeyguardStateControllerImpl(Context context,
-            KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils) {
+            KeyguardUpdateMonitor keyguardUpdateMonitor, LockPatternUtils lockPatternUtils,
+            SmartspaceTransitionController smartspaceTransitionController) {
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mLockPatternUtils = lockPatternUtils;
         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
+        mSmartspaceTransitionController = smartspaceTransitionController;
 
         update(true /* updateAlways */);
         if (Build.IS_DEBUGGABLE && DEBUG_AUTH_WITH_ADB) {
@@ -158,6 +162,11 @@
         mShowing = showing;
         mOccluded = occluded;
         notifyKeyguardChanged();
+
+        // Update the dismiss amount to the full 0f/1f if we explicitly show or hide the keyguard.
+        // Otherwise, the dismiss amount could be left at a random value if we show/hide during a
+        // dismiss gesture, canceling the gesture.
+        notifyKeyguardDismissAmountChanged(showing ? 0f : 1f, false);
     }
 
     private void notifyKeyguardChanged() {
@@ -228,6 +237,12 @@
     }
 
     @Override
+    public boolean canPerformSmartSpaceTransition() {
+        return canDismissLockScreen()
+                && mSmartspaceTransitionController.isSmartspaceTransitionPossible();
+    }
+
+    @Override
     public boolean isFaceAuthEnabled() {
         return mFaceAuthEnabled;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 07e9fed..4ab07af 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -1023,6 +1023,11 @@
         mValidatedTransports.clear();
         if (mLastDefaultNetworkCapabilities != null) {
             for (int transportType : mLastDefaultNetworkCapabilities.getTransportTypes()) {
+                if (transportType != NetworkCapabilities.TRANSPORT_CELLULAR
+                        && transportType != NetworkCapabilities.TRANSPORT_WIFI
+                        && transportType != NetworkCapabilities.TRANSPORT_ETHERNET) {
+                    continue;
+                }
                 if (transportType == NetworkCapabilities.TRANSPORT_CELLULAR
                         && Utils.tryGetWifiInfoForVcn(mLastDefaultNetworkCapabilities) != null) {
                     mConnectedTransports.set(NetworkCapabilities.TRANSPORT_WIFI);
@@ -1045,11 +1050,15 @@
             Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
         }
 
-        mInetCondition = !mValidatedTransports.isEmpty();
+        mInetCondition = mValidatedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+                || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+                || mValidatedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
 
         pushConnectivityToSignals();
         if (mProviderModel) {
-            mNoDefaultNetwork = mConnectedTransports.isEmpty();
+            mNoDefaultNetwork = !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_CELLULAR)
+                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_WIFI)
+                && !mConnectedTransports.get(NetworkCapabilities.TRANSPORT_ETHERNET);
             mCallbackHandler.setConnectivityStatus(mNoDefaultNetwork, !mInetCondition,
                     mNoNetworksAvailable);
             for (int i = 0; i < mMobileSignalControllers.size(); i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
index 4284148..c5e35a4 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/SystemUIToast.java
@@ -22,16 +22,19 @@
 import android.app.Application;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.UserHandle;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.widget.ImageView;
 import android.widget.TextView;
-import android.widget.ToastPresenter;
 
 import com.android.internal.R;
 import com.android.launcher3.icons.IconFactory;
@@ -52,7 +55,6 @@
     private final String mPackageName;
     private final int mUserId;
     private final LayoutInflater mLayoutInflater;
-    private final boolean mToastStyleEnabled;
 
     final int mDefaultX = 0;
     final int mDefaultHorizontalMargin = 0;
@@ -66,15 +68,14 @@
     @Nullable private final Animator mOutAnimator;
 
     SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
-            String packageName, int userId, boolean toastStyleEnabled, int orientation) {
+            String packageName, int userId, int orientation) {
         this(layoutInflater, context, text, null, packageName, userId,
-                toastStyleEnabled, orientation);
+                orientation);
     }
 
     SystemUIToast(LayoutInflater layoutInflater, Context context, CharSequence text,
             ToastPlugin.Toast pluginToast, String packageName, int userId,
-            boolean toastStyleEnabled, int orientation) {
-        mToastStyleEnabled = toastStyleEnabled;
+            int orientation) {
         mLayoutInflater = layoutInflater;
         mContext = context;
         mText = text;
@@ -167,23 +168,45 @@
             return mPluginToast.getView();
         }
 
-        View toastView;
-        if (mToastStyleEnabled) {
-            toastView = mLayoutInflater.inflate(
+        final View toastView = mLayoutInflater.inflate(
                     com.android.systemui.R.layout.text_toast, null);
-            ((TextView) toastView.findViewById(com.android.systemui.R.id.text)).setText(mText);
+        final TextView textView = toastView.findViewById(com.android.systemui.R.id.text);
+        final ImageView iconView = toastView.findViewById(com.android.systemui.R.id.icon);
+        textView.setText(mText);
 
-            Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId);
-            if (icon == null) {
-                toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE);
-            } else {
-                ((ImageView) toastView.findViewById(com.android.systemui.R.id.icon))
-                        .setImageDrawable(icon);
-            }
-        } else {
-            toastView = ToastPresenter.getTextToastView(mContext, mText);
+        ApplicationInfo appInfo = null;
+        try {
+            appInfo = mContext.getPackageManager()
+                    .getApplicationInfoAsUser(mPackageName, 0, mUserId);
+        } catch (PackageManager.NameNotFoundException e) {
+            Log.e(TAG, "Package name not found package=" + mPackageName
+                    + " user=" + mUserId);
         }
 
+        if (appInfo != null && appInfo.targetSdkVersion < Build.VERSION_CODES.S) {
+            // no two-line limit
+            textView.setMaxLines(Integer.MAX_VALUE);
+
+            // no app icon
+            toastView.findViewById(com.android.systemui.R.id.icon).setVisibility(View.GONE);
+        } else {
+            Drawable icon = getBadgedIcon(mContext, mPackageName, mUserId);
+            if (icon == null) {
+                iconView.setVisibility(View.GONE);
+            } else {
+                iconView.setImageDrawable(icon);
+                if (appInfo.labelRes != 0) {
+                    try {
+                        Resources res = mContext.getPackageManager().getResourcesForApplication(
+                                appInfo,
+                                new Configuration(mContext.getResources().getConfiguration()));
+                        iconView.setContentDescription(res.getString(appInfo.labelRes));
+                    } catch (PackageManager.NameNotFoundException e) {
+                        Log.d(TAG, "Cannot find application resources for icon label.");
+                    }
+                }
+            }
+        }
         return toastView;
     }
 
@@ -205,18 +228,14 @@
             return mPluginToast.getInAnimation();
         }
 
-        return mToastStyleEnabled
-                ? ToastDefaultAnimation.Companion.toastIn(getView())
-                : null;
+        return ToastDefaultAnimation.Companion.toastIn(getView());
     }
 
     private Animator createOutAnimator() {
         if (isPluginToast() && mPluginToast.getOutAnimation() != null) {
             return mPluginToast.getOutAnimation();
         }
-        return mToastStyleEnabled
-                ? ToastDefaultAnimation.Companion.toastOut(getView())
-                : null;
+        return ToastDefaultAnimation.Companion.toastOut(getView());
     }
 
     /**
@@ -225,6 +244,10 @@
      */
     public static Drawable getBadgedIcon(@NonNull Context context, String packageName,
             int userId) {
+        if (!(context.getApplicationContext() instanceof Application)) {
+            return null;
+        }
+
         final ApplicationsState appState =
                 ApplicationsState.getInstance((Application) context.getApplicationContext());
         if (!appState.isUserAdded(userId)) {
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
index 8b782d4..148bffa 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastFactory.java
@@ -27,7 +27,6 @@
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.plugins.ToastPlugin;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.statusbar.FeatureFlags;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -43,17 +42,14 @@
     // only one ToastPlugin can be connected at a time.
     private ToastPlugin mPlugin;
     private final LayoutInflater mLayoutInflater;
-    private final boolean mToastStyleEnabled;
 
     @Inject
     public ToastFactory(
             LayoutInflater layoutInflater,
             PluginManager pluginManager,
-            DumpManager dumpManager,
-            FeatureFlags featureFlags) {
+            DumpManager dumpManager) {
         mLayoutInflater = layoutInflater;
         dumpManager.registerDumpable("ToastFactory", this);
-        mToastStyleEnabled = featureFlags.isToastStyleEnabled();
         pluginManager.addPluginListener(
                 new PluginListener<ToastPlugin>() {
                     @Override
@@ -77,10 +73,10 @@
             int userId, int orientation) {
         if (isPluginAvailable()) {
             return new SystemUIToast(mLayoutInflater, context, text, mPlugin.createToast(text,
-                    packageName, userId), packageName, userId, mToastStyleEnabled, orientation);
+                    packageName, userId), packageName, userId, orientation);
         }
         return new SystemUIToast(mLayoutInflater, context, text, packageName, userId,
-                mToastStyleEnabled, orientation);
+                orientation);
     }
 
     private boolean isPluginAvailable() {
@@ -91,6 +87,5 @@
     public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
         pw.println("ToastFactory:");
         pw.println("    mAttachedPlugin=" + mPlugin);
-        pw.println("    mToastStyleEnabled=" + mToastStyleEnabled);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
index 92ea1d0..42f6687 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastUI.java
@@ -19,6 +19,7 @@
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,7 +35,8 @@
 import android.view.accessibility.IAccessibilityManager;
 import android.widget.ToastPresenter;
 
-import com.android.internal.annotations.VisibleForTesting;
+import androidx.annotation.VisibleForTesting;
+
 import com.android.systemui.SystemUI;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.statusbar.CommandQueue;
@@ -60,11 +62,11 @@
     private final AccessibilityManager mAccessibilityManager;
     private final ToastFactory mToastFactory;
     private final ToastLogger mToastLogger;
-    private SystemUIToast mToast;
     @Nullable private ToastPresenter mPresenter;
     @Nullable private ITransientNotificationCallback mCallback;
     private ToastOutAnimatorListener mToastOutAnimatorListener;
 
+    @VisibleForTesting SystemUIToast mToast;
     private int mOrientation = ORIENTATION_PORTRAIT;
 
     @Inject
@@ -191,7 +193,7 @@
     /**
      * Once the out animation for a toast is finished, start showing the next toast.
      */
-    class ToastOutAnimatorListener implements Animator.AnimatorListener {
+    class ToastOutAnimatorListener extends AnimatorListenerAdapter {
         final ToastPresenter mPrevPresenter;
         final ITransientNotificationCallback mPrevCallback;
         @Nullable Runnable mShowNextToastRunnable;
@@ -210,10 +212,6 @@
         }
 
         @Override
-        public void onAnimationStart(Animator animation) {
-        }
-
-        @Override
         public void onAnimationEnd(Animator animation) {
             mPrevPresenter.hide(mPrevCallback);
             if (mShowNextToastRunnable != null) {
@@ -221,15 +219,5 @@
             }
             mToastOutAnimatorListener = null;
         }
-
-        @Override
-        public void onAnimationCancel(Animator animation) {
-            onAnimationEnd(animation);
-        }
-
-        @Override
-        public void onAnimationRepeat(Animator animation) {
-
-        }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 7244ffe..26f4a2b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -68,8 +68,7 @@
     private static final String[] RESET_EXCEPTION_LIST = new String[] {
             QSTileHost.TILES_SETTING,
             Settings.Secure.DOZE_ALWAYS_ON,
-            Settings.Secure.MEDIA_CONTROLS_RESUME,
-            Secure.MEDIA_CONTROLS_RESUME_BLOCKED
+            Settings.Secure.MEDIA_CONTROLS_RESUME
     };
 
     private final Observer mObserver = new Observer();
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index f3a95f7..bf00667 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -23,7 +23,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.view.ContextThemeWrapper;
 import android.view.View;
 
@@ -32,9 +31,7 @@
 import com.android.systemui.statusbar.CommandQueue;
 import com.android.systemui.statusbar.FeatureFlags;
 
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
 import java.util.function.Consumer;
 
 public class Utils {
@@ -144,7 +141,7 @@
 
     /**
      * Allow media resumption controls. Requires {@link #useQsMediaPlayer(Context)} to be enabled.
-     * Off by default, but can be enabled by setting to 1
+     * On by default, but can be disabled by setting to 0
      */
     public static boolean useMediaResumption(Context context) {
         int flag = Settings.Secure.getInt(context.getContentResolver(),
@@ -153,20 +150,14 @@
     }
 
     /**
-     * Get the set of apps for which the user has manually disabled resumption.
+     * Allow recommendations from smartspace to show in media controls.
+     * Requires {@link #useQsMediaPlayer(Context)} to be enabled.
+     * On by default, but can be disabled by setting to 0
      */
-    public static Set<String> getBlockedMediaApps(Context context) {
-        String list = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.MEDIA_CONTROLS_RESUME_BLOCKED);
-        if (TextUtils.isEmpty(list)) {
-            return new HashSet<>();
-        }
-        String[] names = list.split(":");
-        Set<String> apps = new HashSet<>(names.length);
-        for (String s : names) {
-            apps.add(s);
-        }
-        return apps;
+    public static boolean allowMediaRecommendations(Context context) {
+        int flag = Settings.Secure.getInt(context.getContentResolver(),
+                Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1);
+        return useQsMediaPlayer(context) && flag > 0;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
index b9b7730..0b2f004 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
@@ -116,7 +116,7 @@
             default:
                 throw new IllegalArgumentException("Unknown rotation: " + rot);
         }
-        Configuration c = context.getResources().getConfiguration();
+        Configuration c = new Configuration(context.getResources().getConfiguration());
         c.orientation = orientation;
         Context rotated = context.createConfigurationContext(c);
         return rotated.getResources();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 50b8858..961822a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -337,37 +337,44 @@
 
         mTouchableRegion.setEmpty();
 
-        // Set the touchable region to the union of all child view bounds. We don't use touches on
-        // the volume dialog container itself, so this is fine.
+        // Set the touchable region to the union of all child view bounds and the live caption
+        // tooltip. We don't use touches on the volume dialog container itself, so this is fine.
         for (int i = 0; i < mDialogView.getChildCount(); i++) {
-            final View view = mDialogView.getChildAt(i);
-            final int[] locInWindow = new int[2];
-            view.getLocationInWindow(locInWindow);
+            unionViewBoundstoTouchableRegion(mDialogView.getChildAt(i));
+        }
 
-            float x = locInWindow[0];
-            float y = locInWindow[1];
-
-            // The ringer and rows container has extra height at the top to fit the expanded ringer
-            // drawer. This area should not be touchable unless the ringer drawer is open.
-            if (view == mTopContainer && !mIsRingerDrawerOpen) {
-                if (!isLandscape()) {
-                    y += getRingerDrawerOpenExtraSize();
-                } else {
-                    x += getRingerDrawerOpenExtraSize();
-                }
-            }
-
-            mTouchableRegion.op(
-                    (int) x,
-                    (int) y,
-                    locInWindow[0] + view.getWidth(),
-                    locInWindow[1] + view.getHeight(),
-                    Region.Op.UNION);
+        if (mODICaptionsTooltipView != null && mODICaptionsTooltipView.getVisibility() == VISIBLE) {
+            unionViewBoundstoTouchableRegion(mODICaptionsTooltipView);
         }
 
         internalInsetsInfo.touchableRegion.set(mTouchableRegion);
     }
 
+    private void unionViewBoundstoTouchableRegion(final View view) {
+        final int[] locInWindow = new int[2];
+        view.getLocationInWindow(locInWindow);
+
+        float x = locInWindow[0];
+        float y = locInWindow[1];
+
+        // The ringer and rows container has extra height at the top to fit the expanded ringer
+        // drawer. This area should not be touchable unless the ringer drawer is open.
+        if (view == mTopContainer && !mIsRingerDrawerOpen) {
+            if (!isLandscape()) {
+                y += getRingerDrawerOpenExtraSize();
+            } else {
+                x += getRingerDrawerOpenExtraSize();
+            }
+        }
+
+        mTouchableRegion.op(
+                (int) x,
+                (int) y,
+                locInWindow[0] + view.getWidth(),
+                locInWindow[1] + view.getHeight(),
+                Region.Op.UNION);
+    }
+
     private void initDialog() {
         mDialog = new CustomDialog(mContext);
 
@@ -1058,21 +1065,38 @@
         }
 
         if (mODICaptionsTooltipView != null) {
-            mODICaptionsTooltipView.setAlpha(0.f);
-            mODICaptionsTooltipView.animate()
-                .alpha(1.f)
-                .setStartDelay(mDialogShowAnimationDurationMs)
-                .withEndAction(() -> {
-                    if (D.BUG) Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true");
-                    Prefs.putBoolean(mContext,
-                            Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, true);
-                    mHasSeenODICaptionsTooltip = true;
-                    if (mODICaptionsIcon != null) {
-                        mODICaptionsIcon
-                                .postOnAnimation(getSinglePressFor(mODICaptionsIcon));
-                    }
-                })
-                .start();
+            mODICaptionsTooltipView.setAlpha(0.0f);
+
+            // We need to wait for layout and then center the caption view. Since the height of the
+            // dialog is now dynamic (with the variable ringer drawer height changing the height of
+            // the dialog), we need to do this here in code vs. in XML.
+            mHandler.post(() -> {
+                final int[] odiTooltipLocation = mODICaptionsTooltipView.getLocationOnScreen();
+                final int[] odiButtonLocation = mODICaptionsIcon.getLocationOnScreen();
+
+                final float heightDiffForCentering =
+                        (mODICaptionsTooltipView.getHeight() - mODICaptionsIcon.getHeight()) / 2f;
+
+                mODICaptionsTooltipView.setTranslationY(
+                        odiButtonLocation[1] - odiTooltipLocation[1] - heightDiffForCentering);
+
+                mODICaptionsTooltipView.animate()
+                        .alpha(1.0f)
+                        .setStartDelay(mDialogShowAnimationDurationMs)
+                        .withEndAction(() -> {
+                            if (D.BUG) {
+                                Log.d(TAG, "tool:checkODICaptionsTooltip() putBoolean true");
+                            }
+                            Prefs.putBoolean(mContext,
+                                    Prefs.Key.HAS_SEEN_ODI_CAPTIONS_TOOLTIP, true);
+                            mHasSeenODICaptionsTooltip = true;
+                            if (mODICaptionsIcon != null) {
+                                mODICaptionsIcon
+                                        .postOnAnimation(getSinglePressFor(mODICaptionsIcon));
+                            }
+                        })
+                        .start();
+            });
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
new file mode 100644
index 0000000..9d0cc6a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2021 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.wallet.controller;
+
+import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.DEFAULT_PAYMENT_APP_CHANGE;
+import static com.android.systemui.wallet.controller.QuickAccessWalletController.WalletChangeEvent.WALLET_PREFERENCE_CHANGE;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.provider.Settings;
+import android.service.quickaccesswallet.GetWalletCardsRequest;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quickaccesswallet.QuickAccessWalletClientImpl;
+import android.util.Log;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Controller to handle communication between SystemUI and Quick Access Wallet Client.
+ */
+@SysUISingleton
+public class QuickAccessWalletController {
+
+    /**
+     * Event for the wallet status change, e.g. the default payment app change and the wallet
+     * preference change.
+     */
+    public enum WalletChangeEvent {
+        DEFAULT_PAYMENT_APP_CHANGE,
+        WALLET_PREFERENCE_CHANGE,
+    }
+
+    private static final String TAG = "QAWController";
+    private final Context mContext;
+    private final Executor mExecutor;
+    private final SecureSettings mSecureSettings;
+
+    private QuickAccessWalletClient mQuickAccessWalletClient;
+    private ContentObserver mWalletPreferenceObserver;
+    private ContentObserver mDefaultPaymentAppObserver;
+    private int mWalletPreferenceChangeEvents = 0;
+    private int mDefaultPaymentAppChangeEvents = 0;
+    private boolean mWalletEnabled = false;
+
+    @Inject
+    public QuickAccessWalletController(
+            Context context,
+            @Main Executor executor,
+            SecureSettings secureSettings,
+            QuickAccessWalletClient quickAccessWalletClient) {
+        mContext = context;
+        mExecutor = executor;
+        mSecureSettings = secureSettings;
+        mQuickAccessWalletClient = quickAccessWalletClient;
+    }
+
+    /**
+     * Returns true if the Quick Access Wallet service & feature is available.
+     */
+    public boolean isWalletEnabled() {
+        return mWalletEnabled;
+    }
+
+    /**
+     * Returns the current instance of {@link QuickAccessWalletClient} in the controller.
+     */
+    public QuickAccessWalletClient getWalletClient() {
+        return mQuickAccessWalletClient;
+    }
+
+    /**
+     * Setup the wallet change observers per {@link WalletChangeEvent}
+     *
+     * @param cardsRetriever a callback that retrieves the wallet cards
+     * @param events {@link WalletChangeEvent} need to be handled.
+     */
+    public void setupWalletChangeObservers(
+            QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever,
+            WalletChangeEvent... events) {
+        for (WalletChangeEvent event : events) {
+            if (event == WALLET_PREFERENCE_CHANGE) {
+                setupWalletPreferenceObserver();
+            } else if (event == DEFAULT_PAYMENT_APP_CHANGE) {
+                setupDefaultPaymentAppObserver(cardsRetriever);
+            }
+        }
+    }
+
+    /**
+     * Unregister wallet change observers per {@link WalletChangeEvent} if needed.
+     *
+     */
+    public void unregisterWalletChangeObservers(WalletChangeEvent... events) {
+        for (WalletChangeEvent event : events) {
+            if (event == WALLET_PREFERENCE_CHANGE && mWalletPreferenceObserver != null) {
+                mWalletPreferenceChangeEvents--;
+                if (mWalletPreferenceChangeEvents == 0) {
+                    mSecureSettings.unregisterContentObserver(mWalletPreferenceObserver);
+                }
+            } else if (event == DEFAULT_PAYMENT_APP_CHANGE && mDefaultPaymentAppObserver != null) {
+                mDefaultPaymentAppChangeEvents--;
+                if (mDefaultPaymentAppChangeEvents == 0) {
+                    mSecureSettings.unregisterContentObserver(mDefaultPaymentAppObserver);
+                }
+            }
+        }
+    }
+
+    /**
+     * Update the "show wallet" preference.
+     */
+    public void updateWalletPreference() {
+        mWalletEnabled = mQuickAccessWalletClient.isWalletServiceAvailable()
+                && mQuickAccessWalletClient.isWalletFeatureAvailable()
+                && mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked();
+    }
+
+    /**
+     * Query the wallet cards from {@link QuickAccessWalletClient}.
+     *
+     * @param cardsRetriever a callback to retrieve wallet cards.
+     */
+    public void queryWalletCards(
+            QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
+        if (!mWalletEnabled) {
+            Log.w(TAG, "QuickAccessWallet is unavailable, unable to query cards.");
+            return;
+        }
+        int cardWidth =
+                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width);
+        int cardHeight =
+                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height);
+        int iconSizePx = mContext.getResources().getDimensionPixelSize(R.dimen.wallet_icon_size);
+        GetWalletCardsRequest request =
+                new GetWalletCardsRequest(cardWidth, cardHeight, iconSizePx, /* maxCards= */ 1);
+        mQuickAccessWalletClient.getWalletCards(mExecutor, request, cardsRetriever);
+    }
+
+    /**
+     * Re-create the {@link QuickAccessWalletClient} of the controller.
+     */
+    public void reCreateWalletClient() {
+        mQuickAccessWalletClient = QuickAccessWalletClient.create(mContext);
+    }
+
+    private void setupDefaultPaymentAppObserver(
+            QuickAccessWalletClient.OnWalletCardsRetrievedCallback cardsRetriever) {
+        if (mDefaultPaymentAppObserver == null) {
+            mDefaultPaymentAppObserver = new ContentObserver(null /* handler */) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    mExecutor.execute(() -> {
+                        reCreateWalletClient();
+                        updateWalletPreference();
+                        queryWalletCards(cardsRetriever);
+                    });
+                }
+            };
+
+            mSecureSettings.registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT),
+                    false /* notifyForDescendants */,
+                    mDefaultPaymentAppObserver);
+        }
+        mDefaultPaymentAppChangeEvents++;
+    }
+
+    private void setupWalletPreferenceObserver() {
+        if (mWalletPreferenceObserver == null) {
+            mWalletPreferenceObserver = new ContentObserver(null /* handler */) {
+                @Override
+                public void onChange(boolean selfChange) {
+                    mExecutor.execute(() -> {
+                        updateWalletPreference();
+                    });
+                }
+            };
+
+            mSecureSettings.registerContentObserver(
+                    Settings.Secure.getUriFor(QuickAccessWalletClientImpl.SETTING_KEY),
+                    false /* notifyForDescendants */,
+                    mWalletPreferenceObserver);
+        }
+        mWalletPreferenceChangeEvents++;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
index 83aa01f..c6123e7 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/ui/WalletActivity.java
@@ -24,6 +24,7 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.util.Log;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.Window;
@@ -52,7 +53,7 @@
  */
 public class WalletActivity extends LifecycleActivity {
 
-    private final QuickAccessWalletClient mQuickAccessWalletClient;
+    private static final String TAG = "WalletActivity";
     private final KeyguardStateController mKeyguardStateController;
     private final KeyguardDismissUtil mKeyguardDismissUtil;
     private final ActivityStarter mActivityStarter;
@@ -65,7 +66,6 @@
 
     @Inject
     public WalletActivity(
-            QuickAccessWalletClient quickAccessWalletClient,
             KeyguardStateController keyguardStateController,
             KeyguardDismissUtil keyguardDismissUtil,
             ActivityStarter activityStarter,
@@ -74,7 +74,6 @@
             FalsingManager falsingManager,
             UserTracker userTracker,
             StatusBarKeyguardViewManager keyguardViewManager) {
-        mQuickAccessWalletClient = quickAccessWalletClient;
         mKeyguardStateController = keyguardStateController;
         mKeyguardDismissUtil = keyguardDismissUtil;
         mActivityStarter = activityStarter;
@@ -103,10 +102,11 @@
         getActionBar().setHomeActionContentDescription(R.string.accessibility_desc_close);
         WalletView walletView = requireViewById(R.id.wallet_view);
 
+        QuickAccessWalletClient walletClient = QuickAccessWalletClient.create(this);
         mWalletScreenController = new WalletScreenController(
                 this,
                 walletView,
-                mQuickAccessWalletClient,
+                walletClient,
                 mActivityStarter,
                 mExecutor,
                 mHandler,
@@ -116,6 +116,10 @@
 
         walletView.getAppButton().setOnClickListener(
                 v -> {
+                    if (walletClient.createWalletIntent() == null) {
+                        Log.w(TAG, "Unable to create wallet app intent.");
+                        return;
+                    }
                     if (!mKeyguardStateController.isUnlocked()
                             && mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
                         return;
@@ -123,12 +127,12 @@
 
                     if (mKeyguardStateController.isUnlocked()) {
                         mActivityStarter.startActivity(
-                                mQuickAccessWalletClient.createWalletIntent(), true);
+                                walletClient.createWalletIntent(), true);
                         finish();
                     } else {
                         mKeyguardDismissUtil.executeWhenUnlocked(() -> {
                             mActivityStarter.startActivity(
-                                    mQuickAccessWalletClient.createWalletIntent(), true);
+                                    walletClient.createWalletIntent(), true);
                             finish();
                             return false;
                         }, false, true);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 10322f2..06b0bb2 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -40,8 +40,10 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.plugins.ClockPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -86,6 +88,9 @@
 
     @Mock
     Resources mResources;
+    KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+    @Mock
+    SmartspaceTransitionController mSmartSpaceTransitionController;
     @Mock
     private ClockPlugin mClockPlugin;
     @Mock
@@ -135,7 +140,10 @@
                 mBatteryController,
                 mKeyguardUpdateMonitor,
                 mBypassController,
-                mSmartspaceController);
+                mSmartspaceController,
+                mKeyguardUnlockAnimationController,
+                mSmartSpaceTransitionController
+        );
 
         when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
         when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index 49f1655..f9b6d44 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -22,6 +22,8 @@
 import android.testing.AndroidTestingRunner;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -50,6 +52,10 @@
     ConfigurationController mConfigurationController;
     @Mock
     DozeParameters mDozeParameters;
+    @Mock
+    KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
+    @Mock
+    SmartspaceTransitionController mSmartSpaceTransitionController;
 
     private KeyguardStatusViewController mController;
 
@@ -64,7 +70,9 @@
                 mKeyguardStateController,
                 mKeyguardUpdateMonitor,
                 mConfigurationController,
-                mDozeParameters);
+                mDozeParameters,
+                mKeyguardUnlockAnimationController,
+                mSmartSpaceTransitionController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index d6b82f3..2d19f7d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -55,7 +55,6 @@
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyLong
 import org.mockito.Mock
-import org.mockito.Mockito.anyBoolean
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
@@ -329,11 +328,6 @@
 
         assertThat(dismiss.isEnabled).isEqualTo(true)
         dismiss.callOnClick()
-        val captor = ArgumentCaptor.forClass(ActivityStarter.OnDismissAction::class.java)
-        verify(keyguardDismissUtil).executeWhenUnlocked(captor.capture(), anyBoolean(),
-            eq(false))
-
-        captor.value.onDismiss()
         verify(mediaDataManager).dismissMediaData(eq(mediaKey), anyLong())
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index 59527f6..dfb149d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -8,6 +8,7 @@
 import android.media.MediaMetadata
 import android.media.session.MediaController
 import android.media.session.MediaSession
+import android.provider.Settings
 import android.service.notification.StatusBarNotification
 import android.testing.AndroidTestingRunner
 import android.testing.TestableLooper.RunWithLooper
@@ -83,11 +84,16 @@
     @Captor lateinit var mediaDataCaptor: ArgumentCaptor<MediaData>
     private val clock = FakeSystemClock()
 
+    private val originalSmartspaceSetting = Settings.Secure.getInt(context.contentResolver,
+            Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1)
+
     @Before
     fun setup() {
         foregroundExecutor = FakeExecutor(clock)
         backgroundExecutor = FakeExecutor(clock)
         smartspaceMediaDataProvider = SmartspaceMediaDataProvider()
+        Settings.Secure.putInt(context.contentResolver,
+                Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 1)
         mediaDataManager = MediaDataManager(
             context = context,
             backgroundExecutor = backgroundExecutor,
@@ -139,6 +145,8 @@
     fun tearDown() {
         session.release()
         mediaDataManager.destroy()
+        Settings.Secure.putInt(context.contentResolver,
+                Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, originalSmartspaceSetting)
     }
 
     @Test
@@ -257,55 +265,6 @@
     }
 
     @Test
-    fun testAppBlockedFromResumption() {
-        // GIVEN that the manager has a notification with a resume action
-        whenever(controller.metadata).thenReturn(metadataBuilder.build())
-        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
-        val data = mediaDataCaptor.value
-        assertThat(data.resumption).isFalse()
-        mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
-
-        // and the manager should block the package from creating resume controls
-        val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app")
-        mediaDataManager.appsBlockedFromResume = blocked
-
-        // WHEN the notification is removed
-        mediaDataManager.onNotificationRemoved(KEY)
-
-        // THEN the media data is removed
-        verify(listener).onMediaDataRemoved(eq(KEY))
-    }
-
-    @Test
-    fun testAppUnblockedFromResumption() {
-        // GIVEN that an app was blocked from resuming
-        val blocked = mutableSetOf(PACKAGE_NAME, "com.example.app")
-        mediaDataManager.appsBlockedFromResume = blocked
-
-        // and GIVEN that the manager has a notification from that app with a resume action
-        whenever(controller.metadata).thenReturn(metadataBuilder.build())
-        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
-        assertThat(backgroundExecutor.runAllReady()).isEqualTo(1)
-        assertThat(foregroundExecutor.runAllReady()).isEqualTo(1)
-        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), capture(mediaDataCaptor))
-        val data = mediaDataCaptor.value
-        assertThat(data.resumption).isFalse()
-        mediaDataManager.onMediaDataLoaded(KEY, null, data.copy(resumeAction = Runnable {}))
-
-        // WHEN the app is unblocked
-        mediaDataManager.appsBlockedFromResume = mutableSetOf("com.example.app")
-
-        // and the notification is removed
-        mediaDataManager.onNotificationRemoved(KEY)
-
-        // THEN the entry will stay as a resume control
-        verify(listener).onMediaDataLoaded(eq(PACKAGE_NAME), eq(KEY), capture(mediaDataCaptor))
-    }
-
-    @Test
     fun testAddResumptionControls() {
         // WHEN resumption controls are added
         val desc = MediaDescription.Builder().run {
@@ -382,6 +341,18 @@
     }
 
     @Test
+    fun testOnSmartspaceMediaDataLoaded_settingDisabled_doesNothing() {
+        // WHEN media recommendation setting is off
+        Settings.Secure.putInt(context.contentResolver,
+                Settings.Secure.MEDIA_CONTROLS_RECOMMENDATION, 0)
+        smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
+
+        // THEN smartspace signal is ignored
+        verify(listener, never())
+                .onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean())
+    }
+
+    @Test
     fun testOnMediaDataChanged_updatesLastActiveTime() {
         val currentTime = clock.elapsedRealtime()
         mediaDataManager.onNotificationAdded(KEY, mediaNotification)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 96d1d94..4e1627f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -89,6 +89,7 @@
     @Mock private lateinit var dumpManager: DumpManager
 
     @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback>
+    @Captor lateinit var actionCaptor: ArgumentCaptor<Runnable>
 
     private lateinit var executor: FakeExecutor
     private lateinit var data: MediaData
@@ -224,9 +225,6 @@
         // But we do not tell it to add new controls
         verify(mediaDataManager, never())
                 .addResumptionControls(anyInt(), any(), any(), any(), any(), any(), any())
-
-        // Finally, make sure the resume browser disconnected
-        verify(resumeBrowser).disconnect()
     }
 
     @Test
@@ -267,4 +265,39 @@
         verify(mediaDataManager, times(3)).addResumptionControls(anyInt(),
                 any(), any(), any(), any(), any(), eq(PACKAGE_NAME))
     }
+
+    @Test
+    fun testGetResumeAction_restarts() {
+        // Set up mocks to successfully find a MBS that returns valid media
+        val pm = mock(PackageManager::class.java)
+        whenever(mockContext.packageManager).thenReturn(pm)
+        val resolveInfo = ResolveInfo()
+        val serviceInfo = ServiceInfo()
+        serviceInfo.packageName = PACKAGE_NAME
+        resolveInfo.serviceInfo = serviceInfo
+        resolveInfo.serviceInfo.name = CLASS_NAME
+        val resumeInfo = listOf(resolveInfo)
+        whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+
+        val description = MediaDescription.Builder().setTitle(TITLE).build()
+        val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+        whenever(resumeBrowser.testConnection()).thenAnswer {
+            callbackCaptor.value.addTrack(description, component, resumeBrowser)
+        }
+
+        // When media data is loaded that has not been checked yet, and does have a MBS
+        val dataCopy = data.copy(resumeAction = null, hasCheckedForResume = false)
+        resumeListener.onMediaDataLoaded(KEY, null, dataCopy)
+
+        // Then we test whether the service is valid and set the resume action
+        executor.runAllReady()
+        verify(resumeBrowser).testConnection()
+        verify(mediaDataManager).setResumeAction(eq(KEY), capture(actionCaptor))
+
+        // When the resume action is run
+        actionCaptor.value.run()
+
+        // Then we call restart
+        verify(resumeBrowser).restart()
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
index d26229e..dfa7c66 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
@@ -91,8 +91,9 @@
         setupBrowserFailed()
         resumeBrowser.testConnection()
 
-        // Then it calls onError
+        // Then it calls onError and disconnects
         verify(callback).onError()
+        verify(browser).disconnect()
     }
 
     @Test
@@ -111,8 +112,9 @@
         setupBrowserConnectionNoResults()
         resumeBrowser.testConnection()
 
-        // Then it calls onError
+        // Then it calls onError and disconnects
         verify(callback).onError()
+        verify(browser).disconnect()
     }
 
     @Test
@@ -132,8 +134,9 @@
         setupBrowserFailed()
         resumeBrowser.findRecentMedia()
 
-        // Then it calls onError
+        // Then it calls onError and disconnects
         verify(callback).onError()
+        verify(browser).disconnect()
     }
 
     @Test
@@ -143,8 +146,9 @@
         whenever(browser.getRoot()).thenReturn(null)
         resumeBrowser.findRecentMedia()
 
-        // Then it calls onError
+        // Then it calls onError and disconnects
         verify(callback).onError()
+        verify(browser).disconnect()
     }
 
     @Test
@@ -163,8 +167,9 @@
         setupBrowserConnectionNoResults()
         resumeBrowser.findRecentMedia()
 
-        // Then it calls onError
+        // Then it calls onError and disconnects
         verify(callback).onError()
+        verify(browser).disconnect()
     }
 
     @Test
@@ -173,8 +178,9 @@
         setupBrowserConnectionNotPlayable()
         resumeBrowser.findRecentMedia()
 
-        // Then it calls onError
+        // Then it calls onError and disconnects
         verify(callback).onError()
+        verify(browser).disconnect()
     }
 
     @Test
@@ -193,8 +199,9 @@
         setupBrowserFailed()
         resumeBrowser.restart()
 
-        // Then it calls onError
+        // Then it calls onError and disconnects
         verify(callback).onError()
+        verify(browser).disconnect()
     }
 
     @Test
@@ -202,13 +209,11 @@
         // When restart is called and we connect successfully
         setupBrowserConnection()
         resumeBrowser.restart()
+        verify(callback).onConnected()
 
         // Then it creates a new controller and sends play command
         verify(transportControls).prepare()
         verify(transportControls).play()
-
-        // Then it calls onConnected
-        verify(callback).onConnected()
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
index 7533cf1..b09afab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/QuickAccessWalletTileTest.java
@@ -73,6 +73,7 @@
 import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.settings.SecureSettings;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 
 import com.google.common.util.concurrent.MoreExecutors;
 
@@ -119,6 +120,8 @@
     @Mock
     private SecureSettings mSecureSettings;
     @Mock
+    private QuickAccessWalletController mController;
+    @Mock
     private FeatureFlags mFeatureFlags;
     @Captor
     ArgumentCaptor<Intent> mIntentCaptor;
@@ -145,6 +148,8 @@
         when(mQuickAccessWalletClient.getServiceLabel()).thenReturn(LABEL);
         when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
         when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
+        when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true);
+        when(mController.getWalletClient()).thenReturn(mQuickAccessWalletClient);
 
         mTile = new QuickAccessWalletTile(
                 mHost,
@@ -155,11 +160,11 @@
                 mStatusBarStateController,
                 mActivityStarter,
                 mQSLogger,
-                mQuickAccessWalletClient,
                 mKeyguardStateController,
                 mPackageManager,
                 mSecureSettings,
                 MoreExecutors.directExecutor(),
+                mController,
                 mFeatureFlags);
     }
 
@@ -175,6 +180,15 @@
     }
 
     @Test
+    public void testWalletServiceUnavailable_recreateWalletClient() {
+        when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+
+        mTile.handleSetListening(true);
+
+        verify(mController, times(1)).reCreateWalletClient();
+    }
+
+    @Test
     public void testIsAvailable_qawFeatureAvailable() {
         when(mPackageManager.hasSystemFeature(FEATURE_NFC_HOST_CARD_EMULATION)).thenReturn(true);
         when(mPackageManager.hasSystemFeature("org.chromium.arc")).thenReturn(false);
@@ -330,17 +344,8 @@
     public void testHandleSetListening_queryCards() {
         mTile.handleSetListening(true);
 
-        verify(mQuickAccessWalletClient)
-                .getWalletCards(any(), mRequestCaptor.capture(), mCallbackCaptor.capture());
+        verify(mController).queryWalletCards(mCallbackCaptor.capture());
 
-        GetWalletCardsRequest request = mRequestCaptor.getValue();
-        assertEquals(
-                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width),
-                request.getCardWidthPx());
-        assertEquals(
-                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height),
-                request.getCardHeightPx());
-        assertEquals(1, request.getMaxCards());
         assertThat(mCallbackCaptor.getValue()).isInstanceOf(
                 QuickAccessWalletClient.OnWalletCardsRetrievedCallback.class);
     }
@@ -354,37 +359,6 @@
     }
 
     @Test
-    public void testState_queryCards_hasCards_then_noCards() {
-        when(mKeyguardStateController.isUnlocked()).thenReturn(true);
-        GetWalletCardsResponse responseWithCards =
-                new GetWalletCardsResponse(
-                        Collections.singletonList(createWalletCard(mContext)), 0);
-        GetWalletCardsResponse responseWithoutCards =
-                new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
-
-        mTile.handleSetListening(true);
-
-        verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
-
-        // query wallet cards, has cards
-        mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithCards);
-        mTestableLooper.processAllMessages();
-
-        assertNotNull(mTile.getState().sideViewCustomDrawable);
-
-        mTile.handleSetListening(true);
-
-        verify(mQuickAccessWalletClient, times(2))
-                .getWalletCards(any(), any(), mCallbackCaptor.capture());
-
-        // query wallet cards, has no cards
-        mCallbackCaptor.getValue().onWalletCardsRetrieved(responseWithoutCards);
-        mTestableLooper.processAllMessages();
-
-        assertNull(mTile.getState().sideViewCustomDrawable);
-    }
-
-    @Test
     public void testQueryCards_noCards_notUpdateSideViewDrawable() {
         setUpWalletCard(/* hasCard= */ false);
 
@@ -398,7 +372,7 @@
 
         mTile.handleSetListening(true);
 
-        verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+        verify(mController).queryWalletCards(mCallbackCaptor.capture());
 
         mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
         mTestableLooper.processAllMessages();
@@ -422,7 +396,7 @@
 
         mTile.handleSetListening(true);
 
-        verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+        verify(mController).queryWalletCards(mCallbackCaptor.capture());
 
         mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
         mTestableLooper.processAllMessages();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
index 9b5c33d..116f807 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceControllerTest.kt
@@ -511,6 +511,9 @@
 
         override fun setNextAlarm(image: Drawable?, description: String?) {
         }
+
+        override fun setMediaTarget(target: SmartspaceTarget?) {
+        }
     })
 }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index 83a1872..ee8d120 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -42,7 +42,6 @@
 import android.hardware.biometrics.BiometricSourceType;
 import android.os.PowerManager;
 import android.os.UserManager;
-import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.util.DisplayMetrics;
@@ -112,6 +111,7 @@
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 import com.android.wm.shell.animation.FlingAnimationUtils;
 
 import org.junit.Before;
@@ -252,8 +252,6 @@
     @Mock
     private LockIconViewController mLockIconViewController;
     @Mock
-    private QuickAccessWalletClient mQuickAccessWalletClient;
-    @Mock
     private KeyguardMediaController mKeyguardMediaController;
     @Mock
     private PrivacyDotViewController mPrivacyDotViewController;
@@ -267,6 +265,8 @@
     private FragmentService mFragmentService;
     @Mock
     private FragmentHostManager mFragmentHostManager;
+    @Mock
+    private QuickAccessWalletController mQuickAccessWalletController;
 
     private SysuiStatusBarStateController mStatusBarStateController;
     private NotificationPanelViewController mNotificationPanelViewController;
@@ -380,11 +380,11 @@
                 mAmbientState,
                 mLockIconViewController,
                 mFeatureFlags,
-                mQuickAccessWalletClient,
                 mKeyguardMediaController,
                 mPrivacyDotViewController,
                 mTapAgainViewController,
                 mFragmentService,
+                mQuickAccessWalletController,
                 new FakeExecutor(new FakeSystemClock()),
                 mSecureSettings);
         mNotificationPanelViewController.initDependencies(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
new file mode 100644
index 0000000..4796cd7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -0,0 +1,368 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.graphics.Rect
+import android.test.suitebuilder.annotation.SmallTest
+import android.view.DisplayCutout
+import android.view.WindowMetrics
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.leak.RotationUtils
+import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
+import com.android.systemui.util.leak.RotationUtils.Rotation
+import junit.framework.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class StatusBarContentInsetsProviderTest : SysuiTestCase() {
+    @Mock private lateinit var dc: DisplayCutout
+    @Mock private lateinit var windowMetrics: WindowMetrics
+
+    @Before
+    fun setup() {
+        MockitoAnnotations.initMocks(this)
+    }
+
+    @Test
+    fun testGetBoundingRectForPrivacyChipForRotation_noCutout() {
+        val screenBounds = Rect(0, 0, 1080, 2160)
+        val roundedCornerPadding = 20
+        val sbHeightPortrait = 100
+        val sbHeightLandscape = 60
+        val currentRotation = ROTATION_NONE
+        val chipWidth = 30
+        val dotWidth = 10
+
+        `when`(windowMetrics.bounds).thenReturn(screenBounds)
+
+        var isRtl = false
+        var targetRotation = ROTATION_NONE
+        var bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                null,
+                windowMetrics,
+                sbHeightPortrait,
+                roundedCornerPadding)
+
+        var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+        /* 1080 - 20 (rounded corner) - 30 (chip),
+        *  0 (sb top)
+        *  1080 - 20 (rounded corner) + 10 ( dot),
+        *  100 (sb height portrait)
+        */
+        var expected = Rect(1030, 0, 1070, 100)
+        assertRects(expected, chipBounds, currentRotation, targetRotation)
+        isRtl = true
+        chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+        /* 0 + 20 (rounded corner) - 10 (dot),
+        *  0 (sb top)
+        *  0 + 20 (rounded corner) + 30 (chip),
+        *  100 (sb height portrait)
+        */
+        expected = Rect(10, 0, 50, 100)
+        assertRects(expected, chipBounds, currentRotation, targetRotation)
+
+        isRtl = false
+        targetRotation = ROTATION_LANDSCAPE
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightLandscape,
+                roundedCornerPadding)
+
+        chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+        /* 2160 - 20 (rounded corner) - 30 (chip),
+        *  0 (sb top)
+        *  2160 - 20 (rounded corner) + 10 ( dot),
+        *  60 (sb height landscape)
+        */
+        expected = Rect(2110, 0, 2150, 60)
+        assertRects(expected, chipBounds, currentRotation, targetRotation)
+        isRtl = true
+        chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
+        /* 0 + 20 (rounded corner) - 10 (dot),
+        *  0 (sb top)
+        *  0 + 20 (rounded corner) + 30 (chip),
+        *  60 (sb height landscape)
+        */
+        expected = Rect(10, 0, 50, 60)
+        assertRects(expected, chipBounds, currentRotation, targetRotation)
+    }
+
+    @Test
+    fun testCalculateInsetsForRotationWithRotatedResources_topLeftCutout() {
+        // GIVEN a device in portrait mode with width < height and a display cutout in the top-left
+        val screenBounds = Rect(0, 0, 1080, 2160)
+        val dcBounds = Rect(0, 0, 100, 100)
+        val roundedCornerPadding = 20
+        val sbHeightPortrait = 100
+        val sbHeightLandscape = 60
+        val currentRotation = ROTATION_NONE
+
+        `when`(windowMetrics.bounds).thenReturn(screenBounds)
+        `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
+
+        // THEN rotations which share a short side should use the greater value between rounded
+        // corner padding and the display cutout's size
+        var targetRotation = ROTATION_NONE
+        var expectedBounds = Rect(dcBounds.right,
+                0,
+                screenBounds.right - roundedCornerPadding,
+                sbHeightPortrait)
+
+        var bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightPortrait,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        targetRotation = ROTATION_LANDSCAPE
+        expectedBounds = Rect(dcBounds.height(),
+                0,
+                screenBounds.height() - roundedCornerPadding,
+                sbHeightLandscape)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightLandscape,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        // THEN the side that does NOT share a short side with the display cutout ignores the
+        // display cutout bounds
+        targetRotation = ROTATION_UPSIDE_DOWN
+        expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.width() - roundedCornerPadding,
+                sbHeightPortrait)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightPortrait,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        // Phone in portrait, seascape (rot_270) bounds
+        targetRotation = ROTATION_SEASCAPE
+        expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.height() - dcBounds.height(),
+                sbHeightLandscape)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightLandscape,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+    }
+
+    @Test
+    fun testCalculateInsetsForRotationWithRotatedResources_nonCornerCutout() {
+        // GIVEN phone in portrait mode, where width < height and the cutout is not in the corner
+        // the assumption here is that if the cutout does NOT touch the corner then we have room to
+        // layout the status bar in the given space.
+
+        val screenBounds = Rect(0, 0, 1080, 2160)
+        // cutout centered at the top
+        val dcBounds = Rect(490, 0, 590, 100)
+        val roundedCornerPadding = 20
+        val sbHeightPortrait = 100
+        val sbHeightLandscape = 60
+        val currentRotation = ROTATION_NONE
+
+        `when`(windowMetrics.bounds).thenReturn(screenBounds)
+        `when`(dc.boundingRects).thenReturn(listOf(dcBounds))
+
+        // THEN only the landscape/seascape rotations should avoid the cutout area because of the
+        // potential letterboxing
+        var targetRotation = ROTATION_NONE
+        var expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.right - roundedCornerPadding,
+                sbHeightPortrait)
+
+        var bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightPortrait,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        targetRotation = ROTATION_LANDSCAPE
+        expectedBounds = Rect(dcBounds.height(),
+                0,
+                screenBounds.height() - roundedCornerPadding,
+                sbHeightLandscape)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightLandscape,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        targetRotation = ROTATION_UPSIDE_DOWN
+        expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.right - roundedCornerPadding,
+                sbHeightPortrait)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightPortrait,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        targetRotation = ROTATION_SEASCAPE
+        expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.height() - dcBounds.height(),
+                sbHeightLandscape)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                dc,
+                windowMetrics,
+                sbHeightLandscape,
+                roundedCornerPadding)
+
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+    }
+
+    @Test
+    fun testCalculateInsetsForRotationWithRotatedResources_noCutout() {
+        // GIVEN device in portrait mode, where width < height and no cutout
+        val currentRotation = ROTATION_NONE
+        val screenBounds = Rect(0, 0, 1080, 2160)
+        val roundedCornerPadding = 20
+        val sbHeightPortrait = 100
+        val sbHeightLandscape = 60
+
+        `when`(windowMetrics.bounds).thenReturn(screenBounds)
+
+        // THEN content insets should only use rounded corner padding
+        var targetRotation = ROTATION_NONE
+        var expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.right - roundedCornerPadding,
+                sbHeightPortrait)
+
+        var bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                null, /* no cutout */
+                windowMetrics,
+                sbHeightPortrait,
+                roundedCornerPadding)
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        targetRotation = ROTATION_LANDSCAPE
+        expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.height() - roundedCornerPadding,
+                sbHeightLandscape)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                null, /* no cutout */
+                windowMetrics,
+                sbHeightLandscape,
+                roundedCornerPadding)
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        targetRotation = ROTATION_UPSIDE_DOWN
+        expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.width() - roundedCornerPadding,
+                sbHeightPortrait)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                null, /* no cutout */
+                windowMetrics,
+                sbHeightPortrait,
+                roundedCornerPadding)
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+
+        targetRotation = ROTATION_LANDSCAPE
+        expectedBounds = Rect(roundedCornerPadding,
+                0,
+                screenBounds.height() - roundedCornerPadding,
+                sbHeightLandscape)
+
+        bounds = calculateInsetsForRotationWithRotatedResources(
+                currentRotation,
+                targetRotation,
+                null, /* no cutout */
+                windowMetrics,
+                sbHeightLandscape,
+                roundedCornerPadding)
+        assertRects(expectedBounds, bounds, currentRotation, targetRotation)
+    }
+
+    private fun assertRects(
+        expected: Rect,
+        actual: Rect,
+        @Rotation currentRotation: Int,
+        @Rotation targetRotation: Int
+    ) {
+        assertTrue(
+                "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
+                " targetRotation=${RotationUtils.toString(targetRotation)}" +
+                " expected=$expected actual=$actual",
+                expected.equals(actual))
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
index 53a2efc..4b87ec8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/KeyguardStateControllerTest.java
@@ -32,6 +32,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shared.system.smartspace.SmartspaceTransitionController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -49,12 +50,14 @@
     @Mock
     private LockPatternUtils mLockPatternUtils;
     private KeyguardStateController mKeyguardStateController;
+    @Mock
+    private SmartspaceTransitionController mSmartSpaceTransitionController;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         mKeyguardStateController = new KeyguardStateControllerImpl(mContext,
-                mKeyguardUpdateMonitor, mLockPatternUtils);
+                mKeyguardUpdateMonitor, mLockPatternUtils, mSmartSpaceTransitionController);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
index 365c62c..9b177e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/toast/ToastUITest.java
@@ -17,7 +17,6 @@
 package com.android.systemui.toast;
 
 import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
-import static android.widget.ToastPresenter.TEXT_TOAST_LAYOUT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -31,13 +30,20 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.app.Application;
 import android.app.INotificationManager;
 import android.app.ITransientNotificationCallback;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -47,12 +53,11 @@
 import android.widget.FrameLayout;
 import android.widget.TextView;
 import android.widget.Toast;
-import android.widget.ToastPresenter;
 
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.R;
 import com.android.internal.util.IntPair;
+import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.shared.plugins.PluginManager;
@@ -70,6 +75,7 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
 public class ToastUITest extends SysuiTestCase {
     private static final int ANDROID_UID = 1000;
     private static final int SYSTEMUI_UID = 10140;
@@ -85,12 +91,14 @@
     private static final Binder WINDOW_TOKEN_2 = new Binder();
 
     private static final String TEXT = "Hello World";
-    private static final int MESSAGE_RES_ID = R.id.message;
+    private static final int MESSAGE_RES_ID = R.id.text;
 
     private Context mContextSpy;
     private ToastUI mToastUI;
-    @Mock private LayoutInflater mLayoutInflater;
+    private View mToastView;
+    @Mock private Application mApplication;
     @Mock private CommandQueue mCommandQueue;
+    @Mock private LayoutInflater mLayoutInflater;
     @Mock private WindowManager mWindowManager;
     @Mock private INotificationManager mNotificationManager;
     @Mock private IAccessibilityManager mAccessibilityManager;
@@ -98,6 +106,7 @@
     @Mock private DumpManager mDumpManager;
     @Mock private ToastLogger mToastLogger;
     @Mock private FeatureFlags mFeatureFlags;
+    @Mock private PackageManager mPackageManager;
 
     @Mock private ITransientNotificationCallback mCallback;
     @Captor private ArgumentCaptor<View> mViewCaptor;
@@ -106,29 +115,33 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        when(mLayoutInflater.inflate(eq(TEXT_TOAST_LAYOUT), any())).thenReturn(
-                ToastPresenter.getTextToastView(mContext, TEXT));
-        when(mFeatureFlags.isToastStyleEnabled()).thenReturn(false);
-
+        mToastView = LayoutInflater.from(mContext).inflate(R.layout.text_toast, null);
+        when(mLayoutInflater.inflate(anyInt(), eq(null))).thenReturn(mToastView);
         mContext.addMockSystemService(WindowManager.class, mWindowManager);
         mContextSpy = spy(mContext);
+        when(mContextSpy.getPackageManager()).thenReturn(mPackageManager);
         doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
-
-        doReturn(mContextSpy).when(mContextSpy).createContextAsUser(any(), anyInt());
-        mToastUI = new ToastUI(mContextSpy, mCommandQueue, mNotificationManager,
-                mAccessibilityManager, new ToastFactory(mLayoutInflater, mPluginManager,
-                mDumpManager, mFeatureFlags), mToastLogger);
+        mToastUI = new ToastUI(
+                mContextSpy,
+                mCommandQueue,
+                mNotificationManager,
+                mAccessibilityManager,
+                new ToastFactory(
+                        mLayoutInflater,
+                        mPluginManager,
+                        mDumpManager),
+                mToastLogger);
     }
 
     @Test
-    public void testStart_addToastUIAsCallbackToCommandQueue() throws Exception {
+    public void testStart_addToastUIAsCallbackToCommandQueue() {
         mToastUI.start();
 
         verify(mCommandQueue).addCallback(mToastUI);
     }
 
     @Test
-    public void testShowToast_addsCorrectViewToWindowManager() throws Exception {
+    public void testShowToast_addsCorrectViewToWindowManager() {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 null);
 
@@ -138,7 +151,7 @@
     }
 
     @Test
-    public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() throws Exception {
+    public void testShowToast_addsViewWithCorrectLayoutParamsToWindowManager() {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 null);
 
@@ -217,9 +230,14 @@
     public void testHideToast_removesView() throws Exception {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
-        View view = verifyWmAddViewAndAttachToParent();
+        final SystemUIToast toast = mToastUI.mToast;
 
+        View view = verifyWmAddViewAndAttachToParent();
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+        if (toast.getOutAnimation() != null) {
+            assertThat(toast.getOutAnimation().isRunning()).isTrue();
+            toast.getOutAnimation().cancel(); // if applicable, try to finish anim early
+        }
 
         verify(mWindowManager).removeViewImmediate(view);
     }
@@ -228,51 +246,81 @@
     public void testHideToast_finishesToken() throws Exception {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
+        final SystemUIToast toast = mToastUI.mToast;
 
+        verifyWmAddViewAndAttachToParent();
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+        if (toast.getOutAnimation() != null) {
+            assertThat(toast.getOutAnimation().isRunning()).isTrue();
+            toast.getOutAnimation().cancel(); // if applicable, try to finish anim early
+        }
 
         verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1);
     }
 
     @Test
-    public void testHideToast_callsCallback() throws Exception {
+    public void testHideToast_callsCallback() throws RemoteException {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
+        final SystemUIToast toast = mToastUI.mToast;
 
+        verifyWmAddViewAndAttachToParent();
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+        if (toast.getOutAnimation() != null) {
+            assertThat(toast.getOutAnimation().isRunning()).isTrue();
+            toast.getOutAnimation().cancel();
+        }
 
         verify(mCallback).onToastHidden();
     }
 
     @Test
-    public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws Exception {
+    public void testHideToast_whenNotCurrentToastToken_doesNotHideToast() throws RemoteException {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
+        final SystemUIToast toast = mToastUI.mToast;
 
+        verifyWmAddViewAndAttachToParent();
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_2);
 
+        if (toast.getOutAnimation() != null) {
+            assertThat(toast.getOutAnimation().isRunning()).isFalse();
+        }
+
         verify(mCallback, never()).onToastHidden();
     }
 
     @Test
-    public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws Exception {
+    public void testHideToast_whenNotCurrentToastPackage_doesNotHideToast() throws RemoteException {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
+        final SystemUIToast toast = mToastUI.mToast;
 
+        verifyWmAddViewAndAttachToParent();
         mToastUI.hideToast(PACKAGE_NAME_2, TOKEN_1);
 
+        if (toast.getOutAnimation() != null) {
+            assertThat(toast.getOutAnimation().isRunning()).isFalse();
+        }
+
         verify(mCallback, never()).onToastHidden();
     }
 
     @Test
-    public void testShowToast_afterShowToast_hidesCurrentToast() throws Exception {
+    public void testShowToast_afterShowToast_hidesCurrentToast() throws RemoteException {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
-        View view = verifyWmAddViewAndAttachToParent();
+        final SystemUIToast toast = mToastUI.mToast;
 
+        View view = verifyWmAddViewAndAttachToParent();
         mToastUI.showToast(UID_2, PACKAGE_NAME_2, TOKEN_2, TEXT, WINDOW_TOKEN_2, Toast.LENGTH_LONG,
                 null);
 
+        if (toast.getOutAnimation() != null) {
+            assertThat(toast.getOutAnimation().isRunning()).isTrue();
+            toast.getOutAnimation().cancel(); // end early if applicable
+        }
+
         verify(mWindowManager).removeViewImmediate(view);
         verify(mNotificationManager).finishToken(PACKAGE_NAME_1, TOKEN_1);
         verify(mCallback).onToastHidden();
@@ -287,9 +335,48 @@
     }
 
     @Test
+    public void testShowToast_targetsPreS_unlimitedLines_noAppIcon()
+            throws PackageManager.NameNotFoundException {
+        // GIVEN the application targets R
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.targetSdkVersion = Build.VERSION_CODES.R;
+        when(mPackageManager.getApplicationInfoAsUser(PACKAGE_NAME_1, 0,
+                UserHandle.getUserHandleForUid(UID_1).getIdentifier())).thenReturn(applicationInfo);
+
+        // WHEN the package posts a toast
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+                mCallback);
+
+        // THEN the view can have unlimited lines
+        assertThat(((TextView) mToastUI.mToast.getView()
+                .findViewById(com.android.systemui.R.id.text))
+                .getMaxLines()).isEqualTo(Integer.MAX_VALUE);
+    }
+
+    @Test
+    public void testShowToast_targetsS_twoLineLimit_noAppIcon()
+            throws PackageManager.NameNotFoundException {
+        // GIVEN the application targets S
+        ApplicationInfo applicationInfo = new ApplicationInfo();
+        applicationInfo.targetSdkVersion = Build.VERSION_CODES.S;
+        when(mPackageManager.getApplicationInfoAsUser(PACKAGE_NAME_1, 0,
+                UserHandle.getUserHandleForUid(UID_1).getIdentifier())).thenReturn(applicationInfo);
+
+        // WHEN the package posts a toast
+        mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
+                mCallback);
+
+        // THEN the view is limited to 2 lines
+        assertThat(((TextView) mToastUI.mToast.getView()
+                .findViewById(com.android.systemui.R.id.text))
+                .getMaxLines()).isEqualTo(2);
+    }
+
+    @Test
     public void testHideToast_logs() {
         mToastUI.showToast(UID_1, PACKAGE_NAME_1, TOKEN_1, TEXT, WINDOW_TOKEN_1, Toast.LENGTH_LONG,
                 mCallback);
+        verifyWmAddViewAndAttachToParent();
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
         verify(mToastLogger).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString());
     }
@@ -298,6 +385,7 @@
     public void testHideToast_error_noLog() {
         // no toast was shown, so this hide is invalid
         mToastUI.hideToast(PACKAGE_NAME_1, TOKEN_1);
+        assertThat(mToastUI.mToast).isNull();
         verify(mToastLogger, never()).logOnHideToast(PACKAGE_NAME_1, TOKEN_1.toString());
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
index 5fb779a..1aebf1c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardStateController.java
@@ -127,4 +127,9 @@
     public void notifyPanelFlingEnd() {
 
     }
+
+    @Override
+    public boolean canPerformSmartSpaceTransition() {
+        return false;
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
new file mode 100644
index 0000000..33666bc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2021 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.wallet.controller;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.service.quickaccesswallet.GetWalletCardsRequest;
+import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.SecureSettings;
+
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+@SmallTest
+public class QuickAccessWalletControllerTest extends SysuiTestCase {
+
+    @Mock
+    private QuickAccessWalletClient mQuickAccessWalletClient;
+    @Mock
+    private SecureSettings mSecureSettings;
+    @Mock
+    private QuickAccessWalletClient.OnWalletCardsRetrievedCallback mCardsRetriever;
+    @Captor
+    private ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
+
+    private QuickAccessWalletController mController;
+    private TestableLooper mTestableLooper;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mTestableLooper = TestableLooper.get(this);
+        when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(true);
+        when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(true);
+        when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(true);
+
+        mController = new QuickAccessWalletController(
+                mContext,
+                MoreExecutors.directExecutor(),
+                mSecureSettings,
+                mQuickAccessWalletClient);
+    }
+
+    @Test
+    public void walletEnabled() {
+        mController.updateWalletPreference();
+
+        assertTrue(mController.isWalletEnabled());
+    }
+
+    @Test
+    public void walletServiceUnavailable_walletNotEnabled() {
+        when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+
+        mController.updateWalletPreference();
+
+        assertFalse(mController.isWalletEnabled());
+    }
+
+    @Test
+    public void walletFeatureUnavailable_walletNotEnabled() {
+        when(mQuickAccessWalletClient.isWalletFeatureAvailable()).thenReturn(false);
+
+        mController.updateWalletPreference();
+
+        assertFalse(mController.isWalletEnabled());
+    }
+
+    @Test
+    public void walletFeatureWhenLockedUnavailable_walletNotEnabled() {
+        when(mQuickAccessWalletClient.isWalletFeatureAvailableWhenDeviceLocked()).thenReturn(false);
+
+        mController.updateWalletPreference();
+
+        assertFalse(mController.isWalletEnabled());
+    }
+
+    @Test
+    public void getWalletClient_NoRecreation_sameClient() {
+        assertSame(mQuickAccessWalletClient, mController.getWalletClient());
+    }
+
+    @Test
+    public void getWalletClient_reCreateClient_notSameClient() {
+        mController.reCreateWalletClient();
+
+        assertNotSame(mQuickAccessWalletClient, mController.getWalletClient());
+    }
+
+    @Test
+    public void queryWalletCards_walletNotEnabled_notQuery() {
+        when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
+
+        mController.queryWalletCards(mCardsRetriever);
+
+        verify(mQuickAccessWalletClient, never()).getWalletCards(any(), any(), any());
+    }
+
+    @Test
+    public void queryWalletCards_walletEnabled_queryCards() {
+        mController.updateWalletPreference();
+        mController.queryWalletCards(mCardsRetriever);
+
+        verify(mQuickAccessWalletClient)
+                .getWalletCards(
+                        eq(MoreExecutors.directExecutor()),
+                        mRequestCaptor.capture(),
+                        eq(mCardsRetriever));
+
+        GetWalletCardsRequest request = mRequestCaptor.getValue();
+        assertEquals(1, mRequestCaptor.getValue().getMaxCards());
+        assertEquals(
+                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_width),
+                request.getCardWidthPx());
+        assertEquals(
+                mContext.getResources().getDimensionPixelSize(R.dimen.wallet_tile_card_view_height),
+                request.getCardHeightPx());
+    }
+}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 0408309..71d6a48 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2569,10 +2569,6 @@
                 s.setAllowedBgActivityStartsByBinding(true);
             }
 
-            if ((flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) {
-                s.setAllowedBgFgsStartsByBinding(true);
-            }
-
             if ((flags & Context.BIND_NOT_APP_COMPONENT_USAGE) != 0) {
                 s.isNotAppComponentUsage = true;
             }
@@ -4129,9 +4125,6 @@
             if ((c.flags & Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS) != 0) {
                 s.updateIsAllowedBgActivityStartsByBinding();
             }
-            if ((c.flags & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) {
-                s.updateIsAllowedBgFgsStartsByBinding();
-            }
             if (s.app != null) {
                 updateServiceClientActivitiesLocked(s.app.mServices, c, true);
             }
@@ -5856,8 +5849,6 @@
                     final ProcessStateRecord state = app.mState;
                     if (state.isAllowedStartFgsState()) {
                         return getReasonCodeFromProcState(state.getAllowStartFgsState());
-                    } else if (state.areBackgroundFgsStartsAllowedByToken()) {
-                        return REASON_FGS_BINDING;
                     } else {
                         final ActiveInstrumentation instr = app.getActiveInstrumentation();
                         if (instr != null
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b44fe9f..3e6a0a8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1236,7 +1236,7 @@
      * The temp-allowlist that is allowed to start FGS from background.
      */
     @CompositeRWLock({"this", "mProcLock"})
-    final FgsTempAllowList<Integer, FgsTempAllowListItem> mFgsStartTempAllowList =
+    final FgsTempAllowList<FgsTempAllowListItem> mFgsStartTempAllowList =
             new FgsTempAllowList();
 
     static final FgsTempAllowListItem FAKE_TEMP_ALLOW_LIST_ITEM = new FgsTempAllowListItem(
@@ -1246,7 +1246,7 @@
      * List of uids that are allowed to have while-in-use permission when FGS is started from
      * background.
      */
-    private final FgsTempAllowList<Integer, String> mFgsWhileInUseTempAllowList =
+    private final FgsTempAllowList<String> mFgsWhileInUseTempAllowList =
             new FgsTempAllowList();
 
     /**
@@ -9372,22 +9372,17 @@
             pw.println("  mFgsStartTempAllowList:");
             final long currentTimeNow = System.currentTimeMillis();
             final long elapsedRealtimeNow = SystemClock.elapsedRealtime();
-            final Set<Integer> uids = new ArraySet<>(mFgsStartTempAllowList.keySet());
-            for (Integer uid : uids) {
-                final Pair<Long, FgsTempAllowListItem> entry = mFgsStartTempAllowList.get(uid);
-                if (entry == null) {
-                    continue;
-                }
+            mFgsStartTempAllowList.forEach((uid, entry) -> {
                 pw.print("    " + UserHandle.formatUid(uid) + ": ");
-                entry.second.dump(pw); pw.println();
-                pw.print("ms expiration=");
+                entry.second.dump(pw);
+                pw.print(" expiration=");
                 // Convert entry.mExpirationTime, which is an elapsed time since boot,
                 // to a time since epoch (i.e. System.currentTimeMillis()-based time.)
                 final long expirationInCurrentTime =
                         currentTimeNow - elapsedRealtimeNow + entry.first;
                 TimeUtils.dumpTimeWithDelta(pw, expirationInCurrentTime, currentTimeNow);
                 pw.println();
-            }
+            });
         }
         if (mDebugApp != null || mOrigDebugApp != null || mDebugTransient
                 || mOrigWaitForDebugger) {
@@ -15345,10 +15340,19 @@
                     mDeviceIdleTempAllowlist = appids;
                     if (adding) {
                         if (type == TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_ALLOWED) {
+                            // Note, the device idle temp-allowlist are by app-ids, but here
+                            // mFgsStartTempAllowList contains UIDs.
                             mFgsStartTempAllowList.add(changingUid, durationMs,
                                     new FgsTempAllowListItem(durationMs, reasonCode, reason,
                                     callingUid));
                         }
+                    } else {
+                        // Note in the removing case, we need to remove all the UIDs matching
+                        // the appId, because DeviceIdle's temp-allowlist are based on AppIds,
+                        // not UIDs.
+                        // For eacmple, "cmd deviceidle tempallowlist -r PACKAGE" will
+                        // not only remove this app for user 0, but for all users.
+                        mFgsStartTempAllowList.removeAppId(UserHandle.getAppId(changingUid));
                     }
                     setAppIdTempAllowlistStateLSP(changingUid, adding);
                 }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 6cb374a..4e6e91a 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -771,9 +771,7 @@
                 default:
                     throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
             }
-            // TODO(b/187223764): busTime won't be needed once end_session is a field in BUS.
-            final long busTime = System.currentTimeMillis();
-            final byte[] statsProto = bus.getStatsProto(busTime);
+            final byte[] statsProto = bus.getStatsProto();
 
             data.add(FrameworkStatsLog.buildStatsEvent(atomTag, statsProto));
 
diff --git a/services/core/java/com/android/server/am/FgsTempAllowList.java b/services/core/java/com/android/server/am/FgsTempAllowList.java
index 847e82f..c286556 100644
--- a/services/core/java/com/android/server/am/FgsTempAllowList.java
+++ b/services/core/java/com/android/server/am/FgsTempAllowList.java
@@ -20,11 +20,12 @@
 
 import android.annotation.Nullable;
 import android.os.SystemClock;
-import android.util.ArrayMap;
+import android.os.UserHandle;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 
-import java.util.Set;
+import java.util.function.BiConsumer;
 
 /**
  * List of keys that have expiration time.
@@ -33,19 +34,18 @@
  *
  * <p>This is used for both FGS-BG-start restriction, and FGS-while-in-use permissions check.</p>
  *
- * <p>Note: the underlying data structure is an {@link ArrayMap}, for performance reason, it is only
- * suitable to hold up to hundreds of entries.</p>
- * @param <K> type of the key.
+ * <p>Note: the underlying data structure is an {@link SparseArray}, for performance reason,
+ * it is only suitable to hold up to hundreds of entries.</p>
  * @param <E> type of the additional optional info.
  */
-public class FgsTempAllowList<K, E> {
+public class FgsTempAllowList<E> {
     private static final int DEFAULT_MAX_SIZE = 100;
 
     /**
      * The value is Pair type, Pair.first is the expirationTime(an elapsedRealtime),
      * Pair.second is the optional information entry about this key.
      */
-    private final ArrayMap<K, Pair<Long, E>> mTempAllowList = new ArrayMap<>();
+    private final SparseArray<Pair<Long, E>> mTempAllowList = new SparseArray<>();
     private int mMaxSize = DEFAULT_MAX_SIZE;
     private final Object mLock = new Object();
 
@@ -70,15 +70,14 @@
 
     /**
      * Add a key and its duration with optional info into the temp allowlist.
-     * @param key
      * @param durationMs temp-allowlisted duration in milliseconds.
      * @param entry additional optional information of this key, could be null.
      */
-    public void add(K key, long durationMs, @Nullable E entry) {
+    public void add(int uid, long durationMs, @Nullable E entry) {
         synchronized (mLock) {
             if (durationMs <= 0) {
                 Slog.e(TAG_AM, "FgsTempAllowList bad duration:" + durationMs + " key: "
-                        + key);
+                        + uid);
                 return;
             }
             // The temp allowlist should be a short list with only a few entries in it.
@@ -94,10 +93,10 @@
                     }
                 }
             }
-            final Pair<Long, E> existing = mTempAllowList.get(key);
+            final Pair<Long, E> existing = mTempAllowList.get(uid);
             final long expirationTime = now + durationMs;
             if (existing == null || existing.first < expirationTime) {
-                mTempAllowList.put(key, new Pair(expirationTime, entry));
+                mTempAllowList.put(uid, new Pair(expirationTime, entry));
             }
         }
     }
@@ -105,13 +104,12 @@
     /**
      * If the key has not expired (AKA allowed), return its non-null value.
      * If the key has expired, return null.
-     * @param key
      * @return
      */
     @Nullable
-    public Pair<Long, E> get(K key) {
+    public Pair<Long, E> get(int uid) {
         synchronized (mLock) {
-            final int index = mTempAllowList.indexOfKey(key);
+            final int index = mTempAllowList.indexOfKey(uid);
             if (index < 0) {
                 return null;
             } else if (mTempAllowList.valueAt(index).first < SystemClock.elapsedRealtime()) {
@@ -126,23 +124,48 @@
     /**
      * If the key has not expired (AKA allowed), return true.
      * If the key has expired, return false.
-     * @param key
-     * @return
      */
-    public boolean isAllowed(K key) {
-        Pair<Long, E> entry = get(key);
+    public boolean isAllowed(int uid) {
+        Pair<Long, E> entry = get(uid);
         return entry != null;
     }
 
-    public void remove(K key) {
+    /**
+     * Remove a given UID.
+     */
+    public void removeUid(int uid) {
         synchronized (mLock) {
-            mTempAllowList.remove(key);
+            mTempAllowList.remove(uid);
         }
     }
 
-    public Set<K> keySet() {
+    /**
+     * Remove by appId.
+     */
+    public void removeAppId(int appId) {
         synchronized (mLock) {
-            return mTempAllowList.keySet();
+            // Find all UIDs matching the appId.
+            for (int i = mTempAllowList.size() - 1; i >= 0; i--) {
+                final int uid = mTempAllowList.keyAt(i);
+                if (UserHandle.getAppId(uid) == appId) {
+                    mTempAllowList.removeAt(i);
+                }
+            }
+        }
+    }
+
+    /**
+     * Iterate over the entries.
+     */
+    public void forEach(BiConsumer<Integer, Pair<Long, E>> callback) {
+        synchronized (mLock) {
+            for (int i = 0; i < mTempAllowList.size(); i++) {
+                final int uid = mTempAllowList.keyAt(i);
+                final Pair<Long, E> entry = mTempAllowList.valueAt(i);
+                if (entry != null) {
+                    callback.accept(uid, entry);
+                }
+            }
         }
     }
 }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 0ffaccf..457fe0f 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3074,7 +3074,7 @@
                                 UidRecord.CHANGE_GONE);
                         EventLogTags.writeAmUidStopped(uid);
                         mActiveUids.remove(uid);
-                        mService.mFgsStartTempAllowList.remove(record.info.uid);
+                        mService.mFgsStartTempAllowList.removeUid(record.info.uid);
                         mService.noteUidProcessState(uid, ActivityManager.PROCESS_STATE_NONEXISTENT,
                                 ActivityManager.PROCESS_CAPABILITY_NONE);
                     }
diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java
index d83e13c..1fb5572 100644
--- a/services/core/java/com/android/server/am/ProcessStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessStateRecord.java
@@ -26,7 +26,6 @@
 import android.annotation.ElapsedRealtimeLong;
 import android.app.ActivityManager;
 import android.content.ComponentName;
-import android.os.Binder;
 import android.os.SystemClock;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -302,9 +301,6 @@
     @GuardedBy("mService")
     private int mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
 
-    @GuardedBy("mService")
-    private final ArraySet<Binder> mBackgroundFgsStartTokens = new ArraySet<>();
-
     /**
      * Whether or not this process has been in forced-app-standby state.
      */
@@ -1101,21 +1097,6 @@
     }
 
     @GuardedBy("mService")
-    void addAllowBackgroundFgsStartsToken(Binder entity) {
-        mBackgroundFgsStartTokens.add(entity);
-    }
-
-    @GuardedBy("mService")
-    void removeAllowBackgroundFgsStartsToken(Binder entity) {
-        mBackgroundFgsStartTokens.remove(entity);
-    }
-
-    @GuardedBy("mService")
-    boolean areBackgroundFgsStartsAllowedByToken() {
-        return !mBackgroundFgsStartTokens.isEmpty();
-    }
-
-    @GuardedBy("mService")
     void resetAllowStartFgsState() {
         mAllowStartFgsState = PROCESS_STATE_NONEXISTENT;
     }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index fd59e85..dbb2f65 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -149,10 +149,6 @@
     @GuardedBy("ams")
     private List<IBinder> mBgActivityStartsByStartOriginatingTokens = new ArrayList<>();
 
-    // any current binding to this service has BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND
-    // flag? if true, the process can start FGS from background.
-    boolean mIsAllowedBgFgsStartsByBinding;
-
     // allow while-in-use permissions in foreground service or not.
     // while-in-use permissions in FGS started from background might be restricted.
     boolean mAllowWhileInUsePermissionInFgs;
@@ -445,10 +441,6 @@
             pw.print(prefix); pw.print("mIsAllowedBgActivityStartsByStart=");
             pw.println(mIsAllowedBgActivityStartsByStart);
         }
-        if (mIsAllowedBgFgsStartsByBinding) {
-            pw.print(prefix); pw.print("mIsAllowedBgFgsStartsByBinding=");
-            pw.println(mIsAllowedBgFgsStartsByBinding);
-        }
         pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
                 pw.println(mAllowWhileInUsePermissionInFgs);
         pw.print(prefix); pw.print("recentCallingPackage=");
@@ -634,11 +626,6 @@
             } else {
                 proc.removeAllowBackgroundActivityStartsToken(this);
             }
-            if (mIsAllowedBgFgsStartsByBinding) {
-                proc.mState.addAllowBackgroundFgsStartsToken(this);
-            } else {
-                proc.mState.removeAllowBackgroundFgsStartsToken(this);
-            }
         }
         if (app != null && app != proc) {
             // If the old app is allowed to start bg activities because of a service start, leave it
@@ -726,34 +713,11 @@
         setAllowedBgActivityStartsByBinding(isAllowedByBinding);
     }
 
-    void updateIsAllowedBgFgsStartsByBinding() {
-        boolean isAllowedByBinding = false;
-        for (int conni = connections.size() - 1; conni >= 0; conni--) {
-            ArrayList<ConnectionRecord> cr = connections.valueAt(conni);
-            for (int i = 0; i < cr.size(); i++) {
-                if ((cr.get(i).flags
-                        & Context.BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND) != 0) {
-                    isAllowedByBinding = true;
-                    break;
-                }
-            }
-            if (isAllowedByBinding) {
-                break;
-            }
-        }
-        setAllowedBgFgsStartsByBinding(isAllowedByBinding);
-    }
-
     void setAllowedBgActivityStartsByBinding(boolean newValue) {
         mIsAllowedBgActivityStartsByBinding = newValue;
         updateParentProcessBgActivityStartsToken();
     }
 
-    void setAllowedBgFgsStartsByBinding(boolean newValue) {
-        mIsAllowedBgFgsStartsByBinding = newValue;
-        updateParentProcessBgFgsStartsToken();
-    }
-
     /**
      * Called when the service is started with allowBackgroundActivityStarts set. We allow
      * it for background activity starts, setting up a callback to remove this ability after a
@@ -846,17 +810,6 @@
         }
     }
 
-    private void updateParentProcessBgFgsStartsToken() {
-        if (app == null) {
-            return;
-        }
-        if (mIsAllowedBgFgsStartsByBinding) {
-            app.mState.addAllowBackgroundFgsStartsToken(this);
-        } else {
-            app.mState.removeAllowBackgroundFgsStartsToken(this);
-        }
-    }
-
     /**
      * Returns the originating token if that's the only reason background activity starts are
      * allowed. In order for that to happen the service has to be allowed only due to starts, since
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 3f2b8ff..b714c6d 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -33,6 +33,9 @@
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.os.SystemProperties;
@@ -126,6 +129,7 @@
             ERROR_UNLOCK_ALL_USERS,
             ERROR_PROVIDER_MISMATCH,
             ERROR_KEYSTORE_FAILURE,
+            ERROR_NO_NETWORK,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface RebootEscrowErrorCode {
@@ -139,6 +143,7 @@
     static final int ERROR_UNLOCK_ALL_USERS = 5;
     static final int ERROR_PROVIDER_MISMATCH = 6;
     static final int ERROR_KEYSTORE_FAILURE = 7;
+    static final int ERROR_NO_NETWORK = 8;
 
     private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
 
@@ -235,6 +240,23 @@
                     "server_based_ror_enabled", false);
         }
 
+        public boolean isNetworkConnected() {
+            final ConnectivityManager connectivityManager =
+                    mContext.getSystemService(ConnectivityManager.class);
+            if (connectivityManager == null) {
+                return false;
+            }
+
+            Network activeNetwork = connectivityManager.getActiveNetwork();
+            NetworkCapabilities networkCapabilities =
+                    connectivityManager.getNetworkCapabilities(activeNetwork);
+            return networkCapabilities != null
+                    && networkCapabilities.hasCapability(
+                            NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                    && networkCapabilities.hasCapability(
+                            NetworkCapabilities.NET_CAPABILITY_VALIDATED);
+        }
+
         public Context getContext() {
             return mContext;
         }
@@ -363,7 +385,11 @@
         }
 
         Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
-        mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
+        if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) {
+            mLoadEscrowDataErrorCode = ERROR_NO_NETWORK;
+        } else {
+            mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
+        }
         onGetRebootEscrowKeyFailed(users, attemptNumber);
     }
 
@@ -471,6 +497,8 @@
             mLoadEscrowDataErrorCode = ERROR_UNKNOWN;
         }
 
+        Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: "
+                + serviceType + ", error code: " + mLoadEscrowDataErrorCode);
         // TODO(179105110) report the duration since boot complete.
         mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount,
                 escrowDurationInSeconds, vbmetaDigestStatus, -1);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f0e6939..0dd9b29 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4221,6 +4221,7 @@
             }
         }
 
+        /** Notifications returned here will have allowlistToken stripped from them. */
         private StatusBarNotification sanitizeSbn(String pkg, int userId,
                 StatusBarNotification sbn) {
             if (sbn.getUserId() == userId) {
@@ -4228,11 +4229,16 @@
                     // We could pass back a cloneLight() but clients might get confused and
                     // try to send this thing back to notify() again, which would not work
                     // very well.
+                    Notification notification = sbn.getNotification().clone();
+                    // Remove background token before returning notification to untrusted app, this
+                    // ensures the app isn't able to perform background operations that are
+                    // associated with notification interactions.
+                    notification.setAllowlistToken(null);
                     return new StatusBarNotification(
                             sbn.getPackageName(),
                             sbn.getOpPkg(),
                             sbn.getId(), sbn.getTag(), sbn.getUid(), sbn.getInitialPid(),
-                            sbn.getNotification().clone(),
+                            notification,
                             sbn.getUser(), sbn.getOverrideGroupKey(), sbn.getPostTime());
                 }
             }
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 946f8d5..83d4ce7 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -22,8 +22,8 @@
 import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_APK_FALLBACK;
 import static com.android.internal.art.ArtStatsLog.ART_DATUM_REPORTED__COMPILE_FILTER__ART_COMPILATION_FILTER_FAKE_RUN_FROM_VDEX_FALLBACK;
 
-import android.util.jar.StrictJarFile;
 import android.util.Slog;
+import android.util.jar.StrictJarFile;
 
 import com.android.internal.art.ArtStatsLog;
 import com.android.server.pm.PackageManagerService;
@@ -163,7 +163,7 @@
                 uid,
                 compilationReason,
                 compilerFilter,
-                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_COUNTER_BYTES,
                 getDexBytes(apkPath),
                 dexMetadataType,
                 apkType,
@@ -173,7 +173,7 @@
                 uid,
                 compilationReason,
                 compilerFilter,
-                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME_COUNTER_MILLIS,
                 compileTime,
                 dexMetadataType,
                 apkType,
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 983b5b1..9c25159 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -192,6 +192,7 @@
 
 import com.android.internal.R;
 import com.android.internal.accessibility.AccessibilityShortcutController;
+import com.android.internal.app.AssistUtils;
 import com.android.internal.inputmethod.SoftInputShowHideReason;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
@@ -650,7 +651,8 @@
                 case MSG_LAUNCH_ASSIST:
                     final int deviceId = msg.arg1;
                     final Long eventTime = (Long) msg.obj;
-                    launchAssistAction(null /* hint */, deviceId, eventTime);
+                    launchAssistAction(null /* hint */, deviceId, eventTime,
+                            AssistUtils.INVOCATION_TYPE_UNKNOWN);
                     break;
                 case MSG_LAUNCH_VOICE_ASSIST_WITH_WAKE_LOCK:
                     launchVoiceAssistWithWakeLock();
@@ -1108,7 +1110,8 @@
                 performHapticFeedback(HapticFeedbackConstants.ASSISTANT_BUTTON, false,
                         "Power - Long Press - Go To Assistant");
                 final int powerKeyDeviceId = Integer.MIN_VALUE;
-                launchAssistAction(null, powerKeyDeviceId, eventTime);
+                launchAssistAction(null, powerKeyDeviceId, eventTime,
+                        AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS);
                 break;
         }
     }
@@ -1541,7 +1544,8 @@
                     launchAllAppsAction();
                     break;
                 case LONG_PRESS_HOME_ASSIST:
-                    launchAssistAction(null, deviceId, eventTime);
+                    launchAssistAction(null, deviceId, eventTime,
+                            AssistUtils.INVOCATION_TYPE_HOME_BUTTON_LONG_PRESS);
                     break;
                 case LONG_PRESS_HOME_NOTIFICATION_PANEL:
                     toggleNotificationPanel();
@@ -2772,7 +2776,7 @@
                     } else if (mPendingMetaAction) {
                         launchAssistAction(Intent.EXTRA_ASSIST_INPUT_HINT_KEYBOARD,
                                 event.getDeviceId(),
-                                event.getEventTime());
+                                event.getEventTime(), AssistUtils.INVOCATION_TYPE_UNKNOWN);
                         mPendingMetaAction = false;
                     }
                 }
@@ -3036,7 +3040,8 @@
     // various parts of the UI.
 
     /** Asks the status bar to startAssist(), usually a full "assistant" interface */
-    private void launchAssistAction(String hint, int deviceId, long eventTime) {
+    private void launchAssistAction(String hint, int deviceId, long eventTime,
+            int invocationType) {
         sendCloseSystemWindows(SYSTEM_DIALOG_REASON_ASSIST);
         if (!isUserSetupComplete()) {
             // Disable opening assist window during setup
@@ -3053,6 +3058,7 @@
             args.putBoolean(hint, true);
         }
         args.putLong(Intent.EXTRA_TIME, eventTime);
+        args.putInt(AssistUtils.INVOCATION_TYPE_KEY, invocationType);
 
         ((SearchManager) mContext.createContextAsUser(UserHandle.of(mCurrentUserId), 0)
                 .getSystemService(Context.SEARCH_SERVICE)).launchAssist(args);
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index a82c91e..dc868b3 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -126,6 +126,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.connectivity.WifiActivityEnergyInfo;
+import android.os.incremental.IncrementalManager;
 import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
@@ -426,6 +427,7 @@
     private final Object mHealthHalLock = new Object();
     private final Object mAttributedAppOpsLock = new Object();
     private final Object mSettingsStatsLock = new Object();
+    private final Object mInstalledIncrementalPackagesLock = new Object();
 
     public StatsPullAtomService(Context context) {
         super(context);
@@ -695,6 +697,10 @@
                         synchronized (mSettingsStatsLock) {
                             return pullSettingsStatsLocked(atomTag, data);
                         }
+                    case FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE:
+                        synchronized (mInstalledIncrementalPackagesLock) {
+                            return pullInstalledIncrementalPackagesLocked(atomTag, data);
+                        }
                     default:
                         throw new UnsupportedOperationException("Unknown tagId=" + atomTag);
                 }
@@ -877,6 +883,7 @@
         registerBatteryVoltage();
         registerBatteryCycleCount();
         registerSettingsStats();
+        registerInstalledIncrementalPackages();
     }
 
     private void initAndRegisterNetworkStatsPullers() {
@@ -3949,6 +3956,31 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private void registerInstalledIncrementalPackages() {
+        int tagId = FrameworkStatsLog.INSTALLED_INCREMENTAL_PACKAGE;
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                null, // use default PullAtomMetadata values
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
+    int pullInstalledIncrementalPackagesLocked(int atomTag, List<StatsEvent> pulledData) {
+        final PackageManager pm = mContext.getPackageManager();
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_INCREMENTAL_DELIVERY)) {
+            // Incremental is not enabled on this device. The result list will be empty.
+            return StatsManager.PULL_SUCCESS;
+        }
+        List<PackageInfo> installedPackages = pm.getInstalledPackages(0);
+        for (PackageInfo pi : installedPackages) {
+            if (IncrementalManager.isIncrementalPath(pi.applicationInfo.getBaseCodePath())) {
+                pulledData.add(FrameworkStatsLog.buildStatsEvent(atomTag, pi.applicationInfo.uid));
+            }
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     // Thermal event received from vendor thermal management subsystem
     private static final class ThermalEventListener extends IThermalEventListener.Stub {
         @Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0bc3d4a..38966b9 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -181,7 +181,6 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
-import static com.android.server.wm.ActivityTaskManagerService.SKIP_LAYOUT_REASON_ALLOWED;
 import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutMillisLocked;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
 import static com.android.server.wm.IdentifierProto.HASH_CODE;
@@ -5359,11 +5358,6 @@
                 mAtmService.deferWindowLayout();
                 try {
                     task.completePauseLocked(true /* resumeNext */, null /* resumingActivity */);
-                    // If there is no possible transition to execute, then allow to skip layout
-                    // because it may be done by next resumed activity.
-                    if (!pausingActivity.mDisplayContent.areOpeningAppsReady()) {
-                        mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_ALLOWED);
-                    }
                 } finally {
                     mAtmService.continueWindowLayout();
                 }
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8262c59..66e55e0 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1549,8 +1549,6 @@
         mService.getTransitionController().collect(r);
         try {
             mService.deferWindowLayout();
-            // Allow to skip layout because it may be done by the window of the starting activity.
-            mService.addWindowLayoutReasons(ActivityTaskManagerService.SKIP_LAYOUT_REASON_ALLOWED);
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
             result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                     startFlags, doResume, options, inTask, restrictedBgActivity, intentGrants);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 57c2a13..134ecde 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -657,27 +657,14 @@
     @IntDef({
             LAYOUT_REASON_CONFIG_CHANGED,
             LAYOUT_REASON_VISIBILITY_CHANGED,
-            SKIP_LAYOUT_REASON_ALLOWED,
-            SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT,
     })
     @interface LayoutReason {
     }
 
-    static final int LAYOUT_REASON_MASK = 0x0000ffff;
     static final int LAYOUT_REASON_CONFIG_CHANGED = 0x1;
     static final int LAYOUT_REASON_VISIBILITY_CHANGED = 0x2;
-    static final int SKIP_LAYOUT_REASON_MASK = 0xfffe0000;
 
-    /**
-     * Allow to call {@link WindowSurfacePlacer#endDeferAndSkipLayout} if there is a reason
-     * included in {@link #SKIP_LAYOUT_REASON_MASK} was added.
-     */
-    static final int SKIP_LAYOUT_REASON_ALLOWED = 1 << 16;
-
-    /** Used when the client is scheduled to call relayout. */
-    static final int SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT = 1 << 17;
-
-    /** The reasons to perform or skip surface placement. */
+    /** The reasons to perform surface placement. */
     @LayoutReason
     private int mLayoutReasons;
 
@@ -4205,35 +4192,18 @@
         mWindowManager.mWindowPlacerLocked.deferLayout();
     }
 
-    /**
-     * @see WindowSurfacePlacer#continueLayout
-     * @see WindowSurfacePlacer#endDeferAndSkipLayout
-     */
+    /** @see WindowSurfacePlacer#continueLayout */
     void continueWindowLayout() {
-        if ((mLayoutReasons & SKIP_LAYOUT_REASON_ALLOWED) != 0) {
-            final int skipReasons = mLayoutReasons & SKIP_LAYOUT_REASON_MASK;
-            if (skipReasons != 0) {
-                if (DEBUG_ALL) {
-                    Slog.i(TAG, "continueWindowLayout skip-reasons="
-                            + Integer.toHexString(skipReasons));
-                }
-                mWindowManager.mWindowPlacerLocked.endDeferAndSkipLayout();
-                return;
-            }
-        }
-
-        final int reasons = mLayoutReasons & LAYOUT_REASON_MASK;
-        mWindowManager.mWindowPlacerLocked.continueLayout(reasons != 0);
-        if (DEBUG_ALL && reasons != 0 && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
-            Slog.i(TAG, "continueWindowLayout reasons=" + Integer.toHexString(reasons));
+        mWindowManager.mWindowPlacerLocked.continueLayout(mLayoutReasons != 0);
+        if (DEBUG_ALL && !mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+            Slog.i(TAG, "continueWindowLayout reason=" + mLayoutReasons);
         }
     }
 
     /**
-     * If a reason within {@link #LAYOUT_REASON_MASK} is added between {@link #deferWindowLayout}
-     * and {@link #continueWindowLayout}, {@link WindowSurfacePlacer#performSurfacePlacement} will
-     * be called when the last defer count is gone. Note that the {@link #SKIP_LAYOUT_REASON_MASK}
-     * has higher priority to determine whether to perform layout.
+     * If a reason is added between {@link #deferWindowLayout} and {@link #continueWindowLayout},
+     * it will make sure {@link WindowSurfacePlacer#performSurfacePlacement} is called when the last
+     * defer count is gone.
      */
     void addWindowLayoutReasons(@LayoutReason int reasons) {
         mLayoutReasons |= reasons;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index b140451..7fe0f5b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -938,9 +938,6 @@
         if (r.app != null) {
             r.app.updateServiceConnectionActivities();
         }
-        // Expect a window of the starting activity will perform relayout.
-        mService.addWindowLayoutReasons(
-                ActivityTaskManagerService.SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT);
 
         return true;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 13fd76c..306137a 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4035,22 +4035,6 @@
         return mLayoutNeeded;
     }
 
-    /** Returns {@code true} if all opening apps may be ready to execute transition. */
-    boolean areOpeningAppsReady() {
-        final int size = mOpeningApps.size();
-        for (int i = size - 1; i >= 0; i--) {
-            final ActivityRecord r = mOpeningApps.valueAt(i);
-            if (!r.hasVisible) {
-                return false;
-            }
-            final WindowState w = r.findMainWindow();
-            if (w != null && !w.isDrawn()) {
-                return false;
-            }
-        }
-        return size > 0;
-    }
-
     void dumpTokens(PrintWriter pw, boolean dumpAll) {
         if (mTokenMap.isEmpty()) {
             return;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2330482..039422d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -106,7 +106,6 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.H.FIRST_ACTIVITY_TASK_MSG;
-import static com.android.server.wm.ActivityTaskManagerService.SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT;
 import static com.android.server.wm.ActivityTaskSupervisor.DEFER_RESUME;
 import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP;
 import static com.android.server.wm.ActivityTaskSupervisor.PRESERVE_WINDOWS;
@@ -6289,8 +6288,6 @@
             if (lastResumed != null) {
                 lastResumed.setWillCloseOrEnterPip(true);
             }
-            // There may be a relayout from resuming next activity after the previous is paused.
-            mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT);
             return true;
         } else if (mResumedActivity == next && next.isState(RESUMED)
                 && taskDisplayArea.allResumedActivitiesComplete()) {
@@ -6500,7 +6497,6 @@
                         ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                 dc.isNextTransitionForward()));
                 mAtmService.getLifecycleManager().scheduleTransaction(transaction);
-                mAtmService.addWindowLayoutReasons(SKIP_LAYOUT_REASON_EXPECT_NEXT_RELAYOUT);
 
                 ProtoLog.d(WM_DEBUG_STATES, "resumeTopActivityLocked: Resumed %s", next);
             } catch (Exception e) {
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 2d2b1f4..2ee5fb0 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -101,16 +101,6 @@
         }
     }
 
-    /**
-     * Resumes layout passes but skip to perform layout even if there was a request. This can only
-     * be called when there will be another layout request from client side, e.g. an activity is
-     * starting or resuming. And there should be no other significant changes need to apply.
-     */
-    void endDeferAndSkipLayout() {
-        mDeferDepth--;
-        mDeferredRequests = 0;
-    }
-
     boolean isLayoutDeferred() {
         return mDeferDepth > 0;
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 68cb8f9..3dc7bc1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -48,14 +48,18 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManagerInternal;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkPolicyManager;
+import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.Build;
 import android.os.Looper;
 import android.os.SystemClock;
@@ -70,6 +74,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
 import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.junit.MockitoJUnitRunner;
@@ -83,6 +88,8 @@
     @Mock
     private Context mContext;
     @Mock
+    private BatteryManagerInternal mBatteryManagerInternal;
+    @Mock
     private ConnectivityManager mConnManager;
     @Mock
     private NetworkPolicyManager mNetPolicyManager;
@@ -108,6 +115,9 @@
         LocalServices.removeServiceForTest(NetworkPolicyManagerInternal.class);
         LocalServices.addService(NetworkPolicyManagerInternal.class, mNetPolicyManagerInternal);
 
+        LocalServices.removeServiceForTest(BatteryManagerInternal.class);
+        LocalServices.addService(BatteryManagerInternal.class, mBatteryManagerInternal);
+
         when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
 
         // Freeze the clocks at this moment in time
@@ -143,8 +153,18 @@
                         DataUnit.MEBIBYTES.toBytes(1))
                 .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
 
+        final ArgumentCaptor<BroadcastReceiver> chargingCaptor =
+                ArgumentCaptor.forClass(BroadcastReceiver.class);
+        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
+                .thenReturn(false);
         final ConnectivityController controller = new ConnectivityController(mService);
+        verify(mContext).registerReceiver(chargingCaptor.capture(),
+                ArgumentMatchers.argThat(filter ->
+                        filter.hasAction(BatteryManager.ACTION_CHARGING)
+                                && filter.hasAction(BatteryManager.ACTION_DISCHARGING)));
         when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(10 * 60_000L);
+        final BroadcastReceiver chargingReceiver = chargingCaptor.getValue();
+        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));
 
         // Slow network is too slow
         assertFalse(controller.isSatisfied(createJobStatus(job), net,
@@ -166,7 +186,18 @@
         assertTrue(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
                         .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
+        // Slow network is too slow, but device is charging and network is unmetered.
+        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
+                .thenReturn(true);
+        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
+        assertTrue(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
+                        .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
+                mConstants));
 
+        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
+                .thenReturn(false);
+        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_DISCHARGING));
         when(mService.getMaxJobExecutionTimeMs(any())).thenReturn(60_000L);
 
         // Slow network is too slow
@@ -189,6 +220,14 @@
         assertFalse(controller.isSatisfied(createJobStatus(job), net,
                 createCapabilitiesBuilder().setLinkUpstreamBandwidthKbps(130)
                         .setLinkDownstreamBandwidthKbps(130).build(), mConstants));
+        // Slow network is too slow, but device is charging and network is unmetered.
+        when(mBatteryManagerInternal.isPowered(eq(BatteryManager.BATTERY_PLUGGED_ANY)))
+                .thenReturn(true);
+        chargingReceiver.onReceive(mContext, new Intent(BatteryManager.ACTION_CHARGING));
+        assertTrue(controller.isSatisfied(createJobStatus(job), net,
+                createCapabilitiesBuilder().addCapability(NET_CAPABILITY_NOT_METERED)
+                        .setLinkUpstreamBandwidthKbps(1).setLinkDownstreamBandwidthKbps(1).build(),
+                mConstants));
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java b/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java
index f85f0f8..50d4d84 100644
--- a/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/FgsTempAllowListTest.java
@@ -29,6 +29,9 @@
 
 import org.junit.Test;
 
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Supplier;
+
 /**
  * Build/Install/Run:
  *  atest FrameworksServicesTests:TempAllowListTest
@@ -41,7 +44,7 @@
      */
     @Test
     public void testIsAllowed() {
-        FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList();
+        FgsTempAllowList<String> allowList = new FgsTempAllowList();
         allowList.add(10001, 2000, "description1");
         allowList.add(10002, 2000, "description2");
 
@@ -55,7 +58,7 @@
         assertNotNull(entry2);
         assertEquals(entry2.second, "description2");
 
-        allowList.remove(10001);
+        allowList.removeUid(10001);
         assertFalse(allowList.isAllowed(10001));
         assertNull(allowList.get(10001));
     }
@@ -65,7 +68,7 @@
      */
     @Test
     public void testExpired() {
-        FgsTempAllowList<Integer, String> allowList = new FgsTempAllowList();
+        FgsTempAllowList<String> allowList = new FgsTempAllowList();
         // temp allow for 2000ms.
         allowList.add(10001, 2000, "uid1-2000ms");
         // sleep for 3000ms.
@@ -74,4 +77,51 @@
         assertFalse(allowList.isAllowed(10001));
         assertNull(allowList.get(10001));
     }
+
+    @Test
+    public void testRemoveAppId() {
+        FgsTempAllowList<String> allowList = new FgsTempAllowList();
+        allowList.add(10001, 2000, "description1");
+        allowList.add(10002, 2000, "description2");
+        allowList.add(10_10001, 2000, "description3");
+
+        assertTrue(allowList.isAllowed(10001));
+        assertTrue(allowList.isAllowed(10002));
+        assertTrue(allowList.isAllowed(10_10001));
+
+        allowList.removeAppId(10001);
+
+        assertFalse(allowList.isAllowed(10001));
+        assertTrue(allowList.isAllowed(10002));
+        assertFalse(allowList.isAllowed(10_10001));
+    }
+
+    @Test
+    public void testForEach() {
+        final FgsTempAllowList<String> allowList = new FgsTempAllowList();
+
+
+        // Call forEach(), return the sum of all the UIDs, and make sure the item is
+        // "uid" + uid.
+        final Supplier<Integer> callForEach = () -> {
+            final AtomicInteger sum = new AtomicInteger();
+            sum.set(0);
+            allowList.forEach((uid, entry) -> {
+                sum.set(sum.get() + uid);
+                assertEquals(entry.second, "uid" + uid);
+            });
+            return sum.get();
+        };
+
+        // Call on th empty list.
+        assertEquals(0, (int) callForEach.get());
+
+        // Add one item.
+        allowList.add(1, 2000, "uid1");
+        assertEquals(1, (int) callForEach.get());
+
+        // Add one more item.
+        allowList.add(10, 2000, "uid10");
+        assertEquals(11, (int) callForEach.get());
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index aecc794..b01c1c8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -165,7 +165,17 @@
             mRebootEscrow = null;
             mServerBased = true;
             RebootEscrowProviderServerBasedImpl.Injector injector =
-                    new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
+                    new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) {
+                        @Override
+                        long getServiceTimeoutInSeconds() {
+                            return 30;
+                        }
+
+                        @Override
+                        long getServerBlobLifetimeInMillis() {
+                            return 600_000;
+                        }
+                    };
             mDefaultRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(
                     storage, injector);
             mUserManager = userManager;
@@ -189,6 +199,11 @@
         }
 
         @Override
+        public boolean isNetworkConnected() {
+            return false;
+        }
+
+        @Override
         public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() {
             mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider;
             return mRebootEscrowProviderInUse;
@@ -602,7 +617,7 @@
         // Sleep 5s for the retry to complete
         Thread.sleep(5 * 1000);
         assertFalse(metricsSuccessCaptor.getValue());
-        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED),
+        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
                 metricsErrorCodeCaptor.getValue());
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
index ef5f00b..fdb6e9f5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/ArtStatsLogUtilsTest.java
@@ -28,8 +28,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
-import org.mockito.Mock;
 import org.mockito.InOrder;
+import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
 import java.io.IOException;
@@ -277,7 +277,7 @@
                 UID,
                 COMPILATION_REASON,
                 COMPILER_FILTER,
-                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_BYTES,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_COUNTER_BYTES,
                 DEX_CONTENT.length,
                 dexMetadataType,
                 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
@@ -287,7 +287,7 @@
                 UID,
                 COMPILATION_REASON,
                 COMPILER_FILTER,
-                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME,
+                ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME_COUNTER_MILLIS,
                 COMPILE_TIME,
                 dexMetadataType,
                 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE,
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index c527e66..6b7fc2f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3682,6 +3682,13 @@
             "emergency_number_prefix_string_array";
 
     /**
+     * Indicates whether carrier treats "*67" or "*82" as a temporary mode CLIR.
+     * @hide
+     */
+    public static final String KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL =
+            "carrier_supports_caller_id_vertical_service_codes_bool";
+
+    /**
      * Smart forwarding config. Smart forwarding is a feature to configure call forwarding to a
      * different SIM in the device when one SIM is not reachable. The config here specifies a smart
      * forwarding component that will launch UI for changing the configuration. An empty string
@@ -5496,6 +5503,7 @@
                         1 /* Roaming Indicator Off */
                 });
         sDefaults.putStringArray(KEY_EMERGENCY_NUMBER_PREFIX_STRING_ARRAY, new String[0]);
+        sDefaults.putBoolean(KEY_CARRIER_SUPPORTS_CALLER_ID_VERTICAL_SERVICE_CODES_BOOL, false);
         sDefaults.putBoolean(KEY_USE_USIM_BOOL, false);
         sDefaults.putBoolean(KEY_SHOW_WFC_LOCATION_PRIVACY_POLICY_BOOL, false);
         sDefaults.putBoolean(KEY_AUTO_CANCEL_CS_REJECT_NOTIFICATION, true);
diff --git a/telephony/java/android/telephony/ims/DelegateStateCallback.java b/telephony/java/android/telephony/ims/DelegateStateCallback.java
index 2b4fb7d..734b520 100644
--- a/telephony/java/android/telephony/ims/DelegateStateCallback.java
+++ b/telephony/java/android/telephony/ims/DelegateStateCallback.java
@@ -79,7 +79,7 @@
      * messages routing should be delayed until the {@link SipDelegate} sends the IMS configuration
      * change event to reduce conditions where the remote application is using a stale IMS
      * configuration.
-     * @deprecated This is being removed from API surface, Use
+     * @removed This is being removed from API surface, Use
      * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead.
      */
     @Deprecated
diff --git a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
index 08513c2..fe14dd1 100644
--- a/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
+++ b/telephony/java/android/telephony/ims/SipDelegateImsConfiguration.java
@@ -34,7 +34,7 @@
 
 /**
  * @hide
- * @deprecated Use {@link SipDelegateConfiguration} instead.
+ * @removed Use {@link SipDelegateConfiguration} instead.
  */
 @Deprecated
 @SystemApi
diff --git a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
index c078637..42c53f2 100644
--- a/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
+++ b/telephony/java/android/telephony/ims/stub/DelegateConnectionStateCallback.java
@@ -136,7 +136,7 @@
      * not compleed yet.
      *
      * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
-     * @deprecated Will not be in final API, use
+     * @removed Will not be in final API, use
      * {@link #onConfigurationChanged(SipDelegateConfiguration)} instead}.
      */
     @Deprecated
@@ -161,7 +161,7 @@
      *
      * @param registeredSipConfig The configuration of the IMS stack registered on the IMS network.
      */
-    default void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig) {}
+    void onConfigurationChanged(@NonNull SipDelegateConfiguration registeredSipConfig);
 
     /**
      * The previously created {@link SipDelegateConnection} instance delivered via
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 91ecbf0..a096c1f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2440,6 +2440,12 @@
     boolean removeUceRequestDisallowedStatus(int subId);
 
     /**
+     * Set the timeout for contact capabilities request.
+     * Note: This is designed for a SHELL command only.
+     */
+    boolean setCapabilitiesRequestTimeout(int subId, long timeoutAfterMs);
+
+    /**
      * Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
      * specified thresholds.
      */
diff --git a/tools/apilint/deprecated_at_birth.py b/tools/apilint/deprecated_at_birth.py
new file mode 100644
index 0000000..297d9c3b
--- /dev/null
+++ b/tools/apilint/deprecated_at_birth.py
@@ -0,0 +1,313 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2021 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.
+
+"""
+Usage: deprecated_at_birth.py path/to/next/ path/to/previous/
+Usage: deprecated_at_birth.py prebuilts/sdk/31/public/api/ prebuilts/sdk/30/public/api/
+"""
+
+import re, sys, os, collections, traceback, argparse
+
+
+BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
+
+def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False):
+    # manually derived from http://en.wikipedia.org/wiki/ANSI_escape_code#Codes
+    codes = []
+    if reset: codes.append("0")
+    else:
+        if not fg is None: codes.append("3%d" % (fg))
+        if not bg is None:
+            if not bright: codes.append("4%d" % (bg))
+            else: codes.append("10%d" % (bg))
+        if bold: codes.append("1")
+        elif dim: codes.append("2")
+        else: codes.append("22")
+    return "\033[%sm" % (";".join(codes))
+
+
+def ident(raw):
+    """Strips superficial signature changes, giving us a strong key that
+    can be used to identify members across API levels."""
+    raw = raw.replace(" deprecated ", " ")
+    raw = raw.replace(" synchronized ", " ")
+    raw = raw.replace(" final ", " ")
+    raw = re.sub("<.+?>", "", raw)
+    raw = re.sub("@[A-Za-z]+ ", "", raw)
+    raw = re.sub("@[A-Za-z]+\(.+?\) ", "", raw)
+    if " throws " in raw:
+        raw = raw[:raw.index(" throws ")]
+    return raw
+
+
+class Field():
+    def __init__(self, clazz, line, raw, blame):
+        self.clazz = clazz
+        self.line = line
+        self.raw = raw.strip(" {;")
+        self.blame = blame
+
+        raw = raw.split()
+        self.split = list(raw)
+
+        raw = [ r for r in raw if not r.startswith("@") ]
+        for r in ["method", "field", "public", "protected", "static", "final", "abstract", "default", "volatile", "transient"]:
+            while r in raw: raw.remove(r)
+
+        self.typ = raw[0]
+        self.name = raw[1].strip(";")
+        if len(raw) >= 4 and raw[2] == "=":
+            self.value = raw[3].strip(';"')
+        else:
+            self.value = None
+        self.ident = ident(self.raw)
+
+    def __hash__(self):
+        return hash(self.raw)
+
+    def __repr__(self):
+        return self.raw
+
+
+class Method():
+    def __init__(self, clazz, line, raw, blame):
+        self.clazz = clazz
+        self.line = line
+        self.raw = raw.strip(" {;")
+        self.blame = blame
+
+        # drop generics for now
+        raw = re.sub("<.+?>", "", raw)
+
+        raw = re.split("[\s(),;]+", raw)
+        for r in ["", ";"]:
+            while r in raw: raw.remove(r)
+        self.split = list(raw)
+
+        raw = [ r for r in raw if not r.startswith("@") ]
+        for r in ["method", "field", "public", "protected", "static", "final", "abstract", "default", "volatile", "transient"]:
+            while r in raw: raw.remove(r)
+
+        self.typ = raw[0]
+        self.name = raw[1]
+        self.args = []
+        self.throws = []
+        target = self.args
+        for r in raw[2:]:
+            if r == "throws": target = self.throws
+            else: target.append(r)
+        self.ident = ident(self.raw)
+
+    def __hash__(self):
+        return hash(self.raw)
+
+    def __repr__(self):
+        return self.raw
+
+
+class Class():
+    def __init__(self, pkg, line, raw, blame):
+        self.pkg = pkg
+        self.line = line
+        self.raw = raw.strip(" {;")
+        self.blame = blame
+        self.ctors = []
+        self.fields = []
+        self.methods = []
+
+        raw = raw.split()
+        self.split = list(raw)
+        if "class" in raw:
+            self.fullname = raw[raw.index("class")+1]
+        elif "enum" in raw:
+            self.fullname = raw[raw.index("enum")+1]
+        elif "interface" in raw:
+            self.fullname = raw[raw.index("interface")+1]
+        elif "@interface" in raw:
+            self.fullname = raw[raw.index("@interface")+1]
+        else:
+            raise ValueError("Funky class type %s" % (self.raw))
+
+        if "extends" in raw:
+            self.extends = raw[raw.index("extends")+1]
+            self.extends_path = self.extends.split(".")
+        else:
+            self.extends = None
+            self.extends_path = []
+
+        self.fullname = self.pkg.name + "." + self.fullname
+        self.fullname_path = self.fullname.split(".")
+
+        self.name = self.fullname[self.fullname.rindex(".")+1:]
+
+    def __hash__(self):
+        return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods)))
+
+    def __repr__(self):
+        return self.raw
+
+
+class Package():
+    def __init__(self, line, raw, blame):
+        self.line = line
+        self.raw = raw.strip(" {;")
+        self.blame = blame
+
+        raw = raw.split()
+        self.name = raw[raw.index("package")+1]
+        self.name_path = self.name.split(".")
+
+    def __repr__(self):
+        return self.raw
+
+
+def _parse_stream(f, api={}):
+    line = 0
+    pkg = None
+    clazz = None
+    blame = None
+
+    re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$")
+    for raw in f:
+        line += 1
+        raw = raw.rstrip()
+        match = re_blame.match(raw)
+        if match is not None:
+            blame = match.groups()[0:2]
+            raw = match.groups()[2]
+        else:
+            blame = None
+
+        if raw.startswith("package"):
+            pkg = Package(line, raw, blame)
+        elif raw.startswith("  ") and raw.endswith("{"):
+            clazz = Class(pkg, line, raw, blame)
+            api[clazz.fullname] = clazz
+        elif raw.startswith("    ctor"):
+            clazz.ctors.append(Method(clazz, line, raw, blame))
+        elif raw.startswith("    method"):
+            clazz.methods.append(Method(clazz, line, raw, blame))
+        elif raw.startswith("    field"):
+            clazz.fields.append(Field(clazz, line, raw, blame))
+
+    return api
+
+
+def _parse_stream_path(path):
+    api = {}
+    print "Parsing", path
+    for f in os.listdir(path):
+        f = os.path.join(path, f)
+        if not os.path.isfile(f): continue
+        if not f.endswith(".txt"): continue
+        if f.endswith("removed.txt"): continue
+        print "\t", f
+        with open(f) as s:
+            api = _parse_stream(s, api)
+    print "Parsed", len(api), "APIs"
+    print
+    return api
+
+
+class Failure():
+    def __init__(self, sig, clazz, detail, error, rule, msg):
+        self.sig = sig
+        self.error = error
+        self.rule = rule
+        self.msg = msg
+
+        if error:
+            self.head = "Error %s" % (rule) if rule else "Error"
+            dump = "%s%s:%s %s" % (format(fg=RED, bg=BLACK, bold=True), self.head, format(reset=True), msg)
+        else:
+            self.head = "Warning %s" % (rule) if rule else "Warning"
+            dump = "%s%s:%s %s" % (format(fg=YELLOW, bg=BLACK, bold=True), self.head, format(reset=True), msg)
+
+        self.line = clazz.line
+        blame = clazz.blame
+        if detail is not None:
+            dump += "\n    in " + repr(detail)
+            self.line = detail.line
+            blame = detail.blame
+        dump += "\n    in " + repr(clazz)
+        dump += "\n    in " + repr(clazz.pkg)
+        dump += "\n    at line " + repr(self.line)
+        if blame is not None:
+            dump += "\n    last modified by %s in %s" % (blame[1], blame[0])
+
+        self.dump = dump
+
+    def __repr__(self):
+        return self.dump
+
+
+failures = {}
+
+def _fail(clazz, detail, error, rule, msg):
+    """Records an API failure to be processed later."""
+    global failures
+
+    sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg)
+    sig = sig.replace(" deprecated ", " ")
+
+    failures[sig] = Failure(sig, clazz, detail, error, rule, msg)
+
+
+def warn(clazz, detail, rule, msg):
+    _fail(clazz, detail, False, rule, msg)
+
+def error(clazz, detail, rule, msg):
+    _fail(clazz, detail, True, rule, msg)
+
+
+if __name__ == "__main__":
+    next_path = sys.argv[1]
+    prev_path = sys.argv[2]
+
+    next_api = _parse_stream_path(next_path)
+    prev_api = _parse_stream_path(prev_path)
+
+    # Remove all existing things so we're left with new
+    for prev_clazz in prev_api.values():
+        if prev_clazz.fullname not in next_api: continue
+        cur_clazz = next_api[prev_clazz.fullname]
+
+        sigs = { i.ident: i for i in prev_clazz.ctors }
+        cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ]
+        sigs = { i.ident: i for i in prev_clazz.methods }
+        cur_clazz.methods = [ i for i in cur_clazz.methods if i.ident not in sigs ]
+        sigs = { i.ident: i for i in prev_clazz.fields }
+        cur_clazz.fields = [ i for i in cur_clazz.fields if i.ident not in sigs ]
+
+        # Forget about class entirely when nothing new
+        if len(cur_clazz.ctors) == 0 and len(cur_clazz.methods) == 0 and len(cur_clazz.fields) == 0:
+            del next_api[prev_clazz.fullname]
+
+    for clazz in next_api.values():
+        if "@Deprecated " in clazz.raw and not clazz.fullname in prev_api:
+            error(clazz, None, None, "Found API deprecation at birth")
+
+        if "@Deprecated " in clazz.raw: continue
+
+        for i in clazz.ctors + clazz.methods + clazz.fields:
+            if "@Deprecated " in i.raw:
+                error(clazz, i, None, "Found API deprecation at birth " + i.ident)
+
+    print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True),
+                                            format(reset=True)))
+    for f in sorted(failures):
+        print failures[f]
+        print
diff --git a/wifi/OWNERS b/wifi/OWNERS
index 2caf9ed..8381522 100644
--- a/wifi/OWNERS
+++ b/wifi/OWNERS
@@ -1,5 +1,3 @@
 set noparent
 
-dysu@google.com
-etancohen@google.com
-satk@google.com
+include platform/packages/modules/Wifi:/WIFI_OWNERS