Merge "Fix all RemoteInputView colors when isColorized changes." into sc-dev
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 4fb9926..869d790 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -61,6 +61,7 @@
 
   public abstract class Context {
     method @NonNull public android.os.UserHandle getUser();
+    field public static final String PAC_PROXY_SERVICE = "pac_proxy";
     field public static final String TEST_NETWORK_SERVICE = "test_network";
   }
 
@@ -78,14 +79,6 @@
 
 }
 
-package android.content.rollback {
-
-  public class RollbackManagerFrameworkInitializer {
-    method public static void initialize();
-  }
-
-}
-
 package android.hardware.usb {
 
   public class UsbManager {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ea57e98..a81411b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13591,7 +13591,7 @@
     method @NonNull public byte[] getEncodedMessage();
     method @NonNull public String getHeaderSection();
     method @NonNull public String getStartLine();
-    method @Nullable public String getViaBranchParameter();
+    method @NonNull public String getViaBranchParameter();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.SipMessage> CREATOR;
   }
@@ -14237,7 +14237,7 @@
   public final class UwbManager {
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
     method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
-    method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
+    method @NonNull @RequiresPermission(allOf={android.Manifest.permission.UWB_PRIVILEGED, android.Manifest.permission.UWB_RANGING}) public android.os.CancellationSignal openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
     method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
   }
diff --git a/core/java/android/app/NotificationHistory.java b/core/java/android/app/NotificationHistory.java
index 0c8188b..eb9ec86 100644
--- a/core/java/android/app/NotificationHistory.java
+++ b/core/java/android/app/NotificationHistory.java
@@ -404,6 +404,26 @@
     }
 
     /**
+     * Removes all notifications from a channel and regenerates the string pool
+     */
+    public boolean removeChannelFromWrite(String packageName, String channelId) {
+        boolean removed = false;
+        for (int i = mNotificationsToWrite.size() - 1; i >= 0; i--) {
+            HistoricalNotification hn = mNotificationsToWrite.get(i);
+            if (packageName.equals(hn.getPackage())
+                    && Objects.equals(channelId, hn.getChannelId())) {
+                removed = true;
+                mNotificationsToWrite.remove(i);
+            }
+        }
+        if (removed) {
+            poolStringsFromNotifications();
+        }
+
+        return removed;
+    }
+
+    /**
      * Gets pooled strings in order to write them to disk
      */
     public @NonNull String[] getPooledStringsToWrite() {
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 31b0d41..47a9fbb 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -722,7 +722,7 @@
                 new CachedServiceFetcher<UwbManager>() {
                     @Override
                     public UwbManager createService(ContextImpl ctx) {
-                        return UwbManager.getInstance();
+                        return UwbManager.getInstance(ctx);
                     }
                 });
 
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 0be7b73..5446deb 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -38,12 +38,14 @@
 import android.bluetooth.le.ScanResult;
 import android.bluetooth.le.ScanSettings;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.AttributionSource;
 import android.content.Context;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.ParcelUuid;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
@@ -853,8 +855,8 @@
         }
         synchronized (mLock) {
             if (sBluetoothLeScanner == null) {
-                sBluetoothLeScanner = new BluetoothLeScanner(mManagerService, getOpPackageName(),
-                        getAttributionTag());
+                sBluetoothLeScanner =
+                        new BluetoothLeScanner(mManagerService, getAttributionSource());
             }
         }
         return sBluetoothLeScanner;
@@ -1664,13 +1666,11 @@
         return ActivityThread.currentOpPackageName();
     }
 
-    private String getAttributionTag() {
-        // Workaround for legacy API for getting a BluetoothAdapter not
-        // passing a context
+    private AttributionSource getAttributionSource() {
         if (mContext != null) {
-            return mContext.getAttributionTag();
+            return mContext.getAttributionSource();
         }
-        return null;
+        return new AttributionSource(Process.myUid(), ActivityThread.currentOpPackageName(), null);
     }
 
     /**
@@ -1710,7 +1710,7 @@
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
-                return mService.startDiscovery(getOpPackageName(), getAttributionTag());
+                return mService.startDiscovery(getAttributionSource());
             }
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 2888fbd..2601cd4 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -26,6 +26,7 @@
 import android.bluetooth.BluetoothGatt;
 import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothManager;
+import android.content.AttributionSource;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -82,9 +83,7 @@
     private final Handler mHandler;
     private BluetoothAdapter mBluetoothAdapter;
     private final Map<ScanCallback, BleScanCallbackWrapper> mLeScanClients;
-
-    private final String mOpPackageName;
-    private final String mFeatureId;
+    private final AttributionSource mAttributionSource;
 
     /**
      * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
@@ -95,13 +94,12 @@
      * @hide
      */
     public BluetoothLeScanner(IBluetoothManager bluetoothManager,
-            @NonNull String opPackageName, @Nullable String featureId) {
+            @NonNull AttributionSource attributionSource) {
         mBluetoothManager = bluetoothManager;
         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
         mHandler = new Handler(Looper.getMainLooper());
         mLeScanClients = new HashMap<ScanCallback, BleScanCallbackWrapper>();
-        mOpPackageName = opPackageName;
-        mFeatureId = featureId;
+        mAttributionSource = attributionSource;
     }
 
     /**
@@ -256,8 +254,7 @@
                 wrapper.startRegistration();
             } else {
                 try {
-                    gatt.startScanForIntent(callbackIntent, settings, filters, mOpPackageName,
-                            mFeatureId);
+                    gatt.startScanForIntent(callbackIntent, settings, filters, mAttributionSource);
                 } catch (RemoteException e) {
                     return ScanCallback.SCAN_FAILED_INTERNAL_ERROR;
                 }
@@ -298,7 +295,7 @@
         IBluetoothGatt gatt;
         try {
             gatt = mBluetoothManager.getBluetoothGatt();
-            gatt.stopScanForIntent(callbackIntent, mOpPackageName);
+            gatt.stopScanForIntent(callbackIntent);
         } catch (RemoteException e) {
         }
     }
@@ -458,7 +455,7 @@
                         } else {
                             mScannerId = scannerId;
                             mBluetoothGatt.startScan(mScannerId, mSettings, mFilters,
-                                    mResultStorages, mOpPackageName, mFeatureId);
+                                    mResultStorages, mAttributionSource);
                         }
                     } catch (RemoteException e) {
                         Log.e(TAG, "fail to start le scan: " + e);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 232daa8..3676982 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4165,6 +4165,7 @@
      * @see android.net.PacProxyManager
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public static final String PAC_PROXY_SERVICE = "pac_proxy";
 
     /**
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 72c9879..97e1b54 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -72,7 +72,6 @@
 import com.android.internal.util.Parcelling.BuiltIn.ForStringSet;
 
 import java.security.PublicKey;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
@@ -1346,7 +1345,7 @@
         this.gwpAsanMode = in.readInt();
         this.minExtensionVersions = in.readSparseIntArray();
         this.mBooleans = in.readLong();
-        this.mProperties = in.createTypedArrayMap(Property.CREATOR);
+        this.mProperties = in.readHashMap(boot);
         this.memtagMode = in.readInt();
         this.nativeHeapZeroInitialized = in.readInt();
         this.requestOptimizedExternalStorageAccess = sForBoolean.unparcel(in);
diff --git a/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java b/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java
index c5e4f99..24617a0 100644
--- a/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java
+++ b/core/java/android/content/rollback/RollbackManagerFrameworkInitializer.java
@@ -15,7 +15,6 @@
  */
 package android.content.rollback;
 
-import android.annotation.SystemApi;
 import android.app.SystemServiceRegistry;
 import android.content.Context;
 
@@ -24,7 +23,6 @@
  *
  * @hide
  */
-@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
 public class RollbackManagerFrameworkInitializer {
     private RollbackManagerFrameworkInitializer() {}
 
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 1e62a15..c1009ff 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -1503,7 +1503,11 @@
         */
         public boolean cameraIdHasConcurrentStreamsLocked(String cameraId) {
             if (!mDeviceStatus.containsKey(cameraId)) {
-                Log.e(TAG, "cameraIdHasConcurrentStreamsLocked called on non existing camera id");
+                // physical camera ids aren't advertised in concurrent camera id combinations.
+                if (DEBUG) {
+                    Log.v(TAG, " physical camera id " + cameraId + " is hidden." +
+                            " Available logical camera ids : " + mDeviceStatus.toString());
+                }
                 return false;
             }
             for (Set<String> comb : mConcurrentCameraIdCombinations) {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
index 8eb1dcc..bf45932 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
@@ -25,6 +25,7 @@
 import android.media.ImageReader;
 import android.media.ImageWriter;
 import android.annotation.NonNull;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.Size;
@@ -39,6 +40,7 @@
     private final IPreviewImageProcessorImpl mProcessor;
     private final long mOutputSurfaceUsage;
     private final int mOutputSurfaceFormat;
+    private final Handler mHandler;
 
     private ImageReader mIntermediateReader = null;
     private Surface mIntermediateSurface = null;
@@ -48,10 +50,11 @@
     private boolean mOutputAbandoned = false;
 
     public CameraExtensionForwardProcessor(@NonNull IPreviewImageProcessorImpl processor,
-                                           int format, long surfaceUsage) {
+            int format, long surfaceUsage, @NonNull Handler handler) {
         mProcessor = processor;
         mOutputSurfaceUsage = surfaceUsage;
         mOutputSurfaceFormat = format;
+        mHandler = handler;
     }
 
     public void close() {
@@ -98,7 +101,7 @@
                     mResolution.getHeight(), CameraExtensionCharacteristics.PROCESSING_INPUT_FORMAT,
                     FORWARD_QUEUE_SIZE, mOutputSurfaceUsage);
             mIntermediateSurface = mIntermediateReader.getSurface();
-            mIntermediateReader.setOnImageAvailableListener(new ForwardCallback(), null);
+            mIntermediateReader.setOnImageAvailableListener(new ForwardCallback(), mHandler);
 
             mProcessor.onOutputSurface(mIntermediateSurface, mOutputSurfaceFormat);
             // PreviewImageProcessorImpl always expect the extension processing format as input
@@ -124,11 +127,15 @@
         @Override public void onImageAvailable(ImageReader reader) {
             Image processedImage = null;
             try {
-                processedImage = mIntermediateReader.acquireNextImage();
+                processedImage = reader.acquireNextImage();
             } catch (IllegalStateException e) {
                 Log.e(TAG, "Failed to acquire processed image!");
                 return;
             }
+            if (processedImage == null) {
+                Log.e(TAG, "Invalid image");
+                return;
+            }
 
             if (mOutputSurface != null && mOutputSurface.isValid() && !mOutputAbandoned) {
                 if (mOutputWriter == null) {
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 1f5098f..3d771c01 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -352,7 +352,7 @@
             try {
                 mPreviewImageProcessor = new CameraExtensionForwardProcessor(
                         mPreviewExtender.getPreviewImageProcessor(), repeatingSurfaceInfo.mFormat,
-                        repeatingSurfaceInfo.mUsage);
+                        repeatingSurfaceInfo.mUsage, mHandler);
             } catch (ClassCastException e) {
                 throw new UnsupportedOperationException("Failed casting preview processor!");
             }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 171c6a2..f50aa99 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -78,5 +78,4 @@
 
     boolean isUidNetworkingBlocked(int uid, boolean meteredNetwork);
     boolean isUidRestrictedOnMeteredNetworks(int uid);
-    boolean checkUidNetworkingBlocked(int uid, int uidRules, boolean isNetworkMetered, boolean isBackgroundRestricted);
 }
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index b037261..1eef7d9 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -198,15 +198,11 @@
         final int oemManaged = getOemBitfield(snapshot.networkCapabilities);
 
         if (legacyType == TYPE_WIFI) {
-            if (snapshot.networkCapabilities.getSsid() != null) {
-                networkId = snapshot.networkCapabilities.getSsid();
-                if (networkId == null) {
-                    // TODO: Figure out if this code path never runs. If so, remove them.
-                    final WifiManager wifi = (WifiManager) context.getSystemService(
-                            Context.WIFI_SERVICE);
-                    final WifiInfo info = wifi.getConnectionInfo();
-                    networkId = info != null ? info.getSSID() : null;
-                }
+            networkId = snapshot.networkCapabilities.getSsid();
+            if (networkId == null) {
+                final WifiManager wifi = context.getSystemService(WifiManager.class);
+                final WifiInfo info = wifi.getConnectionInfo();
+                networkId = info != null ? info.getSSID() : null;
             }
         }
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 68606ec..da3febd 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -564,31 +564,6 @@
     }
 
     /**
-     * Figure out if networking is blocked for a given set of conditions.
-     *
-     * This is used by ConnectivityService via passing stale copies of conditions, so it must not
-     * take any locks.
-     *
-     * @param uid The target uid.
-     * @param uidRules The uid rules which are obtained from NetworkPolicyManagerService.
-     * @param isNetworkMetered True if the network is metered.
-     * @param isBackgroundRestricted True if data saver is enabled.
-     *
-     * @return true if networking is blocked for the UID under the specified conditions.
-     *
-     * @hide
-     */
-    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
-            boolean isNetworkMetered, boolean isBackgroundRestricted) {
-        try {
-            return mService.checkUidNetworkingBlocked(uid, uidRules, isNetworkMetered,
-                    isBackgroundRestricted);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Check that the given uid is restricted from doing networking on metered networks.
      *
      * @param uid The target uid.
diff --git a/core/java/android/text/method/TranslationTransformationMethod.java b/core/java/android/text/method/TranslationTransformationMethod.java
index 54c0ffc..7db999a8 100644
--- a/core/java/android/text/method/TranslationTransformationMethod.java
+++ b/core/java/android/text/method/TranslationTransformationMethod.java
@@ -78,11 +78,24 @@
         if (TextUtils.isEmpty(translatedText) || isWhitespace(translatedText.toString())) {
             return source;
         } else {
+            // TODO(b/179693024): Remove this once we have the fix to pad the view text instead.
+            translatedText = ellipsize(translatedText, ((TextView) view).getText().length());
             // TODO(b/174283799): apply the spans to the text
             return translatedText;
         }
     }
 
+    private static CharSequence ellipsize(CharSequence text, int newLength) {
+        if (text.length() <= newLength) {
+            return text;
+        }
+        String ellipsis = String.valueOf('\u2026');
+        if (newLength == 1) {
+            return ellipsis;
+        }
+        return TextUtils.concat(TextUtils.trimToSize(text, newLength - 1), ellipsis);
+    }
+
     @Override
     public void onFocusChanged(View view, CharSequence sourceText,
             boolean focused, int direction,
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index cd6585c..5fd0c33 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -30,6 +30,7 @@
 
 import java.io.PrintWriter;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.time.LocalTime;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -43,11 +44,39 @@
 public class TimeUtils {
     /** @hide */ public TimeUtils() {}
     /** {@hide} */
-    private static SimpleDateFormat sLoggingFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+    private static final SimpleDateFormat sLoggingFormat =
+            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
     /** @hide */
     public static final SimpleDateFormat sDumpDateFormat =
             new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+
+    /**
+     * This timestamp is used in TimeUtils methods and by the SettingsUI to filter time zones
+     * to only "effective" ones in a country. It is compared against the notUsedAfter metadata that
+     * Android records for some time zones.
+     *
+     * <p>What is notUsedAfter?</p>
+     * Android chooses to avoid making users choose between functionally identical time zones at the
+     * expense of not being able to represent local times in the past.
+     *
+     * notUsedAfter exists because some time zones can "merge" with other time zones after a given
+     * point in time (i.e. they change to have identical transitions, offsets, display names, etc.).
+     * From the notUsedAfter time, the zone will express the same local time as the one it merged
+     * with.
+     *
+     * <p>Why hardcoded?</p>
+     * Rather than using System.currentTimeMillis(), a timestamp known to be in the recent past is
+     * used to ensure consistent behavior across devices and time, and avoid assumptions that the
+     * system clock on a device is currently set correctly. The fixed value should be updated
+     * occasionally, but it doesn't have to be very often as effective time zones for a country
+     * don't change very often.
+     *
+     * @hide
+     */
+    public static final Instant MIN_USE_DATE_OF_TIMEZONE =
+            Instant.ofEpochMilli(1546300800000L); // 1/1/2019 00:00 UTC
+
     /**
      * Tries to return a time zone that would have had the specified offset
      * and DST value at the specified moment in the specified country.
@@ -109,7 +138,7 @@
 
         List<String> timeZoneIds = new ArrayList<>();
         for (TimeZoneMapping timeZoneMapping : countryTimeZones.getTimeZoneMappings()) {
-            if (timeZoneMapping.isShownInPicker()) {
+            if (timeZoneMapping.isShownInPickerAt(MIN_USE_DATE_OF_TIMEZONE)) {
                 timeZoneIds.add(timeZoneMapping.getTimeZoneId());
             }
         }
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 85f2c1c..5c7f0f5 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -17,6 +17,7 @@
 package android.uwb;
 
 import android.annotation.NonNull;
+import android.content.AttributionSource;
 import android.os.CancellationSignal;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
@@ -42,6 +43,9 @@
     /**
      * Open a new ranging session
      *
+     * @param attributionSource Attribution source to use for the enforcement of
+     *                          {@link android.Manifest.permission#ULTRAWIDEBAND_RANGING} runtime
+     *                          permission.
      * @param params the parameters that define the ranging session
      * @param executor {@link Executor} to run callbacks
      * @param callbacks {@link RangingSession.Callback} to associate with the {@link RangingSession}
@@ -49,7 +53,8 @@
      * @return a {@link CancellationSignal} that may be used to cancel the opening of the
      *         {@link RangingSession}.
      */
-    public CancellationSignal openSession(@NonNull PersistableBundle params,
+    public CancellationSignal openSession(@NonNull AttributionSource attributionSource,
+            @NonNull PersistableBundle params,
             @NonNull Executor executor,
             @NonNull RangingSession.Callback callbacks) {
         synchronized (this) {
@@ -58,6 +63,7 @@
                     new RangingSession(executor, callbacks, mAdapter, sessionHandle);
             mRangingSessionTable.put(sessionHandle, session);
             try {
+                // TODO: Pass in the attributionSource to the service.
                 mAdapter.openRanging(sessionHandle, this, params);
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 844bbbe..bed77e6 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -16,7 +16,7 @@
 
 package android.uwb;
 
-import android.Manifest;
+import android.Manifest.permission;
 import android.annotation.CallbackExecutor;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -24,7 +24,9 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.content.AttributionSource;
 import android.content.Context;
+import android.content.ContextParams;
 import android.os.CancellationSignal;
 import android.os.IBinder;
 import android.os.PersistableBundle;
@@ -47,9 +49,10 @@
 @SystemApi
 @SystemService(Context.UWB_SERVICE)
 public final class UwbManager {
-    private IUwbAdapter mUwbAdapter;
     private static final String SERVICE_NAME = "uwb";
 
+    private final Context mContext;
+    private final IUwbAdapter mUwbAdapter;
     private final AdapterStateListener mAdapterStateListener;
     private final RangingManager mRangingManager;
 
@@ -116,9 +119,11 @@
     /**
      * Use <code>Context.getSystemService(UwbManager.class)</code> to get an instance.
      *
+     * @param ctx Context of the client.
      * @param adapter an instance of an {@link android.uwb.IUwbAdapter}
      */
-    private UwbManager(IUwbAdapter adapter) {
+    private UwbManager(@NonNull Context ctx, @NonNull IUwbAdapter adapter) {
+        mContext = ctx;
         mUwbAdapter = adapter;
         mAdapterStateListener = new AdapterStateListener(adapter);
         mRangingManager = new RangingManager(adapter);
@@ -127,7 +132,7 @@
     /**
      * @hide
      */
-    public static UwbManager getInstance() {
+    public static UwbManager getInstance(@NonNull Context ctx) {
         IBinder b = ServiceManager.getService(SERVICE_NAME);
         if (b == null) {
             return null;
@@ -138,7 +143,7 @@
             return null;
         }
 
-        return new UwbManager(adapter);
+        return new UwbManager(ctx, adapter);
     }
 
     /**
@@ -153,7 +158,7 @@
      * @param executor an {@link Executor} to execute given callback
      * @param callback user implementation of the {@link AdapterStateCallback}
      */
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
+    @RequiresPermission(permission.UWB_PRIVILEGED)
     public void registerAdapterStateCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull AdapterStateCallback callback) {
         mAdapterStateListener.register(executor, callback);
@@ -168,7 +173,7 @@
      *
      * @param callback user implementation of the {@link AdapterStateCallback}
      */
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
+    @RequiresPermission(permission.UWB_PRIVILEGED)
     public void unregisterAdapterStateCallback(@NonNull AdapterStateCallback callback) {
         mAdapterStateListener.unregister(callback);
     }
@@ -182,7 +187,7 @@
      * @return {@link PersistableBundle} of the device's supported UWB protocols and parameters
      */
     @NonNull
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
+    @RequiresPermission(permission.UWB_PRIVILEGED)
     public PersistableBundle getSpecificationInfo() {
         try {
             return mUwbAdapter.getSpecificationInfo();
@@ -199,7 +204,7 @@
      * @return the timestamp resolution in nanoseconds
      */
     @SuppressLint("MethodNameUnits")
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
+    @RequiresPermission(permission.UWB_PRIVILEGED)
     public long elapsedRealtimeResolutionNanos() {
         try {
             return mUwbAdapter.getTimestampResolutionNanos();
@@ -235,10 +240,14 @@
      *         {@link RangingSession.Callback#onOpened(RangingSession)}.
      */
     @NonNull
-    @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
+    @RequiresPermission(allOf = {
+            permission.UWB_PRIVILEGED,
+            permission.UWB_RANGING
+    })
     public CancellationSignal openRangingSession(@NonNull PersistableBundle parameters,
             @NonNull @CallbackExecutor Executor executor,
             @NonNull RangingSession.Callback callbacks) {
-        return mRangingManager.openSession(parameters, executor, callbacks);
+        return mRangingManager.openSession(
+                mContext.getAttributionSource(), parameters, executor, callbacks);
     }
 }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index 3d1c8ff..f487c6c 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -34,6 +34,7 @@
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.WindowConfiguration;
 import android.graphics.Insets;
@@ -808,7 +809,7 @@
         dest.writeTypedObject(mRoundedCorners, flags);
     }
 
-    public static final @android.annotation.NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
+    public static final @NonNull Creator<InsetsState> CREATOR = new Creator<InsetsState>() {
 
         public InsetsState createFromParcel(Parcel in) {
             return new InsetsState(in);
@@ -842,5 +843,16 @@
                 + ", mSources= { " + joiner
                 + " }";
     }
+
+    public @NonNull String toSourceVisibilityString() {
+        StringJoiner joiner = new StringJoiner(", ");
+        for (int i = 0; i < SIZE; i++) {
+            InsetsSource source = mSources[i];
+            if (source != null) {
+                joiner.add(typeToString(i) + ": " + (source.isVisible() ? "visible" : "invisible"));
+            }
+        }
+        return joiner.toString();
+    }
 }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1e2b2ae..dd5c954 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -19782,6 +19782,11 @@
             return;
         }
 
+        if (mAttachInfo == null) {
+            // View is not attached.
+            return;
+        }
+
         final int h = dr.getIntrinsicHeight();
         final int w = dr.getIntrinsicWidth();
         final Rect rect = mAttachInfo.mTmpInvalRect;
diff --git a/core/java/com/android/internal/jank/FrameTracker.java b/core/java/com/android/internal/jank/FrameTracker.java
index 7f0178e..594a1a7 100644
--- a/core/java/com/android/internal/jank/FrameTracker.java
+++ b/core/java/com/android/internal/jank/FrameTracker.java
@@ -26,7 +26,9 @@
 
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_METRICS_LOGGED;
 import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_BEGIN;
+import static com.android.internal.jank.InteractionJankMonitor.ACTION_SESSION_CANCEL;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.HardwareRendererObserver;
@@ -45,6 +47,9 @@
 import com.android.internal.jank.InteractionJankMonitor.Session;
 import com.android.internal.util.FrameworkStatsLog;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * A class that allows the app to get the frame metrics from HardwareRendererObserver.
  * @hide
@@ -57,6 +62,26 @@
     private static final long INVALID_ID = -1;
     public static final int NANOS_IN_MILLISECOND = 1_000_000;
 
+    static final int REASON_END_UNKNOWN = -1;
+    static final int REASON_END_NORMAL = 0;
+    static final int REASON_END_SURFACE_DESTROYED = 1;
+    static final int REASON_CANCEL_NORMAL = 16;
+    static final int REASON_CANCEL_NOT_BEGUN = 17;
+    static final int REASON_CANCEL_SAME_VSYNC = 18;
+
+    /** @hide */
+    @IntDef({
+            REASON_END_UNKNOWN,
+            REASON_END_NORMAL,
+            REASON_END_SURFACE_DESTROYED,
+            REASON_CANCEL_NORMAL,
+            REASON_CANCEL_NOT_BEGUN,
+            REASON_CANCEL_SAME_VSYNC,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Reasons {
+    }
+
     private final HardwareRendererObserver mObserver;
     private SurfaceControl mSurfaceControl;
     private final int mTraceThresholdMissedFrames;
@@ -156,7 +181,14 @@
                 // force finish the session.
                 mHandler.postDelayed(() -> {
                     synchronized (FrameTracker.this) {
+                        if (DEBUG) {
+                            Log.d(TAG, "surfaceDestroyed: " + mSession.getName()
+                                    + ", finalized=" + mMetricsFinalized
+                                    + ", info=" + mJankInfos.size()
+                                    + ", vsync=" + mBeginVsyncId + "-" + mEndVsyncId);
+                        }
                         if (!mMetricsFinalized) {
+                            end(REASON_END_SURFACE_DESTROYED);
                             finish(mJankInfos.size() - 1);
                         }
                     }
@@ -176,6 +208,9 @@
         mSession.setTimeStamp(System.nanoTime());
         Trace.beginAsyncSection(mSession.getName(), (int) mBeginVsyncId);
         mRendererWrapper.addObserver(mObserver);
+        if (DEBUG) {
+            Log.d(TAG, "begin: " + mSession.getName() + ", begin=" + mBeginVsyncId);
+        }
         if (mSurfaceControl != null) {
             mSurfaceControlWrapper.addJankStatsListener(this, mSurfaceControl);
         }
@@ -187,15 +222,25 @@
     /**
      * End the trace session of the CUJ.
      */
-    public synchronized void end() {
+    public synchronized void end(@Reasons int reason) {
+        if (mEndVsyncId != INVALID_ID) return;
         mEndVsyncId = mChoreographer.getVsyncId();
+
         // Cancel the session if:
         // 1. The session begins and ends at the same vsync id.
         // 2. The session never begun.
-        if (mEndVsyncId == mBeginVsyncId || mBeginVsyncId == INVALID_ID) {
-            cancel();
+        if (mBeginVsyncId == INVALID_ID) {
+            cancel(REASON_CANCEL_NOT_BEGUN);
+        } else if (mEndVsyncId == mBeginVsyncId) {
+            cancel(REASON_CANCEL_SAME_VSYNC);
         } else {
+            if (DEBUG) {
+                Log.d(TAG, "end: " + mSession.getName()
+                        + ", end=" + mEndVsyncId + ", reason=" + reason);
+            }
             Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
+            mSession.setReason(reason);
+            InteractionJankMonitor.getInstance().removeTimeout(mSession.getCuj());
         }
         // We don't remove observer here,
         // will remove it when all the frame metrics in this duration are called back.
@@ -205,7 +250,7 @@
     /**
      * Cancel the trace session of the CUJ.
      */
-    public synchronized void cancel() {
+    public synchronized void cancel(@Reasons int reason) {
         // We don't need to end the trace section if it never begun.
         if (mBeginVsyncId != INVALID_ID) {
             Trace.endAsyncSection(mSession.getName(), (int) mBeginVsyncId);
@@ -215,10 +260,16 @@
         // Always remove the observers in cancel call to avoid leakage.
         removeObservers();
 
+        if (DEBUG) {
+            Log.d(TAG, "cancel: " + mSession.getName()
+                    + ", begin=" + mBeginVsyncId + ", end=" + mEndVsyncId + ", reason=" + reason);
+        }
+
+        mSession.setReason(reason);
         // Notify the listener the session has been cancelled.
         // We don't notify the listeners if the session never begun.
-        if (mListener != null && mBeginVsyncId != INVALID_ID) {
-            mListener.onNotifyCujEvents(mSession, InteractionJankMonitor.ACTION_SESSION_CANCEL);
+        if (mListener != null) {
+            mListener.onNotifyCujEvents(mSession, ACTION_SESSION_CANCEL);
         }
     }
 
@@ -399,6 +450,7 @@
         }
         if (DEBUG) {
             Log.i(TAG, "FrameTracker: CUJ=" + mSession.getName()
+                    + " (" + mBeginVsyncId + "," + mEndVsyncId + ")"
                     + " totalFrames=" + totalFramesCount
                     + " missedAppFrames=" + missedAppFramesCount
                     + " missedSfFrames=" + missedSfFramesCounts
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index fbc92c1..6c56d42 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -19,6 +19,7 @@
 import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
 
 import static com.android.internal.jank.FrameTracker.ChoreographerWrapper;
+import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NOT_BEGUN;
 import static com.android.internal.jank.FrameTracker.SurfaceControlWrapper;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
 import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
@@ -104,7 +105,7 @@
     public static final String BUNDLE_KEY_CUJ_NAME = ACTION_PREFIX + ".CUJ_NAME";
     public static final String BUNDLE_KEY_TIMESTAMP = ACTION_PREFIX + ".TIMESTAMP";
     @VisibleForTesting
-    public static final String PROP_NOTIFY_CUJ_EVENT = "debug.notify_cuj_events";
+    public static final String PROP_NOTIFY_CUJ_EVENT = "debug.jank.notify_cuj_events";
 
     // Every value must have a corresponding entry in CUJ_STATSD_INTERACTION_TYPE.
     public static final int CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE = 0;
@@ -287,6 +288,10 @@
     }
 
     private void notifyEvents(Context context, String action, Session session) {
+        if (action.equals(ACTION_SESSION_CANCEL)
+                && session.getReason() == REASON_CANCEL_NOT_BEGUN) {
+            return;
+        }
         Intent intent = new Intent(action);
         intent.putExtra(BUNDLE_KEY_CUJ_NAME, getNameOfCuj(session.getCuj()));
         intent.putExtra(BUNDLE_KEY_TIMESTAMP, session.getTimeStamp());
@@ -294,6 +299,16 @@
         context.sendBroadcast(intent);
     }
 
+    void removeTimeout(@CujType int cujType) {
+        synchronized (this) {
+            Runnable timeout = mTimeoutActions.get(cujType);
+            if (timeout != null) {
+                mWorker.getThreadHandler().removeCallbacks(timeout);
+                mTimeoutActions.remove(cujType);
+            }
+        }
+    }
+
     /**
      * Begin a trace session.
      *
@@ -351,16 +366,11 @@
         synchronized (this) {
 
             // remove the timeout action first.
-            Runnable timeout = mTimeoutActions.get(cujType);
-            if (timeout != null) {
-                mWorker.getThreadHandler().removeCallbacks(timeout);
-                mTimeoutActions.remove(cujType);
-            }
-
+            removeTimeout(cujType);
             FrameTracker tracker = getTracker(cujType);
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
-            tracker.end();
+            tracker.end(FrameTracker.REASON_END_NORMAL);
             mRunningTrackers.remove(cujType);
             return true;
         }
@@ -375,16 +385,11 @@
         //TODO (163505250): This should be no-op if not in droid food rom.
         synchronized (this) {
             // remove the timeout action first.
-            Runnable timeout = mTimeoutActions.get(cujType);
-            if (timeout != null) {
-                mWorker.getThreadHandler().removeCallbacks(timeout);
-                mTimeoutActions.remove(cujType);
-            }
-
+            removeTimeout(cujType);
             FrameTracker tracker = getTracker(cujType);
             // Skip this call since we haven't started a trace yet.
             if (tracker == null) return false;
-            tracker.cancel();
+            tracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
             mRunningTrackers.remove(cujType);
             return true;
         }
@@ -509,6 +514,8 @@
         @CujType
         private int mCujType;
         private long mTimeStamp;
+        @FrameTracker.Reasons
+        private int mReason = FrameTracker.REASON_END_UNKNOWN;
 
         public Session(@CujType int cujType) {
             mCujType = cujType;
@@ -543,5 +550,13 @@
         public long getTimeStamp() {
             return mTimeStamp;
         }
+
+        public void setReason(@FrameTracker.Reasons int reason) {
+            mReason = reason;
+        }
+
+        public int getReason() {
+            return mReason;
+        }
     }
 }
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 9a91d20..424632fe 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -258,6 +258,7 @@
     private Drawable mLastOriginalBackgroundDrawable;
     private Drawable mResizingBackgroundDrawable;
     private BackgroundBlurDrawable mBackgroundBlurDrawable;
+    private BackgroundBlurDrawable mLastBackgroundBlurDrawable;
 
     /**
      * Temporary holder for a window background when it is set before {@link #mWindow} is
@@ -289,7 +290,6 @@
 
     private int mOriginalBackgroundBlurRadius = 0;
     private int mBackgroundBlurRadius = 0;
-    private int mLastBackgroundBlurRadius = 0;
     private boolean mCrossWindowBlurEnabled;
     private final ViewTreeObserver.OnPreDrawListener mBackgroundBlurOnPreDrawListener = () -> {
         updateBackgroundBlurCorners();
@@ -1278,13 +1278,13 @@
         }
 
         if (mBackgroundInsets.equals(mLastBackgroundInsets)
-                && mBackgroundBlurRadius == mLastBackgroundBlurRadius
+                && mBackgroundBlurDrawable == mLastBackgroundBlurDrawable
                 && mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) {
             return;
         }
 
         Drawable destDrawable = mOriginalBackgroundDrawable;
-        if (mBackgroundBlurRadius > 0) {
+        if (mBackgroundBlurDrawable != null) {
             destDrawable = new LayerDrawable(new Drawable[] {mBackgroundBlurDrawable,
                                                              mOriginalBackgroundDrawable});
         }
@@ -1309,7 +1309,7 @@
         super.setBackgroundDrawable(destDrawable);
 
         mLastBackgroundInsets = mBackgroundInsets;
-        mLastBackgroundBlurRadius = mBackgroundBlurRadius;
+        mLastBackgroundBlurDrawable = mBackgroundBlurDrawable;
         mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable;
     }
 
@@ -1334,11 +1334,11 @@
                 ? mOriginalBackgroundBlurRadius : 0;
         if (mBackgroundBlurDrawable == null && mBackgroundBlurRadius > 0) {
             mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable();
+            updateBackgroundDrawable();
         }
 
         if (mBackgroundBlurDrawable != null) {
             mBackgroundBlurDrawable.setBlurRadius(mBackgroundBlurRadius);
-            updateBackgroundDrawable();
         }
     }
 
@@ -1357,12 +1357,20 @@
                 updateBackgroundBlurRadius();
             }
         } else if (mCrossWindowBlurEnabledListener != null) {
-            mCrossWindowBlurEnabledListener = null;
+            updateBackgroundBlurRadius();
+            removeBackgroundBlurDrawable();
+        }
+    }
+
+    void removeBackgroundBlurDrawable() {
+        if (mCrossWindowBlurEnabledListener != null) {
             getContext().getSystemService(WindowManager.class)
                     .removeCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
-            getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
-            updateBackgroundBlurRadius();
+            mCrossWindowBlurEnabledListener = null;
         }
+        getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
+        mBackgroundBlurDrawable = null;
+        updateBackgroundDrawable();
     }
 
     @Override
@@ -1847,6 +1855,8 @@
             mFloatingToolbar = null;
         }
 
+        removeBackgroundBlurDrawable();
+
         PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
         if (st != null && st.menu != null && mFeatureId < 0) {
             st.menu.close();
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index af595a4..180fc12 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4428,6 +4428,9 @@
       -->
     </integer-array>
 
+    <!-- Indicates whether device has a power button fingerprint sensor. -->
+    <bool name="config_is_powerbutton_fps" translatable="false" >false</bool>
+
     <!-- Messages that should not be shown to the user during face auth enrollment. This should be
          used to hide messages that may be too chatty or messages that the user can't do much about.
          Entries are defined in android.hardware.biometrics.face@1.0 types.hal -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index ba9e454..eb1fe1d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2558,6 +2558,7 @@
   <java-symbol type="array" name="config_biometric_sensors" />
   <java-symbol type="bool" name="allow_test_udfps" />
   <java-symbol type="array" name="config_udfps_sensor_props" />
+  <java-symbol type="bool" name="config_is_powerbutton_fps" />
 
   <java-symbol type="array" name="config_face_acquire_enroll_ignorelist" />
   <java-symbol type="array" name="config_face_acquire_vendor_enroll_ignorelist" />
diff --git a/core/tests/coretests/src/android/app/NotificationHistoryTest.java b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
index 3df0a68..bd493f4 100644
--- a/core/tests/coretests/src/android/app/NotificationHistoryTest.java
+++ b/core/tests/coretests/src/android/app/NotificationHistoryTest.java
@@ -29,6 +29,8 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
@@ -327,6 +329,48 @@
     }
 
     @Test
+    public void testRemoveChannelFromWrite() {
+        NotificationHistory history = new NotificationHistory();
+
+        List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
+        Set<String> postRemoveExpectedStrings = new HashSet<>();
+        for (int i = 1; i <= 10; i++) {
+            HistoricalNotification n = getHistoricalNotification("pkg", i);
+
+            // Remove channel numbers 5 and 6
+            if (i != 5 && i != 6) {
+                postRemoveExpectedStrings.add(n.getPackage());
+                postRemoveExpectedStrings.add(n.getChannelName());
+                postRemoveExpectedStrings.add(n.getChannelId());
+                if (n.getConversationId() != null) {
+                    postRemoveExpectedStrings.add(n.getConversationId());
+                }
+                postRemoveExpectedEntries.add(n);
+            }
+
+            history.addNotificationToWrite(n);
+        }
+        // add second notification with the same channel id that will also be removed
+        history.addNotificationToWrite(getHistoricalNotification("pkg", 6));
+
+        history.poolStringsFromNotifications();
+
+        assertThat(history.getNotificationsToWrite().size()).isEqualTo(11);
+        // 1 package name and 20 unique channel names and ids and 5 conversation ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
+
+        history.removeChannelFromWrite("pkg", "channelId5");
+        history.removeChannelFromWrite("pkg", "channelId6");
+
+        // 1 package names and 8 * 2 unique channel names and ids and 4 conversation ids
+        assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
+        assertThat(Arrays.asList(history.getPooledStringsToWrite()))
+                .containsExactlyElementsIn(postRemoveExpectedStrings);
+        assertThat(history.getNotificationsToWrite())
+                .containsExactlyElementsIn(postRemoveExpectedEntries);
+    }
+
+    @Test
     public void testParceling() {
         NotificationHistory history = new NotificationHistory();
 
diff --git a/core/tests/coretests/src/android/window/OWNERS b/core/tests/coretests/src/android/window/OWNERS
new file mode 100644
index 0000000..6c80cf9
--- /dev/null
+++ b/core/tests/coretests/src/android/window/OWNERS
@@ -0,0 +1,2 @@
+include /services/core/java/com/android/server/wm/OWNERS
+charlesccchen@google.com
diff --git a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
index 64bcc1c..6d85c7f 100644
--- a/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/FrameTrackerTest.java
@@ -129,7 +129,7 @@
 
         // end the trace session, the last janky frame is after the end() so is discarded.
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end();
+        mTracker.end(FrameTracker.REASON_END_NORMAL);
         sendFrame(5, JANK_NONE, 102L);
         sendFrame(500, JANK_APP_DEADLINE_MISSED, 103L);
 
@@ -151,7 +151,7 @@
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end();
+        mTracker.end(FrameTracker.REASON_END_NORMAL);
         sendFrame(4, JANK_NONE, 102L);
 
         verify(mTracker).removeObservers();
@@ -174,7 +174,7 @@
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end();
+        mTracker.end(FrameTracker.REASON_END_NORMAL);
         sendFrame(4, JANK_NONE, 102L);
 
         verify(mTracker).removeObservers();
@@ -197,7 +197,7 @@
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end();
+        mTracker.end(FrameTracker.REASON_END_NORMAL);
         sendFrame(4, JANK_NONE, 102L);
 
         verify(mTracker).removeObservers();
@@ -220,7 +220,7 @@
 
         // end the trace session, simulate one more valid callback came after the end call.
         when(mChoreographer.getVsyncId()).thenReturn(102L);
-        mTracker.end();
+        mTracker.end(FrameTracker.REASON_END_NORMAL);
         sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L);
 
         // One more callback with VSYNC after the end() vsync id.
@@ -247,7 +247,7 @@
         // a janky frame
         sendFrame(50, JANK_APP_DEADLINE_MISSED, 102L);
 
-        mTracker.cancel();
+        mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
         verify(mTracker).removeObservers();
         // Since the tracker has been cancelled, shouldn't trigger perfetto.
         verify(mTracker, never()).triggerPerfetto();
@@ -267,11 +267,11 @@
 
         // end the trace session
         when(mChoreographer.getVsyncId()).thenReturn(101L);
-        mTracker.end();
+        mTracker.end(FrameTracker.REASON_END_NORMAL);
         sendFrame(4, JANK_NONE, 102L);
 
         // Since the begin vsync id (101) equals to the end vsync id (101), will be treat as cancel.
-        verify(mTracker).cancel();
+        verify(mTracker).cancel(FrameTracker.REASON_CANCEL_SAME_VSYNC);
 
         // Observers should be removed in this case, or FrameTracker object will be leaked.
         verify(mTracker).removeObservers();
@@ -282,13 +282,13 @@
 
     @Test
     public void testCancelWhenSessionNeverBegun() {
-        mTracker.cancel();
+        mTracker.cancel(FrameTracker.REASON_CANCEL_NORMAL);
         verify(mTracker).removeObservers();
     }
 
     @Test
     public void testEndWhenSessionNeverBegun() {
-        mTracker.end();
+        mTracker.end(FrameTracker.REASON_END_NORMAL);
         verify(mTracker).removeObservers();
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
index 8f4948c..5f4b854 100644
--- a/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
+++ b/core/tests/coretests/src/com/android/internal/jank/InteractionJankMonitorTest.java
@@ -103,7 +103,7 @@
         assertThat(monitor.begin(mView, session.getCuj())).isTrue();
         verify(tracker).begin();
         assertThat(monitor.end(session.getCuj())).isTrue();
-        verify(tracker).end();
+        verify(tracker).end(FrameTracker.REASON_END_NORMAL);
     }
 
     @Test
@@ -154,7 +154,7 @@
         assertThat(runnable).isNotNull();
         mWorker.getThreadHandler().removeCallbacks(runnable);
         runnable.run();
-        verify(tracker).cancel();
+        verify(tracker).cancel(FrameTracker.REASON_CANCEL_NORMAL);
     }
 
     @Test
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index e41805d..8271bed 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -23,6 +23,7 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
+import android.content.AttributionSource;
 import android.os.PersistableBundle;
 import android.os.RemoteException;
 
@@ -45,13 +46,17 @@
     private static final Executor EXECUTOR = UwbTestUtils.getExecutor();
     private static final PersistableBundle PARAMS = new PersistableBundle();
     private static final @RangingChangeReason int REASON = RangingChangeReason.UNKNOWN;
+    private static final int UID = 343453;
+    private static final String PACKAGE_NAME = "com.uwb.test";
+    private static final AttributionSource ATTRIBUTION_SOURCE =
+            new AttributionSource.Builder(UID).setPackageName(PACKAGE_NAME).build();
 
     @Test
     public void testOpenSession_OpenRangingInvoked() throws RemoteException {
         IUwbAdapter adapter = mock(IUwbAdapter.class);
         RangingManager rangingManager = new RangingManager(adapter);
         RangingSession.Callback callback = mock(RangingSession.Callback.class);
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
         verify(adapter, times(1)).openRanging(any(), eq(rangingManager), eq(PARAMS));
     }
 
@@ -74,11 +79,11 @@
                 ArgumentCaptor.forClass(SessionHandle.class);
 
         RangingManager rangingManager = new RangingManager(adapter);
-        rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
         verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
 
-        rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
         verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
@@ -100,7 +105,7 @@
         ArgumentCaptor<SessionHandle> sessionHandleCaptor =
                 ArgumentCaptor.forClass(SessionHandle.class);
 
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
         verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle handle = sessionHandleCaptor.getValue();
 
@@ -145,11 +150,11 @@
         ArgumentCaptor<SessionHandle> sessionHandleCaptor =
                 ArgumentCaptor.forClass(SessionHandle.class);
 
-        rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
         verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
 
-        rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
         verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
 
@@ -172,12 +177,12 @@
                 ArgumentCaptor.forClass(SessionHandle.class);
 
         RangingManager rangingManager = new RangingManager(adapter);
-        rangingManager.openSession(PARAMS, EXECUTOR, callback1);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback1);
         verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle sessionHandle1 = sessionHandleCaptor.getValue();
 
         rangingManager.onRangingStarted(sessionHandle1, PARAMS);
-        rangingManager.openSession(PARAMS, EXECUTOR, callback2);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback2);
         verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle sessionHandle2 = sessionHandleCaptor.getValue();
         rangingManager.onRangingStarted(sessionHandle2, PARAMS);
@@ -224,7 +229,7 @@
         ArgumentCaptor<SessionHandle> sessionHandleCaptor =
                 ArgumentCaptor.forClass(SessionHandle.class);
 
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
         verify(adapter, times(1)).openRanging(sessionHandleCaptor.capture(), any(), any());
         SessionHandle handle = sessionHandleCaptor.getValue();
 
@@ -232,7 +237,7 @@
         verify(callback, times(1)).onOpenFailed(eq(reasonOut), eq(PARAMS));
 
         // Open a new session
-        rangingManager.openSession(PARAMS, EXECUTOR, callback);
+        rangingManager.openSession(ATTRIBUTION_SOURCE, PARAMS, EXECUTOR, callback);
         verify(adapter, times(2)).openRanging(sessionHandleCaptor.capture(), any(), any());
         handle = sessionHandleCaptor.getValue();
         rangingManager.onRangingOpened(handle);
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 8dd7f31..6c03ddc 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -17,6 +17,7 @@
 package android.graphics;
 
 import android.annotation.NonNull;
+import android.os.SystemProperties;
 import android.util.Pools.SynchronizedPool;
 
 import dalvik.annotation.optimization.CriticalNative;
@@ -36,7 +37,15 @@
     // view hierarchy because display lists are generated recursively.
     private static final int POOL_LIMIT = 25;
 
-    private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
+    /** @hide */
+    private static int getPanelFrameSize() {
+        final int DefaultSize = 100 * 1024 * 1024; // 100 MB;
+        return Math.max(SystemProperties.getInt("ro.hwui.max_texture_allocation_size", DefaultSize),
+                DefaultSize);
+    }
+
+    /** @hide */
+    public static final int MAX_BITMAP_SIZE = getPanelFrameSize();
 
     private static final SynchronizedPool<RecordingCanvas> sPool =
             new SynchronizedPool<>(POOL_LIMIT);
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index fb08974..9ee1ef1 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -41,8 +41,8 @@
     private static final int ENTER_ANIM_DURATION = 450;
     private static final int EXIT_ANIM_DURATION = 300;
     private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
-    private static final Interpolator FAST_OUT_LINEAR_IN =
-            new PathInterpolator(0.4f, 0f, 1f, 1f);
+    private static final Interpolator FAST_OUT_SLOW_IN =
+            new PathInterpolator(0.4f, 0f, 0.2f, 1f);
     private Consumer<RippleAnimationSession> mOnSessionEnd;
     private final AnimationProperties<Float, Paint> mProperties;
     private AnimationProperties<CanvasProperty<Float>, CanvasProperty<Paint>> mCanvasProperties;
@@ -173,7 +173,7 @@
     private void startAnimation(Animator expand) {
         expand.setDuration(ENTER_ANIM_DURATION);
         expand.addListener(new AnimatorListener(this));
-        expand.setInterpolator(FAST_OUT_LINEAR_IN);
+        expand.setInterpolator(FAST_OUT_SLOW_IN);
         expand.start();
         mAnimateSparkle = true;
     }
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 6b2b959..aaab3bd 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -48,7 +48,7 @@
             + "  float s = 0.0;\n"
             + "  for (float i = 0; i < 4; i += 1) {\n"
             + "    float l = i * 0.01;\n"
-            + "    float h = l + 0.1;\n"
+            + "    float h = l + 0.2;\n"
             + "    float o = smoothstep(n - l, h, n);\n"
             + "    o *= abs(sin(PI * o * (t + 0.55 * i)));\n"
             + "    s += o;\n"
@@ -62,7 +62,7 @@
             + "  return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n"
             + "}\n"
             + "float softRing(vec2 uv, vec2 xy, float radius, float progress, float blur) {\n"
-            + "  float thickness = 0.2 * radius;\n"
+            + "  float thickness = 0.3 * radius;\n"
             + "  float currentRadius = radius * progress;\n"
             + "  float circle_outer = softCircle(uv, xy, currentRadius + thickness, blur);\n"
             + "  float circle_inner = softCircle(uv, xy, currentRadius - thickness, blur);\n"
@@ -73,18 +73,19 @@
             + "    return (sub - start) / (end - start); \n"
             + "}\n";
     private static final String SHADER_MAIN = "vec4 main(vec2 p) {\n"
-            + "    float fadeIn = subProgress(0., 0.175, in_progress);\n"
-            + "    float fadeOutNoise = subProgress(0.375, 1., in_progress);\n"
-            + "    float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n"
+            + "    float fadeIn = subProgress(0., 0.1, in_progress);\n"
+            + "    float scaleIn = subProgress(0., 0.45, in_progress);\n"
+            + "    float fadeOutNoise = subProgress(0.5, 1., in_progress);\n"
+            + "    float fadeOutRipple = subProgress(0.5, 0.75, in_progress);\n"
             + "    vec2 center = mix(in_touch, in_origin, fadeIn);\n"
-            + "    float ring = softRing(p, center, in_maxRadius, fadeIn, 0.45);\n"
-            + "    float alpha = 1. - fadeOutNoise;\n"
+            + "    float ring = softRing(p, center, in_maxRadius, scaleIn, 0.45);\n"
+            + "    float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
             + "    vec2 uv = p * in_resolutionScale;\n"
             + "    vec2 densityUv = uv - mod(uv, in_noiseScale);\n"
             + "    float sparkle = sparkles(densityUv, in_noisePhase) * ring * alpha;\n"
             + "    float fade = min(fadeIn, 1. - fadeOutRipple);\n"
             + "    vec4 circle = in_color * (softCircle(p, center, in_maxRadius "
-            + "      * fadeIn, 0.2) * fade);\n"
+            + "      * scaleIn, 0.2) * fade);\n"
             + "    float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
             + "    return mix(circle, vec4(sparkle), sparkle) * mask;\n"
             + "}";
@@ -134,7 +135,7 @@
     }
 
     public void setResolution(float w, float h, int density) {
-        float densityScale = density * DisplayMetrics.DENSITY_DEFAULT_SCALE;
+        float densityScale = density * DisplayMetrics.DENSITY_DEFAULT_SCALE * 1.25f;
         setUniform("in_resolutionScale", new float[] {1f / w, 1f / h});
         setUniform("in_noiseScale", new float[] {densityScale / w, densityScale / h});
     }
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index ec3b102..66d842e 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -34,9 +34,11 @@
 import java.security.KeyPairGenerator;
 import java.security.KeyStore;
 import java.security.SecureRandom;
+import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Random;
 import java.util.Set;
@@ -248,8 +250,9 @@
             KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
             keyStore.load(null);
 
+            Certificate[] certs = keyStore.getCertificateChain(keystoreAlias);
             X509Certificate[] certificateChain =
-                    (X509Certificate[]) keyStore.getCertificateChain(keystoreAlias);
+                Arrays.copyOf(certs, certs.length, X509Certificate[].class);
 
             keyStore.deleteEntry(keystoreAlias);
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 65f3d3a..4cf8ab4 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -526,6 +526,10 @@
      * Handles a pointer event sent from pip input consumer.
      */
     void handlePointerEvent(MotionEvent ev) {
+        if (mPipMenuView == null) {
+            return;
+        }
+
         if (ev.isTouchEvent()) {
             mPipMenuView.dispatchTouchEvent(ev);
         } else {
diff --git a/packages/Connectivity/framework/Android.bp b/packages/Connectivity/framework/Android.bp
index 4185a42..5f5ebf4 100644
--- a/packages/Connectivity/framework/Android.bp
+++ b/packages/Connectivity/framework/Android.bp
@@ -108,6 +108,7 @@
         "//packages/modules/Connectivity/Tethering/tests:__subpackages__",
         "//packages/modules/Connectivity/tests:__subpackages__",
         "//packages/modules/NetworkStack/tests:__subpackages__",
+        "//packages/modules/Wifi/service/tests/wifitests",
     ],
     apex_available: [
         "com.android.tethering",
diff --git a/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl b/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
index 0d70f2a..3495efc 100644
--- a/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
+++ b/packages/Connectivity/framework/aidl-export/android/net/IpPrefix.aidl
@@ -18,5 +18,5 @@
 package android.net;
 
 // @JavaOnlyStableParcelable only affects the parcelable when built as stable aidl (aidl_interface
-// build rule). IpPrefix is also used in cpp but only as non-stable aidl.
-@JavaOnlyStableParcelable parcelable IpPrefix cpp_header "binder/IpPrefix.h";
+// build rule).
+@JavaOnlyStableParcelable parcelable IpPrefix;
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 350b47f..b77d821 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -3143,18 +3143,27 @@
     }
 
     /**
-     * Set a network-independent global http proxy.  This is not normally what you want
-     * for typical HTTP proxies - they are general network dependent.  However if you're
-     * doing something unusual like general internal filtering this may be useful.  On
-     * a private network where the proxy is not accessible, you may break HTTP using this.
+     * Set a network-independent global HTTP proxy.
      *
-     * @param p A {@link ProxyInfo} object defining the new global
-     *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
+     * This sets an HTTP proxy that applies to all networks and overrides any network-specific
+     * proxy. If set, HTTP libraries that are proxy-aware will use this global proxy when
+     * accessing any network, regardless of what the settings for that network are.
+     *
+     * Note that HTTP proxies are by nature typically network-dependent, and setting a global
+     * proxy is likely to break networking on multiple networks. This method is only meant
+     * for device policy clients looking to do general internal filtering or similar use cases.
+     *
+     * {@see #getGlobalProxy}
+     * {@see LinkProperties#getHttpProxy}
+     *
+     * @param p A {@link ProxyInfo} object defining the new global HTTP proxy. Calling this
+     *          method with a {@code null} value will clear the global HTTP proxy.
      * @hide
      */
+    // Used by Device Policy Manager to set the global proxy.
     @SystemApi(client = MODULE_LIBRARIES)
     @RequiresPermission(android.Manifest.permission.NETWORK_STACK)
-    public void setGlobalProxy(@Nullable ProxyInfo p) {
+    public void setGlobalProxy(@Nullable final ProxyInfo p) {
         try {
             mService.setGlobalProxy(p);
         } catch (RemoteException e) {
diff --git a/packages/Connectivity/framework/src/android/net/DnsResolver.java b/packages/Connectivity/framework/src/android/net/DnsResolver.java
index 3f7660f..dac88ad 100644
--- a/packages/Connectivity/framework/src/android/net/DnsResolver.java
+++ b/packages/Connectivity/framework/src/android/net/DnsResolver.java
@@ -500,7 +500,7 @@
                             try {
                                 resp = resNetworkResult(fd);  // Closes fd, marks it invalid.
                             } catch (ErrnoException e) {
-                                Log.e(TAG, "resNetworkResult:" + e.toString());
+                                Log.w(TAG, "resNetworkResult:" + e.toString());
                                 exception = e;
                             }
                         }
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
index 705d3f4..38c06dd 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -41,6 +41,15 @@
 public class InstallSuccess extends AlertActivity {
     private static final String LOG_TAG = InstallSuccess.class.getSimpleName();
 
+    @Nullable
+    private PackageUtil.AppSnippet mAppSnippet;
+
+    @Nullable
+    private String mAppPackageName;
+
+    @Nullable
+    private Intent mLaunchIntent;
+
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -55,59 +64,73 @@
             Intent intent = getIntent();
             ApplicationInfo appInfo =
                     intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+            mAppPackageName = appInfo.packageName;
             Uri packageURI = intent.getData();
 
             // Set header icon and title
-            PackageUtil.AppSnippet as;
             PackageManager pm = getPackageManager();
 
             if ("package".equals(packageURI.getScheme())) {
-                as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
+                mAppSnippet = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
                         pm.getApplicationIcon(appInfo));
             } else {
                 File sourceFile = new File(packageURI.getPath());
-                as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+                mAppSnippet = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
             }
 
-            mAlert.setIcon(as.icon);
-            mAlert.setTitle(as.label);
-            mAlert.setView(R.layout.install_content_view);
-            mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null,
-                    null);
-            mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
-                    (ignored, ignored2) -> {
-                        if (appInfo.packageName != null) {
-                            Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
-                        }
-                        finish();
-                    }, null);
-            setupAlert();
-            requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
-            // Enable or disable "launch" button
-            Intent launchIntent = getPackageManager().getLaunchIntentForPackage(
-                    appInfo.packageName);
-            boolean enabled = false;
-            if (launchIntent != null) {
-                List<ResolveInfo> list = getPackageManager().queryIntentActivities(launchIntent,
-                        0);
-                if (list != null && list.size() > 0) {
-                    enabled = true;
-                }
-            }
+            mLaunchIntent = getPackageManager().getLaunchIntentForPackage(mAppPackageName);
 
-            Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
-            if (enabled) {
-                launchButton.setOnClickListener(view -> {
-                    try {
-                        startActivity(launchIntent);
-                    } catch (ActivityNotFoundException | SecurityException e) {
-                        Log.e(LOG_TAG, "Could not start activity", e);
+            bindUi();
+        }
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        bindUi();
+    }
+
+    private void bindUi() {
+        if (mAppSnippet == null) {
+            return;
+        }
+
+        mAlert.setIcon(mAppSnippet.icon);
+        mAlert.setTitle(mAppSnippet.label);
+        mAlert.setView(R.layout.install_content_view);
+        mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.launch), null,
+                null);
+        mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.done),
+                (ignored, ignored2) -> {
+                    if (mAppPackageName != null) {
+                        Log.i(LOG_TAG, "Finished installing " + mAppPackageName);
                     }
                     finish();
-                });
-            } else {
-                launchButton.setEnabled(false);
+                }, null);
+        setupAlert();
+        requireViewById(R.id.install_success).setVisibility(View.VISIBLE);
+        // Enable or disable "launch" button
+        boolean enabled = false;
+        if (mLaunchIntent != null) {
+            List<ResolveInfo> list = getPackageManager().queryIntentActivities(mLaunchIntent,
+                    0);
+            if (list != null && list.size() > 0) {
+                enabled = true;
             }
         }
+
+        Button launchButton = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
+        if (enabled) {
+            launchButton.setOnClickListener(view -> {
+                try {
+                    startActivity(mLaunchIntent);
+                } catch (ActivityNotFoundException | SecurityException e) {
+                    Log.e(LOG_TAG, "Could not start activity", e);
+                }
+                finish();
+            });
+        } else {
+            launchButton.setEnabled(false);
+        }
     }
 }
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/image_frame.xml b/packages/SettingsLib/SettingsTheme/res/layout/settings_icon.xml
similarity index 100%
rename from packages/SettingsLib/SettingsTheme/res/layout/image_frame.xml
rename to packages/SettingsLib/SettingsTheme/res/layout/settings_icon.xml
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/settings_preference.xml b/packages/SettingsLib/SettingsTheme/res/layout/settings_preference.xml
index 3a289a7..d2b3169 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout/settings_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/settings_preference.xml
@@ -29,7 +29,7 @@
     android:clipToPadding="false"
     android:baselineAligned="false">
 
-    <include layout="@layout/image_frame"/>
+    <include layout="@layout/settings_icon"/>
 
     <RelativeLayout
         android:layout_width="0dp"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 42d7e58..85ecb1c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -757,6 +757,7 @@
                   android:excludeFromRecents="true"
                   android:showWhenLocked="true"
                   android:showForAllUsers="true"
+                  android:finishOnTaskLaunch="true"
                   android:launchMode="singleInstance"
                   android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
                   android:visibleToInstantApps="true">
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
index 26cead2..c9f2401 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QSTile.java
@@ -143,6 +143,7 @@
         public SlashState slash;
         public boolean handlesLongClick = true;
         public boolean showRippleEffect = true;
+        public Drawable sideViewDrawable;
 
         public boolean copyTo(State other) {
             if (other == null) throw new IllegalArgumentException();
@@ -163,7 +164,8 @@
                     || !Objects.equals(other.dualTarget, dualTarget)
                     || !Objects.equals(other.slash, slash)
                     || !Objects.equals(other.handlesLongClick, handlesLongClick)
-                    || !Objects.equals(other.showRippleEffect, showRippleEffect);
+                    || !Objects.equals(other.showRippleEffect, showRippleEffect)
+                    || !Objects.equals(other.sideViewDrawable, sideViewDrawable);
             other.icon = icon;
             other.iconSupplier = iconSupplier;
             other.label = label;
@@ -179,6 +181,7 @@
             other.slash = slash != null ? slash.copy() : null;
             other.handlesLongClick = handlesLongClick;
             other.showRippleEffect = showRippleEffect;
+            other.sideViewDrawable = sideViewDrawable;
             return changed;
         }
 
@@ -204,6 +207,7 @@
             sb.append(",isTransient=").append(isTransient);
             sb.append(",state=").append(state);
             sb.append(",slash=\"").append(slash).append("\"");
+            sb.append(",sideViewDrawable").append(sideViewDrawable);
             return sb.append(']');
         }
 
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 45d8dc1..73b02f4 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -1,37 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+<!--
+  ~ Copyright (C) 2020 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+            android:paddingMode="stack" >
     <item android:id="@android:id/background"
-          android:gravity="center_vertical|fill_horizontal">
-        <shape android:shape="rectangle"
-               android:tint="?android:attr/colorControlActivated">
-            <size android:height="@dimen/seek_bar_height" />
-            <solid android:color="@color/white_disabled" />
-            <corners android:radius="@dimen/seek_bar_corner_radius" />
-        </shape>
+        android:gravity="center_vertical|fill_horizontal">
+        <inset
+            android:insetLeft="@dimen/rounded_slider_track_inset"
+            android:insetRight="@dimen/rounded_slider_track_inset" >
+            <shape>
+                <size android:height="@dimen/rounded_slider_track_width" />
+                <corners android:radius="@dimen/rounded_slider_track_corner_radius" />
+                <solid android:color="?android:attr/textColorPrimary" />
+            </shape>
+        </inset>
     </item>
     <item android:id="@android:id/progress"
           android:gravity="center_vertical|fill_horizontal">
-        <scale android:scaleWidth="100%">
-            <shape android:shape="rectangle"
-                   android:tint="?android:attr/colorControlActivated">
-                <size android:height="@dimen/seek_bar_height" />
-                <solid android:color="@android:color/white" />
-                <corners android:radius="@dimen/seek_bar_corner_radius" />
-            </shape>
-        </scale>
+            <com.android.systemui.util.RoundedCornerProgressDrawable
+                android:drawable="@drawable/brightness_progress_full_drawable"
+            />
     </item>
-</layer-list>
+</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
deleted file mode 100644
index 73b02f4..0000000
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable_thick.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
-            android:paddingMode="stack" >
-    <item android:id="@android:id/background"
-        android:gravity="center_vertical|fill_horizontal">
-        <inset
-            android:insetLeft="@dimen/rounded_slider_track_inset"
-            android:insetRight="@dimen/rounded_slider_track_inset" >
-            <shape>
-                <size android:height="@dimen/rounded_slider_track_width" />
-                <corners android:radius="@dimen/rounded_slider_track_corner_radius" />
-                <solid android:color="?android:attr/textColorPrimary" />
-            </shape>
-        </inset>
-    </item>
-    <item android:id="@android:id/progress"
-          android:gravity="center_vertical|fill_horizontal">
-            <com.android.systemui.util.RoundedCornerProgressDrawable
-                android:drawable="@drawable/brightness_progress_full_drawable"
-            />
-    </item>
-</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/global_actions_grid_v2.xml b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
index 30ffc32..f06a4be 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_v2.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_v2.xml
@@ -30,12 +30,6 @@
           android:layout_width="match_parent"
           android:layout_height="wrap_content"/>
 
-      <LinearLayout
-          android:id="@+id/global_actions_controls"
-          android:layout_width="match_parent"
-          android:layout_height="wrap_content"
-          android:orientation="vertical"/>
-
     </LinearLayout>
   </com.android.systemui.globalactions.MinHeightScrollView>
 </LinearLayout>
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 8b2d4e0..e775de2 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -52,8 +52,9 @@
         android:id="@+id/preview"
         android:layout_width="0px"
         android:layout_height="0px"
-        android:layout_marginBottom="42dp"
         android:paddingHorizontal="48dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="42dp"
         app:layout_constrainedHeight="true"
         app:layout_constrainedWidth="true"
         app:layout_constraintTop_toBottomOf="@id/save"
@@ -68,7 +69,8 @@
         android:id="@+id/crop_view"
         android:layout_width="0px"
         android:layout_height="0px"
-        android:layout_marginBottom="42dp"
+        android:paddingTop="8dp"
+        android:paddingBottom="42dp"
         app:layout_constrainedHeight="true"
         app:layout_constrainedWidth="true"
         app:layout_constraintTop_toTopOf="@id/preview"
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 1ce87c1..f8812ba 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -16,8 +16,9 @@
 -->
 <com.android.systemui.qs.tileimpl.ButtonRelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
+    android:layout_width="0dp"
     android:layout_height="wrap_content"
+    android:layout_weight="1"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:paddingTop="12dp">
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index 9cc09aa..f4d5304 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2020 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
@@ -13,69 +13,31 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_height="wrap_content"
     android:layout_width="match_parent"
-    android:layout_gravity="center_vertical"
-    style="@style/BrightnessDialogContainer">
+    android:layout_gravity="center">
 
     <com.android.systemui.settings.brightness.BrightnessSliderView
         android:id="@+id/brightness_slider"
-        android:layout_width="0dp"
+        android:layout_width="match_parent"
         android:layout_height="@dimen/brightness_mirror_height"
         android:layout_gravity="center_vertical"
-        android:layout_weight="1"
         android:contentDescription="@string/accessibility_brightness"
-        android:importantForAccessibility="no"
-        systemui:text="@string/status_bar_settings_auto_brightness_label" >
+        android:importantForAccessibility="no" >
 
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" >
-
-        <CheckBox
-            android:id="@+id/toggle"
-            android:layout_width="48dp"
-            android:layout_height="0dp"
-            android:layout_alignParentStart="true"
-            android:layout_alignParentTop="true"
-            android:layout_alignParentBottom="true"
-            android:button="@null"
-            android:background="@*android:drawable/switch_track_material"
-            android:visibility="gone"
-        />
         <com.android.systemui.settings.brightness.ToggleSeekBar
             android:id="@+id/slider"
-            android:layout_width="0dp"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_toEndOf="@id/toggle"
-            android:layout_centerVertical="true"
-            android:layout_alignParentStart="true"
-            android:layout_alignParentEnd="true"
-            android:paddingStart="20dp"
-            android:paddingEnd="20dp"
-            android:paddingTop="16dp"
-            android:paddingBottom="16dp"
-            android:thumb="@drawable/ic_brightness_thumb"
+            android:layout_gravity="center_vertical"
+            android:minHeight="48dp"
+            android:thumb="@null"
+            android:background="@null"
+            android:paddingStart="0dp"
+            android:paddingEnd="0dp"
             android:progressDrawable="@drawable/brightness_progress_drawable"
             android:splitTrack="false"
         />
-        <TextView
-            android:id="@+id/label"
-            android:layout_width="0dp"
-            android:layout_height="wrap_content"
-            android:layout_alignStart="@id/toggle"
-            android:layout_alignEnd="@id/toggle"
-            android:layout_centerVertical="true"
-            android:gravity="center"
-            android:paddingTop="26dp"
-            android:textColor="#666666"
-            android:textSize="12sp"
-            android:visibility="gone"
-        />
-
-        </RelativeLayout>
     </com.android.systemui.settings.brightness.BrightnessSliderView>
-
-</LinearLayout>
+</FrameLayout>
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog_thick.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog_thick.xml
deleted file mode 100644
index 6cee38d..0000000
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog_thick.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2020 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_height="wrap_content"
-    android:layout_width="match_parent"
-    android:layout_gravity="center">
-
-    <com.android.systemui.settings.brightness.BrightnessSliderView
-        android:id="@+id/brightness_slider"
-        android:layout_width="match_parent"
-        android:layout_height="@dimen/brightness_mirror_height"
-        android:layout_gravity="center_vertical"
-        android:contentDescription="@string/accessibility_brightness"
-        android:importantForAccessibility="no" >
-
-        <com.android.systemui.settings.brightness.ToggleSeekBar
-            android:id="@+id/slider"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical"
-            android:minHeight="48dp"
-            android:thumb="@null"
-            android:background="@null"
-            android:paddingStart="0dp"
-            android:paddingEnd="0dp"
-            android:progressDrawable="@drawable/brightness_progress_drawable_thick"
-            android:splitTrack="false"
-        />
-    </com.android.systemui.settings.brightness.BrightnessSliderView>
-</FrameLayout>
diff --git a/packages/SystemUI/res/values-h740dp-port/dimens.xml b/packages/SystemUI/res/values-h740dp-port/dimens.xml
index 4a23ee6..966066f 100644
--- a/packages/SystemUI/res/values-h740dp-port/dimens.xml
+++ b/packages/SystemUI/res/values-h740dp-port/dimens.xml
@@ -19,9 +19,9 @@
     <dimen name="qs_tile_margin_vertical">24dp</dimen>
 
     <!-- The height of the qs customize header. Should be
-         (qs_panel_padding_top (48dp) +  brightness_mirror_height (56dp) + qs_tile_margin_top (18dp)) -
+         (qs_panel_padding_top (48dp) +  brightness_mirror_height (48dp) + qs_tile_margin_top (18dp)) -
          (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
     -->
-    <dimen name="qs_customize_header_min_height">54dp</dimen>
+    <dimen name="qs_customize_header_min_height">46dp</dimen>
     <dimen name="qs_tile_margin_top">18dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 2a2bb99..5a21b30 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -449,7 +449,7 @@
 
     <dimen name="notification_panel_width">@dimen/match_parent</dimen>
 
-    <dimen name="brightness_mirror_height">56dp</dimen>
+    <dimen name="brightness_mirror_height">48dp</dimen>
 
     <!-- The width of the panel that holds the quick settings. -->
     <dimen name="qs_panel_width">@dimen/notification_panel_width</dimen>
@@ -531,10 +531,10 @@
     <dimen name="qs_tile_margin_top_bottom">12dp</dimen>
     <dimen name="qs_tile_margin_top_bottom_negative">-12dp</dimen>
     <!-- The height of the qs customize header. Should be
-         (qs_panel_padding_top (48dp) +  brightness_mirror_height (56dp) + qs_tile_margin_top (0dp)) -
+         (qs_panel_padding_top (48dp) +  brightness_mirror_height (48dp) + qs_tile_margin_top (0dp)) -
          (Toolbar_minWidth (56dp) + qs_tile_margin_top_bottom (12dp))
     -->
-    <dimen name="qs_customize_header_min_height">36dp</dimen>
+    <dimen name="qs_customize_header_min_height">28dp</dimen>
     <dimen name="qs_tile_margin_top">0dp</dimen>
     <dimen name="qs_tile_icon_background_stroke_width">-1dp</dimen>
     <dimen name="qs_tile_background_size">44dp</dimen>
@@ -1437,4 +1437,6 @@
     <dimen name="min_wallet_empty_height">208dp</dimen>
     <dimen name="wallet_card_border_width">1dp</dimen>
     <dimen name="wallet_empty_state_corner_radius">24dp</dimen>
+    <dimen name="wallet_tile_card_view_height">32dp</dimen>
+    <dimen name="wallet_tile_card_view_width">50dp</dimen>
 </resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 6af982d..0763109 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -33,8 +33,6 @@
     <!-- AOD/Lockscreen alternate layout -->
     <bool name="flag_keyguard_layout">false</bool>
 
-    <bool name="flag_brightness_slider">false</bool>
-
     <!-- People Tile flag -->
     <bool name="flag_conversations">false</bool>
 
diff --git a/packages/SystemUI/res/values/ids.xml b/packages/SystemUI/res/values/ids.xml
index 5f68bdb4..9665c89 100644
--- a/packages/SystemUI/res/values/ids.xml
+++ b/packages/SystemUI/res/values/ids.xml
@@ -167,5 +167,11 @@
     <item type="id" name="accessibility_action_move_right"/>
     <item type="id" name="accessibility_action_move_up"/>
     <item type="id" name="accessibility_action_move_down"/>
+
+    <!-- Accessibility actions for Accessibility floating menu. -->
+    <item type="id" name="action_move_top_left"/>
+    <item type="id" name="action_move_top_right"/>
+    <item type="id" name="action_move_bottom_left"/>
+    <item type="id" name="action_move_bottom_right"/>
 </resources>
 
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8dad40b..054abe2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1874,13 +1874,13 @@
     <string name="notification_channel_summary_automatic_demoted">&lt;b>Status:&lt;/b> Ranked Lower</string>
 
     <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level summary -->
-    <string name="notification_channel_summary_priority">Shows at top of conversation section, appears as floating bubble, displays profile picture on lock screen</string>
+    <string name="notification_channel_summary_priority">Always shown at the top of your notifications, even when Priority mode is on</string>
 
     <!--[CHAR LIMIT=30] Linkable text to Settings app -->
     <string name="notification_conversation_channel_settings">Settings</string>
 
     <!-- [CHAR LIMIT=150] Notification Importance title: important conversation level -->
-    <string name="notification_priority_title">Priority</string>
+    <string name="notification_priority_title">Priority conversations</string>
 
     <!-- Text shown in notification guts for conversation notifications that don't implement the full feature -->
     <string name="no_shortcut"><xliff:g id="app_name" example="YouTube">%1$s</xliff:g> doesn\u2019t support conversation features</string>
@@ -2648,14 +2648,14 @@
     <string name="priority_onboarding_title">Conversation set to priority</string>
     <!--  Text explaining that the following actions are the behaviors of priority conversations.
     E.g. priority conversations will show at the top of the conversation section [CHAR LIMIT=75]  -->
-    <string name="priority_onboarding_behavior">Priority conversations will:</string>
-    <!--  Text explaining that priority conversations show at the top of the conversation section [CHAR LIMIT=75]  -->
-    <string name="priority_onboarding_show_at_top_text">Show at top of conversation section</string>
-    <!--  Text explaining that priority conversations show an avatar on the lock screen [CHAR LIMIT=75]  -->
-    <string name="priority_onboarding_show_avatar_text">Show profile picture on lock screen</string>
-    <!--  Text explaining that priority conversations will appear as a bubble [CHAR LIMIT=75]  -->
-    <string name="priority_onboarding_appear_as_bubble_text">Appear as a floating bubble on top of apps</string>
-    <!--  Text explaining that priority conversations can interrupt DnD settings [CHAR LIMIT=75]  -->
+    <string name="priority_onboarding_behavior">Priority conversations</string>
+    <!--  Text explaining that priority conversations show at the top of the conversation section [CHAR LIMIT=120]  -->
+    <string name="priority_onboarding_show_at_top_text">These conversations are shown at the top of your list and can always reach you when Priority mode is on</string>
+    <!--  Text explaining that priority conversations show an avatar on the lock screen [CHAR LIMIT=120]  -->
+    <string name="priority_onboarding_show_avatar_text">Profile pictures are shown on the lock screen</string>
+    <!--  Text explaining that priority conversations will appear as a bubble [CHAR LIMIT=120]  -->
+    <string name="priority_onboarding_appear_as_bubble_text">You can easily find these conversations in bubbles on your Home screen</string>
+    <!--  Text explaining that priority conversations can interrupt DnD settings [CHAR LIMIT=120]  -->
     <string name="priority_onboarding_ignores_dnd_text">Interrupt Do Not Disturb</string>
     <!--  Title for the affirmative button [CHAR LIMIT=50]  -->
     <string name="priority_onboarding_done_button_title">Got it</string>
@@ -2693,6 +2693,14 @@
     <string name="accessibility_floating_button_migration_tooltip">Accessibility button replaced the accessibility gesture\n\n<annotation id="link">View settings</annotation></string>
     <!-- Message for the accessibility floating button docking tooltip. It shows when the user first time drag the button. It will tell the user about docking behavior. [CHAR LIMIT=70] -->
     <string name="accessibility_floating_button_docking_tooltip">Move button to the edge to hide it temporarily</string>
+    <!-- Action in accessibility menu to move the accessibility floating button to the top left of the screen. [CHAR LIMIT=30] -->
+    <string name="accessibility_floating_button_action_move_top_left">Move top left</string>
+    <!-- Action in accessibility menu to move the accessibility floating button to the top right of the screen. [CHAR LIMIT=30] -->
+    <string name="accessibility_floating_button_action_move_top_right">Move top right</string>
+    <!-- Action in accessibility menu to move the accessibility floating button to the bottom left of the screen. [CHAR LIMIT=30]-->
+    <string name="accessibility_floating_button_action_move_bottom_left">Move bottom left</string>
+    <!-- Action in accessibility menu to move the accessibility floating button to the bottom right of the screen. [CHAR LIMIT=30]-->
+    <string name="accessibility_floating_button_action_move_bottom_right">Move bottom right</string>
 
     <!-- Device Controls strings -->
     <!-- Device Controls empty state, title [CHAR LIMIT=30] -->
@@ -2870,14 +2878,24 @@
     <string name="over_timestamp">Over <xliff:g id="duration" example="1 week">%1$s</xliff:g> ago</string>
     <!-- Status text on the Conversation widget for a birthday today [CHAR LIMIT=20] -->
     <string name="birthday_status">Birthday</string>
+    <!-- Content description text on the Conversation widget for a birthday today [CHAR LIMIT=150] -->
+    <string name="birthday_status_content_description">It\’s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s birthday</string>
     <!-- Status text on the Conversation widget for an upcoming birthday [CHAR LIMIT=20] -->
     <string name="upcoming_birthday_status">Birthday soon</string>
+    <!-- Content description text on the Conversation widget for an upcoming birthday [CHAR LIMIT=150] -->
+    <string name="upcoming_birthday_status_content_description">It\’s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s birthday soon</string>
     <!-- Status text on the Conversation widget for an anniversary [CHAR LIMIT=20] -->
     <string name="anniversary_status">Anniversary</string>
+    <!-- Content description text on the Conversation widget for an anniversary [CHAR LIMIT=150] -->
+    <string name="anniversary_status_content_description">It\’s <xliff:g id="name" example="Anna">%1$s</xliff:g>\’s anniversary</string>
     <!-- Status text on the Conversation widget for sharing location [CHAR LIMIT=20] -->
     <string name="location_status">Sharing location</string>
+    <!-- Content description text on the Conversation widget for sharing location [CHAR LIMIT=150] -->
+    <string name="location_status_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> is sharing location</string>
     <!-- Status text on the Conversation widget for a new story posted [CHAR LIMIT=20] -->
     <string name="new_story_status">New story</string>
+    <!-- Content description text on the Conversation widget for a new story posted on social channels, usually a photograph. [CHAR LIMIT=150] -->
+    <string name="new_story_status_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> shared a new story</string>
     <!-- Status text on the Conversation widget for watching a video [CHAR LIMIT=20] -->
     <string name="video_status">Watching</string>
     <!-- Status text on the Conversation widget for listening to audio [CHAR LIMIT=20] -->
@@ -2888,6 +2906,8 @@
     <string name="empty_user_name">Friends</string>
     <!-- Empty status shown before user has selected a friend for their Conversation widget [CHAR LIMIT=20] -->
     <string name="empty_status">Let’s chat tonight!</string>
+    <!-- Empty status shown temporarily before we've been able to load the conversation for their Conversation widget after reboots [CHAR LIMIT=40] -->
+    <string name="status_before_loading">Content will show up soon</string>
     <!-- Default text for missed call notifications on their Conversation widget [CHAR LIMIT=20] -->
     <string name="missed_call">Missed call</string>
     <!-- Text when a Notification may have more messages than the number indicated [CHAR LIMIT=5] -->
@@ -2896,6 +2916,10 @@
     <string name="people_tile_description">See recent messages, missed calls, and status updates</string>
     <!-- Title text displayed for the Conversation widget [CHAR LIMIT=50] -->
     <string name="people_tile_title">Conversation</string>
+    <!-- Content description text on the Conversation widget when a person has sent a new text message [CHAR LIMIT=150] -->
+    <string name="new_notification_text_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent a message</string>
+    <!-- Content description text on the Conversation widget when a person has sent a new image message [CHAR LIMIT=150] -->
+    <string name="new_notification_image_content_description"><xliff:g id="name" example="Anna">%1$s</xliff:g> sent an image</string>
 
     <!-- Title to display in a notification when ACTION_BATTERY_CHANGED.EXTRA_PRESENT field is false
     [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 621ca59..de09eaa6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1777,6 +1777,7 @@
                         break;
                     case MSG_TIME_FORMAT_UPDATE:
                         handleTimeFormatUpdate((String) msg.obj);
+                        break;
                     case MSG_REQUIRE_NFC_UNLOCK:
                         handleRequireUnlockForNfc();
                         break;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
index ab05c2a..57be4e8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuView.java
@@ -31,6 +31,7 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
 import android.util.DisplayMetrics;
@@ -39,6 +40,8 @@
 import android.view.ViewConfiguration;
 import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import android.view.animation.OvershootInterpolator;
 import android.widget.FrameLayout;
 
@@ -284,6 +287,44 @@
         // Do Nothing
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        setupAccessibilityActions(info);
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (super.performAccessibilityAction(action, arguments)) {
+            return true;
+        }
+
+        fadeIn();
+
+        final Rect bounds = getAvailableBounds();
+        if (action == R.id.action_move_top_left) {
+            snapToLocation(bounds.left, bounds.top);
+            return true;
+        }
+
+        if (action == R.id.action_move_top_right) {
+            snapToLocation(bounds.right, bounds.top);
+            return true;
+        }
+
+        if (action == R.id.action_move_bottom_left) {
+            snapToLocation(bounds.left, bounds.bottom);
+            return true;
+        }
+
+        if (action == R.id.action_move_bottom_right) {
+            snapToLocation(bounds.right, bounds.bottom);
+            return true;
+        }
+
+        return false;
+    }
+
     void show() {
         if (isShowing()) {
             return;
@@ -380,6 +421,33 @@
         mUiHandler.postDelayed(() -> mFadeOutAnimator.start(), FADE_EFFECT_DURATION_MS);
     }
 
+    private void setupAccessibilityActions(AccessibilityNodeInfo info) {
+        final Resources res = mContext.getResources();
+        final AccessibilityAction moveTopLeft =
+                new AccessibilityAction(R.id.action_move_top_left,
+                        res.getString(
+                                R.string.accessibility_floating_button_action_move_top_left));
+        info.addAction(moveTopLeft);
+
+        final AccessibilityAction moveTopRight =
+                new AccessibilityAction(R.id.action_move_top_right,
+                        res.getString(
+                                R.string.accessibility_floating_button_action_move_top_right));
+        info.addAction(moveTopRight);
+
+        final AccessibilityAction moveBottomLeft =
+                new AccessibilityAction(R.id.action_move_bottom_left,
+                        res.getString(
+                                R.string.accessibility_floating_button_action_move_bottom_left));
+        info.addAction(moveBottomLeft);
+
+        final AccessibilityAction moveBottomRight =
+                new AccessibilityAction(R.id.action_move_bottom_right,
+                        res.getString(
+                                R.string.accessibility_floating_button_action_move_bottom_right));
+        info.addAction(moveBottomRight);
+    }
+
     private boolean onTouched(MotionEvent event) {
         final int action = event.getAction();
         final int currentX = (int) event.getX();
@@ -524,7 +592,8 @@
         updateLocationWith(mAlignment, mPercentageY);
     }
 
-    private void snapToLocation(int endX, int endY) {
+    @VisibleForTesting
+    void snapToLocation(int endX, int endY) {
         mDragAnimator.cancel();
         mDragAnimator.removeAllUpdateListeners();
         mDragAnimator.addUpdateListener(anim -> onDragAnimationUpdate(anim, endX, endY));
@@ -662,6 +731,11 @@
                 : R.dimen.accessibility_floating_menu_large_single_radius;
     }
 
+    @VisibleForTesting
+    Rect getAvailableBounds() {
+        return new Rect(0, 0, mScreenWidth - getWindowWidth(), mScreenHeight - getWindowHeight());
+    }
+
     private int getLayoutWidth() {
         return mPadding * 2 + mIconWidth;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 60fdbab..5647e43 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -32,9 +32,10 @@
  * - optionally can override dozeTimeTick to adjust views for burn-in mitigation
  */
 abstract class UdfpsAnimationView extends FrameLayout {
-
+    // mAlpha takes into consideration the status bar expansion amount to fade out icon when
+    // the status bar is expanded
     private int mAlpha;
-    private boolean mPauseAuth;
+    boolean mPauseAuth;
 
     public UdfpsAnimationView(Context context, @Nullable AttributeSet attrs) {
         super(context, attrs);
@@ -75,7 +76,7 @@
         getDrawable().setAlpha(calculateAlpha());
     }
 
-    protected final int calculateAlpha() {
+    int calculateAlpha() {
         return mPauseAuth ? mAlpha : 255;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
index 2f025f6..f4993f4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java
@@ -16,10 +16,6 @@
 
 package com.android.systemui.biometrics;
 
-import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER;
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED;
-
 import android.annotation.NonNull;
 import android.graphics.PointF;
 import android.graphics.RectF;
@@ -68,18 +64,13 @@
 
     @Override
     protected void onViewAttached() {
-        mStatusBarStateController.addCallback(mStateListener);
-        mStateListener.onStateChanged(mStatusBarStateController.getState());
         mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener);
-
         mDumpManger.registerDumpable(getDumpTag(), this);
     }
 
     @Override
     protected void onViewDetached() {
-        mStatusBarStateController.removeCallback(mStateListener);
         mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener);
-
         mDumpManger.unregisterDumpable(getDumpTag());
     }
 
@@ -106,9 +97,7 @@
      * authentication.
      */
     boolean shouldPauseAuth() {
-        return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD)
-                || mStatusBarState == SHADE_LOCKED
-                || mStatusBarState == FULLSCREEN_USER_SWITCHER;
+        return mNotificationShadeExpanded;
     }
 
     /**
@@ -188,13 +177,4 @@
                     updatePauseAuth();
                 }
             };
-
-    private final StatusBarStateController.StateListener mStateListener =
-            new StatusBarStateController.StateListener() {
-                @Override
-                public void onStateChanged(int newState) {
-                    mStatusBarState = newState;
-                    updatePauseAuth();
-                }
-            };
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 7b5d7cb..2bdbf51 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -52,6 +52,7 @@
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.doze.DozeReceiver;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -90,6 +91,7 @@
     @NonNull private final DumpManager mDumpManager;
     @NonNull private final AuthRippleController mAuthRippleController;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
     // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
     // sensors, this, in addition to a lot of the code here, will be updated.
     @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps;
@@ -310,7 +312,8 @@
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull DumpManager dumpManager,
             @NonNull AuthRippleController authRippleController,
-            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor) {
+            @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
+            @NonNull KeyguardViewMediator keyguardViewMediator) {
         mContext = context;
         mInflater = inflater;
         // The fingerprint manager is queried for UDFPS before this class is constructed, so the
@@ -324,6 +327,7 @@
         mDumpManager = dumpManager;
         mAuthRippleController = authRippleController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+        mKeyguardViewMediator = keyguardViewMediator;
 
         mSensorProps = findFirstUdfps();
         // At least one UDFPS sensor exists
@@ -491,7 +495,8 @@
                         mKeyguardViewManager,
                         mKeyguardUpdateMonitor,
                         mFgExecutor,
-                        mDumpManager
+                        mDumpManager,
+                        mKeyguardViewMediator
                 );
             case IUdfpsOverlayController.REASON_AUTH_BP:
                 // note: empty controller, currently shows no visual affordance
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
index 4590182..fc906f2 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java
@@ -97,6 +97,11 @@
         }
     }
 
+    @Override
+    int calculateAlpha() {
+        return mPauseAuth ? 0 : 255;
+    }
+
     void onDozeAmountChanged(float linear, float eased) {
         mFingerprintDrawable.onDozeAmountChanged(linear, eased);
     }
@@ -153,6 +158,10 @@
         return mStatusBarState == StatusBarState.SHADE_LOCKED;
     }
 
+    boolean isHome() {
+        return mStatusBarState == StatusBarState.SHADE;
+    }
+
     /**
      * Animates out the bg protection circle behind the fp icon to unhighlight the icon.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
index 9d846fa..dc0c685 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.biometrics;
 
+import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
+
 import android.annotation.NonNull;
 import android.hardware.biometrics.BiometricSourceType;
 
@@ -24,7 +26,9 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -45,12 +49,15 @@
     @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
     @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @NonNull private final DelayableExecutor mExecutor;
+    @NonNull private final KeyguardViewMediator mKeyguardViewMediator;
 
     @Nullable private Runnable mCancelRunnable;
     private boolean mShowBouncer;
     private boolean mQsExpanded;
     private boolean mFaceDetectRunning;
     private boolean mHintShown;
+    private boolean mTransitioningFromHome;
+    private int mStatusBarState;
 
     protected UdfpsKeyguardViewController(
             @NonNull UdfpsKeyguardView view,
@@ -59,11 +66,13 @@
             @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
             @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
             @NonNull DelayableExecutor mainDelayableExecutor,
-            @NonNull DumpManager dumpManager) {
+            @NonNull DumpManager dumpManager,
+            @NonNull KeyguardViewMediator keyguardViewMediator) {
         super(view, statusBarStateController, statusBar, dumpManager);
         mKeyguardViewManager = statusBarKeyguardViewManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mExecutor = mainDelayableExecutor;
+        mKeyguardViewMediator = keyguardViewMediator;
     }
 
     @Override
@@ -94,6 +103,7 @@
         mStatusBarStateController.removeCallback(mStateListener);
         mAlternateAuthInterceptor.hideAlternateAuthBouncer();
         mKeyguardViewManager.setAlternateAuthInterceptor(null);
+        mTransitioningFromHome = false;
 
         if (mCancelRunnable != null) {
             mCancelRunnable.run();
@@ -106,17 +116,18 @@
         super.dump(fd, pw, args);
         pw.println("mShowBouncer=" + mShowBouncer);
         pw.println("mFaceDetectRunning=" + mFaceDetectRunning);
+        pw.println("mTransitioningFromHomeToKeyguard=" + mTransitioningFromHome);
     }
 
     /**
      * Overrides non-bouncer show logic in shouldPauseAuth to still auth.
      */
-    private void showBouncer(boolean forceShow) {
-        if (mShowBouncer == forceShow) {
+    private void showBouncer(boolean show) {
+        if (mShowBouncer == show) {
             return;
         }
 
-        mShowBouncer = forceShow;
+        mShowBouncer = show;
         updatePauseAuth();
         if (mShowBouncer) {
             mView.animateUdfpsBouncer();
@@ -128,18 +139,26 @@
     /**
      * Returns true if the fingerprint manager is running but we want to temporarily pause
      * authentication. On the keyguard, we may want to show udfps when the shade
-     * is expanded, so this can be overridden with the forceShow method.
+     * is expanded, so this can be overridden with the showBouncer method.
      */
     public boolean shouldPauseAuth() {
         if (mShowBouncer) {
             return false;
         }
 
+        if (mStatusBarState != KEYGUARD) {
+            return true;
+        }
+
+        if (mTransitioningFromHome && mKeyguardViewMediator.isAnimatingScreenOff()) {
+            return true;
+        }
+
         if (mQsExpanded) {
             return true;
         }
 
-        return super.shouldPauseAuth();
+        return false;
     }
 
     private void cancelDelayedHint() {
@@ -176,12 +195,25 @@
             new StatusBarStateController.StateListener() {
         @Override
         public void onDozeAmountChanged(float linear, float eased) {
-            mView.onDozeAmountChanged(linear, eased);
             if (linear != 0) showBouncer(false);
+            mView.onDozeAmountChanged(linear, eased);
+            if (linear == 1f) {
+                // transition has finished
+                mTransitioningFromHome = false;
+            }
+            updatePauseAuth();
+        }
+
+        @Override
+        public void onStatePreChange(int oldState, int newState) {
+            mTransitioningFromHome = oldState == StatusBarState.SHADE
+                    && newState == StatusBarState.KEYGUARD;
+            updatePauseAuth();
         }
 
         @Override
         public void onStateChanged(int statusBarState) {
+            mStatusBarState = statusBarState;
             mView.setStatusBarState(statusBarState);
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
index 96f2072..8c3ef68 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -44,8 +44,9 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dump.DumpManager
-import com.android.systemui.globalactions.GlobalActionsDialog
 import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED
 import com.android.systemui.util.concurrency.DelayableExecutor
 import java.io.FileDescriptor
 import java.io.PrintWriter
@@ -198,11 +199,11 @@
                 // When a component is uninstalled, allow seeding to happen again if the user
                 // reinstalls the app
                 val prefs = userStructure.userContext.getSharedPreferences(
-                    GlobalActionsDialog.PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
+                    PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
                 val completedSeedingPackageSet = prefs.getStringSet(
-                    GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
+                    PREFS_CONTROLS_SEEDING_COMPLETED, mutableSetOf<String>())
                 val servicePackageSet = serviceInfoSet.map { it.packageName }
-                prefs.edit().putStringSet(GlobalActionsDialog.PREFS_CONTROLS_SEEDING_COMPLETED,
+                prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
                     completedSeedingPackageSet.intersect(servicePackageSet)).apply()
 
                 var changed = false
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 7dd1d28..6f94943 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.controls.management
 
+import android.app.ActivityOptions
 import android.content.ComponentName
 import android.content.Intent
 import android.os.Bundle
@@ -34,7 +35,6 @@
 import com.android.systemui.controls.controller.StructureInfo
 import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.controls.ui.ControlsUiController
-import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.settings.CurrentUserTracker
 import com.android.systemui.util.LifecycleActivity
 import javax.inject.Inject
@@ -45,7 +45,6 @@
 class ControlsEditingActivity @Inject constructor(
     private val controller: ControlsControllerImpl,
     private val broadcastDispatcher: BroadcastDispatcher,
-    private val globalActionsComponent: GlobalActionsComponent,
     private val customIconCache: CustomIconCache,
     private val uiController: ControlsUiController
 ) : LifecycleActivity() {
@@ -62,7 +61,6 @@
     private lateinit var model: FavoritesModel
     private lateinit var subtitle: TextView
     private lateinit var saveButton: View
-    private var backToGlobalActions = true
 
     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
         private val startingUser = controller.currentUserId
@@ -86,11 +84,6 @@
             structure = it
         } ?: run(this::finish)
 
-        backToGlobalActions = intent.getBooleanExtra(
-            ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
-            true
-        )
-
         bindViews()
 
         bindButtons()
@@ -109,15 +102,6 @@
     }
 
     override fun onBackPressed() {
-        if (backToGlobalActions) {
-            globalActionsComponent.handleShowGlobalActionsMenu()
-        } else {
-            val i = Intent().apply {
-                component = ComponentName(applicationContext, ControlsActivity::class.java)
-                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-            }
-            startActivity(i)
-        }
         animateExitAndFinish()
     }
 
@@ -161,8 +145,12 @@
             setText(R.string.save)
             setOnClickListener {
                 saveFavorites()
+                startActivity(
+                    Intent(applicationContext, ControlsActivity::class.java),
+                    ActivityOptions
+                        .makeSceneTransitionAnimation(this@ControlsEditingActivity).toBundle()
+                )
                 animateExitAndFinish()
-                globalActionsComponent.handleShowGlobalActionsMenu()
             }
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 3099014..dca52a9 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -43,7 +43,6 @@
 import com.android.systemui.controls.ui.ControlsActivity
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.settings.CurrentUserTracker
 import com.android.systemui.util.LifecycleActivity
 import java.text.Collator
@@ -56,7 +55,6 @@
     private val controller: ControlsControllerImpl,
     private val listingController: ControlsListingController,
     private val broadcastDispatcher: BroadcastDispatcher,
-    private val globalActionsComponent: GlobalActionsComponent,
     private val uiController: ControlsUiController
 ) : LifecycleActivity() {
 
@@ -92,7 +90,6 @@
     private lateinit var comparator: Comparator<StructureContainer>
     private var cancelLoadRunnable: Runnable? = null
     private var isPagerLoaded = false
-    private var backToGlobalActions = true
 
     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
         private val startingUser = controller.currentUserId
@@ -133,11 +130,6 @@
         component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
         fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
 
-        backToGlobalActions = intent.getBooleanExtra(
-            ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
-            true
-        )
-
         bindViews()
     }
 
@@ -311,13 +303,6 @@
     private fun bindButtons() {
         otherAppsButton = requireViewById<Button>(R.id.other_apps).apply {
             setOnClickListener {
-                val i = Intent().apply {
-                    component = ComponentName(context, ControlsProviderSelectorActivity::class.java)
-                    putExtra(
-                        ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
-                        backToGlobalActions
-                    )
-                }
                 if (doneButton.isEnabled) {
                     // The user has made changes
                     Toast.makeText(
@@ -326,8 +311,11 @@
                             Toast.LENGTH_SHORT
                             ).show()
                 }
-                startActivity(i, ActivityOptions
-                    .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle())
+                startActivity(
+                    Intent(context, ControlsProviderSelectorActivity::class.java),
+                    ActivityOptions
+                        .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()
+                )
                 animateExitAndFinish()
             }
         }
@@ -349,15 +337,10 @@
     }
 
     private fun openControlsOrigin() {
-        if (backToGlobalActions) {
-            globalActionsComponent.handleShowGlobalActionsMenu()
-        } else {
-            val i = Intent().apply {
-                component = ComponentName(applicationContext, ControlsActivity::class.java)
-                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-            }
-            startActivity(i)
-        }
+        startActivity(
+            Intent(applicationContext, ControlsActivity::class.java),
+            ActivityOptions.makeSceneTransitionAnimation(this).toBundle()
+        )
     }
 
     override fun onPause() {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index fa1c41f..cba3dab 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -36,7 +36,6 @@
 import com.android.systemui.controls.ui.ControlsUiController
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.globalactions.GlobalActionsComponent
 import com.android.systemui.settings.CurrentUserTracker
 import com.android.systemui.util.LifecycleActivity
 import java.util.concurrent.Executor
@@ -50,7 +49,6 @@
     @Background private val backExecutor: Executor,
     private val listingController: ControlsListingController,
     private val controlsController: ControlsController,
-    private val globalActionsComponent: GlobalActionsComponent,
     private val broadcastDispatcher: BroadcastDispatcher,
     private val uiController: ControlsUiController
 ) : LifecycleActivity() {
@@ -59,7 +57,6 @@
         private const val TAG = "ControlsProviderSelectorActivity"
     }
 
-    private var backToGlobalActions = true
     private lateinit var recyclerView: RecyclerView
     private val currentUserTracker = object : CurrentUserTracker(broadcastDispatcher) {
         private val startingUser = listingController.currentUserId
@@ -105,23 +102,13 @@
             }
         }
         requireViewById<View>(R.id.done).visibility = View.GONE
-
-        backToGlobalActions = intent.getBooleanExtra(
-            ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
-            true
-        )
     }
 
     override fun onBackPressed() {
-        if (backToGlobalActions) {
-            globalActionsComponent.handleShowGlobalActionsMenu()
-        } else {
-            val i = Intent().apply {
-                component = ComponentName(applicationContext, ControlsActivity::class.java)
-                addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-            }
-            startActivity(i)
+        val i = Intent().apply {
+            component = ComponentName(applicationContext, ControlsActivity::class.java)
         }
+        startActivity(i, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
         animateExitAndFinish()
     }
 
@@ -169,10 +156,6 @@
                             listingController.getAppLabel(it))
                     putExtra(Intent.EXTRA_COMPONENT_NAME, it)
                     putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
-                    putExtra(
-                        ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
-                        backToGlobalActions
-                    )
                 }
                 startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
                 animateExitAndFinish()
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
index 0db15e8..8029ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinator.kt
@@ -26,7 +26,7 @@
 interface ControlActionCoordinator {
 
     // If launched from an Activity, continue within this stack
-    var activityContext: Context?
+    var activityContext: Context
 
     /**
      * Close any dialogs which may have been open
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 477c220..d3d6e03 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -63,7 +63,7 @@
     private var actionsInProgress = mutableSetOf<String>()
     private val isLocked: Boolean
         get() = !keyguardStateController.isUnlocked()
-    override var activityContext: Context? = null
+    override lateinit var activityContext: Context
 
     companion object {
         private const val RESPONSE_TIMEOUT_IN_MILLIS = 3000L
@@ -153,14 +153,9 @@
                 // pending actions will only run after the control state has been refreshed
                 pendingAction = action
             }
-            val wasLocked = isLocked
             activityStarter.dismissKeyguardThenExecute({
                 Log.d(ControlsUiController.TAG, "Device unlocked, invoking controls action")
-                if (wasLocked && activityContext == null) {
-                    globalActionsComponent.handleShowGlobalActionsMenu()
-                } else {
-                    action.invoke()
-                }
+                action.invoke()
                 true
             }, { pendingAction = null }, true /* afterKeyguardGone */)
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index a35b792..c241c08 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -41,6 +41,14 @@
 
         setContentView(R.layout.controls_fullscreen)
 
+        getLifecycle().addObserver(
+            ControlsAnimations.observerForAnimations(
+                requireViewById<ViewGroup>(R.id.control_detail_root),
+                window,
+                intent
+            )
+        )
+
         requireViewById<ViewGroup>(R.id.control_detail_root).apply {
             setOnApplyWindowInsetsListener {
                 v: View, insets: WindowInsets ->
@@ -61,7 +69,7 @@
 
         parent = requireViewById<ViewGroup>(R.id.global_actions_controls)
         parent.alpha = 0f
-        uiController.show(parent, { animateExitAndFinish() }, this)
+        uiController.show(parent, { finish() }, this)
     }
 
     override fun onResume() {
@@ -71,7 +79,7 @@
     }
 
     override fun onBackPressed() {
-        animateExitAndFinish()
+        finish()
     }
 
     override fun onStop() {
@@ -79,8 +87,4 @@
 
         uiController.hide()
     }
-
-    private fun animateExitAndFinish() {
-        ControlsAnimations.exitAnimation(parent, { finish() }).start()
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
index f86948e..ac13aad 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiController.kt
@@ -28,10 +28,9 @@
     companion object {
         public const val TAG = "ControlsUiController"
         public const val EXTRA_ANIMATE = "extra_animate"
-        public const val BACK_TO_GLOBAL_ACTIONS = "back_to_global_actions"
     }
 
-    fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context?)
+    fun show(parent: ViewGroup, onDismiss: Runnable, activityContext: Context)
     fun hide()
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index d08882b..954bfb3 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -19,6 +19,8 @@
 import android.animation.Animator
 import android.animation.AnimatorListenerAdapter
 import android.animation.ObjectAnimator
+import android.app.Activity
+import android.app.ActivityOptions
 import android.content.ComponentName
 import android.content.Context
 import android.content.Intent
@@ -129,6 +131,7 @@
     override val available: Boolean
         get() = controlsController.get().available
 
+    private lateinit var activityContext: Context
     private lateinit var listingCallback: ControlsListingController.ControlsListingCallback
 
     private fun createCallback(
@@ -153,11 +156,12 @@
     override fun show(
         parent: ViewGroup,
         onDismiss: Runnable,
-        activityContext: Context?
+        activityContext: Context
     ) {
         Log.d(ControlsUiController.TAG, "show()")
         this.parent = parent
         this.onDismiss = onDismiss
+        this.activityContext = activityContext
         hidden = false
         retainCache = false
 
@@ -198,7 +202,7 @@
                 controlViewsById.clear()
                 controlsById.clear()
 
-                show(parent, onDismiss, controlActionCoordinator.activityContext)
+                show(parent, onDismiss, activityContext)
                 val showAnim = ObjectAnimator.ofFloat(parent, "alpha", 0.0f, 1.0f)
                 showAnim.setInterpolator(DecelerateInterpolator(1.0f))
                 showAnim.setDuration(FADE_IN_MILLIS)
@@ -220,7 +224,7 @@
         inflater.inflate(R.layout.controls_no_favorites, parent, true)
 
         val viewGroup = parent.requireViewById(R.id.controls_no_favorites_group) as ViewGroup
-        viewGroup.setOnClickListener { v: View -> startProviderSelectorActivity(v.context) }
+        viewGroup.setOnClickListener { _: View -> startProviderSelectorActivity() }
 
         val subtitle = parent.requireViewById<TextView>(R.id.controls_subtitle)
         subtitle.setText(context.resources.getString(R.string.quick_controls_subtitle))
@@ -234,20 +238,18 @@
         }
     }
 
-    private fun startFavoritingActivity(context: Context, si: StructureInfo) {
-        startTargetedActivity(context, si, ControlsFavoritingActivity::class.java)
+    private fun startFavoritingActivity(si: StructureInfo) {
+        startTargetedActivity(si, ControlsFavoritingActivity::class.java)
     }
 
-    private fun startEditingActivity(context: Context, si: StructureInfo) {
-        startTargetedActivity(context, si, ControlsEditingActivity::class.java)
+    private fun startEditingActivity(si: StructureInfo) {
+        startTargetedActivity(si, ControlsEditingActivity::class.java)
     }
 
-    private fun startTargetedActivity(context: Context, si: StructureInfo, klazz: Class<*>) {
-        val i = Intent(context, klazz).apply {
-            addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-        }
+    private fun startTargetedActivity(si: StructureInfo, klazz: Class<*>) {
+        val i = Intent(activityContext, klazz)
         putIntentExtras(i, si)
-        startActivity(context, i)
+        startActivity(i)
 
         retainCache = true
     }
@@ -261,27 +263,22 @@
         }
     }
 
-    private fun startProviderSelectorActivity(context: Context) {
-        val i = Intent(context, ControlsProviderSelectorActivity::class.java).apply {
-            addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-        }
-        startActivity(context, i)
+    private fun startProviderSelectorActivity() {
+        startActivity(Intent(activityContext, ControlsProviderSelectorActivity::class.java))
     }
 
-    private fun startActivity(context: Context, intent: Intent) {
+    private fun startActivity(intent: Intent) {
         // Force animations when transitioning from a dialog to an activity
         intent.putExtra(ControlsUiController.EXTRA_ANIMATE, true)
-        intent.putExtra(
-            ControlsUiController.BACK_TO_GLOBAL_ACTIONS,
-            controlActionCoordinator.activityContext == null
-        )
-        onDismiss.run()
 
-        activityStarter.dismissKeyguardThenExecute({
-            shadeController.collapsePanel(false)
-            context.startActivity(intent)
-            true
-        }, null, true)
+        if (keyguardStateController.isUnlocked()) {
+            activityContext.startActivity(
+                intent,
+                ActivityOptions.makeSceneTransitionAnimation(activityContext as Activity).toBundle()
+            )
+        } else {
+            activityStarter.postStartActivityDismissingKeyguard(intent, 0 /* delay */)
+        }
     }
 
     private fun showControlsView(items: List<SelectionItem>) {
@@ -328,9 +325,9 @@
                         ) {
                             when (pos) {
                                 // 0: Add Control
-                                0 -> startFavoritingActivity(view.context, selectedStructure)
+                                0 -> startFavoritingActivity(selectedStructure)
                                 // 1: Edit controls
-                                1 -> startEditingActivity(view.context, selectedStructure)
+                                1 -> startEditingActivity(selectedStructure)
                             }
                             dismiss()
                         }
@@ -399,15 +396,9 @@
         val inflater = LayoutInflater.from(context)
         inflater.inflate(R.layout.controls_with_favorites, parent, true)
 
-        if (controlActionCoordinator.activityContext == null) {
-            parent.requireViewById<View>(R.id.controls_spacer).apply {
-                visibility = View.VISIBLE
-            }
-        } else {
-            parent.requireViewById<ImageView>(R.id.controls_close).apply {
-                setOnClickListener { _: View -> onDismiss.run() }
-                visibility = View.VISIBLE
-            }
+        parent.requireViewById<ImageView>(R.id.controls_close).apply {
+            setOnClickListener { _: View -> onDismiss.run() }
+            visibility = View.VISIBLE
         }
 
         val maxColumns = findMaxColumns()
@@ -522,8 +513,6 @@
     override fun hide() {
         hidden = true
 
-        controlActionCoordinator.activityContext = null
-
         closeDialogs(true)
         controlsController.get().unsubscribe()
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 34d94d8..ab1de95 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -17,8 +17,6 @@
 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
 
 import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
-import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE;
-import static com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE_AFTER_UNLOCK;
 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_GLOBAL_ACTIONS_SHOWING;
 
 import android.animation.Animator;
@@ -30,10 +28,8 @@
 import android.app.PendingIntent;
 import android.app.admin.DevicePolicyManager;
 import android.app.trust.TrustManager;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.DialogInterface;
-import android.content.SharedPreferences;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.graphics.drawable.Drawable;
@@ -47,7 +43,6 @@
 import android.transition.AutoTransition;
 import android.transition.TransitionManager;
 import android.transition.TransitionSet;
-import android.util.Log;
 import android.view.IWindowManager;
 import android.view.View;
 import android.view.ViewGroup;
@@ -69,18 +64,12 @@
 import com.android.systemui.Interpolators;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.controls.ControlsServiceInfo;
-import com.android.systemui.controls.controller.ControlsController;
-import com.android.systemui.controls.dagger.ControlsComponent;
-import com.android.systemui.controls.management.ControlsAnimations;
-import com.android.systemui.controls.ui.ControlsUiController;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions.GlobalActionsManager;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -91,12 +80,6 @@
 import com.android.systemui.util.settings.GlobalSettings;
 import com.android.systemui.util.settings.SecureSettings;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
 import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
@@ -105,7 +88,7 @@
 /**
  * Helper to show the global actions dialog.  Each item is an {@link Action} that may show depending
  * on whether the keyguard is showing, and whether the device is provisioned.
- * This version includes wallet and controls.
+ * This version includes wallet.
  */
 public class GlobalActionsDialog extends GlobalActionsDialogLite
         implements DialogInterface.OnDismissListener,
@@ -116,10 +99,6 @@
 
     private static final String TAG = "GlobalActionsDialog";
 
-    public static final String PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted";
-    public static final String PREFS_CONTROLS_FILE = "controls_prefs";
-    private static final int SEEDING_MAX = 2;
-
     private final LockPatternUtils mLockPatternUtils;
     private final KeyguardStateController mKeyguardStateController;
     private final NotificationShadeDepthController mDepthController;
@@ -129,13 +108,9 @@
     private final IStatusBarService mStatusBarService;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private GlobalActionsPanelPlugin mWalletPlugin;
-    private Optional<ControlsUiController> mControlsUiControllerOptional;
-    private List<ControlsServiceInfo> mControlsServiceInfos = new ArrayList<>();
-    private ControlsComponent mControlsComponent;
-    private Optional<ControlsController> mControlsControllerOptional;
-    private UserContextProvider mUserContextProvider;
+
     @VisibleForTesting
-    boolean mShowLockScreenCardsAndControls = false;
+    boolean mShowLockScreenCards = false;
 
     /**
      * @param context everything needs a context :(
@@ -158,9 +133,7 @@
             IWindowManager iWindowManager,
             @Background Executor backgroundExecutor,
             UiEventLogger uiEventLogger,
-            RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler,
-            ControlsComponent controlsComponent,
-            UserContextProvider userContextProvider) {
+            RingerModeTracker ringerModeTracker, SysUiState sysUiState, @Main Handler handler) {
 
         super(context, windowManagerFuncs,
                 audioManager, iDreamManager,
@@ -186,11 +159,7 @@
         mSysuiColorExtractor = colorExtractor;
         mStatusBarService = statusBarService;
         mNotificationShadeWindowController = notificationShadeWindowController;
-        mControlsComponent = controlsComponent;
-        mControlsUiControllerOptional = controlsComponent.getControlsUiController();
-        mControlsControllerOptional = controlsComponent.getControlsController();
         mSysUiState = sysUiState;
-        mUserContextProvider = userContextProvider;
         mActivityStarter = activityStarter;
         keyguardStateController.addCallback(new KeyguardStateController.Callback() {
             @Override
@@ -201,10 +170,7 @@
                     if (dialog.mWalletViewController != null) {
                         dialog.mWalletViewController.onDeviceLockStateChanged(!unlocked);
                     }
-                    if (!dialog.isShowingControls()
-                            && mControlsComponent.getVisibility() == AVAILABLE) {
-                        dialog.showControls(mControlsUiControllerOptional.get());
-                    }
+
                     if (unlocked) {
                         dialog.hideLockMessage();
                     }
@@ -212,25 +178,7 @@
             }
         });
 
-        if (mControlsComponent.getControlsListingController().isPresent()) {
-            mControlsComponent.getControlsListingController().get()
-                    .addCallback(list -> {
-                        mControlsServiceInfos = list;
-                        // This callback may occur after the dialog has been shown. If so, add
-                        // controls into the already visible space or show the lock msg if needed.
-                        if (mDialog != null) {
-                            ActionsDialog dialog = (ActionsDialog) mDialog;
-                            if (!dialog.isShowingControls()
-                                    && mControlsComponent.getVisibility() == AVAILABLE) {
-                                dialog.showControls(mControlsUiControllerOptional.get());
-                            } else if (shouldShowLockMessage(dialog)) {
-                                dialog.showLockMessage();
-                            }
-                        }
-                    });
-        }
-
-        // Listen for changes to show controls on the power menu while locked
+        // Listen for changes to show pay on the power menu while locked
         onPowerMenuLockScreenSettingsChanged();
         mGlobalSettings.registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT),
@@ -244,65 +192,6 @@
     }
 
     /**
-     * See if any available control service providers match one of the preferred components. If
-     * they do, and there are no current favorites for that component, query the preferred
-     * component for a limited number of suggested controls.
-     */
-    private void seedFavorites() {
-        if (!mControlsControllerOptional.isPresent()
-                || mControlsServiceInfos.isEmpty()) {
-            return;
-        }
-
-        String[] preferredControlsPackages = getContext().getResources()
-                .getStringArray(com.android.systemui.R.array.config_controlsPreferredPackages);
-
-        SharedPreferences prefs = mUserContextProvider.getUserContext()
-                .getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE);
-        Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
-                Collections.emptySet());
-
-        List<ComponentName> componentsToSeed = new ArrayList<>();
-        for (int i = 0; i < Math.min(SEEDING_MAX, preferredControlsPackages.length); i++) {
-            String pkg = preferredControlsPackages[i];
-            for (ControlsServiceInfo info : mControlsServiceInfos) {
-                if (!pkg.equals(info.componentName.getPackageName())) continue;
-                if (seededPackages.contains(pkg)) {
-                    break;
-                } else if (mControlsControllerOptional.get()
-                        .countFavoritesForComponent(info.componentName) > 0) {
-                    // When there are existing controls but no saved preference, assume it
-                    // is out of sync, perhaps through a device restore, and update the
-                    // preference
-                    addPackageToSeededSet(prefs, pkg);
-                    break;
-                }
-                componentsToSeed.add(info.componentName);
-                break;
-            }
-        }
-
-        if (componentsToSeed.isEmpty()) return;
-
-        mControlsControllerOptional.get().seedFavoritesForComponents(
-                componentsToSeed,
-                (response) -> {
-                    Log.d(TAG, "Controls seeded: " + response);
-                    if (response.getAccepted()) {
-                        addPackageToSeededSet(prefs, response.getPackageName());
-                    }
-                });
-    }
-
-    private void addPackageToSeededSet(SharedPreferences prefs, String pkg) {
-        Set<String> seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED,
-                Collections.emptySet());
-        Set<String> updatedPkgs = new HashSet<>(seededPackages);
-        updatedPkgs.add(pkg);
-        prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, updatedPkgs).apply();
-    }
-
-    /**
      * Show the global actions dialog (creating if necessary)
      *
      * @param keyguardShowing True if keyguard is showing
@@ -313,12 +202,6 @@
         super.showOrHideDialog(keyguardShowing, isDeviceProvisioned);
     }
 
-    @Override
-    protected void handleShow() {
-        seedFavorites();
-        super.handleShow();
-    }
-
     /**
      * Returns the maximum number of power menu items to show based on which GlobalActions
      * layout is being used.
@@ -340,14 +223,9 @@
         initDialogItems();
 
         mDepthController.setShowingHomeControls(true);
-        ControlsUiController uiController = null;
-        if (mControlsComponent.getVisibility() == AVAILABLE) {
-            uiController = mControlsUiControllerOptional.get();
-        }
         ActionsDialog dialog = new ActionsDialog(getContext(), mAdapter, mOverflowAdapter,
                 this::getWalletViewController, mDepthController, mSysuiColorExtractor,
                 mStatusBarService, mNotificationShadeWindowController,
-                controlsAvailable(), uiController,
                 mSysUiState, this::onRotate, isKeyguardShowing(), mPowerAdapter);
 
         if (shouldShowLockMessage(dialog)) {
@@ -407,10 +285,6 @@
         private final Provider<GlobalActionsPanelPlugin.PanelViewController> mWalletFactory;
         @Nullable private GlobalActionsPanelPlugin.PanelViewController mWalletViewController;
         private ResetOrientationData mResetOrientationData;
-        private final boolean mControlsAvailable;
-
-        private ControlsUiController mControlsUiController;
-        private ViewGroup mControlsView;
         @VisibleForTesting ViewGroup mLockMessageContainer;
         private TextView mLockMessage;
 
@@ -419,15 +293,12 @@
                 NotificationShadeDepthController depthController,
                 SysuiColorExtractor sysuiColorExtractor, IStatusBarService statusBarService,
                 NotificationShadeWindowController notificationShadeWindowController,
-                boolean controlsAvailable, @Nullable ControlsUiController controlsUiController,
                 SysUiState sysuiState, Runnable onRotateCallback, boolean keyguardShowing,
                 MyPowerOptionsAdapter powerAdapter) {
             super(context, com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions,
                     adapter, overflowAdapter, depthController, sysuiColorExtractor,
                     statusBarService, notificationShadeWindowController, sysuiState,
                     onRotateCallback, keyguardShowing, powerAdapter);
-            mControlsAvailable = controlsAvailable;
-            mControlsUiController = controlsUiController;
             mWalletFactory = walletFactory;
 
             // Update window attributes
@@ -448,16 +319,6 @@
             initializeLayout();
         }
 
-        private boolean isShowingControls() {
-            return mControlsUiController != null;
-        }
-
-        private void showControls(ControlsUiController controller) {
-            mControlsUiController = controller;
-            mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
-                    null /* activityContext */);
-        }
-
         private boolean isWalletViewAvailable() {
             return mWalletViewController != null && mWalletViewController.getPanelContent() != null;
         }
@@ -524,10 +385,8 @@
                     new FrameLayout.LayoutParams(
                             FrameLayout.LayoutParams.MATCH_PARENT,
                             FrameLayout.LayoutParams.MATCH_PARENT);
-            if (!mControlsAvailable) {
-                panelParams.topMargin = mContext.getResources().getDimensionPixelSize(
-                        com.android.systemui.R.dimen.global_actions_wallet_top_margin);
-            }
+            panelParams.topMargin = mContext.getResources().getDimensionPixelSize(
+                    com.android.systemui.R.dimen.global_actions_wallet_top_margin);
             View walletView = mWalletViewController.getPanelContent();
             panelContainer.addView(walletView, panelParams);
             // Smooth transitions when wallet is resized, which can happen when a card is added
@@ -554,7 +413,6 @@
         @Override
         protected void initializeLayout() {
             super.initializeLayout();
-            mControlsView = findViewById(com.android.systemui.R.id.global_actions_controls);
             mLockMessageContainer = requireViewById(
                     com.android.systemui.R.id.global_actions_lock_message_container);
             mLockMessage = requireViewById(com.android.systemui.R.id.global_actions_lock_message);
@@ -577,10 +435,6 @@
                         windowInsets.getStableInsetBottom());
                 return WindowInsets.CONSUMED;
             });
-            if (mControlsUiController != null) {
-                mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
-                        null /* activityContext */);
-            }
 
             mBackgroundDrawable.setAlpha(0);
             float xOffset = mGlobalActionsLayout.getAnimationOffsetX();
@@ -609,20 +463,11 @@
         @Override
         protected void dismissInternal() {
             super.dismissInternal();
-            if (mControlsUiController != null) mControlsUiController.closeDialogs(false);
-        }
-
-        private void dismissForControlsActivity() {
-            dismissWithAnimation(() -> {
-                ViewGroup root = (ViewGroup) mGlobalActionsLayout.getParent();
-                ControlsAnimations.exitAnimation(root, this::completeDismiss).start();
-            });
         }
 
         @Override
         protected void completeDismiss() {
             dismissWallet();
-            if (mControlsUiController != null) mControlsUiController.hide();
             resetOrientation();
             super.completeDismiss();
         }
@@ -647,15 +492,7 @@
         public void refreshDialog() {
             // ensure dropdown menus are dismissed before re-initializing the dialog
             dismissWallet();
-            if (mControlsUiController != null) {
-                mControlsUiController.hide();
-            }
-
             super.refreshDialog();
-            if (mControlsUiController != null) {
-                mControlsUiController.show(mControlsView, this::dismissForControlsActivity,
-                        null /* activityContext */);
-            }
         }
 
         void hideLockMessage() {
@@ -699,15 +536,8 @@
         return isPanelDebugModeEnabled(context);
     }
 
-    private boolean controlsAvailable() {
-        return isDeviceProvisioned()
-                && mControlsComponent.isEnabled()
-                && !mControlsServiceInfos.isEmpty();
-    }
-
     private boolean shouldShowLockMessage(ActionsDialog dialog) {
-        return mControlsComponent.getVisibility() == AVAILABLE_AFTER_UNLOCK
-                || isWalletAvailableAfterUnlock(dialog);
+        return isWalletAvailableAfterUnlock(dialog);
     }
 
     // Temporary while we move items out of the power menu
@@ -715,12 +545,12 @@
         boolean isLockedAfterBoot = mLockPatternUtils.getStrongAuthForUser(getCurrentUser().id)
                 == STRONG_AUTH_REQUIRED_AFTER_BOOT;
         return !mKeyguardStateController.isUnlocked()
-                && (!mShowLockScreenCardsAndControls || isLockedAfterBoot)
+                && (!mShowLockScreenCards || isLockedAfterBoot)
                 && dialog.isWalletViewAvailable();
     }
 
     private void onPowerMenuLockScreenSettingsChanged() {
-        mShowLockScreenCardsAndControls = mSecureSettings.getInt(
+        mShowLockScreenCards = mSecureSettings.getInt(
                 Settings.Secure.POWER_MENU_LOCKED_SHOW_CONTENT, 0) != 0;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index faac196..525bad8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -23,7 +23,6 @@
 import android.os.Looper;
 import android.os.UserHandle;
 import android.os.UserManager;
-import android.provider.Settings;
 import android.provider.Settings.Secure;
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
@@ -57,6 +56,7 @@
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.tuner.TunerService.Tunable;
 import com.android.systemui.util.leak.GarbageMonitor;
+import com.android.systemui.util.settings.SecureSettings;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -79,6 +79,7 @@
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
     private static final int MAX_QS_INSTANCE_ID = 1 << 20;
 
+    public static final int POSITION_AT_END = -1;
     public static final String TILES_SETTING = Secure.QS_TILES;
 
     private final Context mContext;
@@ -101,6 +102,7 @@
     private final Optional<StatusBar> mStatusBarOptional;
     private Context mUserContext;
     private UserTracker mUserTracker;
+    private SecureSettings mSecureSettings;
 
     @Inject
     public QSTileHost(Context context,
@@ -116,7 +118,8 @@
             Optional<StatusBar> statusBarOptional,
             QSLogger qsLogger,
             UiEventLogger uiEventLogger,
-            UserTracker userTracker) {
+            UserTracker userTracker,
+            SecureSettings secureSettings) {
         mIconController = iconController;
         mContext = context;
         mUserContext = context;
@@ -135,6 +138,7 @@
         pluginManager.addPluginListener(this, QSFactory.class, true);
         mDumpManager.registerDumpable(TAG, this);
         mUserTracker = userTracker;
+        mSecureSettings = secureSettings;
 
         mainHandler.post(() -> {
             // This is technically a hack to avoid circular dependency of
@@ -343,19 +347,43 @@
         if (mAutoTiles != null) mAutoTiles.unmarkTileAsAutoAdded(spec);
     }
 
+    /**
+     * Add a tile to the end
+     *
+     * @param spec string matching a pre-defined tilespec
+     */
     public void addTile(String spec) {
-        changeTileSpecs(tileSpecs-> !tileSpecs.contains(spec) && tileSpecs.add(spec));
+        addTile(spec, POSITION_AT_END);
     }
 
-    private void saveTilesToSettings(List<String> tileSpecs) {
-        Settings.Secure.putStringForUser(mContext.getContentResolver(), TILES_SETTING,
-                TextUtils.join(",", tileSpecs), null /* tag */,
-                false /* default */, mCurrentUser, true /* overrideable by restore */);
+    /**
+     * Add a tile into the requested spot, or at the end if the position is greater than the number
+     * of tiles.
+     * @param spec string matching a pre-defined tilespec
+     * @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
+     */
+    public void addTile(String spec, int requestPosition) {
+        changeTileSpecs(tileSpecs -> {
+            if (tileSpecs.contains(spec)) return false;
+
+            int size = tileSpecs.size();
+            if (requestPosition == POSITION_AT_END || requestPosition >= size) {
+                tileSpecs.add(spec);
+            } else {
+                tileSpecs.add(requestPosition, spec);
+            }
+            return true;
+        });
+    }
+
+    void saveTilesToSettings(List<String> tileSpecs) {
+        mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
+                null /* tag */, false /* default */, mCurrentUser,
+                true /* overrideable by restore */);
     }
 
     private void changeTileSpecs(Predicate<List<String>> changeFunction) {
-        final String setting = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                TILES_SETTING, mCurrentUser);
+        final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
         final List<String> tileSpecs = loadTileSpecs(mContext, setting);
         if (changeFunction.test(tileSpecs)) {
             saveTilesToSettings(tileSpecs);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index d41bd7a..75a7e8e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -32,6 +32,7 @@
 import com.android.systemui.statusbar.phone.ManagedProfileController;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DeviceControlsController;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -61,6 +62,7 @@
             NightDisplayListener nightDisplayListener,
             CastController castController,
             ReduceBrightColorsController reduceBrightColorsController,
+            DeviceControlsController deviceControlsController,
             @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
         AutoTileManager manager = new AutoTileManager(
                 context,
@@ -74,6 +76,7 @@
                 nightDisplayListener,
                 castController,
                 reduceBrightColorsController,
+                deviceControlsController,
                 isReduceBrightColorsAvailable
         );
         manager.init();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index c7ed89b..3d5a709 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -43,7 +43,7 @@
     protected TextView mLabel;
     protected TextView mSecondLine;
     private ImageView mPadLock;
-    private int mState;
+    protected int mState;
     protected ViewGroup mLabelContainer;
     private View mExpandIndicator;
     private View mExpandSpace;
@@ -133,14 +133,7 @@
     protected void handleStateChanged(QSTile.State state) {
         super.handleStateChanged(state);
         if (!Objects.equals(mLabel.getText(), state.label) || mState != state.state) {
-            ColorStateList labelColor;
-            if (state.state == Tile.STATE_ACTIVE) {
-                labelColor = mColorLabelActive;
-            } else if (state.state == Tile.STATE_INACTIVE) {
-                labelColor = mColorLabelInactive;
-            } else {
-                labelColor = mColorLabelUnavailable;
-            }
+            ColorStateList labelColor = getLabelColor(state.state);
             changeLabelColor(labelColor);
             mState = state.state;
             mLabel.setText(state.label);
@@ -163,6 +156,15 @@
         mPadLock.setVisibility(state.disabledByPolicy ? View.VISIBLE : View.GONE);
     }
 
+    protected final ColorStateList getLabelColor(int state) {
+        if (state == Tile.STATE_ACTIVE) {
+            return mColorLabelActive;
+        } else if (state == Tile.STATE_INACTIVE) {
+            return mColorLabelInactive;
+        }
+        return mColorLabelUnavailable;
+    }
+
     protected void changeLabelColor(ColorStateList color) {
         mLabel.setTextColor(color);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
index 188e89e..7a8b2c6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewHorizontal.kt
@@ -24,6 +24,8 @@
 import android.graphics.drawable.RippleDrawable
 import android.service.quicksettings.Tile.STATE_ACTIVE
 import android.view.Gravity
+import android.view.View
+import android.widget.ImageView
 import android.widget.LinearLayout
 import android.widget.RelativeLayout
 import com.android.systemui.R
@@ -41,6 +43,7 @@
     private var paintColor = Color.WHITE
     private var paintAnimator: ValueAnimator? = null
     private var labelAnimator: ValueAnimator? = null
+    private var mSideView: ImageView = ImageView(mContext)
     override var heightOverride: Int = HeightOverrideable.NO_OVERRIDE
 
     init {
@@ -56,7 +59,16 @@
         val iconSize = context.resources.getDimensionPixelSize(R.dimen.qs_icon_size)
         addView(mIcon, 0, LayoutParams(iconSize, iconSize))
 
+        mSideView.visibility = View.GONE
+        addView(
+                mSideView,
+                -1,
+                LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT).apply {
+                    gravity = Gravity.CENTER_VERTICAL
+        })
+
         mColorLabelActive = ColorStateList.valueOf(getColorForState(getContext(), STATE_ACTIVE))
+        changeLabelColor(getLabelColor(mState)) // Matches the default state of the tile
     }
 
     override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
@@ -121,14 +133,13 @@
         if (allowAnimations) {
             animateBackground(newColor)
         } else {
-            if (newColor != paintColor) {
-                clearBackgroundAnimator()
-                colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
-                    paintColor = newColor
-                }
+            clearBackgroundAnimator()
+            colorBackgroundDrawable?.setTintList(ColorStateList.valueOf(newColor))?.also {
                 paintColor = newColor
             }
+            paintColor = newColor
         }
+        loadSideViewDrawableIfNecessary(state)
     }
 
     private fun animateBackground(newBackgroundColor: Int) {
@@ -181,5 +192,21 @@
         labelAnimator?.cancel()?.also { labelAnimator = null }
     }
 
+    private fun loadSideViewDrawableIfNecessary(state: QSTile.State) {
+        if (state.sideViewDrawable != null) {
+            (mSideView.layoutParams as MarginLayoutParams).apply {
+                marginStart =
+                        context.resources.getDimensionPixelSize(R.dimen.qs_label_container_margin)
+            }
+            mSideView.setImageDrawable(state.sideViewDrawable)
+            mSideView.visibility = View.VISIBLE
+            mSideView.adjustViewBounds = true
+            mSideView.scaleType = ImageView.ScaleType.FIT_CENTER
+        } else {
+            mSideView.setImageDrawable(null)
+            mSideView.visibility = GONE
+        }
+    }
+
     override fun handleExpand(dualTarget: Boolean) {}
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
index a74a50e..a2c7633 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DeviceControlsTile.kt
@@ -39,8 +39,6 @@
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.logging.QSLogger
 import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.statusbar.FeatureFlags
-import com.android.systemui.util.settings.GlobalSettings
 import java.util.concurrent.atomic.AtomicBoolean
 import javax.inject.Inject
 
@@ -53,9 +51,7 @@
     statusBarStateController: StatusBarStateController,
     activityStarter: ActivityStarter,
     qsLogger: QSLogger,
-    private val controlsComponent: ControlsComponent,
-    private val featureFlags: FeatureFlags,
-    globalSettings: GlobalSettings
+    private val controlsComponent: ControlsComponent
 ) : QSTileImpl<QSTile.State>(
         host,
         backgroundLooper,
@@ -67,11 +63,6 @@
         qsLogger
 ) {
 
-    companion object {
-        const val SETTINGS_FLAG = "controls_lockscreen"
-    }
-
-    private val controlsLockscreen = globalSettings.getInt(SETTINGS_FLAG, 0) != 0
     private var hasControlsApps = AtomicBoolean(false)
     private val intent = Intent(Settings.ACTION_DEVICE_CONTROLS_SETTINGS)
 
@@ -92,9 +83,7 @@
     }
 
     override fun isAvailable(): Boolean {
-        return featureFlags.isKeyguardLayoutEnabled &&
-                controlsLockscreen &&
-                controlsComponent.getControlsController().isPresent
+        return controlsComponent.getControlsController().isPresent
     }
 
     override fun newTileState(): QSTile.State {
@@ -114,7 +103,7 @@
                 val i = Intent().apply {
                     component = ComponentName(mContext, ControlsActivity::class.java)
                     addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
-                    putExtra(ControlsUiController.BACK_TO_GLOBAL_ACTIONS, false)
+                    putExtra(ControlsUiController.EXTRA_ANIMATE, true)
                 }
                 mContext.startActivity(i)
             }
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 60c5d1c..032e47e 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QuickAccessWalletTile.java
@@ -20,11 +20,20 @@
 
 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;
 import android.service.quicksettings.Tile;
+import android.util.Log;
 
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Background;
@@ -40,20 +49,29 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.settings.SecureSettings;
 
+import java.util.List;
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 
 /** Quick settings tile: Quick access wallet **/
 public class QuickAccessWalletTile extends QSTileImpl<QSTile.State> {
 
+    private static final String TAG = "QuickAccessWalletTile";
     private static final String FEATURE_CHROME_OS = "org.chromium.arc";
+
     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 FeatureFlags mFeatureFlags;
 
+    @VisibleForTesting Drawable mCardViewDrawable;
+
     @Inject
     public QuickAccessWalletTile(
             QSHost host,
@@ -68,6 +86,7 @@
             KeyguardStateController keyguardStateController,
             PackageManager packageManager,
             SecureSettings secureSettings,
+            @Background Executor executor,
             FeatureFlags featureFlags) {
         super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
@@ -75,6 +94,7 @@
         mKeyguardStateController = keyguardStateController;
         mPackageManager = packageManager;
         mSecureSettings = secureSettings;
+        mExecutor = executor;
         mFeatureFlags = featureFlags;
     }
 
@@ -87,6 +107,14 @@
     }
 
     @Override
+    protected void handleSetListening(boolean listening) {
+        super.handleSetListening(listening);
+        if (listening) {
+            queryWalletCards();
+        }
+    }
+
+    @Override
     protected void handleClick() {
         mActivityStarter.postStartActivityDismissingKeyguard(
                 mQuickAccessWalletClient.createWalletIntent(), /* delay= */ 0);
@@ -108,6 +136,7 @@
         } else {
             state.state = Tile.STATE_UNAVAILABLE;
         }
+        state.sideViewDrawable = mCardViewDrawable;
     }
 
     @Override
@@ -133,4 +162,40 @@
         CharSequence qawLabel = mQuickAccessWalletClient.getServiceLabel();
         return qawLabel == null ? mLabel : qawLabel;
     }
+
+    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= */ 2);
+        mQuickAccessWalletClient.getWalletCards(mExecutor, request, mCardRetriever);
+    }
+
+    private class WalletCardRetriever implements
+            QuickAccessWalletClient.OnWalletCardsRetrievedCallback {
+
+        @Override
+        public void onWalletCardsRetrieved(@NonNull GetWalletCardsResponse response) {
+            Log.i(TAG, "Successfully retrieved wallet cards.");
+            List<WalletCard> cards = response.getWalletCards();
+            if (cards.isEmpty()) {
+                Log.d(TAG, "No wallet cards exist.");
+                mCardViewDrawable = null;
+                refreshState();
+                return;
+            }
+            mCardViewDrawable = cards.get(0).getCardImage().loadDrawable(mContext);
+            refreshState();
+        }
+
+        @Override
+        public void onWalletCardRetrievalError(@NonNull GetWalletCardsError error) {
+            Log.w(TAG, "Error retrieve wallet cards");
+            mCardViewDrawable = null;
+            refreshState();
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index 04d1996..9ce0eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -411,19 +411,22 @@
         float imageRatio = bounds.width() / (float) bounds.height();
         int previewWidth = mPreview.getWidth() - mPreview.getPaddingLeft()
                 - mPreview.getPaddingRight();
-        float viewRatio = previewWidth / (float) mPreview.getHeight();
+        int previewHeight = mPreview.getHeight() - mPreview.getPaddingTop()
+                - mPreview.getPaddingBottom();
+        float viewRatio = previewWidth / (float) previewHeight;
 
         if (imageRatio > viewRatio) {
             // Image is full width and height is constrained, compute extra padding to inform
             // CropView
-            float imageHeight = mPreview.getHeight() * viewRatio / imageRatio;
-            int extraPadding = (int) (mPreview.getHeight() - imageHeight) / 2;
-            mCropView.setExtraPadding(extraPadding, extraPadding);
+            float imageHeight = previewHeight * viewRatio / imageRatio;
+            int extraPadding = (int) (previewHeight - imageHeight) / 2;
+            mCropView.setExtraPadding(extraPadding + mPreview.getPaddingTop(),
+                    extraPadding + mPreview.getPaddingBottom());
             mCropView.setImageWidth(previewWidth);
         } else {
             // Image is full height
-            mCropView.setExtraPadding(0, 0);
-            mCropView.setImageWidth((int) (mPreview.getHeight() * imageRatio));
+            mCropView.setExtraPadding(mPreview.getPaddingTop(),  mPreview.getPaddingBottom());
+            mCropView.setImageWidth((int) (previewHeight * imageRatio));
         }
 
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index bdb3926..357256c 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -58,10 +58,9 @@
     private static final int SLIDER_ANIMATION_DURATION = 3000;
 
     private static final int MSG_UPDATE_SLIDER = 1;
-    private static final int MSG_SET_CHECKED = 2;
-    private static final int MSG_ATTACH_LISTENER = 3;
-    private static final int MSG_DETACH_LISTENER = 4;
-    private static final int MSG_VR_MODE_CHANGED = 5;
+    private static final int MSG_ATTACH_LISTENER = 2;
+    private static final int MSG_DETACH_LISTENER = 3;
+    private static final int MSG_VR_MODE_CHANGED = 4;
 
     private static final Uri BRIGHTNESS_MODE_URI =
             Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS_MODE);
@@ -80,7 +79,6 @@
     private final int mDisplayId;
     private final Context mContext;
     private final ToggleSlider mControl;
-    private final boolean mAutomaticAvailable;
     private final DisplayManager mDisplayManager;
     private final CurrentUserTracker mUserTracker;
     private final IVrManager mVrManager;
@@ -219,16 +217,12 @@
     private final Runnable mUpdateModeRunnable = new Runnable() {
         @Override
         public void run() {
-            if (mAutomaticAvailable) {
-                int automatic;
-                automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
-                        Settings.System.SCREEN_BRIGHTNESS_MODE,
-                        Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
-                        UserHandle.USER_CURRENT);
-                mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
-            } else {
-                mHandler.obtainMessage(MSG_SET_CHECKED, 0).sendToTarget();
-            }
+            int automatic;
+            automatic = Settings.System.getIntForUser(mContext.getContentResolver(),
+                    Settings.System.SCREEN_BRIGHTNESS_MODE,
+                    Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL,
+                    UserHandle.USER_CURRENT);
+            mAutomatic = automatic != Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL;
         }
     };
 
@@ -266,9 +260,6 @@
                     case MSG_UPDATE_SLIDER:
                         updateSlider(Float.intBitsToFloat(msg.arg1), msg.arg2 != 0);
                         break;
-                    case MSG_SET_CHECKED:
-                        mControl.setChecked(msg.arg1 != 0);
-                        break;
                     case MSG_ATTACH_LISTENER:
                         mControl.setOnChangedListener(BrightnessController.this);
                         break;
@@ -312,9 +303,6 @@
         mDefaultBacklightForVr = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT_VR);
 
-
-        mAutomaticAvailable = context.getResources().getBoolean(
-                com.android.internal.R.bool.config_automatic_brightness_available);
         mDisplayManager = context.getSystemService(DisplayManager.class);
         mVrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
                 Context.VR_SERVICE));
@@ -339,8 +327,7 @@
     }
 
     @Override
-    public void onChanged(boolean tracking, boolean automatic,
-            int value, boolean stopTracking) {
+    public void onChanged(boolean tracking, int value, boolean stopTracking) {
         if (mExternalChange) return;
 
         if (mSliderAnimator != null) {
@@ -398,12 +385,6 @@
         });
     }
 
-    private void setMode(int mode) {
-        Settings.System.putIntForUser(mContext.getContentResolver(),
-                Settings.System.SCREEN_BRIGHTNESS_MODE, mode,
-                mUserTracker.getCurrentUserId());
-    }
-
     private void setBrightness(float brightness) {
         mDisplayManager.setTemporaryBrightness(mDisplayId, brightness);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
index ab4895e..db82057 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSlider.java
@@ -21,7 +21,6 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.CompoundButton;
 import android.widget.SeekBar;
 
 import androidx.annotation.Nullable;
@@ -31,7 +30,6 @@
 import com.android.systemui.R;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.plugins.FalsingManager;
-import com.android.systemui.statusbar.FeatureFlags;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.util.ViewController;
 
@@ -95,14 +93,12 @@
     @Override
     protected void onViewAttached() {
         mBrightnessSliderView.setOnSeekBarChangeListener(mSeekListener);
-        mBrightnessSliderView.setOnCheckedChangeListener(mCheckListener);
         mBrightnessSliderView.setOnInterceptListener(mOnInterceptListener);
     }
 
     @Override
     protected void onViewDetached() {
         mBrightnessSliderView.setOnSeekBarChangeListener(null);
-        mBrightnessSliderView.setOnCheckedChangeListener(null);
         mBrightnessSliderView.setOnDispatchTouchEventListener(null);
         mBrightnessSliderView.setOnInterceptListener(null);
     }
@@ -132,7 +128,6 @@
     private void setMirror(ToggleSlider toggleSlider) {
         mMirror = toggleSlider;
         if (mMirror != null) {
-            mMirror.setChecked(mBrightnessSliderView.isChecked());
             mMirror.setMax(mBrightnessSliderView.getMax());
             mMirror.setValue(mBrightnessSliderView.getValue());
             mBrightnessSliderView.setOnDispatchTouchEventListener(this::mirrorTouchEvent);
@@ -166,16 +161,6 @@
     }
 
     @Override
-    public void setChecked(boolean checked) {
-        mBrightnessSliderView.setChecked(checked);
-    }
-
-    @Override
-    public boolean isChecked() {
-        return mBrightnessSliderView.isChecked();
-    }
-
-    @Override
     public void setMax(int max) {
         mBrightnessSliderView.setMax(max);
         if (mMirror != null) {
@@ -206,7 +191,7 @@
         @Override
         public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
             if (mListener != null) {
-                mListener.onChanged(mTracking, isChecked(), progress, false);
+                mListener.onChanged(mTracking, progress, false);
             }
         }
 
@@ -215,12 +200,9 @@
             mTracking = true;
 
             if (mListener != null) {
-                mListener.onChanged(mTracking, isChecked(),
-                        getValue(), false);
+                mListener.onChanged(mTracking, getValue(), false);
             }
 
-            setChecked(false);
-
             if (mMirrorController != null) {
                 mMirrorController.showMirror();
                 mMirrorController.setLocation((View) mBrightnessSliderView.getParent());
@@ -232,8 +214,7 @@
             mTracking = false;
 
             if (mListener != null) {
-                mListener.onChanged(mTracking, isChecked(),
-                        getValue(), true);
+                mListener.onChanged(mTracking, getValue(), true);
             }
 
             if (mMirrorController != null) {
@@ -242,35 +223,15 @@
         }
     };
 
-    private final CompoundButton.OnCheckedChangeListener mCheckListener =
-            new CompoundButton.OnCheckedChangeListener() {
-        @Override
-        public void onCheckedChanged(CompoundButton toggle, boolean checked) {
-            enableSlider(!checked);
-
-            if (mListener != null) {
-                mListener.onChanged(mTracking, checked, getValue(), false);
-            }
-
-            if (mMirror != null) {
-                mMirror.setChecked(checked);
-            }
-        }
-    };
-
     /**
      * Creates a {@link BrightnessSlider} with its associated view.
-     *
-     * The views inflated are determined by {@link FeatureFlags#useNewBrightnessSlider()}.
      */
     public static class Factory {
 
-        final FeatureFlags mFeatureFlags;
         private final FalsingManager mFalsingManager;
 
         @Inject
-        public Factory(FeatureFlags featureFlags, FalsingManager falsingManager) {
-            mFeatureFlags = featureFlags;
+        public Factory(FalsingManager falsingManager) {
             mFalsingManager = falsingManager;
         }
 
@@ -296,9 +257,7 @@
 
         /** Get the layout to inflate based on what slider to use */
         private int getLayout() {
-            return mFeatureFlags.useNewBrightnessSlider()
-                    ? R.layout.quick_settings_brightness_dialog_thick
-                    : R.layout.quick_settings_brightness_dialog;
+            return R.layout.quick_settings_brightness_dialog;
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
index 5b71c62..dbd6758 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessSliderView.java
@@ -17,18 +17,13 @@
 package com.android.systemui.settings.brightness;
 
 import android.content.Context;
-import android.content.res.TypedArray;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
-import android.widget.CompoundButton;
-import android.widget.CompoundButton.OnCheckedChangeListener;
 import android.widget.FrameLayout;
 import android.widget.SeekBar.OnSeekBarChangeListener;
-import android.widget.TextView;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.settingslib.RestrictedLockUtils;
 import com.android.systemui.Gefingerpoken;
@@ -37,23 +32,11 @@
 /**
  * {@code FrameLayout} used to show and manipulate a {@link ToggleSeekBar}.
  *
- * It can additionally control a {@link CompoundButton} and display a label. For the class to work,
- * add children before inflation with the following ids:
- * <ul>
- *     <li>{@code @id/slider} of type {@link ToggleSeekBar}</li>
- *     <li>{@code @id/toggle} of type {@link CompoundButton} (optional)</li>
- *     <li>{@code @id/label} of type {@link TextView} (optional)</li>
- * </ul>
  */
 public class BrightnessSliderView extends FrameLayout {
 
-    @Nullable
-    private CompoundButton mToggle;
     @NonNull
     private ToggleSeekBar mSlider;
-    @Nullable
-    private TextView mLabel;
-    private final CharSequence mText;
     private DispatchTouchEventListener mListener;
     private Gefingerpoken mOnInterceptListener;
 
@@ -62,31 +45,15 @@
     }
 
     public BrightnessSliderView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public BrightnessSliderView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        final TypedArray a = context.obtainStyledAttributes(
-                attrs, R.styleable.ToggleSliderView, defStyle, 0);
-        mText = a.getString(R.styleable.ToggleSliderView_text);
-
-        a.recycle();
+        super(context, attrs);
     }
 
     // Inflated from quick_settings_brightness_dialog or quick_settings_brightness_dialog_thick
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mToggle = findViewById(R.id.toggle);
 
         mSlider = requireViewById(R.id.slider);
-
-        mLabel = findViewById(R.id.label);
-        if (mLabel != null) {
-            mLabel.setText(mText);
-        }
         mSlider.setAccessibilityLabel(getContentDescription().toString());
     }
 
@@ -125,25 +92,11 @@
     }
 
     /**
-     * Attaches a listener to the {@link CompoundButton} in the view (if present) so changes to its
-     * state can be observed
-     * @param checkListener use {@code null} to remove listener
-     */
-    public void setOnCheckedChangeListener(OnCheckedChangeListener checkListener) {
-        if (mToggle != null) {
-            mToggle.setOnCheckedChangeListener(checkListener);
-        }
-    }
-
-    /**
      * Enforces admin rules for toggling auto-brightness and changing value of brightness
      * @param admin
      * @see ToggleSeekBar#setEnforcedAdmin
      */
     public void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin) {
-        if (mToggle != null) {
-            mToggle.setEnabled(admin == null);
-        }
         mSlider.setEnabled(admin == null);
         mSlider.setEnforcedAdmin(admin);
     }
@@ -157,26 +110,6 @@
     }
 
     /**
-     * Sets the state of the {@link CompoundButton} if present
-     * @param checked
-     */
-    public void setChecked(boolean checked) {
-        if (mToggle != null) {
-            mToggle.setChecked(checked);
-        }
-    }
-
-    /**
-     * @return the state of the {@link CompoundButton} if present, or {@code true} if not.
-     */
-    public boolean isChecked() {
-        if (mToggle != null) {
-            return mToggle.isChecked();
-        }
-        return true;
-    }
-
-    /**
      * @return the maximum value of the {@link ToggleSeekBar}.
      */
     public int getMax() {
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
index 71e4818..a988c7a 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/ToggleSlider.java
@@ -23,8 +23,7 @@
 
 public interface ToggleSlider {
     interface Listener {
-        void onChanged(boolean tracking, boolean automatic, int value,
-                       boolean stopTracking);
+        void onChanged(boolean tracking, int value, boolean stopTracking);
     }
 
     void setEnforcedAdmin(RestrictedLockUtils.EnforcedAdmin admin);
@@ -32,8 +31,6 @@
     boolean mirrorTouchEvent(MotionEvent ev);
 
     void setOnChangedListener(Listener l);
-    default void setChecked(boolean checked) {}
-    default boolean isChecked() { return false; }
     void setMax(int max);
     int getMax();
     void setValue(int value);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index b937dad..f51fbed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -62,11 +62,6 @@
         return mFlagReader.isEnabled(R.bool.flag_keyguard_layout);
     }
 
-    /** b/178485354 */
-    public boolean useNewBrightnessSlider() {
-        return mFlagReader.isEnabled(R.bool.flag_brightness_slider);
-    }
-
     public boolean useNewLockscreenAnimations() {
         return mFlagReader.isEnabled(R.bool.flag_lockscreen_animations);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
index 91a4f94..50cbbd5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinator.kt
@@ -393,11 +393,6 @@
     override fun onDozingChanged(isDozing: Boolean) {
         if (isDozing) {
             setNotificationsVisible(visible = false, animate = false, increaseSpeed = false)
-        } else {
-            // We only unset the flag once we fully went asleep. If the user interrupts the
-            // animation in the middle, we have to abort the animation as well to make sure
-            // the notifications are visible again.
-            animatingScreenOff = false
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
index 204dd9f..88e5364 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoTileManager.java
@@ -36,6 +36,7 @@
 import com.android.systemui.statusbar.policy.CastController.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
 import com.android.systemui.statusbar.policy.DataSaverController.Listener;
+import com.android.systemui.statusbar.policy.DeviceControlsController;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.statusbar.policy.HotspotController.Callback;
 import com.android.systemui.util.UserAwareController;
@@ -58,6 +59,7 @@
     public static final String WORK = "work";
     public static final String NIGHT = "night";
     public static final String CAST = "cast";
+    public static final String DEVICE_CONTROLS = "controls";
     public static final String BRIGHTNESS = "reduce_brightness";
     static final String SETTING_SEPARATOR = ":";
 
@@ -74,6 +76,7 @@
     private final ManagedProfileController mManagedProfileController;
     private final NightDisplayListener mNightDisplayListener;
     private final CastController mCastController;
+    private final DeviceControlsController mDeviceControlsController;
     private final ReduceBrightColorsController mReduceBrightColorsController;
     private final boolean mIsReduceBrightColorsAvailable;
     private final ArrayList<AutoAddSetting> mAutoAddSettingList = new ArrayList<>();
@@ -88,6 +91,7 @@
             NightDisplayListener nightDisplayListener,
             CastController castController,
             ReduceBrightColorsController reduceBrightColorsController,
+            DeviceControlsController deviceControlsController,
             @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
         mContext = context;
         mHost = host;
@@ -102,6 +106,7 @@
         mCastController = castController;
         mReduceBrightColorsController = reduceBrightColorsController;
         mIsReduceBrightColorsAvailable = isReduceBrightColorsAvailable;
+        mDeviceControlsController = deviceControlsController;
     }
 
     /**
@@ -138,6 +143,9 @@
         if (!mAutoTracker.isAdded(BRIGHTNESS) && mIsReduceBrightColorsAvailable) {
             mReduceBrightColorsController.addCallback(mReduceBrightColorsCallback);
         }
+        if (!mAutoTracker.isAdded(DEVICE_CONTROLS)) {
+            mDeviceControlsController.setCallback(mDeviceControlsCallback);
+        }
 
         int settingsN = mAutoAddSettingList.size();
         for (int i = 0; i < settingsN; i++) {
@@ -158,6 +166,7 @@
             mReduceBrightColorsController.removeCallback(mReduceBrightColorsCallback);
         }
         mCastController.removeCallback(mCastCallback);
+        mDeviceControlsController.removeCallback();
         int settingsN = mAutoAddSettingList.size();
         for (int i = 0; i < settingsN; i++) {
             mAutoAddSettingList.get(i).setListening(false);
@@ -274,6 +283,17 @@
         }
     };
 
+    private final DeviceControlsController.Callback mDeviceControlsCallback =
+            new DeviceControlsController.Callback() {
+        @Override
+        public void onControlsAvailable(int position) {
+            if (mAutoTracker.isAdded(DEVICE_CONTROLS)) return;
+            mHost.addTile(DEVICE_CONTROLS, position);
+            mAutoTracker.setTileAdded(DEVICE_CONTROLS);
+            mHandler.post(() -> mDeviceControlsController.removeCallback());
+        }
+    };
+
     @VisibleForTesting
     final NightDisplayListener.Callback mNightDisplayCallback =
             new NightDisplayListener.Callback() {
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 aa0be8a..8ed9cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -239,6 +239,7 @@
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -791,6 +792,7 @@
             BrightnessSlider.Factory brightnessSliderFactory,
             WiredChargingRippleController chargingRippleAnimationController,
             OngoingCallController ongoingCallController,
+            TunerService tunerService,
             FeatureFlags featureFlags) {
         super(context);
         mNotificationsController = notificationsController;
@@ -873,6 +875,15 @@
         mOngoingCallController = ongoingCallController;
         mFeatureFlags = featureFlags;
 
+        tunerService.addTunable(
+                (key, newValue) -> {
+                    if (key.equals(Settings.Secure.DOZE_ALWAYS_ON)) {
+                        updateLightRevealScrimVisibility();
+                    }
+                },
+                Settings.Secure.DOZE_ALWAYS_ON
+        );
+
         mExpansionChangedListeners = new ArrayList<>();
 
         mBubbleExpandListener =
@@ -1216,15 +1227,7 @@
 
         mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
         mChargingRippleAnimationController.setViewHost(mNotificationShadeWindowView);
-
-
-        if (mFeatureFlags.useNewLockscreenAnimations()
-                && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
-            mLightRevealScrim.setVisibility(View.VISIBLE);
-            mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
-        } else {
-            mLightRevealScrim.setVisibility(View.GONE);
-        }
+        updateLightRevealScrimVisibility();
 
         mNotificationPanelViewController.initDependencies(
                 this,
@@ -4654,4 +4657,19 @@
     public void removeExpansionChangedListener(@NonNull ExpansionChangedListener listener) {
         mExpansionChangedListeners.remove(listener);
     }
+
+    private void updateLightRevealScrimVisibility() {
+        if (mLightRevealScrim == null) {
+            // status bar may not be inflated yet
+            return;
+        }
+
+        if (mFeatureFlags.useNewLockscreenAnimations()
+                && (mDozeParameters.getAlwaysOn() || mDozeParameters.isQuickPickupEnabled())) {
+            mLightRevealScrim.setVisibility(View.VISIBLE);
+            mLightRevealScrim.setRevealEffect(LiftReveal.INSTANCE);
+        } else {
+            mLightRevealScrim.setVisibility(View.GONE);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
index 785d98c..2c2779e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.java
@@ -100,6 +100,7 @@
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.volume.VolumeComponent;
 import com.android.systemui.wmshell.BubblesManager;
 import com.android.wm.shell.bubbles.Bubbles;
@@ -208,6 +209,7 @@
             BrightnessSlider.Factory brightnessSliderFactory,
             WiredChargingRippleController chargingRippleAnimationController,
             OngoingCallController ongoingCallController,
+            TunerService tunerService,
             FeatureFlags featureFlags) {
         return new StatusBar(
                 context,
@@ -291,6 +293,7 @@
                 brightnessSliderFactory,
                 chargingRippleAnimationController,
                 ongoingCallController,
+                tunerService,
                 featureFlags);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
new file mode 100644
index 0000000..b211898
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsController.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.policy
+
+/**
+ * Supports adding a DeviceControls QS tile
+ */
+interface DeviceControlsController {
+    interface Callback {
+        /**
+         * If controls become available, initiate this callback with the desired position
+         */
+        fun onControlsAvailable(position: Int)
+    }
+
+    /** Add callback, supporting only a single callback at once */
+    fun setCallback(callback: Callback)
+
+    /** Remove any existing callback, if any */
+    fun removeCallback()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
new file mode 100644
index 0000000..d3907ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImpl.kt
@@ -0,0 +1,173 @@
+/*
+ * 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.policy
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.SharedPreferences
+import android.util.Log
+
+import com.android.systemui.R
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.dagger.ControlsComponent
+import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.qs.QSTileHost.POSITION_AT_END
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.policy.DeviceControlsController.Callback
+
+import javax.inject.Inject
+
+/**
+ * Watches for Device Controls QS Tile activation, which can happen in two ways:
+ * <ol>
+ *   <li>Migration from Power Menu - For existing Android 11 users, create a tile in a high
+ *       priority position.
+ *   <li>Device controls service becomes available - For non-migrated users, create a tile and
+ *       place at the end of active tiles, and initiate seeding where possible.
+ * </ol>
+ */
+@SysUISingleton
+public class DeviceControlsControllerImpl @Inject constructor(
+    private val context: Context,
+    private val controlsComponent: ControlsComponent,
+    private val userContextProvider: UserContextProvider
+) : DeviceControlsController {
+
+    private var callback: Callback? = null
+    internal var position: Int? = null
+
+    private val listingCallback = object : ControlsListingController.ControlsListingCallback {
+        override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
+            if (!serviceInfos.isEmpty()) {
+                seedFavorites(serviceInfos)
+            }
+        }
+    }
+
+    companion object {
+        private const val TAG = "DeviceControlsControllerImpl"
+        internal const val QS_PRIORITY_POSITION = 3
+        internal const val QS_DEFAULT_POSITION = POSITION_AT_END
+
+        internal const val PREFS_CONTROLS_SEEDING_COMPLETED = "SeedingCompleted"
+        internal const val PREFS_CONTROLS_FILE = "controls_prefs"
+        private const val SEEDING_MAX = 2
+    }
+
+    private fun checkMigrationToQs() {
+        controlsComponent.getControlsController().ifPresent {
+            if (!it.getFavorites().isEmpty()) {
+                position = QS_PRIORITY_POSITION
+            }
+        }
+    }
+
+    /**
+     * This migration logic assumes that something like [AutoTileManager] is tracking state
+     * externally, and won't call this method after receiving a response via
+     * [Callback#onControlsAvailable], once per user. Otherwise the calculated position may be
+     * incorrect.
+     */
+    override fun setCallback(callback: Callback) {
+        // Treat any additional call as a reset before recalculating
+        removeCallback()
+
+        checkMigrationToQs()
+        controlsComponent.getControlsListingController().ifPresent {
+            it.addCallback(listingCallback)
+        }
+
+        this.callback = callback
+        fireControlsAvailable()
+    }
+
+    override fun removeCallback() {
+        position = null
+        callback = null
+        controlsComponent.getControlsListingController().ifPresent {
+            it.removeCallback(listingCallback)
+        }
+    }
+
+    private fun fireControlsAvailable() {
+        position?.let {
+            Log.i(TAG, "Setting DeviceControlsTile position: $it")
+            callback?.onControlsAvailable(it)
+        }
+    }
+
+    /**
+     * See if any available control service providers match one of the preferred components. If
+     * they do, and there are no current favorites for that component, query the preferred
+     * component for a limited number of suggested controls.
+     */
+    private fun seedFavorites(serviceInfos: List<ControlsServiceInfo>) {
+        val preferredControlsPackages = context.getResources().getStringArray(
+            R.array.config_controlsPreferredPackages)
+
+        val prefs = userContextProvider.userContext.getSharedPreferences(
+            PREFS_CONTROLS_FILE, Context.MODE_PRIVATE)
+        val seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet())
+
+        val controlsController = controlsComponent.getControlsController().get()
+        val componentsToSeed = mutableListOf<ComponentName>()
+        var i = 0
+        while (i < Math.min(SEEDING_MAX, preferredControlsPackages.size)) {
+            val pkg = preferredControlsPackages[i]
+            serviceInfos.forEach {
+                if (pkg.equals(it.componentName.packageName) && !seededPackages.contains(pkg)) {
+                    if (controlsController.countFavoritesForComponent(it.componentName) > 0) {
+                        // When there are existing controls but no saved preference, assume it
+                        // is out of sync, perhaps through a device restore, and update the
+                        // preference
+                        addPackageToSeededSet(prefs, pkg)
+                    } else {
+                        componentsToSeed.add(it.componentName)
+                    }
+                }
+            }
+            i++
+        }
+
+        if (componentsToSeed.isEmpty()) return
+
+        controlsController.seedFavoritesForComponents(
+                componentsToSeed,
+                { response ->
+                    Log.d(TAG, "Controls seeded: $response")
+                    if (response.accepted) {
+                        addPackageToSeededSet(prefs, response.packageName)
+                        if (position == null) {
+                            position = QS_DEFAULT_POSITION
+                        }
+                        fireControlsAvailable()
+
+                        controlsComponent.getControlsListingController().ifPresent {
+                            it.removeCallback(listingCallback)
+                        }
+                    }
+                })
+    }
+
+    private fun addPackageToSeededSet(prefs: SharedPreferences, pkg: String) {
+        val seededPackages = prefs.getStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet())
+        val updatedPkgs = seededPackages.toMutableSet()
+        updatedPkgs.add(pkg)
+        prefs.edit().putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, updatedPkgs).apply()
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
index 7a4b912..7666022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java
@@ -26,6 +26,8 @@
 import com.android.systemui.statusbar.policy.BluetoothControllerImpl;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastControllerImpl;
+import com.android.systemui.statusbar.policy.DeviceControlsController;
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.ExtensionControllerImpl;
 import com.android.systemui.statusbar.policy.FlashlightController;
@@ -44,8 +46,6 @@
 import com.android.systemui.statusbar.policy.RotationLockControllerImpl;
 import com.android.systemui.statusbar.policy.SecurityController;
 import com.android.systemui.statusbar.policy.SecurityControllerImpl;
-import com.android.systemui.statusbar.policy.SensorPrivacyController;
-import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.ZenModeController;
@@ -115,6 +115,11 @@
 
     /** */
     @Binds
+    DeviceControlsController provideDeviceControlsController(
+            DeviceControlsControllerImpl controllerImpl);
+
+    /** */
+    @Binds
     NetworkController.AccessPointController provideAccessPointController(
             AccessPointControllerImpl accessPointControllerImpl);
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 1026a5c..a8a3d79c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -301,11 +301,13 @@
         // 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);
             mTouchableRegion.op(
-                    view.getLeft(),
-                    view.getTop(),
-                    view.getRight(),
-                    view.getBottom(),
+                    locInWindow[0],
+                    locInWindow[1],
+                    locInWindow[0] + view.getWidth(),
+                    locInWindow[1] + view.getHeight(),
                     Region.Op.UNION);
         }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
index 8683dd6..814f073 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuViewTest.java
@@ -31,6 +31,7 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.GradientDrawable;
 import android.graphics.drawable.LayerDrawable;
@@ -40,6 +41,7 @@
 import android.view.View;
 import android.view.ViewPropertyAnimator;
 import android.view.WindowManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 import androidx.annotation.NonNull;
 import androidx.recyclerview.widget.RecyclerView;
@@ -78,6 +80,8 @@
 
     private RecyclerView mListView;
 
+    private Rect mAvailableBounds = new Rect(100, 200, 300, 400);
+
     private int mMenuHalfWidth;
     private int mMenuHalfHeight;
     private int mScreenHalfWidth;
@@ -339,6 +343,66 @@
         assertThat(mMenuView.mShapeType).isEqualTo(/* halfOval */ 1);
     }
 
+    @Test
+    public void getAccessibilityActionList_matchResult() {
+        final AccessibilityNodeInfo infos = new AccessibilityNodeInfo();
+        mMenuView.onInitializeAccessibilityNodeInfo(infos);
+
+        assertThat(infos.getActionList().size()).isEqualTo(4);
+    }
+
+    @Test
+    public void accessibilityActionMove_moveTopLeft_success() {
+        final AccessibilityFloatingMenuView menuView =
+                spy(new AccessibilityFloatingMenuView(mContext));
+        doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+
+        final boolean isActionPerformed =
+                menuView.performAccessibilityAction(R.id.action_move_top_left, null);
+
+        assertThat(isActionPerformed).isTrue();
+        verify(menuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.top);
+    }
+
+    @Test
+    public void accessibilityActionMove_moveTopRight_success() {
+        final AccessibilityFloatingMenuView menuView =
+                spy(new AccessibilityFloatingMenuView(mContext));
+        doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+
+        final boolean isActionPerformed =
+                menuView.performAccessibilityAction(R.id.action_move_top_right, null);
+
+        assertThat(isActionPerformed).isTrue();
+        verify(menuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.top);
+    }
+
+    @Test
+    public void accessibilityActionMove_moveBottomLeft_success() {
+        final AccessibilityFloatingMenuView menuView =
+                spy(new AccessibilityFloatingMenuView(mContext));
+        doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+
+        final boolean isActionPerformed =
+                menuView.performAccessibilityAction(R.id.action_move_bottom_left, null);
+
+        assertThat(isActionPerformed).isTrue();
+        verify(menuView).snapToLocation(mAvailableBounds.left, mAvailableBounds.bottom);
+    }
+
+    @Test
+    public void accessibilityActionMove_moveBottomRight_success() {
+        final AccessibilityFloatingMenuView menuView =
+                spy(new AccessibilityFloatingMenuView(mContext));
+        doReturn(mAvailableBounds).when(menuView).getAvailableBounds();
+
+        final boolean isActionPerformed =
+                menuView.performAccessibilityAction(R.id.action_move_bottom_right, null);
+
+        assertThat(isActionPerformed).isTrue();
+        verify(menuView).snapToLocation(mAvailableBounds.right, mAvailableBounds.bottom);
+    }
+
     @After
     public void tearDown() {
         mInterceptMotionEvent = null;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index beca965..9504970 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -47,6 +47,7 @@
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
@@ -103,6 +104,8 @@
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     @Mock
+    private KeyguardViewMediator mKeyguardViewMediator;
+    @Mock
     private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback;
 
     private FakeExecutor mFgExecutor;
@@ -155,7 +158,8 @@
                 mStatusBarKeyguardViewManager,
                 mDumpManager,
                 mAuthRippleController,
-                mKeyguardUpdateMonitor);
+                mKeyguardUpdateMonitor,
+                mKeyguardViewMediator);
         verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
         mOverlayController = mOverlayCaptor.getValue();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index 879cdbfb..2383c7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -18,7 +18,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.atLeast;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -30,6 +30,7 @@
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.keyguard.KeyguardViewMediator;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.phone.StatusBar;
@@ -44,7 +45,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.List;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
@@ -65,12 +65,13 @@
     private DelayableExecutor mExecutor;
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock
+    private KeyguardViewMediator mKeyguardViewMediator;
 
     private UdfpsKeyguardViewController mController;
 
     // Capture listeners so that they can be used to send events
     @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
-    private StatusBarStateController.StateListener mParentStatusBarStateListener;
     private StatusBarStateController.StateListener mStatusBarStateListener;
 
     @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor;
@@ -83,6 +84,7 @@
     @Before
     public void setUp() {
         MockitoAnnotations.initMocks(this);
+        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
         mController = new UdfpsKeyguardViewController(
                 mView,
                 mStatusBarStateController,
@@ -90,7 +92,8 @@
                 mStatusBarKeyguardViewManager,
                 mKeyguardUpdateMonitor,
                 mExecutor,
-                mDumpManager);
+                mDumpManager,
+                mKeyguardViewMediator);
     }
 
     @Test
@@ -108,7 +111,7 @@
     @Test
     public void testViewControllerQueriesSBStateOnAttached() {
         mController.onViewAttached();
-        verify(mStatusBarStateController, times(2)).getState();
+        verify(mStatusBarStateController).getState();
         verify(mStatusBarStateController).getDozeAmount();
 
         final float dozeAmount = .88f;
@@ -117,7 +120,7 @@
         captureStatusBarStateListeners();
 
         mController.onViewAttached();
-        verify(mView).setPauseAuth(true);
+        verify(mView, atLeast(1)).setPauseAuth(true);
         verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount);
     }
 
@@ -128,7 +131,6 @@
         captureExpansionListener();
         mController.onViewDetached();
 
-        verify(mStatusBarStateController).removeCallback(mParentStatusBarStateListener);
         verify(mStatusBarStateController).removeCallback(mStatusBarStateListener);
         verify(mStatusBar).removeExpansionChangedListener(mExpansionListener);
     }
@@ -168,6 +170,47 @@
     }
 
     @Test
+    public void testShouldPauseAuthOnShade() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+        captureExpansionListener();
+
+        // WHEN not on keyguard yet (shade = home)
+        sendStatusBarStateChanged(StatusBarState.SHADE);
+
+        // THEN pause auth
+        assertTrue(mController.shouldPauseAuth());
+    }
+
+    @Test
+    public void testShouldPauseAuthAnimatingScreenOffFromShade() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+        captureExpansionListener();
+
+        // WHEN transitioning from home/shade => keyguard + animating screen off
+        mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD);
+        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
+
+        // THEN pause auth
+        assertTrue(mController.shouldPauseAuth());
+    }
+
+    @Test
+    public void testDoNotPauseAuthAnimatingScreenOffFromLS() {
+        mController.onViewAttached();
+        captureStatusBarStateListeners();
+        captureExpansionListener();
+
+        // WHEN animating screen off transition from LS => AOD
+        sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+        when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true);
+
+        // THEN don't pause auth
+        assertFalse(mController.shouldPauseAuth());
+    }
+
+    @Test
     public void testOverrideShouldPauseAuthOnShadeLocked() {
         mController.onViewAttached();
         captureStatusBarStateListeners();
@@ -203,15 +246,11 @@
 
     private void sendStatusBarStateChanged(int statusBarState) {
         mStatusBarStateListener.onStateChanged(statusBarState);
-        mParentStatusBarStateListener.onStateChanged(statusBarState);
     }
 
     private void captureStatusBarStateListeners() {
-        verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture());
-        List<StatusBarStateController.StateListener> stateListeners =
-                mStateListenerCaptor.getAllValues();
-        mParentStatusBarStateListener = stateListeners.get(0);
-        mStatusBarStateListener = stateListeners.get(1);
+        verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
+        mStatusBarStateListener = mStateListenerCaptor.getValue();
     }
 
     private void captureExpansionListener() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
index 93769c4..3130e97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/globalactions/GlobalActionsDialogTest.java
@@ -58,15 +58,10 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.colorextraction.SysuiColorExtractor;
-import com.android.systemui.controls.controller.ControlsController;
-import com.android.systemui.controls.dagger.ControlsComponent;
-import com.android.systemui.controls.management.ControlsListingController;
-import com.android.systemui.controls.ui.ControlsUiController;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
-import com.android.systemui.settings.UserContextProvider;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.statusbar.NotificationShadeDepthController;
 import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -118,11 +113,8 @@
     @Mock private SysuiColorExtractor mColorExtractor;
     @Mock private IStatusBarService mStatusBarService;
     @Mock private NotificationShadeWindowController mNotificationShadeWindowController;
-    @Mock private ControlsUiController mControlsUiController;
     @Mock private IWindowManager mWindowManager;
     @Mock private Executor mBackgroundExecutor;
-    @Mock private ControlsListingController mControlsListingController;
-    @Mock private ControlsController mControlsController;
     @Mock private UiEventLogger mUiEventLogger;
     @Mock private RingerModeTracker mRingerModeTracker;
     @Mock private RingerModeLiveData mRingerModeLiveData;
@@ -130,10 +122,8 @@
     @Mock GlobalActionsPanelPlugin mWalletPlugin;
     @Mock GlobalActionsPanelPlugin.PanelViewController mWalletController;
     @Mock private Handler mHandler;
-    @Mock private UserContextProvider mUserContextProvider;
     @Mock private UserTracker mUserTracker;
     @Mock private SecureSettings mSecureSettings;
-    private ControlsComponent mControlsComponent;
 
     private TestableLooper mTestableLooper;
 
@@ -144,18 +134,6 @@
         allowTestableLooperAsMainThread();
 
         when(mRingerModeTracker.getRingerMode()).thenReturn(mRingerModeLiveData);
-        when(mUserContextProvider.getUserContext()).thenReturn(mContext);
-        mControlsComponent = new ControlsComponent(
-                true,
-                mContext,
-                () -> mControlsController,
-                () -> mControlsUiController,
-                () -> mControlsListingController,
-                mLockPatternUtils,
-                mKeyguardStateController,
-                mUserTracker,
-                mSecureSettings
-        );
 
         mGlobalActionsDialog = new GlobalActionsDialog(mContext,
                 mWindowManagerFuncs,
@@ -186,9 +164,7 @@
                 mUiEventLogger,
                 mRingerModeTracker,
                 mSysUiState,
-                mHandler,
-                mControlsComponent,
-                mUserContextProvider
+                mHandler
         );
         mGlobalActionsDialog.setZeroDialogPressDelayForTesting();
 
@@ -511,7 +487,7 @@
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
         when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
         when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
-        mGlobalActionsDialog.mShowLockScreenCardsAndControls = false;
+        mGlobalActionsDialog.mShowLockScreenCards = false;
         setupDefaultActions();
         when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
         when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
@@ -528,14 +504,14 @@
     }
 
     @Test
-    public void testShouldNotShowLockScreenMessage_whenWalletOrControlsShownOnLockScreen()
+    public void testShouldNotShowLockScreenMessage_whenWalletShownOnLockScreen()
             throws RemoteException {
         mGlobalActionsDialog = spy(mGlobalActionsDialog);
         mGlobalActionsDialog.mDialog = null;
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
         when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
         when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
-        mGlobalActionsDialog.mShowLockScreenCardsAndControls = true;
+        mGlobalActionsDialog.mShowLockScreenCards = true;
         setupDefaultActions();
         when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
         when(mWalletController.getPanelContent()).thenReturn(new FrameLayout(mContext));
@@ -552,7 +528,7 @@
     }
 
     @Test
-    public void testShouldNotShowLockScreenMessage_whenControlsAndWalletBothDisabled()
+    public void testShouldNotShowLockScreenMessage_whenWalletBothDisabled()
             throws RemoteException {
         mGlobalActionsDialog = spy(mGlobalActionsDialog);
         mGlobalActionsDialog.mDialog = null;
@@ -560,11 +536,10 @@
 
         when(mActivityManager.getCurrentUser()).thenReturn(newUserInfo());
         when(mLockPatternUtils.getStrongAuthForUser(anyInt())).thenReturn(STRONG_AUTH_NOT_REQUIRED);
-        mGlobalActionsDialog.mShowLockScreenCardsAndControls = true;
+        mGlobalActionsDialog.mShowLockScreenCards = true;
         setupDefaultActions();
         when(mWalletPlugin.onPanelShown(any(), anyBoolean())).thenReturn(mWalletController);
         when(mWalletController.getPanelContent()).thenReturn(null);
-        when(mControlsUiController.getAvailable()).thenReturn(false);
 
         mGlobalActionsDialog.showOrHideDialog(false, true, mWalletPlugin);
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 1260eaf..b0e3e3e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -61,6 +61,7 @@
 import com.android.systemui.statusbar.policy.UserSwitcherController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.InjectionInflationController;
+import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -130,7 +131,8 @@
                 mock(PluginManager.class), mock(TunerService.class),
                 () -> mock(AutoTileManager.class), mock(DumpManager.class),
                 mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
-                mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class));
+                mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
+                mock(SecureSettings.class));
         qs.setHost(host);
 
         qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 5422ae8..e4b95af 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -196,7 +196,7 @@
     }
 
     @Test
-    public void testNetworkLoggingEnabled() {
+    public void testNetworkLoggingEnabled_deviceOwner() {
         when(mSecurityController.isDeviceManaged()).thenReturn(true);
         when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
         mFooter.refreshState();
@@ -222,6 +222,18 @@
     }
 
     @Test
+    public void testNetworkLoggingEnabled_managedProfileOwner() {
+        when(mSecurityController.hasWorkProfile()).thenReturn(true);
+        when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
+        mFooter.refreshState();
+
+        TestableLooper.get(this).processAllMessages();
+        assertEquals(mContext.getString(
+                R.string.quick_settings_disclosure_managed_profile_network_activity),
+                mFooterText.getText());
+    }
+
+    @Test
     public void testManagedCACertsInstalled() {
         when(mSecurityController.isDeviceManaged()).thenReturn(true);
         when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 2ca8082..57e9d2b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -21,20 +21,21 @@
 import static junit.framework.Assert.assertTrue;
 import static junit.framework.TestCase.assertFalse;
 
+import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.ActivityManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.os.Handler;
 import android.os.Looper;
-import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -64,11 +65,13 @@
 import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.mockito.MockitoSession;
@@ -78,7 +81,6 @@
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.List;
-import java.util.Objects;
 import java.util.Optional;
 
 import javax.inject.Provider;
@@ -119,6 +121,8 @@
     private UiEventLogger mUiEventLogger;
     @Mock
     private UserTracker mUserTracker;
+    @Mock
+    private SecureSettings mSecureSettings;
 
     private Handler mHandler;
     private TestableLooper mLooper;
@@ -138,11 +142,12 @@
         mHandler = new Handler(mLooper.getLooper());
         mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
                 mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
-                mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker);
+                mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
+                mSecureSettings);
         setUpTileFactory();
 
-        Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
-                "", ActivityManager.getCurrentUser());
+        when(mSecureSettings.getStringForUser(eq(QSTileHost.TILES_SETTING), anyInt()))
+                .thenReturn("");
     }
 
     @After
@@ -162,6 +167,8 @@
                         return new TestTile1(mQSTileHost);
                     } else if ("spec2".equals(spec)) {
                         return new TestTile2(mQSTileHost);
+                    } else if ("spec3".equals(spec)) {
+                        return new TestTile3(mQSTileHost);
                     } else if ("na".equals(spec)) {
                         return new NotAvailableTile(mQSTileHost);
                     } else if (CUSTOM_TILE_SPEC.equals(spec)) {
@@ -232,6 +239,8 @@
 
     @Test
     public void testNoRepeatedSpecs_addTile() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles, "spec1,spec2");
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");
 
         mQSTileHost.addTile("spec1");
@@ -242,6 +251,48 @@
     }
 
     @Test
+    public void testAddTileAtValidPosition() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
+
+        mQSTileHost.addTile("spec2", 1);
+
+        assertEquals(3, mQSTileHost.mTileSpecs.size());
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("spec3", mQSTileHost.mTileSpecs.get(2));
+    }
+
+    @Test
+    public void testAddTileAtInvalidPositionAddsToEnd() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
+
+        mQSTileHost.addTile("spec2", 100);
+
+        assertEquals(3, mQSTileHost.mTileSpecs.size());
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("spec3", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+    }
+
+    @Test
+    public void testAddTileAtEnd() {
+        mContext.getOrCreateTestableResources()
+                .addOverride(R.string.quick_settings_tiles, "spec1,spec3");
+        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec3");
+
+        mQSTileHost.addTile("spec2", QSTileHost.POSITION_AT_END);
+
+        assertEquals(3, mQSTileHost.mTileSpecs.size());
+        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
+        assertEquals("spec3", mQSTileHost.mTileSpecs.get(1));
+        assertEquals("spec2", mQSTileHost.mTileSpecs.get(2));
+    }
+
+    @Test
     public void testNoRepeatedSpecs_customTile() {
         mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC);
 
@@ -318,16 +369,17 @@
         verify(mQSLogger, never()).logTileDestroyed(isNull(), anyString());
     }
 
-    private static class TestQSTileHost extends QSTileHost {
+    private class TestQSTileHost extends QSTileHost {
         TestQSTileHost(Context context, StatusBarIconController iconController,
                 QSFactory defaultFactory, Handler mainHandler, Looper bgLooper,
                 PluginManager pluginManager, TunerService tunerService,
                 Provider<AutoTileManager> autoTiles, DumpManager dumpManager,
                 BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
-                UiEventLogger uiEventLogger, UserTracker userTracker) {
+                UiEventLogger uiEventLogger, UserTracker userTracker,
+                SecureSettings secureSettings) {
             super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
                     tunerService, autoTiles, dumpManager, broadcastDispatcher,
-                    Optional.of(statusBar), qsLogger, uiEventLogger, userTracker);
+                    Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings);
         }
 
         @Override
@@ -339,18 +391,16 @@
         }
 
         @Override
-        public void changeTiles(List<String> previousTiles, List<String> newTiles) {
-            String previousSetting = Settings.Secure.getStringForUser(
-                    getContext().getContentResolver(), TILES_SETTING,
-                    ActivityManager.getCurrentUser());
-            super.changeTiles(previousTiles, newTiles);
+        void saveTilesToSettings(List<String> tileSpecs) {
+            super.saveTilesToSettings(tileSpecs);
+
+            ArgumentCaptor<String> specs = ArgumentCaptor.forClass(String.class);
+            verify(mSecureSettings, atLeastOnce()).putStringForUser(eq(QSTileHost.TILES_SETTING),
+                    specs.capture(), isNull(), eq(false), anyInt(), eq(true));
+
             // After tiles are changed, make sure to call onTuningChanged with the new setting if it
             // changed
-            String newSetting = Settings.Secure.getStringForUser(getContext().getContentResolver(),
-                    TILES_SETTING, ActivityManager.getCurrentUser());
-            if (!Objects.equals(newSetting, previousSetting)) {
-                onTuningChanged(TILES_SETTING, newSetting);
-            }
+            onTuningChanged(TILES_SETTING, specs.getValue());
         }
     }
 
@@ -415,6 +465,13 @@
         }
     }
 
+    private class TestTile3 extends TestTile {
+
+        protected TestTile3(QSHost host) {
+            super(host);
+        }
+    }
+
     private class NotAvailableTile extends TestTile {
 
         protected NotAvailableTile(QSHost host) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 2a3bc31..641f917 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.policy.BluetoothController;
 import com.android.systemui.tuner.TunerService;
+import com.android.systemui.util.settings.SecureSettings;
 
 import org.junit.After;
 import org.junit.Before;
@@ -95,6 +96,8 @@
     private UiEventLogger mUiEventLogger;
     @Mock
     private UserTracker mUserTracker;
+    @Mock
+    private SecureSettings  mSecureSettings;
 
     @Before
     public void setUp() throws Exception {
@@ -114,7 +117,8 @@
                 Optional.of(mStatusBar),
                 mQSLogger,
                 mUiEventLogger,
-                mUserTracker);
+                mUserTracker,
+                mSecureSettings);
         mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
                 mUserTracker);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
index 9fe5687..a59f45d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DeviceControlsTileTest.kt
@@ -38,11 +38,9 @@
 import com.android.systemui.plugins.statusbar.StatusBarStateController
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.statusbar.FeatureFlags
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.capture
 import com.android.systemui.util.settings.FakeSettings
-import com.android.systemui.util.settings.GlobalSettings
 import com.android.systemui.util.settings.SecureSettings
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -83,9 +81,6 @@
     @Mock
     private lateinit var controlsController: ControlsController
     @Mock
-    private lateinit var featureFlags: FeatureFlags
-    private lateinit var globalSettings: GlobalSettings
-    @Mock
     private lateinit var serviceInfo: ControlsServiceInfo
     @Mock
     private lateinit var uiEventLogger: UiEventLogger
@@ -116,11 +111,6 @@
 
         setupControlsComponent()
 
-        globalSettings = FakeSettings()
-
-        globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 1)
-        `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(true)
-
         tile = createTile()
     }
 
@@ -156,13 +146,6 @@
     }
 
     @Test
-    fun testNotAvailableFeature() {
-        `when`(featureFlags.isKeyguardLayoutEnabled).thenReturn(false)
-
-        assertThat(tile.isAvailable).isFalse()
-    }
-
-    @Test
     fun testNotAvailableControls() {
         featureEnabled = false
         tile = createTile()
@@ -179,14 +162,6 @@
     }
 
     @Test
-    fun testNotAvailableControlsLockscreenFlag() {
-        globalSettings.putInt(DeviceControlsTile.SETTINGS_FLAG, 0)
-        tile = createTile()
-
-        assertThat(tile.isAvailable).isFalse()
-    }
-
-    @Test
     fun testObservingCallback() {
         verify(controlsListingController).observe(
                 any(LifecycleOwner::class.java),
@@ -329,9 +304,7 @@
                 statusBarStateController,
                 activityStarter,
                 qsLogger,
-                controlsComponent,
-                featureFlags,
-                globalSettings
+                controlsComponent
         )
     }
 }
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 33166cc..96d7979 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
@@ -19,21 +19,36 @@
 import static android.content.pm.PackageManager.FEATURE_NFC_HOST_CARD_EMULATION;
 import static android.provider.Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static junit.framework.TestCase.assertEquals;
 import static junit.framework.TestCase.assertFalse;
+import static junit.framework.TestCase.assertNotNull;
 import static junit.framework.TestCase.assertNull;
 import static junit.framework.TestCase.assertTrue;
 
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyZeroInteractions;
 import static org.mockito.Mockito.when;
 
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Icon;
 import android.os.Handler;
+import android.service.quickaccesswallet.GetWalletCardsError;
+import android.service.quickaccesswallet.GetWalletCardsRequest;
+import android.service.quickaccesswallet.GetWalletCardsResponse;
 import android.service.quickaccesswallet.QuickAccessWalletClient;
+import android.service.quickaccesswallet.QuickAccessWalletService;
+import android.service.quickaccesswallet.WalletCard;
 import android.service.quicksettings.Tile;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -56,17 +71,30 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 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;
 
+import java.util.Collections;
+
 @RunWith(AndroidTestingRunner.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class QuickAccessWalletTileTest extends SysuiTestCase {
 
+    private static final String CARD_ID = "card_id";
+    private static final Icon CARD_IMAGE =
+            Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888));
+
+    private final Intent mWalletIntent = new Intent(QuickAccessWalletService.ACTION_VIEW_WALLET)
+            .setComponent(new ComponentName(mContext.getPackageName(), "WalletActivity"));
+
     @Mock
     private QSTileHost mHost;
     @Mock
@@ -88,6 +116,10 @@
     private SecureSettings mSecureSettings;
     @Mock
     private FeatureFlags mFeatureFlags;
+    @Captor
+    ArgumentCaptor<GetWalletCardsRequest> mRequestCaptor;
+    @Captor
+    ArgumentCaptor<QuickAccessWalletClient.OnWalletCardsRetrievedCallback> mCallbackCaptor;
 
     private TestableLooper mTestableLooper;
     private QuickAccessWalletTile mTile;
@@ -115,6 +147,7 @@
                 mKeyguardStateController,
                 mPackageManager,
                 mSecureSettings,
+                MoreExecutors.directExecutor(),
                 mFeatureFlags);
     }
 
@@ -204,4 +237,112 @@
 
         assertEquals(Tile.STATE_UNAVAILABLE, state.state);
     }
+
+    @Test
+    public void testHandleSetListening_queryCards() {
+        mTile.handleSetListening(true);
+
+        verify(mQuickAccessWalletClient)
+                .getWalletCards(any(), mRequestCaptor.capture(), 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(2, request.getMaxCards());
+        assertThat(mCallbackCaptor.getValue()).isInstanceOf(
+                QuickAccessWalletClient.OnWalletCardsRetrievedCallback.class);
+    }
+
+    @Test
+    public void testHandleSetListening_queryCards_hasCards_updateSideViewDrawable() {
+        GetWalletCardsResponse response =
+                new GetWalletCardsResponse(
+                        Collections.singletonList(createWalletCard(mContext)), 0);
+
+        mTile.handleSetListening(true);
+
+        verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+        mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+        mTestableLooper.processAllMessages();
+
+        assertNotNull(mTile.getState().sideViewDrawable);
+    }
+
+    @Test
+    public void testState_queryCards_hasCards_then_noCards() {
+        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().sideViewDrawable);
+
+        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().sideViewDrawable);
+    }
+
+    @Test
+    public void testHandleSetListening_queryCards_noCards_notUpdateSideViewDrawable() {
+        QSTile.State state = new QSTile.State();
+        GetWalletCardsResponse response = new GetWalletCardsResponse(Collections.EMPTY_LIST, 0);
+
+        mTile.handleSetListening(true);
+
+        verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+        mCallbackCaptor.getValue().onWalletCardsRetrieved(response);
+        mTestableLooper.processAllMessages();
+
+        assertNull(mTile.getState().sideViewDrawable);
+    }
+
+    @Test
+    public void testHandleSetListening_queryCards_error_notUpdateSideViewDrawable() {
+        String errorMessage = "getWalletCardsError";
+        GetWalletCardsError error = new GetWalletCardsError(CARD_IMAGE, errorMessage);
+
+        mTile.handleSetListening(true);
+
+        verify(mQuickAccessWalletClient).getWalletCards(any(), any(), mCallbackCaptor.capture());
+
+        mCallbackCaptor.getValue().onWalletCardRetrievalError(error);
+        mTestableLooper.processAllMessages();
+
+        assertNull(mTile.getState().sideViewDrawable);
+    }
+
+    @Test
+    public void testHandleSetListening_notListening_notQueryCards() {
+        mTile.handleSetListening(false);
+
+        verifyZeroInteractions(mQuickAccessWalletClient);
+    }
+
+    private WalletCard createWalletCard(Context context) {
+        PendingIntent pendingIntent =
+                PendingIntent.getActivity(context, 0, mWalletIntent, PendingIntent.FLAG_IMMUTABLE);
+        return new WalletCard.Builder(CARD_ID, CARD_IMAGE, "description", pendingIntent).build();
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
index 6d293b5..3ed8ecf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessSliderTest.kt
@@ -20,7 +20,6 @@
 import android.view.MotionEvent
 import android.view.View
 import android.view.ViewGroup
-import android.widget.CompoundButton
 import android.widget.SeekBar
 import androidx.test.filters.SmallTest
 import com.android.settingslib.RestrictedLockUtils
@@ -37,14 +36,12 @@
 import org.junit.runner.RunWith
 import org.mockito.ArgumentCaptor
 import org.mockito.ArgumentMatchers.anyBoolean
-import org.mockito.ArgumentMatchers.anyInt
 import org.mockito.Captor
 import org.mockito.Mock
 import org.mockito.Mockito.isNull
 import org.mockito.Mockito.mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.notNull
-import org.mockito.Mockito.times
 import org.mockito.Mockito.verify
 import org.mockito.MockitoAnnotations
 import org.mockito.Mockito.`when` as whenever
@@ -72,10 +69,6 @@
     private lateinit var seekBarChangeCaptor: ArgumentCaptor<SeekBar.OnSeekBarChangeListener>
     @Mock
     private lateinit var seekBar: SeekBar
-    @Captor
-    private lateinit var checkedChangeCaptor: ArgumentCaptor<CompoundButton.OnCheckedChangeListener>
-    @Mock
-    private lateinit var compoundButton: CompoundButton
     private var mFalsingManager: FalsingManagerFake = FalsingManagerFake()
 
     private lateinit var mController: BrightnessSlider
@@ -101,7 +94,6 @@
     fun testListenersAddedOnAttach() {
         mController.onViewAttached()
 
-        verify(brightnessSliderView).setOnCheckedChangeListener(notNull())
         verify(brightnessSliderView).setOnSeekBarChangeListener(notNull())
     }
 
@@ -111,7 +103,6 @@
         mController.onViewDetached()
 
         verify(brightnessSliderView).setOnSeekBarChangeListener(isNull())
-        verify(brightnessSliderView).setOnCheckedChangeListener(isNull())
         verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull())
     }
 
@@ -127,7 +118,6 @@
 
         verify(brightnessSliderView, never()).max
         verify(brightnessSliderView, never()).value
-        verify(brightnessSliderView, never()).isChecked
         verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull())
     }
 
@@ -139,7 +129,6 @@
 
         verify(brightnessSliderView, never()).max
         verify(brightnessSliderView, never()).value
-        verify(brightnessSliderView, never()).isChecked
         verify(brightnessSliderView).setOnDispatchTouchEventListener(isNull())
     }
 
@@ -150,40 +139,15 @@
         val checked = true
         whenever(brightnessSliderView.max).thenReturn(maxValue)
         whenever(brightnessSliderView.value).thenReturn(progress)
-        whenever(brightnessSliderView.isChecked).thenReturn(checked)
 
         mController.setMirrorControllerAndMirror(mirrorController)
 
         verify(mirror).max = maxValue
-        verify(mirror).isChecked = checked
         verify(mirror).value = progress
         verify(brightnessSliderView).setOnDispatchTouchEventListener(notNull())
     }
 
     @Test
-    fun testSetCheckedRelayed_true() {
-        mController.isChecked = true
-        verify(brightnessSliderView).isChecked = true
-    }
-
-    @Test
-    fun testSetCheckedRelayed_false() {
-        mController.isChecked = false
-        verify(brightnessSliderView).isChecked = false
-    }
-
-    @Test
-    fun testGetChecked() {
-        whenever(brightnessSliderView.isChecked).thenReturn(true)
-
-        assertThat(mController.isChecked).isTrue()
-
-        whenever(brightnessSliderView.isChecked).thenReturn(false)
-
-        assertThat(mController.isChecked).isFalse()
-    }
-
-    @Test
     fun testSetMaxRelayed() {
         mController.max = 120
         verify(brightnessSliderView).max = 120
@@ -229,13 +193,12 @@
     @Test
     fun testSeekBarProgressChanged() {
         mController.onViewAttached()
-        whenever(brightnessSliderView.isChecked).thenReturn(true)
 
         verify(brightnessSliderView).setOnSeekBarChangeListener(capture(seekBarChangeCaptor))
 
         seekBarChangeCaptor.value.onProgressChanged(seekBar, 23, true)
 
-        verify(listener).onChanged(anyBoolean(), eq(true), eq(23), eq(false))
+        verify(listener).onChanged(anyBoolean(), eq(23), eq(false))
     }
 
     @Test
@@ -243,7 +206,6 @@
         val parent = mock(ViewGroup::class.java)
         whenever(brightnessSliderView.value).thenReturn(42)
         whenever(brightnessSliderView.parent).thenReturn(parent)
-        whenever(brightnessSliderView.isChecked).thenReturn(true)
 
         mController.onViewAttached()
         mController.setMirrorControllerAndMirror(mirrorController)
@@ -251,7 +213,7 @@
 
         seekBarChangeCaptor.value.onStartTrackingTouch(seekBar)
 
-        verify(listener).onChanged(eq(true), eq(true), eq(42), eq(false))
+        verify(listener).onChanged(eq(true), eq(42), eq(false))
         verify(mirrorController).showMirror()
         verify(mirrorController).setLocation(parent)
     }
@@ -259,7 +221,6 @@
     @Test
     fun testSeekBarTrackingStopped() {
         whenever(brightnessSliderView.value).thenReturn(23)
-        whenever(brightnessSliderView.isChecked).thenReturn(true)
 
         mController.onViewAttached()
         mController.setMirrorControllerAndMirror(mirrorController)
@@ -267,38 +228,7 @@
 
         seekBarChangeCaptor.value.onStopTrackingTouch(seekBar)
 
-        verify(listener).onChanged(eq(false), eq(true), eq(23), eq(true))
+        verify(listener).onChanged(eq(false), eq(23), eq(true))
         verify(mirrorController).hideMirror()
     }
-
-    @Test
-    fun testButtonCheckedChanged_false() {
-        val checked = false
-
-        mController.onViewAttached()
-        mController.setMirrorControllerAndMirror(mirrorController)
-        verify(brightnessSliderView).setOnCheckedChangeListener(capture(checkedChangeCaptor))
-
-        checkedChangeCaptor.value.onCheckedChanged(compoundButton, checked)
-
-        verify(brightnessSliderView).enableSlider(!checked)
-        verify(listener).onChanged(anyBoolean(), eq(checked), anyInt(), eq(false))
-        // Called once with false when the mirror is set
-        verify(mirror, times(2)).isChecked = checked
-    }
-
-    @Test
-    fun testButtonCheckedChanged_true() {
-        val checked = true
-
-        mController.onViewAttached()
-        mController.setMirrorControllerAndMirror(mirrorController)
-        verify(brightnessSliderView).setOnCheckedChangeListener(capture(checkedChangeCaptor))
-
-        checkedChangeCaptor.value.onCheckedChanged(compoundButton, checked)
-
-        verify(brightnessSliderView).enableSlider(!checked)
-        verify(listener).onChanged(anyBoolean(), eq(checked), anyInt(), eq(false))
-        verify(mirror).isChecked = checked
-    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 094a70e..ac160d6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -54,6 +54,7 @@
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.CastController.CastDevice;
 import com.android.systemui.statusbar.policy.DataSaverController;
+import com.android.systemui.statusbar.policy.DeviceControlsController;
 import com.android.systemui.statusbar.policy.HotspotController;
 import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SecureSettings;
@@ -94,6 +95,7 @@
     @Mock private ManagedProfileController mManagedProfileController;
     @Mock private NightDisplayListener mNightDisplayListener;
     @Mock private ReduceBrightColorsController mReduceBrightColorsController;
+    @Mock private DeviceControlsController mDeviceControlsController;;
     @Mock(answer = Answers.RETURNS_SELF)
     private AutoAddTracker.Builder mAutoAddTrackerBuilder;
     @Mock private Context mUserContext;
@@ -139,6 +141,7 @@
             NightDisplayListener nightDisplayListener,
             CastController castController,
             ReduceBrightColorsController reduceBrightColorsController,
+            DeviceControlsController deviceControlsController,
             @Named(RBC_AVAILABLE) boolean isReduceBrightColorsAvailable) {
         return new AutoTileManager(context, autoAddTrackerBuilder, mQsTileHost,
                 Handler.createAsync(TestableLooper.get(this).getLooper()),
@@ -149,13 +152,15 @@
                 nightDisplayListener,
                 castController,
                 reduceBrightColorsController,
+                deviceControlsController,
                 isReduceBrightColorsAvailable);
     }
 
     private AutoTileManager createAutoTileManager(Context context) {
         return createAutoTileManager(context, mAutoAddTrackerBuilder, mHotspotController,
                 mDataSaverController, mManagedProfileController, mNightDisplayListener,
-                mCastController, mReduceBrightColorsController, mIsReduceBrightColorsAvailable);
+                mCastController, mReduceBrightColorsController, mDeviceControlsController,
+                mIsReduceBrightColorsAvailable);
     }
 
     @Test
@@ -169,10 +174,11 @@
         NightDisplayListener nDS = mock(NightDisplayListener.class);
         CastController cC = mock(CastController.class);
         ReduceBrightColorsController rBC = mock(ReduceBrightColorsController.class);
+        DeviceControlsController dCC = mock(DeviceControlsController.class);
 
         AutoTileManager manager =
                 createAutoTileManager(mock(Context.class), builder, hC, dSC, mPC, nDS, cC, rBC,
-                        true);
+                        dCC, true);
 
         verify(tracker, never()).initialize();
         verify(hC, never()).addCallback(any());
@@ -181,6 +187,7 @@
         verify(nDS, never()).setCallback(any());
         verify(cC, never()).addCallback(any());
         verify(rBC, never()).addCallback(any());
+        verify(dCC, never()).setCallback(any());
         assertNull(manager.getSecureSettingForKey(TEST_SETTING));
         assertNull(manager.getSecureSettingForKey(TEST_SETTING_COMPONENT));
     }
@@ -229,6 +236,10 @@
         inOrderCast.verify(mCastController).removeCallback(any());
         inOrderCast.verify(mCastController).addCallback(any());
 
+        InOrder inOrderDevices = inOrder(mDeviceControlsController);
+        inOrderDevices.verify(mDeviceControlsController).removeCallback();
+        inOrderDevices.verify(mDeviceControlsController).setCallback(any());
+
         SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
         assertEquals(USER + 1, setting.getCurrentUser());
         assertTrue(setting.isListening());
@@ -273,6 +284,10 @@
         inOrderCast.verify(mCastController).removeCallback(any());
         inOrderCast.verify(mCastController, never()).addCallback(any());
 
+        InOrder inOrderDevices = inOrder(mDeviceControlsController);
+        inOrderDevices.verify(mDeviceControlsController).removeCallback();
+        inOrderDevices.verify(mDeviceControlsController).setCallback(any());
+
         SecureSetting setting = mAutoTileManager.getSecureSettingForKey(TEST_SETTING);
         assertEquals(USER + 1, setting.getCurrentUser());
         assertFalse(setting.isListening());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 39f0db5..98a4487 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -143,6 +143,7 @@
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
 import com.android.systemui.statusbar.policy.UserInfoControllerImpl;
 import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.volume.VolumeComponent;
@@ -265,6 +266,7 @@
     @Mock private BrightnessSlider.Factory mBrightnessSliderFactory;
     @Mock private WiredChargingRippleController mWiredChargingRippleController;
     @Mock private OngoingCallController mOngoingCallController;
+    @Mock private TunerService mTunerService;
     @Mock private FeatureFlags mFeatureFlags;
     private ShadeController mShadeController;
     private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -430,6 +432,7 @@
                 mBrightnessSliderFactory,
                 mWiredChargingRippleController,
                 mOngoingCallController,
+                mTunerService,
                 mFeatureFlags);
 
         when(mNotificationShadeWindowView.findViewById(R.id.lock_icon_container)).thenReturn(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
new file mode 100644
index 0000000..95a363e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/DeviceControlsControllerImplTest.kt
@@ -0,0 +1,151 @@
+/*
+ * 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.policy
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.ServiceInfo
+import android.testing.AndroidTestingRunner
+
+import androidx.test.filters.SmallTest
+
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.dagger.ControlsComponent
+import com.android.systemui.controls.controller.ControlsController
+import com.android.systemui.controls.controller.SeedResponse
+import com.android.systemui.controls.controller.StructureInfo
+import com.android.systemui.controls.management.ControlsListingController
+import com.android.systemui.controls.management.ControlsListingController.ControlsListingCallback
+import com.android.systemui.settings.UserContextProvider
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_SEEDING_COMPLETED
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.QS_DEFAULT_POSITION
+import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.QS_PRIORITY_POSITION
+
+import java.util.Optional
+import java.util.function.Consumer
+
+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.Mockito
+import org.mockito.MockitoAnnotations
+import org.mockito.Mockito.never
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.ArgumentMatchers.anyInt
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DeviceControlsControllerImplTest : SysuiTestCase() {
+
+    @Mock
+    private lateinit var controlsComponent: ControlsComponent
+    @Mock
+    private lateinit var controlsController: ControlsController
+    @Mock
+    private lateinit var controlsListingController: ControlsListingController
+    @Mock
+    private lateinit var callback: DeviceControlsController.Callback
+    @Captor
+    private lateinit var listingCallbackCaptor: ArgumentCaptor<ControlsListingCallback>
+    @Mock
+    private lateinit var structureInfo: StructureInfo
+    @Mock
+    private lateinit var serviceInfo: ServiceInfo
+    @Mock
+    private lateinit var userContextProvider: UserContextProvider
+    @Captor
+    private lateinit var seedCallback: ArgumentCaptor<Consumer<SeedResponse>>
+
+    private lateinit var controlsServiceInfo: ControlsServiceInfo
+    private lateinit var controller: DeviceControlsControllerImpl
+
+    companion object {
+        fun <T> capture(argumentCaptor: ArgumentCaptor<T>): T = argumentCaptor.capture()
+        fun <T> eq(value: T): T = Mockito.eq(value) ?: value
+        private val TEST_PKG = "test.pkg"
+        private val TEST_COMPONENT = ComponentName(TEST_PKG, "test.class")
+    }
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        `when`(controlsComponent.getControlsController())
+                .thenReturn(Optional.of(controlsController))
+        `when`(controlsComponent.getControlsListingController())
+                .thenReturn(Optional.of(controlsListingController))
+
+        controller = DeviceControlsControllerImpl(mContext, controlsComponent, userContextProvider)
+
+        `when`(serviceInfo.componentName).thenReturn(TEST_COMPONENT)
+        controlsServiceInfo = ControlsServiceInfo(mContext, serviceInfo)
+
+        `when`(userContextProvider.userContext).thenReturn(mContext)
+        mContext.getSharedPreferences(PREFS_CONTROLS_FILE, Context.MODE_PRIVATE).edit()
+            .putStringSet(PREFS_CONTROLS_SEEDING_COMPLETED, emptySet())
+            .apply()
+    }
+
+    @Test
+    fun testNoCallbackWhenNoServicesAvailable() {
+        `when`(controlsController.getFavorites()).thenReturn(emptyList())
+        controller.setCallback(callback)
+
+        verify(controlsListingController).addCallback(capture(listingCallbackCaptor))
+        listingCallbackCaptor.value.onServicesUpdated(emptyList())
+        verify(callback, never()).onControlsAvailable(anyInt())
+    }
+
+    @Test
+    fun testSetPriorityPositionIsSetWhenFavoritesAreAvailable() {
+        `when`(controlsController.getFavorites()).thenReturn(listOf(structureInfo))
+        controller.setCallback(callback)
+
+        verify(controlsListingController).addCallback(capture(listingCallbackCaptor))
+        listingCallbackCaptor.value.onServicesUpdated(listOf(controlsServiceInfo))
+        verify(callback).onControlsAvailable(QS_PRIORITY_POSITION)
+    }
+
+    @Test
+    fun testSetDefaultPositionIsSetWhenNoFavoritesAreAvailable() {
+        `when`(controlsController.getFavorites()).thenReturn(emptyList())
+        controller.setCallback(callback)
+
+        mContext.getOrCreateTestableResources().addOverride(
+                R.array.config_controlsPreferredPackages,
+                arrayOf(TEST_PKG))
+
+        verify(controlsListingController).addCallback(capture(listingCallbackCaptor))
+        listingCallbackCaptor.value.onServicesUpdated(listOf(controlsServiceInfo))
+
+        verify(controlsController).seedFavoritesForComponents(
+            eq(listOf(TEST_COMPONENT)),
+            capture(seedCallback)
+        )
+        seedCallback.value.accept(SeedResponse(TEST_PKG, true))
+        verify(callback).onControlsAvailable(QS_DEFAULT_POSITION)
+    }
+}
diff --git a/packages/WAPPushManager/AndroidManifest.xml b/packages/WAPPushManager/AndroidManifest.xml
index a75fb2d..15f01e6 100644
--- a/packages/WAPPushManager/AndroidManifest.xml
+++ b/packages/WAPPushManager/AndroidManifest.xml
@@ -27,7 +27,8 @@
 
     <original-package android:name="com.android.smspush" />
     <application
-        android:allowClearUserData="false">
+        android:allowClearUserData="false"
+        android:directBootAware="true">
         <service android:name=".WapPushManager"
             android:permission="com.android.smspush.WAPPUSH_MANAGER_BIND"
             android:exported="true">
diff --git a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
index dc2707b..951e64f 100755
--- a/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
+++ b/packages/WAPPushManager/src/com/android/smspush/WapPushManager.java
@@ -26,17 +26,21 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.database.Cursor;
-import android.database.sqlite.SQLiteOpenHelper;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
 import android.os.Build;
 import android.os.IBinder;
 import android.os.PowerManager;
 import android.os.RemoteException;
+import android.os.UserManager;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IWapPushManager;
 import com.android.internal.telephony.WapPushManagerParams;
 
+import java.io.File;
+
 /**
  * The WapPushManager service is implemented to process incoming
  * WAP Push messages and to maintain the Receiver Application/Application
@@ -67,8 +71,13 @@
     /**
      * Inner class that deals with application ID table
      */
-    private class WapPushManDBHelper extends SQLiteOpenHelper {
-        WapPushManDBHelper(Context context) {
+    @VisibleForTesting
+    public static class WapPushManDBHelper extends SQLiteOpenHelper {
+        /**
+         * Constructor
+         */
+        @VisibleForTesting
+        public WapPushManDBHelper(Context context) {
             super(context, DATABASE_NAME, null, WAP_PUSH_MANAGER_VERSION);
             if (LOCAL_LOGV) Log.v(LOG_TAG, "helper instance created.");
         }
@@ -269,10 +278,6 @@
                 int app_type, boolean need_signature, boolean further_processing) {
             WapPushManDBHelper dbh = getDatabase(mContext);
             SQLiteDatabase db = dbh.getWritableDatabase();
-            WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, x_app_id, content_type);
-            boolean ret = false;
-            boolean insert = false;
-            int sq = 0;
 
             if (!appTypeCheck(app_type)) {
                 Log.w(LOG_TAG, "invalid app_type " + app_type + ". app_type must be "
@@ -280,34 +285,8 @@
                         + WapPushManagerParams.APP_TYPE_SERVICE);
                 return false;
             }
-
-            if (lastapp == null) {
-                insert = true;
-                sq = 0;
-            } else if (!lastapp.packageName.equals(package_name) ||
-                    !lastapp.className.equals(class_name)) {
-                insert = true;
-                sq = lastapp.installOrder + 1;
-            }
-
-            if (insert) {
-                ContentValues values = new ContentValues();
-
-                values.put("x_wap_application", x_app_id);
-                values.put("content_type", content_type);
-                values.put("package_name", package_name);
-                values.put("class_name", class_name);
-                values.put("app_type", app_type);
-                values.put("need_signature", need_signature ? 1 : 0);
-                values.put("further_processing", further_processing ? 1 : 0);
-                values.put("install_order", sq);
-                db.insert(APPID_TABLE_NAME, null, values);
-                if (LOCAL_LOGV) Log.v(LOG_TAG, "add:" + x_app_id + ":" + content_type
-                        + " " + package_name + "." + class_name
-                        + ", newsq:" + sq);
-                ret = true;
-            }
-
+            boolean ret = insertPackage(dbh, db, x_app_id, content_type, package_name, class_name,
+                    app_type, need_signature, further_processing);
             db.close();
 
             return ret;
@@ -404,11 +383,91 @@
     protected WapPushManDBHelper getDatabase(Context context) {
         if (mDbHelper == null) {
             if (LOCAL_LOGV) Log.v(LOG_TAG, "create new db inst.");
-            mDbHelper = new WapPushManDBHelper(context);
+            mDbHelper = new WapPushManDBHelper(context.createDeviceProtectedStorageContext());
         }
+        // Migrate existing legacy database into the device encrypted storage.
+        migrateWapPushManDBIfNeeded(context);
         return mDbHelper;
     }
 
+    /**
+     * Inserts a package information into a database
+     */
+    @VisibleForTesting
+    public boolean insertPackage(WapPushManDBHelper dbh, SQLiteDatabase db, String appId,
+            String contentType, String packageName, String className, int appType,
+            boolean needSignature, boolean furtherProcessing) {
+
+        WapPushManDBHelper.queryData lastapp = dbh.queryLastApp(db, appId, contentType);
+        boolean insert = false;
+        int sq = 0;
+
+        if (lastapp == null) {
+            insert = true;
+            sq = 0;
+        } else if (!lastapp.packageName.equals(packageName)
+                || !lastapp.className.equals(className)) {
+            insert = true;
+            sq = lastapp.installOrder + 1;
+        }
+
+        if (insert) {
+            ContentValues values = new ContentValues();
+
+            values.put("x_wap_application", appId);
+            values.put("content_type", contentType);
+            values.put("package_name", packageName);
+            values.put("class_name", className);
+            values.put("app_type", appType);
+            values.put("need_signature", needSignature ? 1 : 0);
+            values.put("further_processing", furtherProcessing ? 1 : 0);
+            values.put("install_order", sq);
+            db.insert(APPID_TABLE_NAME, null, values);
+            if (LOCAL_LOGV) {
+                Log.v(LOG_TAG, "add:" + appId + ":" + contentType + " " + packageName
+                        + "." + className + ", newsq:" + sq);
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Migrates a legacy database into the device encrypted storage
+     */
+    private void migrateWapPushManDBIfNeeded(Context context) {
+        UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        File file = context.getDatabasePath(DATABASE_NAME);
+        if (!userManager.isUserUnlocked() || !file.exists()) {
+            // Check if the device is unlocked because a legacy database can't access during
+            // DirectBoot.
+            return;
+        }
+
+        // Migration steps below:
+        // 1. Merge the package info to legacy database if there is any package info which is
+        // registered during DirectBoot.
+        // 2. Move the data base to the device encryped storage.
+        WapPushManDBHelper legacyDbHelper = new WapPushManDBHelper(context);
+        SQLiteDatabase legacyDb = legacyDbHelper.getWritableDatabase();
+        SQLiteDatabase db = mDbHelper.getWritableDatabase();
+        Cursor cur = db.query(APPID_TABLE_NAME, null, null, null, null, null, null);
+        while (cur.moveToNext()) {
+            insertPackage(legacyDbHelper, legacyDb,
+                    cur.getString(cur.getColumnIndex("x_wap_application")),
+                    cur.getString(cur.getColumnIndex("content_type")),
+                    cur.getString(cur.getColumnIndex("package_name")),
+                    cur.getString(cur.getColumnIndex("class_name")),
+                    cur.getInt(cur.getColumnIndex("app_type")),
+                    cur.getInt(cur.getColumnIndex("need_signature")) == 1,
+                    cur.getInt(cur.getColumnIndex("further_processing")) == 1);
+        }
+        cur.close();
+        legacyDb.close();
+        db.close();
+        context.createDeviceProtectedStorageContext().moveDatabaseFrom(context, DATABASE_NAME);
+        Log.i(LOG_TAG, "Migrated the legacy database.");
+    }
 
     /**
      * This method is used for testing
diff --git a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
index f7afc57..b9dac4e 100644
--- a/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
+++ b/packages/WAPPushManager/tests/src/com/android/smspush/unitTests/WapPushTest.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.database.sqlite.SQLiteDatabase;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.provider.Telephony.Sms.Intents;
@@ -33,7 +34,9 @@
 import com.android.internal.telephony.uicc.IccUtils;
 import com.android.internal.util.HexDump;
 import com.android.smspush.WapPushManager;
+import com.android.smspush.WapPushManager.WapPushManDBHelper;
 
+import java.io.File;
 import java.util.Random;
 
 /**
@@ -467,8 +470,9 @@
         try {
             super.setUp();
             // get verifier
-            getContext().bindService(new Intent(IDataVerify.class.getName()),
-                    mConn, Context.BIND_AUTO_CREATE);
+            Intent intent = new Intent(IDataVerify.class.getName());
+            intent.setPackage("com.android.smspush.unitTests");
+            getContext().bindService(intent, mConn, Context.BIND_AUTO_CREATE);
         } catch (Exception e) {
             Log.w(LOG_TAG, "super exception");
         }
@@ -552,15 +556,15 @@
     }
 
     /**
-     * Add sqlite injection test
+     * Sqlite injection test
      */
-    public void testAddPackage0() {
+    public void testSqliteInjection() {
         String inject = "' union select 0,'com.android.settings','com.android.settings.Settings',0,0,0--";
 
-        // insert new data
+        // update data
         IWapPushManager iwapman = getInterface();
         try {
-            assertFalse(iwapman.addPackage(
+            assertFalse(iwapman.updatePackage(
                     inject,
                     Integer.toString(mContentTypeValue),
                     mPackageName, mClassName,
@@ -2528,4 +2532,45 @@
         mMessageBody = originalMessageBody;
     }
 
+    /**
+     * DataBase migration test.
+     */
+    public void testDataBaseMigration() {
+        IWapPushManager iwapman = getInterface();
+        WapPushManager wpman = getService();
+        Context context = getContext();
+
+        addPackageToLegacyDB(mAppIdValue, mContentTypeValue, mPackageName, mClassName,
+                WapPushManagerParams.APP_TYPE_SERVICE, true, true);
+        addPackageToLegacyDB(mAppIdValue + 10, mContentTypeValue, mPackageName, mClassName,
+                WapPushManagerParams.APP_TYPE_SERVICE, true, true);
+
+        File oldDbFile = context.getDatabasePath("wappush.db");
+        assertTrue(oldDbFile.exists());
+        assertTrue(wpman.verifyData(Integer.toString(mAppIdValue),
+                Integer.toString(mContentTypeValue),
+                mPackageName, mClassName,
+                WapPushManagerParams.APP_TYPE_SERVICE, true, true));
+        assertFalse(oldDbFile.exists());
+
+        // Clean up DB
+        try {
+            iwapman.deletePackage(Integer.toString(mAppIdValue),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+            iwapman.deletePackage(Integer.toString(mAppIdValue + 10),
+                    Integer.toString(mContentTypeValue), mPackageName, mClassName);
+        } catch (RemoteException e) {
+            assertTrue(false);
+        }
+    }
+
+    private void addPackageToLegacyDB(int appId, int contextType, String packagename,
+            String classnName, int appType, boolean signature, boolean furtherProcessing) {
+        WapPushManager wpman = getService();
+        WapPushManDBHelper dbh = new WapPushManDBHelper(getContext());
+        SQLiteDatabase db = dbh.getWritableDatabase();
+
+        wpman.insertPackage(dbh, db, Integer.toString(appId), Integer.toString(contextType),
+                packagename, classnName, appType, signature, furtherProcessing);
+    }
 }
diff --git a/services/Android.bp b/services/Android.bp
index 81bb579..25b270e 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -123,6 +123,7 @@
     libs: [
         "android.hidl.manager-V1.0-java",
         "framework-tethering.stubs.module_lib",
+        "service-art.stubs.system_server",
     ],
 
     // Uncomment to enable output of certain warnings (deprecated, unchecked)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2390589..185cdfc 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1294,14 +1294,22 @@
 
         mPermissionMonitor = new PermissionMonitor(mContext, mNetd);
 
+        mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
         // Listen for user add/removes to inform PermissionMonitor.
         // Should run on mHandler to avoid any races.
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_ADDED);
-        intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+        final IntentFilter userIntentFilter = new IntentFilter();
+        userIntentFilter.addAction(Intent.ACTION_USER_ADDED);
+        userIntentFilter.addAction(Intent.ACTION_USER_REMOVED);
+        mUserAllContext.registerReceiver(mUserIntentReceiver, userIntentFilter,
+                null /* broadcastPermission */, mHandler);
 
-        mUserAllContext = mContext.createContextAsUser(UserHandle.ALL, 0 /* flags */);
-        mUserAllContext.registerReceiver(mIntentReceiver, intentFilter,
+        // Listen to package add/removes for netd
+        final IntentFilter packageIntentFilter = new IntentFilter();
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        packageIntentFilter.addDataScheme("package");
+        mUserAllContext.registerReceiver(mPackageIntentReceiver, packageIntentFilter,
                 null /* broadcastPermission */, mHandler);
 
         mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNetd);
@@ -4977,7 +4985,7 @@
     }
 
     @Override
-    public void setGlobalProxy(final ProxyInfo proxyProperties) {
+    public void setGlobalProxy(@Nullable final ProxyInfo proxyProperties) {
         PermissionUtils.enforceNetworkStackPermission(mContext);
         mProxyTracker.setGlobalProxy(proxyProperties);
     }
@@ -5322,14 +5330,14 @@
         }
     }
 
-    private void onUserAdded(UserHandle user) {
+    private void onUserAdded(@NonNull final UserHandle user) {
         mPermissionMonitor.onUserAdded(user);
         if (mOemNetworkPreferences.getNetworkPreferences().size() > 0) {
             handleSetOemNetworkPreference(mOemNetworkPreferences, null);
         }
     }
 
-    private void onUserRemoved(UserHandle user) {
+    private void onUserRemoved(@NonNull final UserHandle user) {
         mPermissionMonitor.onUserRemoved(user);
         // If there was a network preference for this user, remove it.
         handleSetProfileNetworkPreference(new ProfileNetworkPreferences.Preference(user, null),
@@ -5339,7 +5347,18 @@
         }
     }
 
-    private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+    private void onPackageChanged(@NonNull final String packageName) {
+        // This is necessary in case a package is added or removed, but also when it's replaced to
+        // run as a new UID by its manifest rules. Also, if a separate package shares the same UID
+        // as one in the preferences, then it should follow the same routing as that other package,
+        // which means updating the rules is never to be needed in this case (whether it joins or
+        // leaves a UID with a preference).
+        if (isMappedInOemNetworkPreference(packageName)) {
+            handleSetOemNetworkPreference(mOemNetworkPreferences, null);
+        }
+    }
+
+    private final BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             ensureRunningOnConnectivityServiceThread();
@@ -5362,6 +5381,22 @@
         }
     };
 
+    private final BroadcastReceiver mPackageIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            ensureRunningOnConnectivityServiceThread();
+            switch (intent.getAction()) {
+                case Intent.ACTION_PACKAGE_ADDED:
+                case Intent.ACTION_PACKAGE_REMOVED:
+                case Intent.ACTION_PACKAGE_REPLACED:
+                    onPackageChanged(intent.getData().getSchemeSpecificPart());
+                    break;
+                default:
+                    Log.wtf(TAG, "received unexpected intent: " + intent.getAction());
+            }
+        }
+    };
+
     private final HashMap<Messenger, NetworkProviderInfo> mNetworkProviderInfos = new HashMap<>();
     private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests = new HashMap<>();
 
@@ -6198,6 +6233,15 @@
     @NonNull
     private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
 
+    /**
+     * Determine whether a given package has a mapping in the current OemNetworkPreferences.
+     * @param packageName the package name to check existence of a mapping for.
+     * @return true if a mapping exists, false otherwise
+     */
+    private boolean isMappedInOemNetworkPreference(@NonNull final String packageName) {
+        return mOemNetworkPreferences.getNetworkPreferences().containsKey(packageName);
+    }
+
     // The always-on request for an Internet-capable network that apps without a specific default
     // fall back to.
     @VisibleForTesting
@@ -6218,7 +6262,7 @@
      * @return the NetworkRequestInfo tracking the given uid.
      */
     @NonNull
-    private NetworkRequestInfo getDefaultRequestTrackingUid(@NonNull final int uid) {
+    private NetworkRequestInfo getDefaultRequestTrackingUid(final int uid) {
         for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
             if (nri == mDefaultRequest) {
                 continue;
@@ -6736,7 +6780,7 @@
                     mDeps.reportNetworkInterfaceForTransports(mContext, iface,
                             caps.getTransportTypes());
                 } catch (Exception e) {
-                    loge("Exception adding interface: " + e);
+                    logw("Exception adding interface: " + e);
                 }
             }
         }
@@ -9695,7 +9739,6 @@
                 new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
         replaceDefaultNetworkRequestsForPreference(nris);
         mOemNetworkPreferences = preference;
-        // TODO http://b/176496396 persist data to shared preferences.
 
         if (null != listener) {
             try {
@@ -9852,7 +9895,6 @@
                     // packages are sent on a network preference as the system will watch for
                     // package installations associated with this network preference and update
                     // accordingly. This is done so as to minimize race conditions on app install.
-                    // TODO b/177092163 add app install watching.
                     continue;
                 }
             }
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 09f4c22..f591802 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -249,6 +249,8 @@
         @Override
         public void onUserSwitching(@Nullable TargetUser from, @NonNull TargetUser to) {
             mStorageManagerService.mCurrentUserId = to.getUserIdentifier();
+            // To reset public volume mounts
+            mStorageManagerService.onUserSwitching(mStorageManagerService.mCurrentUserId);
         }
 
         @Override
@@ -1218,6 +1220,28 @@
         }
     }
 
+    private void onUserSwitching(int userId) {
+        boolean reset = false;
+        List<VolumeInfo> volumesToRemount = new ArrayList<>();
+        synchronized (mLock) {
+            for (int i = 0; i < mVolumes.size(); i++) {
+                final VolumeInfo vol = mVolumes.valueAt(i);
+                if (!vol.isPrimary() && vol.isMountedWritable() && vol.isVisible()
+                        && vol.getMountUserId() != mCurrentUserId) {
+                    // If there's a visible secondary volume mounted,
+                    // we need to update the currentUserId and remount
+                    vol.mountUserId = mCurrentUserId;
+                    volumesToRemount.add(vol);
+                }
+            }
+        }
+
+        for (VolumeInfo vol : volumesToRemount) {
+            mHandler.obtainMessage(H_VOLUME_UNMOUNT, vol).sendToTarget();
+            mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget();
+        }
+    }
+
     private boolean supportsBlockCheckpoint() throws RemoteException {
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
         return mVold.supportsBlockCheckpoint();
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index ed579f2..ed77147 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
@@ -35,7 +37,9 @@
 import android.content.IntentFilter;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.vcn.IVcnManagementService;
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -161,6 +165,9 @@
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final BroadcastReceiver mPkgChangeReceiver;
 
+    @NonNull
+    private final TrackingNetworkCallback mTrackingNetworkCallback = new TrackingNetworkCallback();
+
     /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
     @Nullable private LocationPermissionChecker mLocationPermissionChecker;
 
@@ -356,6 +363,10 @@
     public void systemReady() {
         mContext.getSystemService(ConnectivityManager.class)
                 .registerNetworkProvider(mNetworkProvider);
+        mContext.getSystemService(ConnectivityManager.class)
+                .registerNetworkCallback(
+                        new NetworkRequest.Builder().clearCapabilities().build(),
+                        mTrackingNetworkCallback);
         mTelephonySubscriptionTracker.register();
         mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext());
     }
@@ -530,16 +541,7 @@
 
         if (mVcns.containsKey(subscriptionGroup)) {
             final Vcn vcn = mVcns.get(subscriptionGroup);
-            final int status = vcn.getStatus();
             vcn.updateConfig(config);
-
-            // TODO(b/183174340): Remove this once opportunistic-safe-mode is supported
-            // Only notify VcnStatusCallbacks if this VCN was previously in Safe Mode
-            if (status == VCN_STATUS_CODE_SAFE_MODE) {
-                // TODO(b/181789060): invoke asynchronously after Vcn notifies through VcnCallback
-                notifyAllPermissionedStatusCallbacksLocked(
-                        subscriptionGroup, VCN_STATUS_CODE_ACTIVE);
-            }
         } else {
             startVcnLocked(subscriptionGroup, config);
         }
@@ -791,8 +793,9 @@
                         NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
             }
 
+            final NetworkCapabilities result = ncBuilder.build();
             return new VcnUnderlyingNetworkPolicy(
-                    false /* isTearDownRequested */, ncBuilder.build());
+                    mTrackingNetworkCallback.requiresRestartForCarrierWifi(result), result);
         });
     }
 
@@ -928,8 +931,8 @@
     // TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
     /** Callback for Vcn signals sent up to VcnManagementService. */
     public interface VcnCallback {
-        /** Called by a Vcn to signal that it has entered safe mode. */
-        void onEnteredSafeMode();
+        /** Called by a Vcn to signal that its safe mode status has changed. */
+        void onSafeModeStatusChanged(boolean isInSafeMode);
 
         /** Called by a Vcn to signal that an error occurred. */
         void onGatewayConnectionError(
@@ -939,6 +942,49 @@
                 @Nullable String exceptionMessage);
     }
 
+    /**
+     * TrackingNetworkCallback tracks all active networks
+     *
+     * <p>This is used to ensure that no underlying networks have immutable capabilities changed
+     * without requiring a Network restart.
+     */
+    private class TrackingNetworkCallback extends ConnectivityManager.NetworkCallback {
+        private final Map<Network, NetworkCapabilities> mCaps = new ArrayMap<>();
+
+        @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities caps) {
+            synchronized (mCaps) {
+                mCaps.put(network, caps);
+            }
+        }
+
+        @Override
+        public void onLost(Network network) {
+            synchronized (mCaps) {
+                mCaps.remove(network);
+            }
+        }
+
+        private boolean requiresRestartForCarrierWifi(NetworkCapabilities caps) {
+            if (!caps.hasTransport(TRANSPORT_WIFI) || caps.getSubIds() == null) {
+                return false;
+            }
+
+            synchronized (mCaps) {
+                for (NetworkCapabilities existing : mCaps.values()) {
+                    if (existing.hasTransport(TRANSPORT_WIFI)
+                            && caps.getSubIds().equals(existing.getSubIds())) {
+                        // Restart if any immutable capabilities have changed
+                        return existing.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                                != caps.hasCapability(NET_CAPABILITY_NOT_RESTRICTED);
+                    }
+                }
+            }
+
+            return false;
+        }
+    }
+
     /** VcnCallbackImpl for Vcn signals sent up to VcnManagementService. */
     private class VcnCallbackImpl implements VcnCallback {
         @NonNull private final ParcelUuid mSubGroup;
@@ -948,15 +994,18 @@
         }
 
         @Override
-        public void onEnteredSafeMode() {
+        public void onSafeModeStatusChanged(boolean isInSafeMode) {
             synchronized (mLock) {
                 // Ignore if this subscription group doesn't exist anymore
                 if (!mVcns.containsKey(mSubGroup)) {
                     return;
                 }
 
+                final int status =
+                        isInSafeMode ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
+
                 notifyAllPolicyListenersLocked();
-                notifyAllPermissionedStatusCallbacksLocked(mSubGroup, VCN_STATUS_CODE_SAFE_MODE);
+                notifyAllPermissionedStatusCallbacksLocked(mSubGroup, status);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 1839e2a..e4cb15f 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -23,6 +23,8 @@
 import android.app.ActivityManager;
 import android.app.ActivityThread;
 import android.app.ApplicationExitInfo;
+import android.database.ContentObserver;
+import android.net.Uri;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Message;
@@ -80,6 +82,8 @@
             "compact_full_delta_rss_throttle_kb";
     @VisibleForTesting static final String KEY_COMPACT_PROC_STATE_THROTTLE =
             "compact_proc_state_throttle";
+    @VisibleForTesting static final String KEY_FREEZER_DEBOUNCE_TIMEOUT =
+            "freeze_debounce_timeout";
 
     // Phenotype sends int configurations and we map them to the strings we'll use on device,
     // preventing a weird string value entering the kernel.
@@ -116,6 +120,10 @@
     // Format of this string should be a comma separated list of integers.
     @VisibleForTesting static final String DEFAULT_COMPACT_PROC_STATE_THROTTLE =
             String.valueOf(ActivityManager.PROCESS_STATE_RECEIVER);
+    @VisibleForTesting static final long DEFAULT_FREEZER_DEBOUNCE_TIMEOUT = 600_000L;
+
+    @VisibleForTesting static final Uri CACHED_APP_FREEZER_ENABLED_URI = Settings.Global.getUriFor(
+                Settings.Global.CACHED_APPS_FREEZER_ENABLED);
 
     @VisibleForTesting
     interface PropertyChangedCallbackForTest {
@@ -141,9 +149,6 @@
     static final int SET_FROZEN_PROCESS_MSG = 3;
     static final int REPORT_UNFREEZE_MSG = 4;
 
-    //TODO:change this static definition into a configurable flag.
-    static final long FREEZE_TIMEOUT_MS = 600000;
-
     static final int DO_FREEZE = 1;
     static final int REPORT_UNFREEZE = 2;
 
@@ -198,6 +203,8 @@
                                 updateMinOomAdjThrottle();
                             } else if (KEY_COMPACT_THROTTLE_MAX_OOM_ADJ.equals(name)) {
                                 updateMaxOomAdjThrottle();
+                            } else if (KEY_FREEZER_DEBOUNCE_TIMEOUT.equals(name)) {
+                                updateFreezerDebounceTimeout();
                             }
                         }
                     }
@@ -207,6 +214,23 @@
                 }
             };
 
+    private final class SettingsContentObserver extends ContentObserver {
+        SettingsContentObserver() {
+            super(mAm.mHandler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (CACHED_APP_FREEZER_ENABLED_URI.equals(uri)) {
+                synchronized (mPhenotypeFlagLock) {
+                    updateUseFreezer();
+                }
+            }
+        }
+    }
+
+    private final SettingsContentObserver mSettingsObserver;
+
     private final Object mPhenotypeFlagLock = new Object();
 
     // Configured by phenotype. Updates from the server take effect immediately.
@@ -259,6 +283,8 @@
     @GuardedBy("mProcLock")
     private boolean mFreezerOverride = false;
 
+    @VisibleForTesting volatile long mFreezerDebounceTimeout = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
+
     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
     // when evaluating throttles that we only consider for "full" compaction, so we don't store
     // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and
@@ -293,6 +319,7 @@
         mProcStateThrottle = new HashSet<>();
         mProcessDependencies = processDependencies;
         mTestCallback = callback;
+        mSettingsObserver = new SettingsContentObserver();
     }
 
     /**
@@ -303,6 +330,8 @@
         // TODO: initialize flags to default and only update them if values are set in DeviceConfig
         DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 ActivityThread.currentApplication().getMainExecutor(), mOnFlagsChangedListener);
+        mAm.mContext.getContentResolver().registerContentObserver(
+                CACHED_APP_FREEZER_ENABLED_URI, false, mSettingsObserver);
         synchronized (mPhenotypeFlagLock) {
             updateUseCompaction();
             updateCompactionActions();
@@ -315,6 +344,7 @@
             updateUseFreezer();
             updateMinOomAdjThrottle();
             updateMaxOomAdjThrottle();
+            updateFreezerDebounceTimeout();
         }
     }
 
@@ -367,6 +397,7 @@
                     + " processes.");
             pw.println(" " + KEY_USE_FREEZER + "=" + mUseFreezer);
             pw.println("  " + KEY_FREEZER_STATSD_SAMPLE_RATE + "=" + mFreezerStatsdSampleRate);
+            pw.println("  " + KEY_FREEZER_DEBOUNCE_TIMEOUT + "=" + mFreezerDebounceTimeout);
             if (DEBUG_COMPACTION) {
                 for (Map.Entry<Integer, LastCompactionStats> entry
                         : mLastCompactionStats.entrySet()) {
@@ -627,21 +658,28 @@
             mUseFreezer = isFreezerSupported();
         }
 
-        if (mUseFreezer && mFreezeHandler == null) {
-            Slog.d(TAG_AM, "Freezer enabled");
-            enableFreezer(true);
+        final boolean useFreezer = mUseFreezer;
+        // enableFreezer() would need the global ActivityManagerService lock, post it.
+        mAm.mHandler.post(() -> {
+            if (useFreezer) {
+                Slog.d(TAG_AM, "Freezer enabled");
+                enableFreezer(true);
 
-            if (!mCachedAppOptimizerThread.isAlive()) {
-                mCachedAppOptimizerThread.start();
+                if (!mCachedAppOptimizerThread.isAlive()) {
+                    mCachedAppOptimizerThread.start();
+                }
+
+                if (mFreezeHandler == null) {
+                    mFreezeHandler = new FreezeHandler();
+                }
+
+                Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
+                        Process.THREAD_GROUP_SYSTEM);
+            } else {
+                Slog.d(TAG_AM, "Freezer disabled");
+                enableFreezer(false);
             }
-
-            mFreezeHandler = new FreezeHandler();
-
-            Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
-                    Process.THREAD_GROUP_SYSTEM);
-        } else {
-            enableFreezer(false);
-        }
+        });
     }
 
     @GuardedBy("mPhenotypeFlagLock")
@@ -794,6 +832,16 @@
         }
     }
 
+    @GuardedBy("mPhenotypeFlagLock")
+    private void updateFreezerDebounceTimeout() {
+        mFreezerDebounceTimeout = DeviceConfig.getLong(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                KEY_FREEZER_DEBOUNCE_TIMEOUT, DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
+
+        if (mFreezerDebounceTimeout < 0) {
+            mFullDeltaRssThrottleKb = DEFAULT_FREEZER_DEBOUNCE_TIMEOUT;
+        }
+    }
+
     private boolean parseProcStateThrottle(String procStateThrottleString) {
         String[] procStates = TextUtils.split(procStateThrottleString, ",");
         mProcStateThrottle.clear();
@@ -818,7 +866,7 @@
         return COMPACT_ACTION_STRING[action];
     }
 
-    // This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
+    // This will ensure app will be out of the freezer for at least mFreezerDebounceTimeout.
     @GuardedBy("mAm")
     void unfreezeTemporarily(ProcessRecord app) {
         if (mUseFreezer) {
@@ -838,7 +886,7 @@
         mFreezeHandler.sendMessageDelayed(
                 mFreezeHandler.obtainMessage(
                     SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
-                FREEZE_TIMEOUT_MS);
+                mFreezerDebounceTimeout);
     }
 
     @GuardedBy({"mAm", "mProcLock"})
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 8ad11d1..c8721cc 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1864,6 +1864,7 @@
         }
 
         int capabilityFromFGS = 0; // capability from foreground service.
+        boolean scheduleLikeTopApp = false;
         for (int is = psr.numberOfRunningServices() - 1;
                 is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
                         || schedGroup == ProcessList.SCHED_GROUP_BACKGROUND
@@ -1975,6 +1976,8 @@
                     int clientAdj = cstate.getCurRawAdj();
                     int clientProcState = cstate.getCurRawProcState();
 
+                    final boolean clientIsSystem = clientProcState < PROCESS_STATE_TOP;
+
                     // pass client's mAllowStartFgs to the app if client is not persistent process.
                     if (cstate.getAllowedStartFgs() != REASON_DENIED
                             && cstate.getMaxAdj() >= ProcessList.FOREGROUND_APP_ADJ) {
@@ -2014,6 +2017,10 @@
                         }
                         String adjType = null;
                         if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+                            // Similar to BIND_WAIVE_PRIORITY, keep it unfrozen.
+                            if (clientAdj < ProcessList.CACHED_APP_MIN_ADJ) {
+                                app.mOptRecord.setShouldNotFreeze(true);
+                            }
                             // Not doing bind OOM management, so treat
                             // this guy more like a started service.
                             if (state.hasShownUi() && !state.getCachedIsHomeProcess()) {
@@ -2170,8 +2177,10 @@
                         }
 
                         if (schedGroup < ProcessList.SCHED_GROUP_TOP_APP
-                                && (cr.flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0) {
+                                && (cr.flags & Context.BIND_SCHEDULE_LIKE_TOP_APP) != 0
+                                && clientIsSystem) {
                             schedGroup = ProcessList.SCHED_GROUP_TOP_APP;
+                            scheduleLikeTopApp = true;
                         }
 
                         if (!trackedProcState) {
@@ -2438,7 +2447,8 @@
         // Put bound foreground services in a special sched group for additional
         // restrictions on screen off
         if (procState >= PROCESS_STATE_BOUND_FOREGROUND_SERVICE
-                && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE) {
+                && mService.mWakefulness.get() != PowerManagerInternal.WAKEFULNESS_AWAKE
+                && !scheduleLikeTopApp) {
             if (schedGroup > ProcessList.SCHED_GROUP_RESTRICTED) {
                 schedGroup = ProcessList.SCHED_GROUP_RESTRICTED;
             }
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index f4ce723..026c1d3 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 import java.io.PrintWriter;
 
@@ -28,6 +29,9 @@
 
     private final ActivityManagerGlobalLock mProcLock;
 
+    @VisibleForTesting
+    static final String IS_FROZEN = "isFrozen";
+
     /**
      * The last time that this process was compacted.
      */
@@ -169,5 +173,6 @@
     void dump(PrintWriter pw, String prefix, long nowUptime) {
         pw.print(prefix); pw.print("lastCompactTime="); pw.print(mLastCompactTime);
         pw.print(" lastCompactAction="); pw.println(mLastCompactAction);
+        pw.print(" " + IS_FROZEN + "="); pw.println(mFrozen);
     }
 }
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
index e149ca9..243cc7c 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java
@@ -119,6 +119,7 @@
     private int mCurrentUserId = UserHandle.USER_NULL;
     private final boolean mIsUdfps;
     private final int mSensorId;
+    private boolean mIsPowerbuttonFps;
 
     private final class BiometricTaskStackListener extends TaskStackListener {
         @Override
@@ -345,9 +346,18 @@
         mIsUdfps = !ArrayUtils.isEmpty(
                 mContext.getResources().getIntArray(R.array.config_udfps_sensor_props));
 
-        final @FingerprintSensorProperties.SensorType int sensorType =
-                mIsUdfps ? FingerprintSensorProperties.TYPE_UDFPS_OPTICAL
-                        : FingerprintSensorProperties.TYPE_REAR;
+        // config_is_powerbutton_fps indicates whether device has a power button fingerprint sensor.
+        mIsPowerbuttonFps = mContext.getResources().getBoolean(R.bool.config_is_powerbutton_fps);
+
+        final @FingerprintSensorProperties.SensorType int sensorType;
+        if (mIsUdfps) {
+            sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
+        } else if (mIsPowerbuttonFps) {
+            sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON;
+        } else {
+            sensorType = FingerprintSensorProperties.TYPE_REAR;
+        }
+
         // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
         // cannot be checked
         final boolean resetLockoutRequiresHardwareAuthToken = false;
@@ -835,6 +845,7 @@
         try {
             dump.put("service", TAG);
             dump.put("isUdfps", mIsUdfps);
+            dump.put("isPowerbuttonFps", mIsPowerbuttonFps);
 
             JSONArray sets = new JSONArray();
             for (UserInfo user : UserManager.get(mContext).getUsers()) {
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index cd24576..1bbcede 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -528,8 +528,13 @@
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to register task stack listener!");
         }
+    }
 
-        CameraStatsJobService.schedule(mContext);
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_BOOT_COMPLETED) {
+            CameraStatsJobService.schedule(mContext);
+        }
     }
 
     @Override
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 7bd3ee2..2494816 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -643,6 +643,20 @@
     @GuardedBy("mUidRulesFirstLock")
     private final SparseBooleanArray mInternetPermissionMap = new SparseBooleanArray();
 
+    /**
+     * Map of uid -> UidStateCallbackInfo objects holding the data received from
+     * {@link IUidObserver#onUidStateChanged(int, int, long, int)} callbacks. In order to avoid
+     * creating a new object for every callback received, we hold onto the object created for each
+     * uid and reuse it.
+     *
+     * Note that the lock used for accessing this object should not be used for anything else and we
+     * should not be acquiring new locks or doing any heavy work while this lock is held since this
+     * will be used in the callback from ActivityManagerService.
+     */
+    @GuardedBy("mUidStateCallbackInfos")
+    private final SparseArray<UidStateCallbackInfo> mUidStateCallbackInfos =
+            new SparseArray<>();
+
     private RestrictedModeObserver mRestrictedModeObserver;
 
     // TODO: keep allowlist of system-critical services that should never have
@@ -1022,11 +1036,18 @@
     final private IUidObserver mUidObserver = new IUidObserver.Stub() {
         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq,
                 @ProcessCapability int capability) {
-            // TODO: Avoid creating a new UidStateCallbackInfo object every time
-            // we get a callback for an uid
-            mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED,
-                    new UidStateCallbackInfo(uid, procState, procStateSeq, capability))
+            synchronized (mUidStateCallbackInfos) {
+                UidStateCallbackInfo callbackInfo = mUidStateCallbackInfos.get(uid);
+                if (callbackInfo == null) {
+                    callbackInfo = new UidStateCallbackInfo();
+                    mUidStateCallbackInfos.put(uid, callbackInfo);
+                }
+                callbackInfo.update(uid, procState, procStateSeq, capability);
+                if (!callbackInfo.isPending) {
+                    mUidEventHandler.obtainMessage(UID_MSG_STATE_CHANGED, callbackInfo)
                             .sendToTarget();
+                }
+            }
         }
 
         @Override public void onUidGone(int uid, boolean disabled) {
@@ -1044,14 +1065,14 @@
     };
 
     private static final class UidStateCallbackInfo {
-        public final int uid;
-        public final int procState;
-        public final long procStateSeq;
+        public int uid;
+        public int procState;
+        public long procStateSeq;
         @ProcessCapability
-        public final int capability;
+        public int capability;
+        public boolean isPending;
 
-        UidStateCallbackInfo(int uid, int procState, long procStateSeq,
-                @ProcessCapability int capability) {
+        public void update(int uid, int procState, long procStateSeq, int capability) {
             this.uid = uid;
             this.procState = procState;
             this.procStateSeq = procStateSeq;
@@ -4495,6 +4516,9 @@
         mPowerSaveTempWhitelistAppIds.delete(uid);
         mAppIdleTempWhitelistAppIds.delete(uid);
         mUidFirewallRestrictedModeRules.delete(uid);
+        synchronized (mUidStateCallbackInfos) {
+            mUidStateCallbackInfos.remove(uid);
+        }
 
         // ...then update iptables asynchronously.
         mHandler.obtainMessage(MSG_RESET_FIREWALL_RULES_BY_UID, uid, 0).sendToTarget();
@@ -5128,10 +5152,17 @@
                 case UID_MSG_STATE_CHANGED: {
                     final UidStateCallbackInfo uidStateCallbackInfo =
                             (UidStateCallbackInfo) msg.obj;
-                    final int uid = uidStateCallbackInfo.uid;
-                    final int procState = uidStateCallbackInfo.procState;
-                    final long procStateSeq = uidStateCallbackInfo.procStateSeq;
-                    final int capability = uidStateCallbackInfo.capability;
+                    final int uid;
+                    final int procState;
+                    final long procStateSeq;
+                    final int capability;
+                    synchronized (mUidStateCallbackInfos) {
+                        uid = uidStateCallbackInfo.uid;
+                        procState = uidStateCallbackInfo.procState;
+                        procStateSeq = uidStateCallbackInfo.procStateSeq;
+                        capability = uidStateCallbackInfo.capability;
+                        uidStateCallbackInfo.isPending = false;
+                    }
 
                     handleUidChanged(uid, procState, procStateSeq, capability);
                     return true;
@@ -5575,17 +5606,6 @@
     }
 
     @Override
-    public boolean checkUidNetworkingBlocked(int uid, int uidRules,
-            boolean isNetworkMetered, boolean isBackgroundRestricted) {
-        mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
-        // Log of invoking this function is disabled because it will be called very frequently. And
-        // metrics are unlikely needed on this method because the callers are external and this
-        // method doesn't take any locks or perform expensive operations.
-        return isUidNetworkingBlockedInternal(uid, uidRules, isNetworkMetered,
-                isBackgroundRestricted, null);
-    }
-
-    @Override
     public boolean isUidRestrictedOnMeteredNetworks(int uid) {
         mContext.enforceCallingOrSelfPermission(OBSERVE_NETWORK_POLICY, TAG);
         final int uidRules;
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
index b9984a5..8bd3b1e 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java
@@ -175,6 +175,11 @@
         mFileWriteHandler.post(rcr);
     }
 
+    public void deleteNotificationChannel(String pkg, String channelId) {
+        RemoveChannelRunnable rcr = new RemoveChannelRunnable(pkg, channelId);
+        mFileWriteHandler.post(rcr);
+    }
+
     public void addNotification(final HistoricalNotification notification) {
         synchronized (mLock) {
             mBuffer.addNewNotificationToWrite(notification);
@@ -505,4 +510,47 @@
             }
         }
     }
+
+    final class RemoveChannelRunnable implements Runnable {
+        private String mPkg;
+        private String mChannelId;
+        private NotificationHistory mNotificationHistory;
+
+        RemoveChannelRunnable(String pkg, String channelId) {
+            mPkg = pkg;
+            mChannelId = channelId;
+        }
+
+        @VisibleForTesting
+        void setNotificationHistory(NotificationHistory nh) {
+            mNotificationHistory = nh;
+        }
+
+        @Override
+        public void run() {
+            if (DEBUG) Slog.d(TAG, "RemoveChannelRunnable");
+            synchronized (mLock) {
+                // Remove from pending history
+                mBuffer.removeChannelFromWrite(mPkg, mChannelId);
+
+                Iterator<AtomicFile> historyFileItr = mHistoryFiles.iterator();
+                while (historyFileItr.hasNext()) {
+                    final AtomicFile af = historyFileItr.next();
+                    try {
+                        NotificationHistory notificationHistory = mNotificationHistory != null
+                                ? mNotificationHistory
+                                : new NotificationHistory();
+                        readLocked(af, notificationHistory,
+                                new NotificationHistoryFilter.Builder().build());
+                        if (notificationHistory.removeChannelFromWrite(mPkg, mChannelId)) {
+                            writeLocked(af, notificationHistory);
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Cannot clean up file on channel removal "
+                                + af.getBaseFile().getName(), e);
+                    }
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
index cf3530b..6da898a 100644
--- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java
+++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java
@@ -183,6 +183,22 @@
         }
     }
 
+    public void deleteNotificationChannel(String pkg, int uid, String channelId) {
+        synchronized (mLock) {
+            int userId = UserHandle.getUserId(uid);
+            final NotificationHistoryDatabase userHistory =
+                    getUserHistoryAndInitializeIfNeededLocked(userId);
+            // TODO: it shouldn't be possible to delete a notification entry while the user is
+            // locked but we should handle it
+            if (userHistory == null) {
+                Slog.w(TAG, "Attempted to remove channel for locked/gone/disabled user "
+                        + userId);
+                return;
+            }
+            userHistory.deleteNotificationChannel(pkg, channelId);
+        }
+    }
+
     public void triggerWriteToDisk() {
         synchronized (mLock) {
             final int userCount = mUserState.size();
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 50b9176..6dcf39b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3623,6 +3623,7 @@
             cancelAllNotificationsInt(MY_UID, MY_PID, pkg, channelId, 0, 0, true,
                     callingUser, REASON_CHANNEL_REMOVED, null);
             mPreferencesHelper.deleteNotificationChannel(pkg, callingUid, channelId);
+            mHistoryManager.deleteNotificationChannel(pkg, callingUid, channelId);
             mListeners.notifyNotificationChannelChanged(pkg,
                     UserHandle.getUserHandleForUid(callingUid),
                     mPreferencesHelper.getNotificationChannel(pkg, callingUid, channelId, true),
@@ -7119,6 +7120,7 @@
             final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
                     REQUEST_CODE_TIMEOUT,
                     new Intent(ACTION_NOTIFICATION_TIMEOUT)
+                            .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
                             .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
                                     .appendPath(record.getKey()).build())
                             .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index ecafdfd..4fd360b 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -293,11 +293,11 @@
             Slog.i(TAG, "received progress update: " + progress);
         }
         mLoadingState.setProgress(progress);
-        if (1 - progress < 0.001) {
+        if (Math.abs(1.0f - progress) < 0.00000001f) {
             if (DEBUG) {
                 Slog.i(TAG, "package is fully loaded");
             }
-            mLoadingState.setProgress(1);
+            mLoadingState.setProgress(1.0f);
             if (mLoadingState.isLoading()) {
                 mLoadingState.adoptNewLoadingStateLocked(false);
             }
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 7b71be9..4eafe51 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -309,23 +309,24 @@
     private final String mOriginalInstallerPackageName;
 
     /** Uid of the owner of the installer session */
-    @GuardedBy("mLock")
-    private int mInstallerUid;
+    private volatile int mInstallerUid;
 
     /** Where this install request came from */
     @GuardedBy("mLock")
     private InstallSource mInstallSource;
 
-    @GuardedBy("mLock")
+    private final Object mProgressLock = new Object();
+
+    @GuardedBy("mProgressLock")
     private float mClientProgress = 0;
-    @GuardedBy("mLock")
+    @GuardedBy("mProgressLock")
     private float mInternalProgress = 0;
 
-    @GuardedBy("mLock")
+    @GuardedBy("mProgressLock")
     private float mProgress = 0;
-    @GuardedBy("mLock")
+    @GuardedBy("mProgressLock")
     private float mReportedProgress = -1;
-    @GuardedBy("mLock")
+    @GuardedBy("mProgressLock")
     private float mIncrementalProgress = 0;
 
     /** State of the session. */
@@ -571,7 +572,7 @@
          */
         @Override
         public void installSession(IntentSender statusReceiver) {
-            assertCallerIsOwnerOrRootOrSystemLocked();
+            assertCallerIsOwnerOrRootOrSystem();
             assertNotChildLocked("StagedSession#installSession");
             Preconditions.checkArgument(isCommitted() && isSessionReady());
 
@@ -670,7 +671,7 @@
             final Runnable r;
             synchronized (mLock) {
                 assertNotChildLocked("StagedSession#abandon");
-                assertCallerIsOwnerOrRootLocked();
+                assertCallerIsOwnerOrRoot();
                 if (isInTerminalState()) {
                     // We keep the session in the database if it's in a finalized state. It will be
                     // removed by PackageInstallerService when the last update time is old enough.
@@ -744,7 +745,7 @@
          */
         @Override
         public void verifySession() {
-            assertCallerIsOwnerOrRootOrSystemLocked();
+            assertCallerIsOwnerOrRootOrSystem();
             Preconditions.checkArgument(isCommitted());
             Preconditions.checkArgument(!mSessionApplied && !mSessionFailed);
             verify();
@@ -1229,7 +1230,7 @@
         }
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mProgressLock")
     private void setClientProgressLocked(float progress) {
         // Always publish first staging movement
         final boolean forcePublish = (mClientProgress == 0);
@@ -1239,21 +1240,21 @@
 
     @Override
     public void setClientProgress(float progress) {
-        synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
+        assertCallerIsOwnerOrRoot();
+        synchronized (mProgressLock) {
             setClientProgressLocked(progress);
         }
     }
 
     @Override
     public void addClientProgress(float progress) {
-        synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
+        assertCallerIsOwnerOrRoot();
+        synchronized (mProgressLock) {
             setClientProgressLocked(mClientProgress + progress);
         }
     }
 
-    @GuardedBy("mLock")
+    @GuardedBy("mProgressLock")
     private void computeProgressLocked(boolean forcePublish) {
         if (!mCommitted) {
             mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
@@ -1278,8 +1279,8 @@
 
     @Override
     public String[] getNames() {
+        assertCallerIsOwnerOrRoot();
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotCommittedOrDestroyedLocked("getNames");
 
             return getNamesLocked();
@@ -1362,8 +1363,8 @@
             }
         }
 
+        assertCallerIsOwnerOrRoot();
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotCommittedOrDestroyedLocked("addChecksums");
 
             if (mChecksums.containsKey(name)) {
@@ -1384,8 +1385,8 @@
             throw new IllegalStateException("Must specify package name to remove a split");
         }
 
+        assertCallerIsOwnerOrRoot();
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotCommittedOrDestroyedLocked("removeSplit");
 
             try {
@@ -1431,8 +1432,8 @@
             throw new IllegalStateException(
                     "Cannot write regular files in a data loader installation session.");
         }
+        assertCallerIsOwnerOrRoot();
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotSealedLocked("assertCanWrite");
         }
         if (reverseMode) {
@@ -1605,8 +1606,8 @@
             throw new IllegalStateException(
                     "Cannot read regular files in a data loader installation session.");
         }
+        assertCallerIsOwnerOrRoot();
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotCommittedOrDestroyedLocked("openRead");
             try {
                 return openReadInternalLocked(name);
@@ -1634,8 +1635,7 @@
      * Check if the caller is the owner of this session. Otherwise throw a
      * {@link SecurityException}.
      */
-    @GuardedBy("mLock")
-    private void assertCallerIsOwnerOrRootLocked() {
+    private void assertCallerIsOwnerOrRoot() {
         final int callingUid = Binder.getCallingUid();
         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid) {
             throw new SecurityException("Session does not belong to uid " + callingUid);
@@ -1646,8 +1646,7 @@
      * Check if the caller is the owner of this session. Otherwise throw a
      * {@link SecurityException}.
      */
-    @GuardedBy("mLock")
-    private void assertCallerIsOwnerOrRootOrSystemLocked() {
+    private void assertCallerIsOwnerOrRootOrSystem() {
         final int callingUid = Binder.getCallingUid();
         if (callingUid != Process.ROOT_UID && callingUid != mInstallerUid
                 && callingUid != Process.SYSTEM_UID) {
@@ -1929,9 +1928,9 @@
      */
     private boolean markAsSealed(@NonNull IntentSender statusReceiver, boolean forTransfer) {
         Objects.requireNonNull(statusReceiver);
+        assertCallerIsOwnerOrRoot();
 
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
             assertPreparedAndNotDestroyedLocked("commit of session " + sessionId);
             assertNoWriteFileTransfersOpenLocked();
 
@@ -2004,9 +2003,11 @@
                             "Session destroyed");
                 }
                 if (!isIncrementalInstallation()) {
-                    // For non-incremental installs, client staging is fully done at this point
-                    mClientProgress = 1f;
-                    computeProgressLocked(true);
+                    synchronized (mProgressLock) {
+                        // For non-incremental installs, client staging is fully done at this point
+                        mClientProgress = 1f;
+                        computeProgressLocked(true);
+                    }
                 }
 
                 // This ongoing commit should keep session active, even though client
@@ -2191,7 +2192,7 @@
         }
 
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
+            assertCallerIsOwnerOrRoot();
             assertPreparedAndNotSealedLocked("transfer");
 
             try {
@@ -2376,8 +2377,10 @@
             PackageLite result = parseApkLite();
             if (result != null) {
                 mPackageLite = result;
-                mInternalProgress = 0.5f;
-                computeProgressLocked(true);
+                synchronized (mProgressLock) {
+                    mInternalProgress = 0.5f;
+                    computeProgressLocked(true);
+                }
 
                 extractNativeLibraries(
                         mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
@@ -3554,7 +3557,7 @@
         int activeCount;
         synchronized (mLock) {
             if (checkCaller) {
-                assertCallerIsOwnerOrRootLocked();
+                assertCallerIsOwnerOrRoot();
             }
 
             activeCount = mActiveCount.decrementAndGet();
@@ -3593,7 +3596,7 @@
     private void abandonNonStaged() {
         synchronized (mLock) {
             assertNotChildLocked("abandonNonStaged");
-            assertCallerIsOwnerOrRootLocked();
+            assertCallerIsOwnerOrRoot();
             if (mRelinquished) {
                 if (LOGD) Slog.d(TAG, "Ignoring abandon after commit relinquished control");
                 return;
@@ -3659,7 +3662,7 @@
         }
 
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
+            assertCallerIsOwnerOrRoot();
             assertPreparedAndNotSealedLocked("addFile");
 
             if (!mFiles.add(new FileEntry(mFiles.size(),
@@ -3680,7 +3683,7 @@
         }
 
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
+            assertCallerIsOwnerOrRoot();
             assertPreparedAndNotSealedLocked("removeFile");
 
             if (!mFiles.add(new FileEntry(mFiles.size(),
@@ -3893,7 +3896,7 @@
                         new IPackageLoadingProgressCallback.Stub() {
                             @Override
                             public void onPackageLoadingProgressChanged(float progress) {
-                                synchronized (mLock) {
+                                synchronized (mProgressLock) {
                                     mIncrementalProgress = progress;
                                     computeProgressLocked(true);
                                 }
@@ -3993,7 +3996,7 @@
                         + " as it is in an invalid state.");
             }
             synchronized (mLock) {
-                assertCallerIsOwnerOrRootLocked();
+                assertCallerIsOwnerOrRoot();
                 assertPreparedAndNotSealedLocked("addChildSessionId");
 
                 final int indexOfSession = mChildSessions.indexOfKey(childSessionId);
@@ -4012,7 +4015,7 @@
     @Override
     public void removeChildSessionId(int sessionId) {
         synchronized (mLock) {
-            assertCallerIsOwnerOrRootLocked();
+            assertCallerIsOwnerOrRoot();
             assertPreparedAndNotSealedLocked("removeChildSessionId");
 
             final int indexOfSession = mChildSessions.indexOfKey(sessionId);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index cb68cc9..29b17ff 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7198,7 +7198,7 @@
                         updateSharedLibrariesLocked(pkg, stubPkgSetting, null, null,
                                 Collections.unmodifiableMap(mPackages));
                     } catch (PackageManagerException e) {
-                        Slog.e(TAG, "updateAllSharedLibrariesLPw failed: ", e);
+                        Slog.w(TAG, "updateAllSharedLibrariesLPw failed: ", e);
                     }
                     final int[] userIds = mUserManager.getUserIds();
                     for (final int userId : userIds) {
@@ -7450,7 +7450,7 @@
         if (matches.size() == 1) {
             return matches.get(0).getComponentInfo().packageName;
         } else if (matches.size() == 0) {
-            Log.e(TAG, "There should probably be a verifier, but, none were found");
+            Log.w(TAG, "There should probably be a verifier, but, none were found");
             return null;
         }
         throw new RuntimeException("There must be exactly one verifier; found " + matches);
@@ -22827,7 +22827,7 @@
         if (matches.size() == 1) {
             return matches.get(0).getComponentInfo().packageName;
         } else {
-            Slog.e(TAG, "There should probably be exactly one storage manager; found "
+            Slog.w(TAG, "There should probably be exactly one storage manager; found "
                     + matches.size() + ": matches=" + matches);
             return null;
         }
@@ -25019,7 +25019,7 @@
                     // already held, since it's invoked as a side-effect of
                     // executeBatchLI()
                     if (e != null) {
-                        logCriticalInfo(Log.ERROR, "Failed to create app data for " + packageName
+                        logCriticalInfo(Log.WARN, "Failed to create app data for " + packageName
                                 + ", but trying to recover: " + e);
                         destroyAppDataLeafLIF(pkg, userId, flags);
                         try {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index b51b833..4823c29 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1229,7 +1229,7 @@
                 size++;
             }
             if (mAppIds.get(index) != null) {
-                PackageManagerService.reportSettingsProblem(Log.ERROR,
+                PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Adding duplicate app id: " + appId
                         + " name=" + name);
                 return false;
@@ -1237,7 +1237,7 @@
             mAppIds.set(index, obj);
         } else {
             if (mOtherAppIds.get(appId) != null) {
-                PackageManagerService.reportSettingsProblem(Log.ERROR,
+                PackageManagerService.reportSettingsProblem(Log.WARN,
                         "Adding duplicate shared id: " + appId
                                 + " name=" + name);
                 return false;
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index ff6c2f5..5f5e6a3 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -147,17 +147,18 @@
             return;
         }
         String sessionId = vol.getId();
-        int userId = getConnectionUserIdForVolume(vol);
+        int connectionUserId = getConnectionUserIdForVolume(vol);
 
         StorageUserConnection connection = null;
         synchronized (mLock) {
-            connection = mConnections.get(userId);
+            connection = mConnections.get(connectionUserId);
             if (connection != null) {
                 Slog.i(TAG, "Notifying volume state changed for session with id: " + sessionId);
                 connection.notifyVolumeStateChanged(sessionId,
-                        vol.buildStorageVolume(mContext, userId, false));
+                        vol.buildStorageVolume(mContext, vol.getMountUserId(), false));
             } else {
-                Slog.w(TAG, "No available storage user connection for userId : " + userId);
+                Slog.w(TAG, "No available storage user connection for userId : "
+                        + connectionUserId);
             }
         }
     }
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index 546893588..ae806aa 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -17,6 +17,7 @@
 package com.android.server.vcn;
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
@@ -96,17 +97,21 @@
      */
     private static final int MSG_EVENT_GATEWAY_CONNECTION_QUIT = MSG_EVENT_BASE + 3;
 
+    /**
+     * Triggers reevaluation of safe mode conditions.
+     *
+     * <p>Upon entering safe mode, the VCN will only provide gateway connections opportunistically,
+     * leaving the underlying networks marked as NOT_VCN_MANAGED.
+     *
+     * <p>Any VcnGatewayConnection in safe mode will result in the entire Vcn instance being put
+     * into safe mode. Upon receiving this message, the Vcn MUST query all VcnGatewayConnections to
+     * determine if any are in safe mode.
+     */
+    private static final int MSG_EVENT_SAFE_MODE_STATE_CHANGED = MSG_EVENT_BASE + 4;
+
     /** Triggers an immediate teardown of the entire Vcn, including GatewayConnections. */
     private static final int MSG_CMD_TEARDOWN = MSG_CMD_BASE;
 
-    /**
-     * Causes this VCN to immediately enter safe mode.
-     *
-     * <p>Upon entering safe mode, the VCN will unregister its RequestListener, tear down all of its
-     * VcnGatewayConnections, and notify VcnManagementService that it is in safe mode.
-     */
-    private static final int MSG_CMD_ENTER_SAFE_MODE = MSG_CMD_BASE + 1;
-
     @NonNull private final VcnContext mVcnContext;
     @NonNull private final ParcelUuid mSubscriptionGroup;
     @NonNull private final Dependencies mDeps;
@@ -233,6 +238,11 @@
 
     @Override
     public void handleMessage(@NonNull Message msg) {
+        if (mCurrentStatus != VCN_STATUS_CODE_ACTIVE
+                && mCurrentStatus != VCN_STATUS_CODE_SAFE_MODE) {
+            return;
+        }
+
         switch (msg.what) {
             case MSG_EVENT_CONFIG_UPDATED:
                 handleConfigUpdated((VcnConfig) msg.obj);
@@ -246,12 +256,12 @@
             case MSG_EVENT_GATEWAY_CONNECTION_QUIT:
                 handleGatewayConnectionQuit((VcnGatewayConnectionConfig) msg.obj);
                 break;
+            case MSG_EVENT_SAFE_MODE_STATE_CHANGED:
+                handleSafeModeStatusChanged();
+                break;
             case MSG_CMD_TEARDOWN:
                 handleTeardown();
                 break;
-            case MSG_CMD_ENTER_SAFE_MODE:
-                handleEnterSafeMode();
-                break;
             default:
                 Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what);
         }
@@ -263,40 +273,28 @@
 
         mConfig = config;
 
-        // TODO(b/183174340): Remove this once opportunistic safe mode is supported.
-        if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) {
-            // VCN is already active - teardown any GatewayConnections whose configs have been
-            // removed and get all current requests
-            for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
-                    mVcnGatewayConnections.entrySet()) {
-                final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
-                final VcnGatewayConnection gatewayConnection = entry.getValue();
+        // Teardown any GatewayConnections whose configs have been removed and get all current
+        // requests
+        for (final Entry<VcnGatewayConnectionConfig, VcnGatewayConnection> entry :
+                mVcnGatewayConnections.entrySet()) {
+            final VcnGatewayConnectionConfig gatewayConnectionConfig = entry.getKey();
+            final VcnGatewayConnection gatewayConnection = entry.getValue();
 
-                // GatewayConnectionConfigs must match exactly (otherwise authentication or
-                // connection details may have changed).
-                if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) {
-                    if (gatewayConnection == null) {
-                        Slog.wtf(
-                                getLogTag(),
-                                "Found gatewayConnectionConfig without GatewayConnection");
-                    } else {
-                        gatewayConnection.teardownAsynchronously();
-                    }
+            // GatewayConnectionConfigs must match exactly (otherwise authentication or
+            // connection details may have changed).
+            if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) {
+                if (gatewayConnection == null) {
+                    Slog.wtf(
+                            getLogTag(), "Found gatewayConnectionConfig without GatewayConnection");
+                } else {
+                    gatewayConnection.teardownAsynchronously();
                 }
             }
-
-            // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be
-            // satisfied start a new GatewayConnection)
-            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
-        } else if (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE) {
-            // If this VCN was not previously active, it is exiting Safe Mode. Re-register the
-            // request listener to get NetworkRequests again (and all cached requests).
-            mVcnContext.getVcnNetworkProvider().registerListener(mRequestListener);
-        } else {
-            // Ignored; VCN was not active; config updates ignored.
-            return;
         }
-        mCurrentStatus = VCN_STATUS_CODE_ACTIVE;
+
+        // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be
+        // satisfied start a new GatewayConnection)
+        mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
     }
 
     private void handleTeardown() {
@@ -309,21 +307,27 @@
         mCurrentStatus = VCN_STATUS_CODE_INACTIVE;
     }
 
-    private void handleEnterSafeMode() {
-        // TODO(b/183174340): Remove this once opportunistic-safe-mode is supported
-        handleTeardown();
+    private void handleSafeModeStatusChanged() {
+        boolean hasSafeModeGatewayConnection = false;
 
-        mCurrentStatus = VCN_STATUS_CODE_SAFE_MODE;
-        mVcnCallback.onEnteredSafeMode();
+        // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode
+        for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+            if (gatewayConnection.isInSafeMode()) {
+                hasSafeModeGatewayConnection = true;
+                break;
+            }
+        }
+
+        final int oldStatus = mCurrentStatus;
+        mCurrentStatus =
+                hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE;
+        if (oldStatus != mCurrentStatus) {
+            mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection);
+        }
     }
 
     private void handleNetworkRequested(
             @NonNull NetworkRequest request, int score, int providerId) {
-        if (mCurrentStatus != VCN_STATUS_CODE_ACTIVE) {
-            Slog.v(getLogTag(), "Received NetworkRequest while inactive. Ignore for now");
-            return;
-        }
-
         if (score > getNetworkScore()) {
             if (VDBG) {
                 Slog.v(
@@ -376,25 +380,23 @@
         mVcnGatewayConnections.remove(config);
 
         // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied
-        // start a new GatewayConnection), but only if the Vcn is still alive
-        if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) {
-            mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
-        }
+        // start a new GatewayConnection). VCN is always alive here, courtesy of the liveness check
+        // in handleMessage()
+        mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener);
     }
 
     private void handleSubscriptionsChanged(@NonNull TelephonySubscriptionSnapshot snapshot) {
         mLastSnapshot = snapshot;
 
-        if (mCurrentStatus == VCN_STATUS_CODE_ACTIVE) {
-            for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
-                gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
-            }
+        for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
+            gatewayConnection.updateSubscriptionSnapshot(mLastSnapshot);
         }
     }
 
     private boolean isRequestSatisfiedByGatewayConnectionConfig(
             @NonNull NetworkRequest request, @NonNull VcnGatewayConnectionConfig config) {
         final NetworkCapabilities.Builder builder = new NetworkCapabilities.Builder();
+        builder.addTransportType(TRANSPORT_CELLULAR);
         builder.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
         for (int cap : config.getAllExposedCapabilities()) {
             builder.addCapability(cap);
@@ -418,8 +420,8 @@
     /** Callback used for passing status signals from a VcnGatewayConnection to its managing Vcn. */
     @VisibleForTesting(visibility = Visibility.PACKAGE)
     public interface VcnGatewayStatusCallback {
-        /** Called by a VcnGatewayConnection to indicate that it has entered safe mode. */
-        void onEnteredSafeMode();
+        /** Called by a VcnGatewayConnection to indicate that it's safe mode status has changed. */
+        void onSafeModeStatusChanged();
 
         /** Callback by a VcnGatewayConnection to indicate that an error occurred. */
         void onGatewayConnectionError(
@@ -445,8 +447,8 @@
         }
 
         @Override
-        public void onEnteredSafeMode() {
-            sendMessage(obtainMessage(MSG_CMD_ENTER_SAFE_MODE));
+        public void onSafeModeStatusChanged() {
+            sendMessage(obtainMessage(MSG_EVENT_SAFE_MODE_STATE_CHANGED));
         }
 
         @Override
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 2ba8edd..20c08eb 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -32,6 +32,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.InetAddresses;
 import android.net.IpPrefix;
 import android.net.IpSecManager;
@@ -44,6 +45,7 @@
 import android.net.NetworkAgent;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
+import android.net.NetworkProvider;
 import android.net.RouteInfo;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.Uri;
@@ -92,6 +94,7 @@
 import java.util.Objects;
 import java.util.Set;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 
 /**
  * A single VCN Gateway Connection, providing a single public-facing VCN network.
@@ -504,6 +507,15 @@
     private boolean mIsQuitting = false;
 
     /**
+     * Whether the VcnGatewayConnection is in safe mode.
+     *
+     * <p>Upon hitting the safe mode timeout, this will be set to {@code true}. In safe mode, this
+     * VcnGatewayConnection will continue attempting to connect, and if a successful connection is
+     * made, safe mode will be exited.
+     */
+    private boolean mIsInSafeMode = false;
+
+    /**
      * The token used by the primary/current/active session.
      *
      * <p>This token MUST be updated when a new stateful/async session becomes the
@@ -562,8 +574,7 @@
      * <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable
      * otherwise.
      */
-    @VisibleForTesting(visibility = Visibility.PRIVATE)
-    NetworkAgent mNetworkAgent;
+    private NetworkAgent mNetworkAgent;
 
     @Nullable private WakeupMessage mTeardownTimeoutAlarm;
     @Nullable private WakeupMessage mDisconnectRequestAlarm;
@@ -628,6 +639,14 @@
         start();
     }
 
+    /** Queries whether this VcnGatewayConnection is in safe mode. */
+    public boolean isInSafeMode() {
+        // Accessing internal state; must only be done on looper thread.
+        mVcnContext.ensureRunningOnLooperThread();
+
+        return mIsInSafeMode;
+    }
+
     /**
      * Asynchronously tears down this GatewayConnection, and any resources used.
      *
@@ -1162,6 +1181,15 @@
             }
         }
 
+        protected void handleSafeModeTimeoutExceeded() {
+            mSafeModeTimeoutAlarm = null;
+
+            // Connectivity for this GatewayConnection is broken; tear down the Network.
+            teardownNetwork();
+            mIsInSafeMode = true;
+            mGatewayStatusCallback.onSafeModeStatusChanged();
+        }
+
         protected void logUnexpectedEvent(int what) {
             Slog.d(TAG, String.format(
                     "Unexpected event code %d in state %s", what, this.getClass().getSimpleName()));
@@ -1315,8 +1343,7 @@
                     }
                     break;
                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
-                    mGatewayStatusCallback.onEnteredSafeMode();
-                    mSafeModeTimeoutAlarm = null;
+                    handleSafeModeTimeoutExceeded();
                     break;
                 default:
                     logUnhandledMessage(msg);
@@ -1401,8 +1428,7 @@
                     handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
                     break;
                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
-                    mGatewayStatusCallback.onEnteredSafeMode();
-                    mSafeModeTimeoutAlarm = null;
+                    handleSafeModeTimeoutExceeded();
                     break;
                 default:
                     logUnhandledMessage(msg);
@@ -1432,30 +1458,35 @@
                     buildNetworkCapabilities(mConnectionConfig, mUnderlying);
             final LinkProperties lp =
                     buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+            final NetworkAgentConfig nac =
+                    new NetworkAgentConfig.Builder()
+                            .setLegacyType(ConnectivityManager.TYPE_MOBILE)
+                            .build();
 
             final NetworkAgent agent =
-                    new NetworkAgent(
-                            mVcnContext.getContext(),
-                            mVcnContext.getLooper(),
+                    mDeps.newNetworkAgent(
+                            mVcnContext,
                             TAG,
                             caps,
                             lp,
                             Vcn.getNetworkScore(),
-                            new NetworkAgentConfig.Builder().build(),
-                            mVcnContext.getVcnNetworkProvider()) {
-                        @Override
-                        public void onNetworkUnwanted() {
-                            Slog.d(TAG, "NetworkAgent was unwanted");
-                            teardownAsynchronously();
-                        }
-
-                        @Override
-                        public void onValidationStatus(int status, @Nullable Uri redirectUri) {
-                            if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
-                                clearFailedAttemptCounterAndSafeModeAlarm();
-                            }
-                        }
-                    };
+                            nac,
+                            mVcnContext.getVcnNetworkProvider(),
+                            () -> {
+                                Slog.d(TAG, "NetworkAgent was unwanted");
+                                // If network agent has already been torn down, skip sending the
+                                // disconnect. Unwanted() is always called, even when networkAgents
+                                // are unregistered in teardownNetwork(), so prevent duplicate
+                                // notifications.
+                                if (mNetworkAgent != null) {
+                                    teardownAsynchronously();
+                                }
+                            } /* networkUnwantedCallback */,
+                            (status) -> {
+                                if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
+                                    clearFailedAttemptCounterAndSafeModeAlarm();
+                                }
+                            } /* validationStatusCallback */);
 
             agent.register();
             agent.markConnected();
@@ -1469,6 +1500,11 @@
             // Validated connection, clear failed attempt counter
             mFailedAttempts = 0;
             cancelSafeModeAlarm();
+
+            if (mIsInSafeMode) {
+                mIsInSafeMode = false;
+                mGatewayStatusCallback.onSafeModeStatusChanged();
+            }
         }
 
         protected void applyTransform(
@@ -1491,13 +1527,6 @@
         protected void setupInterface(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull VcnChildSessionConfiguration childConfig) {
-            setupInterface(token, tunnelIface, childConfig, null);
-        }
-
-        protected void setupInterface(
-                int token,
-                @NonNull IpSecTunnelInterface tunnelIface,
                 @NonNull VcnChildSessionConfiguration childConfig,
                 @Nullable VcnChildSessionConfiguration oldChildConfig) {
             try {
@@ -1579,16 +1608,17 @@
                             transformCreatedInfo.direction);
                     break;
                 case EVENT_SETUP_COMPLETED:
+                    final VcnChildSessionConfiguration oldChildConfig = mChildConfig;
                     mChildConfig = ((EventSetupCompletedInfo) msg.obj).childSessionConfig;
 
-                    setupInterfaceAndNetworkAgent(mCurrentToken, mTunnelIface, mChildConfig);
+                    setupInterfaceAndNetworkAgent(
+                            mCurrentToken, mTunnelIface, mChildConfig, oldChildConfig);
                     break;
                 case EVENT_DISCONNECT_REQUESTED:
                     handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
                     break;
                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
-                    mGatewayStatusCallback.onEnteredSafeMode();
-                    mSafeModeTimeoutAlarm = null;
+                    handleSafeModeTimeoutExceeded();
                     break;
                 default:
                     logUnhandledMessage(msg);
@@ -1626,8 +1656,9 @@
         protected void setupInterfaceAndNetworkAgent(
                 int token,
                 @NonNull IpSecTunnelInterface tunnelIface,
-                @NonNull VcnChildSessionConfiguration childConfig) {
-            setupInterface(token, tunnelIface, childConfig);
+                @NonNull VcnChildSessionConfiguration childConfig,
+                @NonNull VcnChildSessionConfiguration oldChildConfig) {
+            setupInterface(token, tunnelIface, childConfig, oldChildConfig);
 
             if (mNetworkAgent == null) {
                 mNetworkAgent = buildNetworkAgent(tunnelIface, childConfig);
@@ -1692,8 +1723,7 @@
                     handleDisconnectRequested((EventDisconnectRequestedInfo) msg.obj);
                     break;
                 case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
-                    mGatewayStatusCallback.onEnteredSafeMode();
-                    mSafeModeTimeoutAlarm = null;
+                    handleSafeModeTimeoutExceeded();
                     break;
                 default:
                     logUnhandledMessage(msg);
@@ -1935,6 +1965,16 @@
     }
 
     @VisibleForTesting(visibility = Visibility.PRIVATE)
+    NetworkAgent getNetworkAgent() {
+        return mNetworkAgent;
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
+    void setNetworkAgent(@Nullable NetworkAgent networkAgent) {
+        mNetworkAgent = networkAgent;
+    }
+
+    @VisibleForTesting(visibility = Visibility.PRIVATE)
     void sendDisconnectRequestedAndAcquireWakelock(String reason, boolean shouldQuit) {
         sendMessageAndAcquireWakeLock(
                 EVENT_DISCONNECT_REQUESTED,
@@ -2018,6 +2058,38 @@
             return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable);
         }
 
+        /** Builds a new NetworkAgent. */
+        public NetworkAgent newNetworkAgent(
+                @NonNull VcnContext vcnContext,
+                @NonNull String tag,
+                @NonNull NetworkCapabilities caps,
+                @NonNull LinkProperties lp,
+                @NonNull int score,
+                @NonNull NetworkAgentConfig nac,
+                @NonNull NetworkProvider provider,
+                @NonNull Runnable networkUnwantedCallback,
+                @NonNull Consumer<Integer> validationStatusCallback) {
+            return new NetworkAgent(
+                    vcnContext.getContext(),
+                    vcnContext.getLooper(),
+                    tag,
+                    caps,
+                    lp,
+                    score,
+                    nac,
+                    provider) {
+                @Override
+                public void onNetworkUnwanted() {
+                    networkUnwantedCallback.run();
+                }
+
+                @Override
+                public void onValidationStatus(int status, @Nullable Uri redirectUri) {
+                    validationStatusCallback.accept(status);
+                }
+            };
+        }
+
         /** Gets the elapsed real time since boot, in millis. */
         public long getElapsedRealTime() {
             return SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index c9af62b..b1606c9 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -776,6 +776,7 @@
             if (mTmpInitial) {
                 w.resetContentChanged();
             }
+            w.mSurfacePlacementNeeded = true;
             w.mLayoutNeeded = false;
             w.prelayout();
             final boolean firstLayout = !w.isLaidOut();
@@ -818,6 +819,7 @@
                     //Slog.i(TAG, "Window " + this + " clearing mContentChanged - initial");
                     w.resetContentChanged();
                 }
+                w.mSurfacePlacementNeeded = true;
                 w.mLayoutNeeded = false;
                 w.prelayout();
                 getDisplayPolicy().layoutWindowLw(w, w.getParentWindow(), mDisplayFrames);
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index f31a643..510c62d 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -240,6 +240,7 @@
     @Override
     public void dump(PrintWriter pw, String prefix) {
         super.dump(pw, prefix);
+        prefix = prefix + "  ";
         pw.print(prefix);
         pw.print("mImeShowing=");
         pw.print(mImeShowing);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7b0fb01..0732314 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -63,7 +63,7 @@
 
 /**
  * Controller for a specific inset source on the server. It's called provider as it provides the
- * {@link InsetsSource} to the client that uses it in {@link InsetsSourceConsumer}.
+ * {@link InsetsSource} to the client that uses it in {@link android.view.InsetsSourceConsumer}.
  */
 class InsetsSourceProvider {
 
@@ -452,40 +452,36 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "InsetsSourceProvider");
-        pw.print(prefix + " mSource="); mSource.dump(prefix + "  ", pw);
+        pw.println(prefix + getClass().getSimpleName());
+        prefix = prefix + "  ";
+        pw.print(prefix + "mSource="); mSource.dump("", pw);
         if (mControl != null) {
-            pw.print(prefix + " mControl=");
-            mControl.dump(prefix + "  ", pw);
+            pw.print(prefix + "mControl=");
+            mControl.dump("", pw);
         }
-        pw.print(prefix + " mFakeControl="); mFakeControl.dump(prefix + "  ", pw);
-        pw.print(" mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
-        pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toString());
+        pw.print(prefix);
+        pw.print("mIsLeashReadyForDispatching="); pw.print(mIsLeashReadyForDispatching);
+        pw.print(" mImeOverrideFrame="); pw.print(mImeOverrideFrame.toShortString());
+        pw.println();
         if (mWin != null) {
-            pw.print(prefix + " mWin=");
-            mWin.dump(pw, prefix + "  ", false /* dumpAll */);
+            pw.print(prefix + "mWin=");
+            pw.println(mWin);
         }
         if (mAdapter != null) {
-            pw.print(prefix + " mAdapter=");
-            mAdapter.dump(pw, prefix + "  ");
+            pw.print(prefix + "mAdapter=");
+            mAdapter.dump(pw, "");
         }
         if (mControlTarget != null) {
-            pw.print(prefix + " mControlTarget=");
-            if (mControlTarget.getWindow() != null) {
-                mControlTarget.getWindow().dump(pw, prefix + "  ", false /* dumpAll */);
-            }
+            pw.print(prefix + "mControlTarget=");
+            pw.println(mControlTarget.getWindow());
         }
         if (mPendingControlTarget != null) {
-            pw.print(prefix + " mPendingControlTarget=");
-            if (mPendingControlTarget.getWindow() != null) {
-                mPendingControlTarget.getWindow().dump(pw, prefix + "  ", false /* dumpAll */);
-            }
+            pw.print(prefix + "mPendingControlTarget=");
+            pw.println(mPendingControlTarget.getWindow());
         }
         if (mFakeControlTarget != null) {
-            pw.print(prefix + " mFakeControlTarget=");
-            if (mFakeControlTarget.getWindow() != null) {
-                mFakeControlTarget.getWindow().dump(pw, prefix + "  ", false /* dumpAll */);
-            }
+            pw.print(prefix + "mFakeControlTarget=");
+            pw.println(mFakeControlTarget.getWindow());
         }
     }
 
@@ -575,8 +571,9 @@
 
         @Override
         public void dump(PrintWriter pw, String prefix) {
-            pw.println(prefix + "ControlAdapter");
-            pw.print(prefix + " mCapturedLeash="); pw.print(mCapturedLeash);
+            pw.print(prefix + "ControlAdapter mCapturedLeash=");
+            pw.print(mCapturedLeash);
+            pw.println();
         }
 
         @Override
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index b6057c6..655007c 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -573,18 +573,17 @@
 
     void dump(String prefix, PrintWriter pw) {
         pw.println(prefix + "WindowInsetsStateController");
-        mState.dump(prefix + "  ", pw);
-        pw.println(prefix + "  " + "Control map:");
+        prefix = prefix + "  ";
+        mState.dump(prefix, pw);
+        pw.println(prefix + "Control map:");
         for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) {
             pw.print(prefix + "  ");
             pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> "
                     + mTypeControlTargetMap.valueAt(i));
         }
-        pw.println(prefix + "  " + "InsetsSourceProviders map:");
+        pw.println(prefix + "InsetsSourceProviders:");
         for (int i = mProviders.size() - 1; i >= 0; i--) {
-            pw.print(prefix + "  ");
-            pw.println(InsetsState.typeToString(mProviders.keyAt(i)) + " -> ");
-            mProviders.valueAt(i).dump(pw, prefix);
+            mProviders.valueAt(i).dump(pw, prefix + "  ");
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 4f8ea1a..869133a 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -171,7 +171,8 @@
         final boolean hasExistingActivity = targetActivity != null;
         if (hasExistingActivity) {
             mRestoreTargetBehindRootTask = getRootTaskAbove(targetRootTask);
-            if (mRestoreTargetBehindRootTask == null) {
+            if (mRestoreTargetBehindRootTask == null
+                    && targetRootTask.getTopMostTask() == targetActivity.getTask()) {
                 notifyAnimationCancelBeforeStart(recentsAnimationRunner);
                 ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS,
                         "No root task above target root task=%s", targetRootTask);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 95f3c37..c766125 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -513,6 +513,13 @@
      */
     boolean mLayoutNeeded;
 
+    /**
+     * If the application is not currently visible but requires a layout,
+     * then make sure we call performSurfacePlacement as well. This is set
+     * in layout if mLayoutNeeded is set until surface placement is done.
+     */
+    boolean mSurfacePlacementNeeded;
+
     /** Currently running an exit animation? */
     boolean mAnimatingExit;
 
@@ -4281,7 +4288,10 @@
             pw.println(prefix + "mEmbeddedDisplayContents=" + mEmbeddedDisplayContents);
         }
         if (dumpAll) {
-            pw.println(prefix + "mRequestedInsetsState: " + mRequestedInsetsState);
+            final String visibilityString = mRequestedInsetsState.toSourceVisibilityString();
+            if (!visibilityString.isEmpty()) {
+                pw.println(prefix + "Requested visibility: " + visibilityString);
+            }
         }
     }
 
@@ -5367,7 +5377,7 @@
         mIsDimming = false;
         applyDims();
         updateSurfacePositionNonOrganized();
-        // Send information to SufaceFlinger about the priority of the current window.
+        // Send information to SurfaceFlinger about the priority of the current window.
         updateFrameRateSelectionPriorityIfNeeded();
         if (isVisibleRequested()) updateGlobalScaleIfNeeded();
 
@@ -5381,13 +5391,16 @@
         if (mSurfaceControl == null) {
             return;
         }
-        if (mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout()) {
+
+        if ((mWmService.mWindowPlacerLocked.isLayoutDeferred() || isGoneForLayout())
+                && !mSurfacePlacementNeeded) {
             // Since this relies on mWindowFrames, changes made while layout is deferred are
             // likely to be invalid. Similarly, if it's goneForLayout, mWindowFrames may not be
             // up-to-date and thus can't be relied on.
             return;
         }
 
+        mSurfacePlacementNeeded = false;
         transformFrameToSurfacePosition(mWindowFrames.mFrame.left, mWindowFrames.mFrame.top,
                 mSurfacePosition);
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 6f71e99..fbf677d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -103,6 +103,7 @@
 import com.android.internal.widget.ILockSettings;
 import com.android.server.am.ActivityManagerService;
 import com.android.server.appbinding.AppBindingService;
+import com.android.server.art.ArtManagerLocal;
 import com.android.server.attention.AttentionManagerService;
 import com.android.server.audio.AudioService;
 import com.android.server.biometrics.AuthService;
@@ -2610,6 +2611,10 @@
         mSystemServiceManager.startService(GAME_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
+        t.traceBegin("ArtManagerLocal");
+        LocalManagerRegistry.addManager(ArtManagerLocal.class, new ArtManagerLocal());
+        t.traceEnd();
+
         t.traceBegin("StartBootPhaseDeviceSpecificServicesReady");
         mSystemServiceManager.startBootPhase(t, SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY);
         t.traceEnd();
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index 06313da..b136d00 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -46,15 +46,7 @@
         ":PackageManagerTestAppVersion4",
         ":PackageManagerTestAppOriginalOverride",
         ":PackageManagerServiceDeviceSideTests",
-        ":PackageManagerTestIntentVerifier",
-        ":PackageManagerTestIntentVerifierTarget1",
-        ":PackageManagerTestIntentVerifierTarget2",
-        ":PackageManagerTestIntentVerifierTarget3",
-        ":PackageManagerTestIntentVerifierTarget4Base",
-        ":PackageManagerTestIntentVerifierTarget4NoAutoVerify",
-        ":PackageManagerTestIntentVerifierTarget4Wildcard",
-        ":PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify",
-    ]
+    ],
 }
 
 genrule {
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt
deleted file mode 100644
index fffda8e..0000000
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/intent/verify/IntentFilterVerificationTest.kt
+++ /dev/null
@@ -1,413 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.test.intent.verify
-
-import com.android.internal.util.test.SystemPreparer
-import com.android.server.pm.test.Partition
-import com.android.server.pm.test.deleteApkFolders
-import com.android.server.pm.test.installJavaResourceApk
-import com.android.server.pm.test.pushApk
-import com.android.server.pm.test.uninstallPackages
-import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
-import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.ClassRule
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.RuleChain
-import org.junit.rules.TemporaryFolder
-import org.junit.runner.RunWith
-import java.io.File
-import java.util.concurrent.TimeUnit
-
-@RunWith(DeviceJUnit4ClassRunner::class)
-class IntentFilterVerificationTest : BaseHostJUnit4Test() {
-
-    companion object {
-        private const val VERIFIER = "PackageManagerTestIntentVerifier.apk"
-        private const val VERIFIER_PKG_NAME = "com.android.server.pm.test.intent.verifier"
-        private const val TARGET_PKG_PREFIX = "$VERIFIER_PKG_NAME.target"
-        private const val TARGET_APK_PREFIX = "PackageManagerTestIntentVerifierTarget"
-        private const val TARGET_ONE = "${TARGET_APK_PREFIX}1.apk"
-        private const val TARGET_ONE_PKG_NAME = "$TARGET_PKG_PREFIX.one"
-        private const val TARGET_TWO = "${TARGET_APK_PREFIX}2.apk"
-        private const val TARGET_TWO_PKG_NAME = "$TARGET_PKG_PREFIX.two"
-        private const val TARGET_THREE = "${TARGET_APK_PREFIX}3.apk"
-        private const val TARGET_THREE_PKG_NAME = "$TARGET_PKG_PREFIX.three"
-        private const val TARGET_FOUR_BASE = "${TARGET_APK_PREFIX}4Base.apk"
-        private const val TARGET_FOUR_PKG_NAME = "$TARGET_PKG_PREFIX.four"
-        private const val TARGET_FOUR_NO_AUTO_VERIFY = "${TARGET_APK_PREFIX}4NoAutoVerify.apk"
-        private const val TARGET_FOUR_WILDCARD = "${TARGET_APK_PREFIX}4Wildcard.apk"
-        private const val TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY =
-                "${TARGET_APK_PREFIX}4WildcardNoAutoVerify.apk"
-
-        @get:ClassRule
-        val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
-    }
-
-    private val tempFolder = TemporaryFolder()
-    private val preparer: SystemPreparer = SystemPreparer(tempFolder,
-            SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
-
-    @Rule
-    @JvmField
-    val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
-
-    private val permissionsFile = File("/system/etc/permissions" +
-            "/privapp-PackageManagerIntentFilterVerificationTest-permissions.xml")
-
-    @Before
-    fun cleanupAndPushPermissionsFile() {
-        // In order for the test app to be the verification agent, it needs a permission file
-        // which can be pushed onto the system and removed afterwards.
-        val file = tempFolder.newFile().apply {
-            """
-                <permissions>
-                    <privapp-permissions package="$VERIFIER_PKG_NAME">
-                        <permission name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"/>
-                    </privapp-permissions>
-                </permissions>
-            """
-                    .trimIndent()
-                    .let { writeText(it) }
-        }
-        device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME,
-                TARGET_FOUR_PKG_NAME)
-        preparer.pushApk(VERIFIER, Partition.SYSTEM_PRIVILEGED)
-                .pushFile(file, permissionsFile.toString())
-                .reboot()
-        runTest("clearResponse")
-    }
-
-    @After
-    fun cleanupAndDeletePermissionsFile() {
-        device.uninstallPackages(TARGET_ONE_PKG_NAME, TARGET_TWO_PKG_NAME, TARGET_THREE_PKG_NAME,
-                TARGET_FOUR_PKG_NAME)
-        preparer.deleteApkFolders(Partition.SYSTEM_PRIVILEGED, VERIFIER)
-                .deleteFile(permissionsFile.toString())
-        device.reboot()
-    }
-
-    @Test
-    fun verifyOne() {
-        installPackage(TARGET_ONE)
-
-        assertReceivedRequests(true, VerifyRequest(
-                scheme = "https",
-                hosts = listOf(
-                        "https_only.pm.server.android.com",
-                        "other_activity.pm.server.android.com",
-                        "http_only.pm.server.android.com",
-                        "verify.pm.server.android.com",
-                        "https_plus_non_web_scheme.pm.server.android.com",
-                        "multiple.pm.server.android.com",
-                        // TODO(b/159952358): the following domain should not be
-                        //  verified, this is because the verifier tries to verify all web domains,
-                        //  even in intent filters not marked for auto verify
-                        "no_verify.pm.server.android.com"
-                ),
-                packageName = TARGET_ONE_PKG_NAME
-        ))
-
-        runTest(StartActivityParams(
-                uri = "https://https_only.pm.server.android.com",
-                expected = "$TARGET_ONE_PKG_NAME.TargetActivity"
-        ))
-    }
-
-    @Test
-    fun nonWebScheme() {
-        installPackage(TARGET_TWO)
-        assertReceivedRequests(null)
-    }
-
-    @Test
-    fun verifyHttpNonSecureOnly() {
-        installPackage(TARGET_THREE)
-        assertReceivedRequests(true, VerifyRequest(
-                scheme = "https",
-                hosts = listOf(
-                        "multiple.pm.server.android.com"
-                ),
-                packageName = TARGET_THREE_PKG_NAME
-        ))
-
-        runTest(StartActivityParams(
-                uri = "http://multiple.pm.server.android.com",
-                expected = "$TARGET_THREE_PKG_NAME.TargetActivity"
-        ))
-    }
-
-    @Test
-    fun multipleResults() {
-        installPackage(TARGET_ONE)
-        installPackage(TARGET_THREE)
-        assertReceivedRequests(true, VerifyRequest(
-                scheme = "https",
-                hosts = listOf(
-                        "https_only.pm.server.android.com",
-                        "other_activity.pm.server.android.com",
-                        "http_only.pm.server.android.com",
-                        "verify.pm.server.android.com",
-                        "https_plus_non_web_scheme.pm.server.android.com",
-                        "multiple.pm.server.android.com",
-                        // TODO(b/159952358): the following domain should not be
-                        //  verified, this is because the verifier tries to verify all web domains,
-                        //  even in intent filters not marked for auto verify
-                        "no_verify.pm.server.android.com"
-                ),
-                packageName = TARGET_ONE_PKG_NAME
-        ), VerifyRequest(
-                scheme = "https",
-                hosts = listOf(
-                        "multiple.pm.server.android.com"
-                ),
-                packageName = TARGET_THREE_PKG_NAME
-        ))
-
-        // Target3 declares http non-s, so it should be included in the set here
-        runTest(StartActivityParams(
-                uri = "http://multiple.pm.server.android.com",
-                expected = listOf(
-                        "$TARGET_ONE_PKG_NAME.TargetActivity2",
-                        "$TARGET_THREE_PKG_NAME.TargetActivity"
-                )
-        ))
-
-        // But it excludes https, so it shouldn't resolve here
-        runTest(StartActivityParams(
-                uri = "https://multiple.pm.server.android.com",
-                expected = "$TARGET_ONE_PKG_NAME.TargetActivity2"
-        ))
-
-        // Remove Target3 and return to single verified Target1 app for http non-s
-        device.uninstallPackage(TARGET_THREE_PKG_NAME)
-        runTest(StartActivityParams(
-                uri = "http://multiple.pm.server.android.com",
-                expected = "$TARGET_ONE_PKG_NAME.TargetActivity2"
-        ))
-    }
-
-    @Test
-    fun demoteAlways() {
-        installPackage(TARGET_FOUR_BASE)
-        assertReceivedRequests(false, VerifyRequest(
-                scheme = "https",
-                host = "failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
-                withBrowsers = true
-        ))
-        runTest(SetActivityAsAlwaysParams(
-                uri = "https://failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME,
-                activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-
-        // Re-installing with same host/verify set will maintain always setting
-        installPackage(TARGET_FOUR_BASE)
-        assertReceivedRequests(null)
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-
-        // Installing with new wildcard host will downgrade out of always, re-including browsers
-        installPackage(TARGET_FOUR_WILDCARD)
-
-        // TODO(b/159952358): The first request without the wildcard should not be sent. This is
-        //  caused by the request being queued even if it should be dropped from the previous
-        //  install case since the host set didn't change.
-        assertReceivedRequests(false, VerifyRequest(
-                scheme = "https",
-                hosts = listOf("failing.pm.server.android.com"),
-                packageName = TARGET_FOUR_PKG_NAME
-        ), VerifyRequest(
-                scheme = "https",
-                hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
-                withBrowsers = true
-        ))
-    }
-
-    @Test
-    fun unverifiedReinstallResendRequest() {
-        installPackage(TARGET_FOUR_BASE)
-        assertReceivedRequests(false, VerifyRequest(
-                scheme = "https",
-                host = "failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-
-        installPackage(TARGET_FOUR_BASE)
-
-        assertReceivedRequests(false, VerifyRequest(
-                scheme = "https",
-                host = "failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-    }
-
-    @Test
-    fun unverifiedUpdateRemovingDomainNoRequestDemoteAlways() {
-        installPackage(TARGET_FOUR_WILDCARD)
-        assertReceivedRequests(false, VerifyRequest(
-                scheme = "https",
-                hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-
-        runTest(SetActivityAsAlwaysParams(
-                uri = "https://failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME,
-                activityName = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-
-        // Re-installing with a smaller host/verify set will not request re-verification
-        installPackage(TARGET_FOUR_BASE)
-        assertReceivedRequests(null)
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-
-        // Re-installing with a (now) larger host/verify set will re-request and demote
-        installPackage(TARGET_FOUR_WILDCARD)
-        // TODO(b/159952358): The first request should not be sent. This is caused by the request
-        //  being queued even if it should be dropped from the previous install case.
-        assertReceivedRequests(false, VerifyRequest(
-                scheme = "https",
-                host = "failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME
-        ), VerifyRequest(
-                scheme = "https",
-                hosts = listOf("failing.pm.server.android.com", "wildcard.tld"),
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
-                withBrowsers = true
-        ))
-    }
-
-    // TODO(b/159952358): I would expect this to demote
-    // TODO(b/32810168)
-    @Test
-    fun verifiedUpdateRemovingAutoVerifyMaintainsAlways() {
-        installPackage(TARGET_FOUR_BASE)
-        assertReceivedRequests(true, VerifyRequest(
-                scheme = "https",
-                host = "failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-
-        installPackage(TARGET_FOUR_NO_AUTO_VERIFY)
-        assertReceivedRequests(null)
-
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-    }
-
-    @Test
-    fun verifiedUpdateRemovingAutoVerifyAddingDomainDemotesAlways() {
-        installPackage(TARGET_FOUR_BASE)
-
-        assertReceivedRequests(true, VerifyRequest(
-                scheme = "https",
-                host = "failing.pm.server.android.com",
-                packageName = TARGET_FOUR_PKG_NAME
-        ))
-
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity"
-        ))
-
-        installPackage(TARGET_FOUR_WILDCARD_NO_AUTO_VERIFY)
-        assertReceivedRequests(null)
-
-        runTest(StartActivityParams(
-                uri = "https://failing.pm.server.android.com",
-                expected = "$TARGET_FOUR_PKG_NAME.TargetActivity",
-                withBrowsers = true
-        ))
-    }
-
-    private fun installPackage(javaResourceName: String) {
-        // Need to pass --user as verification is not currently run for all user installs
-        assertThat(device.installJavaResourceApk(tempFolder, javaResourceName,
-                extraArgs = arrayOf("--user", device.currentUser.toString()))).isNull()
-    }
-
-    private fun assertReceivedRequests(success: Boolean?, vararg expected: VerifyRequest?) {
-        // TODO(b/159952358): This can probably be less than 10
-        // Because tests have to assert that multiple broadcasts aren't received, there's no real
-        // better way to await for a value than sleeping for a long enough time.
-        TimeUnit.SECONDS.sleep(10)
-
-        val params = mutableMapOf<String, String>()
-        if (expected.any { it != null }) {
-            params["expected"] = expected.filterNotNull()
-                    .joinToString(separator = "") { it.serializeToString() }
-        }
-        runTest("compareLastReceived", params)
-
-        if (success != null) {
-            if (success) {
-                runTest("verifyPreviousReceivedSuccess")
-            } else {
-                runTest("verifyPreviousReceivedFailure")
-            }
-            runTest("clearResponse")
-        }
-    }
-
-    private fun runTest(params: IntentVerifyTestParams) =
-            runTest(params.methodName, params.toArgsMap())
-
-    private fun runTest(testName: String, args: Map<String, String> = emptyMap()) {
-        val escapedArgs = args.mapValues {
-            // Need to escape strings so that args are passed properly through the shell command
-            "\"${it.value.trim('"')}\""
-        }
-        runDeviceTests(device, null, VERIFIER_PKG_NAME, "$VERIFIER_PKG_NAME.VerifyReceiverTest",
-                testName, null, 10 * 60 * 1000L, 10 * 60 * 1000L, 0L, true, false, escapedArgs)
-    }
-}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
deleted file mode 100644
index 4f3f2eb..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifier",
-    srcs: [ "src/**/*.kt" ],
-    static_libs: [
-        "androidx.test.core",
-        "androidx.test.espresso.core",
-        "androidx.test.runner",
-        "compatibility-device-util-axt",
-        "junit",
-        "truth-prebuilt",
-        "PackageManagerServiceHostTestsIntentVerifyUtils",
-    ],
-    platform_apis: true,
-}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml
deleted file mode 100644
index 17b50b0..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/AndroidManifest.xml
+++ /dev/null
@@ -1,38 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier"
-    >
-
-    <uses-permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT" />
-    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
-    <uses-permission android:name="android.permission.SET_PREFERRED_APPLICATIONS" />
-
-    <instrumentation
-        android:name="androidx.test.runner.AndroidJUnitRunner"
-        android:targetPackage="com.android.server.pm.test.intent.verifier"
-        />
-
-    <application>
-        <receiver android:name=".VerifyReceiver" android:exported="true">
-            <intent-filter android:priority="999">
-                <action android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION"/>
-                <data android:mimeType="application/vnd.android.package-archive"/>
-            </intent-filter>
-        </receiver>
-    </application>
-
-</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt
deleted file mode 100644
index 073c2be..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiver.kt
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.test.intent.verifier
-
-import android.content.BroadcastReceiver
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.pm.PackageManager
-import com.android.server.pm.test.intent.verify.VerifyRequest
-
-class VerifyReceiver : BroadcastReceiver() {
-
-    override fun onReceive(context: Context, intent: Intent) {
-        if (intent.action != Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION) return
-        val params = intent.toVerifyParams()
-
-        // If the receiver is called for a normal request, proxy it to the real verifier on device
-        if (params.hosts.none { it.contains("pm.server.android.com") }) {
-            sendToRealVerifier(context, Intent(intent))
-            return
-        }
-
-        // When the receiver is invoked for a test install, there is no direct connection to host,
-        // so store the result in a file to read and assert on later. Append is intentional so that
-        // amount of invocations and clean up can be verified.
-        context.filesDir.resolve("test.txt")
-                .appendText(params.serializeToString())
-    }
-
-    private fun sendToRealVerifier(context: Context, intent: Intent) {
-        context.packageManager.queryBroadcastReceivers(intent, 0)
-                .first { it.activityInfo?.packageName != context.packageName }
-                .let { it.activityInfo!! }
-                .let { intent.setComponent(ComponentName(it.packageName, it.name)) }
-                .run { context.sendBroadcast(intent) }
-    }
-
-    private fun Intent.toVerifyParams() = VerifyRequest(
-            id = getIntExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_ID, -1),
-            scheme = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_URI_SCHEME)!!,
-            hosts = getStringExtra(PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_HOSTS)!!
-                    .split(' '),
-            packageName = getStringExtra(
-                    PackageManager.EXTRA_INTENT_FILTER_VERIFICATION_PACKAGE_NAME)!!
-
-    )
-}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
deleted file mode 100644
index 23ed278..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifier/src/com/android/server/pm/test/intent/verifier/VerifyReceiverTest.kt
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.pm.test.intent.verifier
-
-import android.content.ComponentName
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
-import android.content.pm.PackageManager
-import android.net.Uri
-import android.os.Bundle
-import android.os.UserHandle
-import androidx.test.InstrumentationRegistry
-import androidx.test.runner.AndroidJUnit4
-import com.android.compatibility.common.util.ShellIdentityUtils
-import com.android.server.pm.test.intent.verify.SetActivityAsAlwaysParams
-import com.android.server.pm.test.intent.verify.StartActivityParams
-import com.android.server.pm.test.intent.verify.VerifyRequest
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-import java.io.File
-
-@RunWith(AndroidJUnit4::class)
-class VerifyReceiverTest {
-
-    val args: Bundle = InstrumentationRegistry.getArguments()
-    val context: Context = InstrumentationRegistry.getContext()
-
-    private val file = context.filesDir.resolve("test.txt")
-
-    @Test
-    fun clearResponse() {
-        file.delete()
-    }
-
-    @Test
-    fun compareLastReceived() {
-        val lastReceivedText = file.readTextIfExists()
-        val expectedText = args.getString("expected")
-        if (expectedText.isNullOrEmpty()) {
-            assertThat(lastReceivedText).isEmpty()
-            return
-        }
-
-        val expectedParams = expectedText.parseParams()
-        val lastReceivedParams = lastReceivedText.parseParams()
-
-        assertThat(lastReceivedParams).hasSize(expectedParams.size)
-
-        lastReceivedParams.zip(expectedParams).forEach { (actual, expected) ->
-            assertThat(actual.hosts).containsExactlyElementsIn(expected.hosts)
-            assertThat(actual.packageName).isEqualTo(expected.packageName)
-            assertThat(actual.scheme).isEqualTo(expected.scheme)
-        }
-    }
-
-    @Test
-    fun setActivityAsAlways() {
-        val params = SetActivityAsAlwaysParams.fromArgs(
-                args.keySet().associateWith { args.getString(it)!! })
-        val uri = Uri.parse(params.uri)
-        val filter = IntentFilter().apply {
-            addAction(Intent.ACTION_VIEW)
-            addCategory(Intent.CATEGORY_DEFAULT)
-            addCategory(Intent.CATEGORY_BROWSABLE)
-            addDataScheme(uri.scheme)
-            addDataAuthority(uri.authority, null)
-        }
-
-        val intent = Intent(Intent.ACTION_VIEW, uri).apply {
-            addCategory(Intent.CATEGORY_DEFAULT)
-            addCategory(Intent.CATEGORY_BROWSABLE)
-        }
-        val allResults = context.packageManager.queryIntentActivities(intent, 0)
-        val allComponents = allResults
-                .map { ComponentName(it.activityInfo.packageName, it.activityInfo.name) }
-                .toTypedArray()
-        val matchingInfo = allResults.first {
-            it.activityInfo.packageName == params.packageName &&
-                    it.activityInfo.name == params.activityName
-        }
-
-        ShellIdentityUtils.invokeMethodWithShellPermissions(context.packageManager,
-                ShellIdentityUtils.ShellPermissionMethodHelper<Unit, PackageManager> {
-                    it.addUniquePreferredActivity(filter, matchingInfo.match, allComponents,
-                            ComponentName(matchingInfo.activityInfo.packageName,
-                                    matchingInfo.activityInfo.name))
-                    it.updateIntentVerificationStatusAsUser(params.packageName,
-                            PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS,
-                            UserHandle.myUserId())
-                }, "android.permission.SET_PREFERRED_APPLICATIONS")
-    }
-
-    @Test
-    fun verifyPreviousReceivedSuccess() {
-        file.readTextIfExists()
-                .parseParams()
-                .forEach {
-                    context.packageManager.verifyIntentFilter(it.id,
-                            PackageManager.INTENT_FILTER_VERIFICATION_SUCCESS, emptyList())
-                }
-    }
-
-    @Test
-    fun verifyPreviousReceivedFailure() {
-        file.readTextIfExists()
-                .parseParams()
-                .forEach {
-                    context.packageManager.verifyIntentFilter(it.id,
-                            PackageManager.INTENT_FILTER_VERIFICATION_FAILURE, it.hosts)
-                }
-    }
-
-    @Test
-    fun verifyActivityStart() {
-        val params = StartActivityParams
-                .fromArgs(args.keySet().associateWith { args.getString(it)!! })
-        val uri = Uri.parse(params.uri)
-        val intent = Intent(Intent.ACTION_VIEW).apply {
-            data = uri
-            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-            addCategory(Intent.CATEGORY_DEFAULT)
-            addCategory(Intent.CATEGORY_BROWSABLE)
-        }
-
-        val expectedActivities = params.expected.toMutableList()
-
-        if (params.withBrowsers) {
-            // Since the host doesn't know what browsers the device has, query here and add it to
-            // set if it's expected that browser are returned
-            val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://example.com"))
-            expectedActivities += context.packageManager.queryIntentActivities(browserIntent, 0)
-                    .map { it.activityInfo.name }
-        }
-
-        val infos = context.packageManager.queryIntentActivities(intent, 0)
-                .map { it.activityInfo.name }
-        assertThat(infos).containsExactlyElementsIn(expectedActivities)
-    }
-
-    private fun File.readTextIfExists() = if (exists()) readText() else ""
-
-    // Rudimentary list deserialization by splitting text block into 4 line sections
-    private fun String.parseParams() = trim()
-            .lines()
-            .windowed(4, 4)
-            .map { it.joinToString(separator = "\n") }
-            .map { VerifyRequest.deserialize(it) }
-}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
deleted file mode 100644
index 9f9ed24..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/Android.bp
+++ /dev/null
@@ -1,57 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package {
-    // See: http://go/android-license-faq
-    // A large-scale-change added 'default_applicable_licenses' to import
-    // all of the 'license_kinds' from "frameworks_base_license"
-    // to get the below license kinds:
-    //   SPDX-license-identifier-Apache-2.0
-    default_applicable_licenses: ["frameworks_base_license"],
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifierTarget1",
-    manifest: "AndroidManifest1.xml",
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifierTarget2",
-    manifest: "AndroidManifest2.xml",
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifierTarget3",
-    manifest: "AndroidManifest3.xml",
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifierTarget4Base",
-    manifest: "AndroidManifest4Base.xml",
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifierTarget4NoAutoVerify",
-    manifest: "AndroidManifest4NoAutoVerify.xml",
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifierTarget4Wildcard",
-    manifest: "AndroidManifest4Wildcard.xml",
-}
-
-android_test_helper_app {
-    name: "PackageManagerTestIntentVerifierTarget4WildcardNoAutoVerify",
-    manifest: "AndroidManifest4WildcardNoAutoVerify.xml",
-}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml
deleted file mode 100644
index 6cf5c76..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest1.xml
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier.target.one" android:versionCode="1">
-
-    <application>
-        <activity android:name=".TargetActivity" android:exported="true">
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="verify.pm.server.android.com" />
-            </intent-filter>
-
-            <intent-filter android:autoVerify="false">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="no_verify.pm.server.android.com" />
-            </intent-filter>
-
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:host="http_only.pm.server.android.com" />
-            </intent-filter>
-
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="https" />
-                <data android:host="https_only.pm.server.android.com" />
-            </intent-filter>
-
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="htttps" />
-                <data android:host="non_http.pm.server.android.com" />
-            </intent-filter>
-
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="https" />
-                <data android:scheme="non_web_scheme" />
-                <data android:host="https_plus_non_web_scheme.pm.server.android.com" />
-            </intent-filter>
-        </activity>
-
-        <activity android:name=".TargetActivity2" android:exported="true">
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="other_activity.pm.server.android.com" />
-            </intent-filter>
-
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="multiple.pm.server.android.com" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml
deleted file mode 100644
index 087ef705..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest2.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier.target.two"
-    android:versionCode="1">
-
-    <application>
-        <activity android:name=".TargetActivity" android:exported="true">
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:scheme="non_web_scheme" />
-                <data android:host="only_https_plus_non_web_scheme.pm.server.android.com" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml
deleted file mode 100644
index eb75b5e..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest3.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier.target.three"
-    android:versionCode="1">
-
-    <application>
-        <activity android:name=".TargetActivity" android:exported="true">
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:host="multiple.pm.server.android.com" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml
deleted file mode 100644
index 7eacb8b..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Base.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier.target.four"
-    android:versionCode="1">
-
-    <application>
-        <activity android:name=".TargetActivity" android:exported="true">
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="failing.pm.server.android.com" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml
deleted file mode 100644
index ecfee55..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4NoAutoVerify.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier.target.four"
-    android:versionCode="1">
-
-    <application>
-        <activity android:name=".TargetActivity" android:exported="true">
-            <intent-filter android:autoVerify="false">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="failing.pm.server.android.com" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml
deleted file mode 100644
index 0f0f53b..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4Wildcard.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier.target.four"
-    android:versionCode="1">
-
-    <application>
-        <activity android:name=".TargetActivity" android:exported="true">
-            <intent-filter android:autoVerify="true">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="failing.pm.server.android.com" />
-                <data android:host="*.wildcard.tld" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml b/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml
deleted file mode 100644
index d5652e1..0000000
--- a/services/tests/PackageManagerServiceTests/host/test-apps/IntentVerifierTarget/AndroidManifest4WildcardNoAutoVerify.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.server.pm.test.intent.verifier.target.four"
-    android:versionCode="1">
-
-    <application>
-        <activity android:name=".TargetActivity" android:exported="true">
-            <intent-filter android:autoVerify="false">
-                <action android:name="android.intent.action.VIEW" />
-                <category android:name="android.intent.category.BROWSABLE" />
-                <category android:name="android.intent.category.DEFAULT" />
-                <data android:scheme="http" />
-                <data android:scheme="https" />
-                <data android:host="failing.pm.server.android.com" />
-                <data android:host="*.wildcard.tld" />
-            </intent-filter>
-        </activity>
-    </application>
-
-</manifest>
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 1c45203..3404aff 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1361,6 +1361,52 @@
 
     @SuppressWarnings("GuardedBy")
     @Test
+    public void testUpdateOomAdj_DoOne_ScheduleLikeTop() {
+        final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+        final ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        final ProcessRecord client1 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+                MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+        final ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP4_PID, MOCKAPP4_UID,
+                MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false));
+        bindService(app1, client1, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                mock(IBinder.class));
+        bindService(app2, client2, null, Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+                mock(IBinder.class));
+        client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ);
+        client2.mServices.setHasForegroundServices(true, 0);
+
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE);
+        sService.mOomAdjuster.updateOomAdjLocked(app1, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+        sService.mOomAdjuster.updateOomAdjLocked(app2, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+
+        assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT);
+        assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT);
+
+        bindService(app1, client1, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class));
+        bindService(app2, client2, null, Context.BIND_SCHEDULE_LIKE_TOP_APP, mock(IBinder.class));
+        sService.mOomAdjuster.updateOomAdjLocked(app1, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+        sService.mOomAdjuster.updateOomAdjLocked(app2, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+
+        assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ,
+                SCHED_GROUP_TOP_APP);
+        assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT);
+
+        sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP);
+        sService.mOomAdjuster.updateOomAdjLocked(app1, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+        sService.mOomAdjuster.updateOomAdjLocked(app2, false, OomAdjuster.OOM_ADJ_REASON_NONE);
+        assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ,
+                SCHED_GROUP_TOP_APP);
+        assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
     public void testUpdateOomAdj_UidIdle_StopService() {
         final ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index 68f5479..d7fbd49 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -42,6 +42,7 @@
         "androidx.test.ext.truth",
         "androidx.test.runner",
         "androidx.test.rules",
+        "cts-wm-util",
         "platform-compat-test-rules",
         "mockito-target-minus-junit4",
         "platform-test-annotations",
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
index e04841b..6bca5e4 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerTest.java
@@ -46,6 +46,9 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+import android.provider.Settings;
+import android.server.wm.settings.SettingsSession;
 import android.support.test.uiautomator.UiDevice;
 import android.test.suitebuilder.annotation.LargeTest;
 import android.text.TextUtils;
@@ -61,6 +64,8 @@
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Tests for {@link ActivityManager}.
@@ -316,6 +321,102 @@
         }
     }
 
+    @LargeTest
+    @Test
+    public void testAppFreezerWithAllowOomAdj() throws Exception {
+        final long waitFor = 5000;
+        boolean freezerWasEnabled = isFreezerEnabled();
+        SettingsSession<String> freezerEnabled = null;
+        SettingsSession<String> amConstantsSettings = null;
+        DeviceConfigSession<Long> freezerDebounceTimeout = null;
+        MyServiceConnection autoConnection = null;
+        try {
+            if (!freezerWasEnabled) {
+                freezerEnabled = new SettingsSession<>(
+                        Settings.Global.getUriFor(Settings.Global.CACHED_APPS_FREEZER_ENABLED),
+                        Settings.Global::getString, Settings.Global::putString);
+                freezerEnabled.set("enabled");
+                Thread.sleep(waitFor);
+                if (!isFreezerEnabled()) {
+                    // Still not enabled? Probably because the device doesn't support it.
+                    return;
+                }
+            }
+            freezerDebounceTimeout = new DeviceConfigSession<>(
+                    DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+                    CachedAppOptimizer.KEY_FREEZER_DEBOUNCE_TIMEOUT,
+                    DeviceConfig::getLong, CachedAppOptimizer.DEFAULT_FREEZER_DEBOUNCE_TIMEOUT);
+            freezerDebounceTimeout.set(waitFor);
+
+            final String activityManagerConstants = Settings.Global.ACTIVITY_MANAGER_CONSTANTS;
+            amConstantsSettings = new SettingsSession<>(
+                Settings.Global.getUriFor(activityManagerConstants),
+                Settings.Global::getString, Settings.Global::putString);
+
+            amConstantsSettings.set(
+                    ActivityManagerConstants.KEY_MAX_SERVICE_INACTIVITY + "=" + waitFor);
+
+            final Intent intent = new Intent();
+            intent.setClassName(TEST_APP, TEST_CLASS);
+
+            final CountDownLatch latch = new CountDownLatch(1);
+            autoConnection = new MyServiceConnection(latch);
+            mContext.bindService(intent, autoConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT);
+            try {
+                assertTrue("Timeout to bind to service " + intent.getComponent(),
+                        latch.await(AWAIT_TIMEOUT, TimeUnit.MILLISECONDS));
+            } catch (InterruptedException e) {
+                fail("Unable to bind to service " + intent.getComponent());
+            }
+            assertFalse(TEST_APP + " shouldn't be frozen now.", isAppFrozen(TEST_APP));
+
+            // Trigger oomAdjUpdate/
+            toggleScreenOn(false);
+            toggleScreenOn(true);
+
+            // Wait for the freezer kick in if there is any.
+            Thread.sleep(waitFor * 4);
+
+            // It still shouldn't be frozen, although it's been in cached state.
+            assertFalse(TEST_APP + " shouldn't be frozen now.", isAppFrozen(TEST_APP));
+        } finally {
+            toggleScreenOn(true);
+            if (amConstantsSettings != null) {
+                amConstantsSettings.close();
+            }
+            if (freezerEnabled != null) {
+                freezerEnabled.close();
+            }
+            if (freezerDebounceTimeout != null) {
+                freezerDebounceTimeout.close();
+            }
+            if (autoConnection != null) {
+                mContext.unbindService(autoConnection);
+            }
+        }
+    }
+
+    private boolean isFreezerEnabled() throws Exception {
+        final String output = runShellCommand("dumpsys activity settings");
+        final Matcher matcher = Pattern.compile("\\b" + CachedAppOptimizer.KEY_USE_FREEZER
+                + "\\b=\\b(true|false)\\b").matcher(output);
+        if (matcher.find()) {
+            return Boolean.parseBoolean(matcher.group(1));
+        }
+        return false;
+    }
+
+    private boolean isAppFrozen(String packageName) throws Exception {
+        final String output = runShellCommand("dumpsys activity p " + packageName);
+        final Matcher matcher = Pattern.compile("\\b" + ProcessCachedOptimizerRecord.IS_FROZEN
+                + "\\b=\\b(true|false)\\b").matcher(output);
+        if (matcher.find()) {
+            return Boolean.parseBoolean(matcher.group(1));
+        }
+        return false;
+    }
+
     /**
      * Make sure the screen state.
      */
diff --git a/services/tests/servicestests/src/com/android/server/am/DeviceConfigSession.java b/services/tests/servicestests/src/com/android/server/am/DeviceConfigSession.java
new file mode 100644
index 0000000..03cf17c
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/am/DeviceConfigSession.java
@@ -0,0 +1,72 @@
+/*
+ * 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.server.am;
+
+import android.annotation.NonNull;
+import android.provider.DeviceConfig;
+
+import com.android.compatibility.common.util.SystemUtil;
+import com.android.internal.util.function.TriFunction;
+
+/**
+ * An utility class to set/restore the given device_config item.
+ */
+public class DeviceConfigSession<T> implements AutoCloseable {
+    private final TriFunction<String, String, T, T> mGetter;
+
+    private final String mNamespace;
+    private final String mKey;
+    private final T mInitialValue;
+    private final T mDefaultValue;
+    private boolean mHasInitalValue;
+
+    DeviceConfigSession(String namespace, String key,
+            TriFunction<String, String, T, T> getter, T defaultValue) {
+        mNamespace = namespace;
+        mKey = key;
+        mGetter = getter;
+        mDefaultValue = defaultValue;
+        // Try {@DeviceConfig#getString} firstly since the DeviceConfig API doesn't
+        // support "not found" exception.
+        final String initialStringValue = DeviceConfig.getString(namespace, key, null);
+        if (initialStringValue == null) {
+            mHasInitalValue = false;
+            mInitialValue = defaultValue;
+        } else {
+            mHasInitalValue = true;
+            mInitialValue = getter.apply(namespace, key, defaultValue);
+        }
+    }
+
+    public void set(final @NonNull T value) {
+        DeviceConfig.setProperty(mNamespace, mKey,
+                value == null ? null : value.toString(), false);
+    }
+
+    public T get() {
+        return mGetter.apply(mNamespace, mKey, mDefaultValue);
+    }
+
+    @Override
+    public void close() throws Exception {
+        if (mHasInitalValue) {
+            set(mInitialValue);
+        } else {
+            SystemUtil.runShellCommand("device_config delete " + mNamespace + " " + mKey);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 100d3ea..f5876fa 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -33,12 +33,6 @@
 import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
 import static android.net.NetworkPolicyManager.POLICY_NONE;
 import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
-import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
-import static android.net.NetworkPolicyManager.RULE_NONE;
-import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
-import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
-import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.uidPoliciesToString;
 import static android.net.NetworkPolicyManager.uidRulesToString;
 import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
@@ -49,7 +43,6 @@
 import static android.net.NetworkTemplate.buildTemplateMobileAll;
 import static android.net.NetworkTemplate.buildTemplateWifi;
 import static android.net.TrafficStats.MB_IN_BYTES;
-import static android.os.Process.SYSTEM_UID;
 import static android.telephony.CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_THRESHOLD_DISABLED;
 import static android.telephony.CarrierConfigManager.DATA_CYCLE_USE_PLATFORM_DEFAULT;
@@ -141,7 +134,6 @@
 import android.util.ArrayMap;
 import android.util.DataUnit;
 import android.util.Log;
-import android.util.Pair;
 import android.util.Range;
 import android.util.RecurrenceRule;
 
@@ -1844,68 +1836,6 @@
         reset(mStatsService);
     }
 
-    /**
-     * Exhaustively test checkUidNetworkingBlocked to output the expected results based on external
-     * conditions.
-     */
-    @Test
-    public void testCheckUidNetworkingBlocked() {
-        final ArrayList<Pair<Boolean, Integer>> expectedBlockedStates = new ArrayList<>();
-
-        // Metered network. Data saver on.
-        expectedBlockedStates.add(new Pair<>(true, RULE_NONE));
-        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
-        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
-        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
-        expectedBlockedStates.add(new Pair<>(true, RULE_ALLOW_ALL));
-        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
-        verifyNetworkBlockedState(
-                true /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
-        expectedBlockedStates.clear();
-
-        // Metered network. Data saver off.
-        expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
-        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
-        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
-        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_METERED));
-        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
-        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
-        verifyNetworkBlockedState(
-                true /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
-        expectedBlockedStates.clear();
-
-        // Non-metered network. Data saver on.
-        expectedBlockedStates.add(new Pair<>(false, RULE_NONE));
-        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_METERED));
-        expectedBlockedStates.add(new Pair<>(false, RULE_TEMPORARY_ALLOW_METERED));
-        expectedBlockedStates.add(new Pair<>(false, RULE_REJECT_METERED));
-        expectedBlockedStates.add(new Pair<>(false, RULE_ALLOW_ALL));
-        expectedBlockedStates.add(new Pair<>(true, RULE_REJECT_ALL));
-        verifyNetworkBlockedState(
-                false /* metered */, true /* backgroundRestricted */, expectedBlockedStates);
-
-        // Non-metered network. Data saver off. The result is the same as previous case since
-        // the network is blocked only for RULE_REJECT_ALL regardless of data saver.
-        verifyNetworkBlockedState(
-                false /* metered */, false /* backgroundRestricted */, expectedBlockedStates);
-        expectedBlockedStates.clear();
-    }
-
-    private void verifyNetworkBlockedState(boolean metered, boolean backgroundRestricted,
-            ArrayList<Pair<Boolean, Integer>> expectedBlockedStateForRules) {
-
-        for (Pair<Boolean, Integer> pair : expectedBlockedStateForRules) {
-            final boolean expectedResult = pair.first;
-            final int rule = pair.second;
-            assertEquals(formatBlockedStateError(UID_A, rule, metered, backgroundRestricted),
-                    expectedResult, mService.checkUidNetworkingBlocked(UID_A, rule,
-                            metered, backgroundRestricted));
-            assertFalse(formatBlockedStateError(SYSTEM_UID, rule, metered, backgroundRestricted),
-                    mService.checkUidNetworkingBlocked(SYSTEM_UID, rule, metered,
-                            backgroundRestricted));
-        }
-    }
-
     private void enableRestrictedMode(boolean enable) throws Exception {
         mService.mRestrictedNetworkingMode = enable;
         mService.updateRestrictedModeAllowlistUL();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
index f6d6624..809b6d5 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java
@@ -350,6 +350,52 @@
     }
 
     @Test
+    public void testRemoveChannelRunnable() throws Exception {
+        NotificationHistory nh = mock(NotificationHistory.class);
+        NotificationHistoryDatabase.RemoveChannelRunnable rcr =
+                mDataBase.new RemoveChannelRunnable("pkg", "channel");
+        rcr.setNotificationHistory(nh);
+
+        AtomicFile af = mock(AtomicFile.class);
+        when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+        mDataBase.mHistoryFiles.addLast(af);
+
+        when(nh.removeChannelFromWrite("pkg", "channel")).thenReturn(true);
+
+        mDataBase.mBuffer = mock(NotificationHistory.class);
+
+        rcr.run();
+
+        verify(mDataBase.mBuffer).removeChannelFromWrite("pkg", "channel");
+        verify(af).openRead();
+        verify(nh).removeChannelFromWrite("pkg", "channel");
+        verify(af).startWrite();
+    }
+
+    @Test
+    public void testRemoveChannelRunnable_noChanges() throws Exception {
+        NotificationHistory nh = mock(NotificationHistory.class);
+        NotificationHistoryDatabase.RemoveChannelRunnable rcr =
+                mDataBase.new RemoveChannelRunnable("pkg", "channel");
+        rcr.setNotificationHistory(nh);
+
+        AtomicFile af = mock(AtomicFile.class);
+        when(af.getBaseFile()).thenReturn(new File(mRootDir, "af"));
+        mDataBase.mHistoryFiles.addLast(af);
+
+        when(nh.removeChannelFromWrite("pkg", "channel")).thenReturn(false);
+
+        mDataBase.mBuffer = mock(NotificationHistory.class);
+
+        rcr.run();
+
+        verify(mDataBase.mBuffer).removeChannelFromWrite("pkg", "channel");
+        verify(af).openRead();
+        verify(nh).removeChannelFromWrite("pkg", "channel");
+        verify(af, never()).startWrite();
+    }
+
+    @Test
     public void testWriteBufferRunnable() throws Exception {
         NotificationHistory nh = mock(NotificationHistory.class);
         when(nh.getPooledStringsToWrite()).thenReturn(new String[]{});
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
index a0293b7..5892793 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryManagerTest.java
@@ -366,7 +366,7 @@
     @Test
     public void testDeleteConversation_userUnlocked() {
         String pkg = "pkg";
-        Set<String> convos  = Set.of("convo", "another");
+        Set<String> convos = Set.of("convo", "another");
         NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
 
         mHistoryManager.onUserUnlocked(USER_SYSTEM);
@@ -378,6 +378,20 @@
     }
 
     @Test
+    public void testDeleteNotificationChannel_userUnlocked() {
+        String pkg = "pkg";
+        String channelId = "channelId";
+        NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
+
+        mHistoryManager.onUserUnlocked(USER_SYSTEM);
+        mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
+
+        mHistoryManager.deleteNotificationChannel(pkg, 1, channelId);
+
+        verify(userHistory, times(1)).deleteNotificationChannel(pkg, channelId);
+    }
+
+    @Test
     public void testTriggerWriteToDisk() {
         NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
         NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index c8c8c82..8be19f0 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -93,6 +93,7 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
+import android.app.AlarmManager;
 import android.app.AppOpsManager;
 import android.app.AutomaticZenRule;
 import android.app.IActivityManager;
@@ -187,6 +188,7 @@
 import com.android.server.lights.LogicalLight;
 import com.android.server.notification.NotificationManagerService.NotificationAssistants;
 import com.android.server.notification.NotificationManagerService.NotificationListeners;
+import com.android.server.pm.PackageManagerService;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.uri.UriGrantsManagerInternal;
 import com.android.server.utils.quota.MultiRateLimiter;
@@ -307,6 +309,8 @@
     @Mock
     StatsManager mStatsManager;
     @Mock
+    AlarmManager mAlarmManager;
+    @Mock
     MultiRateLimiter mToastRateLimiter;
     BroadcastReceiver mPackageIntentReceiver;
     NotificationRecordLoggerFake mNotificationRecordLogger = new NotificationRecordLoggerFake();
@@ -428,6 +432,7 @@
         LocalServices.addService(DeviceIdleInternal.class, deviceIdleInternal);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
         LocalServices.addService(ActivityManagerInternal.class, mAmi);
+        mContext.addMockSystemService(Context.ALARM_SERVICE, mAlarmManager);
 
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
 
@@ -905,6 +910,26 @@
     }
 
     @Test
+    public void testLimitTimeOutBroadcast() {
+        NotificationChannel channel = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_HIGH);
+        Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setTimeoutAfter(1);
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
+                nb.build(), UserHandle.getUserHandleForUid(mUid), null, 0);
+        NotificationRecord r = new NotificationRecord(mContext, sbn, channel);
+
+        mService.scheduleTimeoutLocked(r);
+        ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+        verify(mAlarmManager).setExactAndAllowWhileIdle(anyInt(), anyLong(), captor.capture());
+        assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
+                captor.getValue().getIntent().getPackage());
+    }
+
+    @Test
     public void testDefaultAssistant_overrideDefault() {
         final int userId = mContext.getUserId();
         final String testComponent = "package/class";
diff --git a/telephony/java/android/telephony/ims/SipMessage.java b/telephony/java/android/telephony/ims/SipMessage.java
index ad6d73c..b529563 100644
--- a/telephony/java/android/telephony/ims/SipMessage.java
+++ b/telephony/java/android/telephony/ims/SipMessage.java
@@ -24,6 +24,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.TextUtils;
 
 import com.android.internal.telephony.SipMessageParsingUtils;
 
@@ -60,14 +61,19 @@
      */
     public SipMessage(@NonNull String startLine, @NonNull String headerSection,
             @NonNull byte[] content) {
-        if (startLine == null || headerSection == null || content == null) {
-            throw new IllegalArgumentException("One or more null parameters entered");
-        }
+        Objects.requireNonNull(startLine, "Required parameter is null: startLine");
+        Objects.requireNonNull(headerSection, "Required parameter is null: headerSection");
+        Objects.requireNonNull(content, "Required parameter is null: content");
+
         mStartLine = startLine;
         mHeaderSection = headerSection;
         mContent = content;
 
         mViaBranchParam = SipMessageParsingUtils.getTransactionId(mHeaderSection);
+        if (TextUtils.isEmpty(mViaBranchParam)) {
+            throw new IllegalArgumentException("header section MUST contain a branch parameter "
+                    + "inside of the Via header.");
+        }
         mCallIdParam = SipMessageParsingUtils.getCallId(mHeaderSection);
     }
 
@@ -107,11 +113,9 @@
 
     /**
      * @return the branch parameter enclosed in the Via header key's value. See RFC 3261 section
-     * 20.42 for more information on the Via header. If {@code null}, then there was either no
-     * Via parameter found in this SIP message's headers or no branch parameter found in the
-     * Via header.
+     * 20.42 for more information on the Via header.
      */
-    public @Nullable String getViaBranchParameter() {
+    public @NonNull String getViaBranchParameter() {
         return mViaBranchParam;
     }
 
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 739946b..5c9ec53 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -28,8 +28,6 @@
 import android.telephony.ims.SipDelegateManager;
 import android.telephony.ims.SipMessage;
 import android.telephony.ims.stub.SipDelegate;
-import android.text.TextUtils;
-import android.util.Log;
 
 import java.util.ArrayList;
 import java.util.Set;
@@ -187,11 +185,6 @@
 
     private void notifyLocalMessageFailedToBeReceived(SipMessage m, int reason) {
         String transactionId = m.getViaBranchParameter();
-        if (TextUtils.isEmpty(transactionId)) {
-            Log.w(LOG_TAG, "failure to parse SipMessage.");
-            throw new IllegalArgumentException("Malformed SipMessage, can not determine "
-                    + "transaction ID.");
-        }
         SipDelegate d = mDelegate;
         if (d != null) {
             mExecutor.execute(() -> d.notifyMessageReceiveError(transactionId, reason));
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index 3cd2726..ad02fe5 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -28,7 +28,6 @@
 import android.telephony.ims.stub.DelegateConnectionMessageCallback;
 import android.telephony.ims.stub.DelegateConnectionStateCallback;
 import android.telephony.ims.stub.SipDelegate;
-import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
 
@@ -267,12 +266,6 @@
 
     private void notifyLocalMessageFailedToSend(SipMessage m, int reason) {
         String transactionId = m.getViaBranchParameter();
-        if (TextUtils.isEmpty(transactionId)) {
-            Log.w(LOG_TAG, "sendMessage detected a malformed SipMessage and can not get a "
-                    + "transaction ID.");
-            throw new IllegalArgumentException("Could not send SipMessage due to malformed header");
-        }
-        mExecutor.execute(() ->
-                mMessageCallback.onMessageSendFailure(transactionId, reason));
+        mExecutor.execute(() -> mMessageCallback.onMessageSendFailure(transactionId, reason));
     }
 }
diff --git a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
index e7718b5..6d852bf 100644
--- a/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/common/java/android/net/NetworkCapabilitiesTest.java
@@ -48,6 +48,7 @@
 
 import static com.android.modules.utils.build.SdkLevel.isAtLeastR;
 import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
+import static com.android.net.module.util.NetworkCapabilitiesUtils.TRANSPORT_USB;
 import static com.android.testutils.MiscAsserts.assertEmpty;
 import static com.android.testutils.MiscAsserts.assertThrows;
 import static com.android.testutils.ParcelUtils.assertParcelSane;
@@ -959,6 +960,11 @@
         assertNotEquals(512, nc.getLinkUpstreamBandwidthKbps());
     }
 
+    private int getMaxTransport() {
+        if (!isAtLeastS() && MAX_TRANSPORT == TRANSPORT_USB) return MAX_TRANSPORT - 1;
+        return MAX_TRANSPORT;
+    }
+
     @Test
     public void testSignalStrength() {
         final NetworkCapabilities nc = new NetworkCapabilities();
@@ -970,7 +976,7 @@
     }
 
     private void assertNoTransport(NetworkCapabilities nc) {
-        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+        for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) {
             assertFalse(nc.hasTransport(i));
         }
     }
@@ -987,7 +993,7 @@
                 assertFalse(nc.hasTransport(i));
             }
         }
-        for (int i = MAX_TRANSPORT; i > maxTransportType; i--) {
+        for (int i = getMaxTransport(); i > maxTransportType; i--) {
             if (positiveSequence) {
                 assertFalse(nc.hasTransport(i));
             } else {
@@ -1001,12 +1007,12 @@
         final NetworkCapabilities nc = new NetworkCapabilities();
         assertNoTransport(nc);
         // Test adding multiple transport types.
-        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+        for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) {
             nc.addTransportType(i);
             checkCurrentTransportTypes(nc, i, true /* positiveSequence */);
         }
         // Test removing multiple transport types.
-        for (int i = MIN_TRANSPORT; i <= MAX_TRANSPORT; i++) {
+        for (int i = MIN_TRANSPORT; i <= getMaxTransport(); i++) {
             nc.removeTransportType(i);
             checkCurrentTransportTypes(nc, i, false /* positiveSequence */);
         }
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f903a45..af7eb59 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -21,6 +21,9 @@
 import static android.Manifest.permission.NETWORK_FACTORY;
 import static android.Manifest.permission.NETWORK_SETTINGS;
 import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
+import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static android.content.Intent.ACTION_PACKAGE_REPLACED;
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.ACTION_USER_UNLOCKED;
@@ -2783,10 +2786,14 @@
     }
 
     private void grantUsingBackgroundNetworksPermissionForUid(final int uid) throws Exception {
-        final String myPackageName = mContext.getPackageName();
-        when(mPackageManager.getPackageInfo(eq(myPackageName), eq(GET_PERMISSIONS)))
+        grantUsingBackgroundNetworksPermissionForUid(uid, mContext.getPackageName());
+    }
+
+    private void grantUsingBackgroundNetworksPermissionForUid(
+            final int uid, final String packageName) throws Exception {
+        when(mPackageManager.getPackageInfo(eq(packageName), eq(GET_PERMISSIONS)))
                 .thenReturn(buildPackageInfo(true, uid));
-        mService.mPermissionMonitor.onPackageAdded(myPackageName, uid);
+        mService.mPermissionMonitor.onPackageAdded(packageName, uid);
     }
 
     @Test
@@ -10433,6 +10440,12 @@
                 .thenReturn(applicationInfo);
     }
 
+    private void mockGetApplicationInfoThrowsNameNotFound(@NonNull final String packageName)
+            throws Exception {
+        when(mPackageManager.getApplicationInfo(eq(packageName), anyInt()))
+                .thenThrow(new PackageManager.NameNotFoundException(packageName));
+    }
+
     private void mockHasSystemFeature(@NonNull final String featureName,
             @NonNull final boolean hasFeature) {
         when(mPackageManager.hasSystemFeature(eq(featureName)))
@@ -10889,15 +10902,23 @@
             @NonNull final UidRangeParcel[] uidRanges,
             @NonNull final String testPackageName)
             throws Exception {
-        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
-
         // These tests work off a single UID therefore using 'start' is valid.
         mockGetApplicationInfo(testPackageName, uidRanges[0].start);
 
+        setOemNetworkPreference(networkPrefToSetup, testPackageName);
+    }
+
+    private void setOemNetworkPreference(final int networkPrefToSetup,
+            @NonNull final String... testPackageNames)
+            throws Exception {
+        mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
+
         // Build OemNetworkPreferences object
-        final OemNetworkPreferences pref = new OemNetworkPreferences.Builder()
-                .addNetworkPreference(testPackageName, networkPrefToSetup)
-                .build();
+        final OemNetworkPreferences.Builder builder = new OemNetworkPreferences.Builder();
+        for (final String packageName : testPackageNames) {
+            builder.addNetworkPreference(packageName, networkPrefToSetup);
+        }
+        final OemNetworkPreferences pref = builder.build();
 
         // Act on ConnectivityService.setOemNetworkPreference()
         final TestOemListenerCallback oemPrefListener = new TestOemListenerCallback();
@@ -11496,8 +11517,7 @@
         // Arrange PackageManager mocks
         final int secondUserTestPackageUid = UserHandle.getUid(secondUser, TEST_PACKAGE_UID);
         final UidRangeParcel[] uidRangesSingleUser =
-                toUidRangeStableParcels(
-                        uidRangesForUids(TEST_PACKAGE_UID));
+                toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
         final UidRangeParcel[] uidRangesBothUsers =
                 toUidRangeStableParcels(
                         uidRangesForUids(TEST_PACKAGE_UID, secondUserTestPackageUid));
@@ -11544,6 +11564,84 @@
                 false /* shouldDestroyNetwork */);
     }
 
+    @Test
+    public void testMultilayerForPackageChangesEvaluatesCorrectly()
+            throws Exception {
+        @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+                OEM_NETWORK_PREFERENCE_OEM_PAID;
+        final String packageScheme = "package:";
+
+        // Arrange PackageManager mocks
+        final String packageToInstall = "package.to.install";
+        final int packageToInstallUid = 81387;
+        final UidRangeParcel[] uidRangesSinglePackage =
+                toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, TEST_PACKAGE_UID);
+        mockGetApplicationInfoThrowsNameNotFound(packageToInstall);
+        setOemNetworkPreference(networkPref, TEST_PACKAGE_NAME, packageToInstall);
+        grantUsingBackgroundNetworksPermissionForUid(Binder.getCallingUid(), packageToInstall);
+
+        // Verify the starting state. No networks should be connected.
+        verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Test that we correctly add the expected values for installed packages.
+        setOemNetworkPreferenceAgentConnected(TRANSPORT_CELLULAR, true);
+        verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                OEM_PREF_ANY_NET_ID, 0 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Set the system to recognize the package to be installed
+        mockGetApplicationInfo(packageToInstall, packageToInstallUid);
+        final UidRangeParcel[] uidRangesAllPackages =
+                toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID, packageToInstallUid));
+
+        // Send a broadcast indicating a package was installed.
+        final Intent addedIntent = new Intent(ACTION_PACKAGE_ADDED);
+        addedIntent.setData(Uri.parse(packageScheme + packageToInstall));
+        processBroadcast(addedIntent);
+
+        // Test the single package is removed and the combined packages are added.
+        verifySetOemNetworkPreferenceForPreference(uidRangesAllPackages, uidRangesSinglePackage,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Set the system to no longer recognize the package to be installed
+        mockGetApplicationInfoThrowsNameNotFound(packageToInstall);
+
+        // Send a broadcast indicating a package was removed.
+        final Intent removedIntent = new Intent(ACTION_PACKAGE_REMOVED);
+        removedIntent.setData(Uri.parse(packageScheme + packageToInstall));
+        processBroadcast(removedIntent);
+
+        // Test the combined packages are removed and the single package is added.
+        verifySetOemNetworkPreferenceForPreference(uidRangesSinglePackage, uidRangesAllPackages,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                false /* shouldDestroyNetwork */);
+
+        // Set the system to change the installed package's uid
+        final int replacedTestPackageUid = TEST_PACKAGE_UID + 1;
+        mockGetApplicationInfo(TEST_PACKAGE_NAME, replacedTestPackageUid);
+        final UidRangeParcel[] uidRangesReplacedPackage =
+                toUidRangeStableParcels(uidRangesForUids(replacedTestPackageUid));
+
+        // Send a broadcast indicating a package was replaced.
+        final Intent replacedIntent = new Intent(ACTION_PACKAGE_REPLACED);
+        replacedIntent.setData(Uri.parse(packageScheme + TEST_PACKAGE_NAME));
+        processBroadcast(replacedIntent);
+
+        // Test the original uid is removed and is replaced with the new uid.
+        verifySetOemNetworkPreferenceForPreference(uidRangesReplacedPackage, uidRangesSinglePackage,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                mCellNetworkAgent.getNetwork().netId, 1 /* times */,
+                false /* shouldDestroyNetwork */);
+    }
+
     /**
      * Test network priority for preference OEM_NETWORK_PREFERENCE_OEM_PAID in the following order:
      * NET_CAPABILITY_NOT_METERED -> NET_CAPABILITY_OEM_PAID -> fallback
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 4ad7136..43e6676 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server;
 
+import static android.net.ConnectivityManager.NetworkCallback;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
@@ -55,8 +57,10 @@
 import android.content.Intent;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
+import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkCapabilities.Transport;
+import android.net.NetworkRequest;
 import android.net.TelephonyNetworkSpecifier;
 import android.net.vcn.IVcnStatusCallback;
 import android.net.vcn.IVcnUnderlyingNetworkPolicyListener;
@@ -258,6 +262,10 @@
 
         verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
         verify(mSubscriptionTracker).register();
+        verify(mConnMgr)
+                .registerNetworkCallback(
+                        eq(new NetworkRequest.Builder().clearCapabilities().build()),
+                        any(NetworkCallback.class));
     }
 
     @Test
@@ -529,17 +537,6 @@
     }
 
     @Test
-    public void testSetVcnConfigInSafeModeNotifiesStatusCallback() throws Exception {
-        setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */);
-        mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
-        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
-
-        mVcnMgmtSvc.setVcnConfig(TEST_UUID_2, TEST_VCN_CONFIG, TEST_PACKAGE_NAME);
-
-        verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_ACTIVE);
-    }
-
-    @Test
     public void testClearVcnConfigRequiresNonSystemServer() throws Exception {
         doReturn(Process.SYSTEM_UID).when(mMockDeps).getBinderCallingUid();
 
@@ -706,10 +703,8 @@
                 .checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
     }
 
-    private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
-            int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) {
-        setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
-
+    private NetworkCapabilities.Builder getNetworkCapabilitiesBuilderForTransport(
+            int subId, int transport) {
         final NetworkCapabilities.Builder ncBuilder =
                 new NetworkCapabilities.Builder()
                         .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
@@ -718,7 +713,16 @@
             ncBuilder.setSubIds(Collections.singleton(subId));
         }
 
-        return mVcnMgmtSvc.getUnderlyingNetworkPolicy(ncBuilder.build(), new LinkProperties());
+        return ncBuilder;
+    }
+
+    private VcnUnderlyingNetworkPolicy startVcnAndGetPolicyForTransport(
+            int subId, ParcelUuid subGrp, boolean isVcnActive, int transport) {
+        setupSubscriptionAndStartVcn(subId, subGrp, isVcnActive);
+
+        return mVcnMgmtSvc.getUnderlyingNetworkPolicy(
+                getNetworkCapabilitiesBuilderForTransport(subId, transport).build(),
+                new LinkProperties());
     }
 
     @Test
@@ -780,6 +784,53 @@
                 true /* isRestricted */);
     }
 
+    private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
+        mVcnMgmtSvc.systemReady();
+
+        final ArgumentCaptor<NetworkCallback> captor =
+                ArgumentCaptor.forClass(NetworkCallback.class);
+        verify(mConnMgr)
+                .registerNetworkCallback(
+                        eq(new NetworkRequest.Builder().clearCapabilities().build()),
+                        captor.capture());
+        captor.getValue().onCapabilitiesChanged(new Network(0), caps);
+    }
+
+    @Test
+    public void testGetUnderlyingNetworkPolicyVcnWifi_unrestrictingExistingNetworkRequiresRestart()
+            throws Exception {
+        final NetworkCapabilities existingNetworkCaps =
+                getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
+                        .removeCapability(NET_CAPABILITY_NOT_RESTRICTED)
+                        .build();
+        setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+
+        // Trigger test without VCN instance alive; expect restart due to change of NOT_RESTRICTED
+        // immutable capability
+        final VcnUnderlyingNetworkPolicy policy =
+                mVcnMgmtSvc.getUnderlyingNetworkPolicy(
+                        getNetworkCapabilitiesBuilderForTransport(
+                                        TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
+                                .build(),
+                        new LinkProperties());
+        assertTrue(policy.isTeardownRequested());
+    }
+
+    @Test
+    public void testGetUnderlyingNetworkPolicyVcnWifi_restrictingExistingNetworkRequiresRestart()
+            throws Exception {
+        final NetworkCapabilities existingNetworkCaps =
+                getNetworkCapabilitiesBuilderForTransport(TEST_SUBSCRIPTION_ID, TRANSPORT_WIFI)
+                        .build();
+        setupTrackedCarrierWifiNetwork(existingNetworkCaps);
+
+        final VcnUnderlyingNetworkPolicy policy =
+                startVcnAndGetPolicyForTransport(
+                        TEST_SUBSCRIPTION_ID, TEST_UUID_2, false /* isActive */, TRANSPORT_WIFI);
+
+        assertTrue(policy.isTeardownRequested());
+    }
+
     @Test
     public void testGetUnderlyingNetworkPolicyNonVcnNetwork() throws Exception {
         setupSubscriptionAndStartVcn(TEST_SUBSCRIPTION_ID, TEST_UUID_1, true /* isActive */);
@@ -840,7 +891,9 @@
     }
 
     private void triggerVcnSafeMode(
-            @NonNull ParcelUuid subGroup, @NonNull TelephonySubscriptionSnapshot snapshot)
+            @NonNull ParcelUuid subGroup,
+            @NonNull TelephonySubscriptionSnapshot snapshot,
+            boolean isInSafeMode)
             throws Exception {
         verify(mMockDeps)
                 .newVcn(
@@ -851,22 +904,32 @@
                         mVcnCallbackCaptor.capture());
 
         VcnCallback vcnCallback = mVcnCallbackCaptor.getValue();
-        vcnCallback.onEnteredSafeMode();
+        vcnCallback.onSafeModeStatusChanged(isInSafeMode);
     }
 
-    @Test
-    public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception {
+    private void verifyVcnSafeModeChangesNotifiesPolicyListeners(boolean enterSafeMode)
+            throws Exception {
         TelephonySubscriptionSnapshot snapshot =
                 triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_1));
 
         mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
 
-        triggerVcnSafeMode(TEST_UUID_1, snapshot);
+        triggerVcnSafeMode(TEST_UUID_1, snapshot, enterSafeMode);
 
         verify(mMockPolicyListener).onPolicyChanged();
     }
 
-    private void triggerVcnStatusCallbackOnEnteredSafeMode(
+    @Test
+    public void testVcnEnteringSafeModeNotifiesPolicyListeners() throws Exception {
+        verifyVcnSafeModeChangesNotifiesPolicyListeners(true /* enterSafeMode */);
+    }
+
+    @Test
+    public void testVcnExitingSafeModeNotifiesPolicyListeners() throws Exception {
+        verifyVcnSafeModeChangesNotifiesPolicyListeners(false /* enterSafeMode */);
+    }
+
+    private void triggerVcnStatusCallbackOnSafeModeStatusChanged(
             @NonNull ParcelUuid subGroup,
             @NonNull String pkgName,
             int uid,
@@ -889,12 +952,13 @@
 
         mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);
 
-        triggerVcnSafeMode(subGroup, snapshot);
+        triggerVcnSafeMode(subGroup, snapshot, true /* enterSafeMode */);
     }
 
     @Test
-    public void testVcnStatusCallbackOnEnteredSafeModeWithCarrierPrivileges() throws Exception {
-        triggerVcnStatusCallbackOnEnteredSafeMode(
+    public void testVcnStatusCallbackOnSafeModeStatusChangedWithCarrierPrivileges()
+            throws Exception {
+        triggerVcnStatusCallbackOnSafeModeStatusChanged(
                 TEST_UUID_1,
                 TEST_PACKAGE_NAME,
                 TEST_UID,
@@ -905,8 +969,9 @@
     }
 
     @Test
-    public void testVcnStatusCallbackOnEnteredSafeModeWithoutCarrierPrivileges() throws Exception {
-        triggerVcnStatusCallbackOnEnteredSafeMode(
+    public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutCarrierPrivileges()
+            throws Exception {
+        triggerVcnStatusCallbackOnSafeModeStatusChanged(
                 TEST_UUID_1,
                 TEST_PACKAGE_NAME,
                 TEST_UID,
@@ -918,8 +983,9 @@
     }
 
     @Test
-    public void testVcnStatusCallbackOnEnteredSafeModeWithoutLocationPermission() throws Exception {
-        triggerVcnStatusCallbackOnEnteredSafeMode(
+    public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutLocationPermission()
+            throws Exception {
+        triggerVcnStatusCallbackOnSafeModeStatusChanged(
                 TEST_UUID_1,
                 TEST_PACKAGE_NAME,
                 TEST_UID,
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 2fadd44..34c0018 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -32,6 +32,7 @@
 import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
@@ -40,6 +41,8 @@
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
+import android.net.ConnectivityManager;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
@@ -58,19 +61,29 @@
 import org.mockito.ArgumentCaptor;
 
 import java.io.IOException;
+import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.util.Arrays;
 import java.util.Collections;
+import java.util.List;
+import java.util.function.Consumer;
 
 /** Tests for VcnGatewayConnection.ConnectedState */
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
     private VcnIkeSession mIkeSession;
+    private NetworkAgent mNetworkAgent;
 
     @Before
     public void setUp() throws Exception {
         super.setUp();
 
+        mNetworkAgent = mock(NetworkAgent.class);
+        doReturn(mNetworkAgent)
+                .when(mDeps)
+                .newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any());
+
         mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
 
         mIkeSession = mGatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network);
@@ -159,21 +172,44 @@
         assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
     }
 
-    @Test
-    public void testChildOpenedRegistersNetwork() throws Exception {
-        // Verify scheduled but not canceled when entering ConnectedState
-        verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+    private void triggerChildOpened() {
+        triggerChildOpened(Collections.singletonList(TEST_INTERNAL_ADDR), TEST_DNS_ADDR);
+    }
 
+    private void triggerChildOpened(List<LinkAddress> internalAddresses, InetAddress dnsAddress) {
         final VcnChildSessionConfiguration mMockChildSessionConfig =
                 mock(VcnChildSessionConfiguration.class);
-        doReturn(Collections.singletonList(TEST_INTERNAL_ADDR))
-                .when(mMockChildSessionConfig)
-                .getInternalAddresses();
-        doReturn(Collections.singletonList(TEST_DNS_ADDR))
+        doReturn(internalAddresses).when(mMockChildSessionConfig).getInternalAddresses();
+        doReturn(Collections.singletonList(dnsAddress))
                 .when(mMockChildSessionConfig)
                 .getInternalDnsServers();
 
         getChildSessionCallback().onOpened(mMockChildSessionConfig);
+    }
+
+    private void triggerValidation(int status) {
+        final ArgumentCaptor<Consumer<Integer>> validationCallbackCaptor =
+                ArgumentCaptor.forClass(Consumer.class);
+        verify(mDeps)
+                .newNetworkAgent(
+                        any(),
+                        any(),
+                        any(),
+                        any(),
+                        anyInt(),
+                        any(),
+                        any(),
+                        any(),
+                        validationCallbackCaptor.capture());
+
+        validationCallbackCaptor.getValue().accept(status);
+    }
+
+    @Test
+    public void testChildOpenedRegistersNetwork() throws Exception {
+        // Verify scheduled but not canceled when entering ConnectedState
+        verifySafeModeTimeoutAlarmAndGetCallback(false /* expectCanceled */);
+        triggerChildOpened();
         mTestLooper.dispatchAll();
 
         assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
@@ -182,15 +218,20 @@
                 ArgumentCaptor.forClass(LinkProperties.class);
         final ArgumentCaptor<NetworkCapabilities> ncCaptor =
                 ArgumentCaptor.forClass(NetworkCapabilities.class);
-        verify(mConnMgr)
-                .registerNetworkAgent(
-                        any(),
-                        any(),
-                        lpCaptor.capture(),
+        verify(mDeps)
+                .newNetworkAgent(
+                        eq(mVcnContext),
+                        any(String.class),
                         ncCaptor.capture(),
+                        lpCaptor.capture(),
+                        anyInt(),
+                        argThat(nac -> nac.getLegacyType() == ConnectivityManager.TYPE_MOBILE),
                         any(),
                         any(),
-                        anyInt());
+                        any());
+        verify(mNetworkAgent).register();
+        verify(mNetworkAgent).markConnected();
+
         verify(mIpSecSvc)
                 .addAddressToTunnelInterface(
                         eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
@@ -208,9 +249,78 @@
 
         // Now that Vcn Network is up, notify it as validated and verify the SafeMode alarm is
         // canceled
-        mGatewayConnection.mNetworkAgent.onValidationStatus(
-                NetworkAgent.VALIDATION_STATUS_VALID, null /* redirectUri */);
+        triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
         verify(mSafeModeTimeoutAlarm).cancel();
+        assertFalse(mGatewayConnection.isInSafeMode());
+    }
+
+    @Test
+    public void testInternalAndDnsAddressesChanged() throws Exception {
+        final List<LinkAddress> startingInternalAddrs =
+                Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR, TEST_INTERNAL_ADDR_2});
+        triggerChildOpened(startingInternalAddrs, TEST_DNS_ADDR);
+        mTestLooper.dispatchAll();
+
+        for (LinkAddress addr : startingInternalAddrs) {
+            verify(mIpSecSvc)
+                    .addAddressToTunnelInterface(
+                            eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any());
+        }
+
+        verify(mDeps)
+                .newNetworkAgent(
+                        any(),
+                        any(),
+                        any(),
+                        argThat(
+                                lp ->
+                                        startingInternalAddrs.equals(lp.getLinkAddresses())
+                                                && Collections.singletonList(TEST_DNS_ADDR)
+                                                        .equals(lp.getDnsServers())),
+                        anyInt(),
+                        any(),
+                        any(),
+                        any(),
+                        any());
+
+        // Trigger another connection event, and verify that the addresses change
+        final List<LinkAddress> newInternalAddrs =
+                Arrays.asList(new LinkAddress[] {TEST_INTERNAL_ADDR_2, TEST_INTERNAL_ADDR_3});
+        triggerChildOpened(newInternalAddrs, TEST_DNS_ADDR_2);
+        mTestLooper.dispatchAll();
+
+        // Verify addresses on tunnel network added/removed
+        for (LinkAddress addr : newInternalAddrs) {
+            verify(mIpSecSvc)
+                    .addAddressToTunnelInterface(
+                            eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(addr), any());
+        }
+        verify(mIpSecSvc)
+                .removeAddressFromTunnelInterface(
+                        eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
+
+        // TODO(b/184579891): Also verify link properties updated and sent when sendLinkProperties
+        // is mockable
+
+        // Verify that IpSecTunnelInterface only created once
+        verify(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
+        verifyNoMoreInteractions(mIpSecSvc);
+    }
+
+    @Test
+    public void testSuccessfulConnectionExitsSafeMode() throws Exception {
+        verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+                mGatewayConnection.mConnectedState);
+
+        assertTrue(mGatewayConnection.isInSafeMode());
+        assertFalse(mGatewayConnection.isQuitting());
+
+        triggerChildOpened();
+        mTestLooper.dispatchAll();
+
+        triggerValidation(NetworkAgent.VALIDATION_STATUS_VALID);
+
+        assertFalse(mGatewayConnection.isInSafeMode());
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
index 7afa449..bfe8c73 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -118,8 +118,9 @@
     }
 
     @Test
-    public void testSafeModeTimeoutNotifiesCallback() {
-        verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mConnectingState);
+    public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() {
+        verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+                mGatewayConnection.mConnectingState);
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
index 99feffd..9da8b45 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -86,8 +86,9 @@
     }
 
     @Test
-    public void testSafeModeTimeoutNotifiesCallback() {
-        verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mDisconnectingState);
+    public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() {
+        verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+                mGatewayConnection.mDisconnectingState);
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
index 85a0277..6dbf7d5 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java
@@ -96,8 +96,9 @@
     }
 
     @Test
-    public void testSafeModeTimeoutNotifiesCallback() {
-        verifySafeModeTimeoutNotifiesCallback(mGatewayConnection.mRetryTimeoutState);
+    public void testSafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent() {
+        verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+                mGatewayConnection.mRetryTimeoutState);
     }
 
     @Test
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index a660735..c5ed8f6 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -21,6 +21,8 @@
 import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.atLeastOnce;
@@ -42,6 +44,7 @@
 import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.Network;
+import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
 import android.net.ipsec.ike.ChildSessionCallback;
 import android.net.ipsec.ike.IkeSessionCallback;
@@ -71,8 +74,14 @@
     protected static final ParcelUuid TEST_SUB_GRP = new ParcelUuid(UUID.randomUUID());
     protected static final InetAddress TEST_DNS_ADDR =
             InetAddresses.parseNumericAddress("2001:DB8:0:1::");
+    protected static final InetAddress TEST_DNS_ADDR_2 =
+            InetAddresses.parseNumericAddress("2001:DB8:0:2::");
     protected static final LinkAddress TEST_INTERNAL_ADDR =
-            new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:0:2::"), 64);
+            new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:1::"), 64);
+    protected static final LinkAddress TEST_INTERNAL_ADDR_2 =
+            new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:2::"), 64);
+    protected static final LinkAddress TEST_INTERNAL_ADDR_3 =
+            new LinkAddress(InetAddresses.parseNumericAddress("2001:DB8:1:3::"), 64);
 
     protected static final int TEST_IPSEC_SPI_VALUE = 0x1234;
     protected static final int TEST_IPSEC_SPI_RESOURCE_ID = 1;
@@ -267,7 +276,12 @@
                 expectCanceled);
     }
 
-    protected void verifySafeModeTimeoutNotifiesCallback(@NonNull State expectedState) {
+    protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
+            @NonNull State expectedState) {
+        // Set a NetworkAgent, and expect it to be unregistered and cleared
+        final NetworkAgent mockNetworkAgent = mock(NetworkAgent.class);
+        mGatewayConnection.setNetworkAgent(mockNetworkAgent);
+
         // SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
         // state)
         final Runnable delayedEvent =
@@ -275,7 +289,11 @@
         delayedEvent.run();
         mTestLooper.dispatchAll();
 
-        verify(mGatewayStatusCallback).onEnteredSafeMode();
+        verify(mGatewayStatusCallback).onSafeModeStatusChanged();
         assertEquals(expectedState, mGatewayConnection.getCurrentState());
+        assertTrue(mGatewayConnection.isInSafeMode());
+
+        verify(mockNetworkAgent).unregister();
+        assertNull(mGatewayConnection.getNetworkAgent());
     }
 }
diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java
index 540be38..90eb75e 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java
@@ -19,14 +19,13 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
-import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
 import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doReturn;
@@ -54,7 +53,6 @@
 import org.mockito.ArgumentCaptor;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 import java.util.Set;
 import java.util.UUID;
@@ -136,6 +134,7 @@
     private void startVcnGatewayWithCapabilities(
             NetworkRequestListener requestListener, int... netCapabilities) {
         final NetworkRequest.Builder requestBuilder = new NetworkRequest.Builder();
+        requestBuilder.addTransportType(TRANSPORT_CELLULAR);
         for (final int netCapability : netCapabilities) {
             requestBuilder.addCapability(netCapability);
         }
@@ -160,8 +159,7 @@
         mTestLooper.dispatchAll();
 
         for (final VcnGatewayConnection gateway : gatewayConnections) {
-            verify(gateway, status == VCN_STATUS_CODE_ACTIVE ? times(1) : never())
-                    .updateSubscriptionSnapshot(eq(updatedSnapshot));
+            verify(gateway).updateSubscriptionSnapshot(eq(updatedSnapshot));
         }
     }
 
@@ -202,32 +200,53 @@
 
     private void verifySafeMode(
             NetworkRequestListener requestListener,
-            Set<VcnGatewayConnection> expectedGatewaysTornDown) {
-        assertEquals(VCN_STATUS_CODE_SAFE_MODE, mVcn.getStatus());
-        for (final VcnGatewayConnection gatewayConnection : expectedGatewaysTornDown) {
-            verify(gatewayConnection).teardownAsynchronously();
+            Set<VcnGatewayConnection> activeGateways,
+            boolean expectInSafeMode) {
+        for (VcnGatewayConnection gatewayConnection : activeGateways) {
+            verify(gatewayConnection, never()).teardownAsynchronously();
         }
-        verify(mVcnNetworkProvider).unregisterListener(requestListener);
-        verify(mVcnCallback).onEnteredSafeMode();
+
+        assertEquals(
+                expectInSafeMode ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE,
+                mVcn.getStatus());
+        verify(mVcnCallback).onSafeModeStatusChanged(expectInSafeMode);
     }
 
     @Test
-    public void testGatewayEnteringSafeModeNotifiesVcn() {
+    public void testGatewayEnteringAndExitingSafeModeNotifiesVcn() {
         final NetworkRequestListener requestListener = verifyAndGetRequestListener();
         final Set<VcnGatewayConnection> gatewayConnections =
                 startGatewaysAndGetGatewayConnections(requestListener);
 
-        // Doesn't matter which callback this gets - any Gateway entering Safemode should shut down
-        // all Gateways
+        // Doesn't matter which callback this gets, or which VCN is in safe mode - any Gateway
+        // entering Safemode should trigger safe mode
         final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
-        statusCallback.onEnteredSafeMode();
+        final VcnGatewayConnection gatewayConnection = gatewayConnections.iterator().next();
+
+        doReturn(true).when(gatewayConnection).isInSafeMode();
+        statusCallback.onSafeModeStatusChanged();
         mTestLooper.dispatchAll();
 
-        verifySafeMode(requestListener, gatewayConnections);
+        verifySafeMode(requestListener, gatewayConnections, true /* expectInSafeMode */);
+
+        // Verify that when all GatewayConnections exit safe mode, the VCN also exits safe mode
+        doReturn(false).when(gatewayConnection).isInSafeMode();
+        statusCallback.onSafeModeStatusChanged();
+        mTestLooper.dispatchAll();
+
+        verifySafeMode(requestListener, gatewayConnections, false /* expectInSafeMode */);
+
+        // Re-trigger, verify safe mode callback does not get fired again for identical state
+        statusCallback.onSafeModeStatusChanged();
+        mTestLooper.dispatchAll();
+
+        // Expect only once still; from above.
+        verify(mVcnCallback).onSafeModeStatusChanged(false);
     }
 
-    @Test
-    public void testGatewayQuit() {
+    private void verifyGatewayQuit(int status) {
+        mVcn.setStatus(status);
+
         final NetworkRequestListener requestListener = verifyAndGetRequestListener();
         final Set<VcnGatewayConnection> gatewayConnections =
                 new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
@@ -240,7 +259,7 @@
         assertEquals(1, mVcn.getVcnGatewayConnections().size());
         verify(mVcnNetworkProvider).resendAllRequests(requestListener);
 
-        // Verify that the VcnGatewayConnection is restarted
+        // Verify that the VcnGatewayConnection is restarted if a request exists for it
         triggerVcnRequestListeners(requestListener);
         mTestLooper.dispatchAll();
         assertEquals(2, mVcn.getVcnGatewayConnections().size());
@@ -254,21 +273,13 @@
     }
 
     @Test
-    public void testGatewayQuitWhileInactive() {
-        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-        final Set<VcnGatewayConnection> gatewayConnections =
-                new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
+    public void testGatewayQuitReevaluatesRequests() {
+        verifyGatewayQuit(VCN_STATUS_CODE_ACTIVE);
+    }
 
-        mVcn.teardownAsynchronously();
-        mTestLooper.dispatchAll();
-
-        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
-        statusCallback.onQuit();
-        mTestLooper.dispatchAll();
-
-        // Verify that the VCN requests the networkRequests be resent
-        assertEquals(1, mVcn.getVcnGatewayConnections().size());
-        verify(mVcnNetworkProvider, never()).resendAllRequests(requestListener);
+    @Test
+    public void testGatewayQuitReevaluatesRequestsInSafeMode() {
+        verifyGatewayQuit(VCN_STATUS_CODE_SAFE_MODE);
     }
 
     @Test
@@ -298,49 +309,4 @@
         verify(removedGatewayConnection).teardownAsynchronously();
         verify(mVcnNetworkProvider).resendAllRequests(requestListener);
     }
-
-    @Test
-    public void testUpdateConfigExitsSafeMode() {
-        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-        final Set<VcnGatewayConnection> gatewayConnections =
-                new ArraySet<>(startGatewaysAndGetGatewayConnections(requestListener));
-
-        final VcnGatewayStatusCallback statusCallback = mGatewayStatusCallbackCaptor.getValue();
-        statusCallback.onEnteredSafeMode();
-        mTestLooper.dispatchAll();
-        verifySafeMode(requestListener, gatewayConnections);
-
-        doAnswer(invocation -> {
-            final NetworkRequestListener listener = invocation.getArgument(0);
-            triggerVcnRequestListeners(listener);
-            return null;
-        }).when(mVcnNetworkProvider).registerListener(eq(requestListener));
-
-        mVcn.updateConfig(mConfig);
-        mTestLooper.dispatchAll();
-
-        // Registered on start, then re-registered with new configs
-        verify(mVcnNetworkProvider, times(2)).registerListener(eq(requestListener));
-        assertEquals(VCN_STATUS_CODE_ACTIVE, mVcn.getStatus());
-        for (final int[] caps : TEST_CAPS) {
-            // Expect each gateway connection created only on initial startup
-            verify(mDeps)
-                    .newVcnGatewayConnection(
-                            eq(mVcnContext),
-                            eq(TEST_SUB_GROUP),
-                            eq(mSubscriptionSnapshot),
-                            argThat(config -> Arrays.equals(caps, config.getExposedCapabilities())),
-                            any());
-        }
-    }
-
-    @Test
-    public void testIgnoreNetworkRequestWhileInactive() {
-        mVcn.setStatus(VCN_STATUS_CODE_INACTIVE);
-
-        final NetworkRequestListener requestListener = verifyAndGetRequestListener();
-        triggerVcnRequestListeners(requestListener);
-
-        verify(mDeps, never()).newVcnGatewayConnection(any(), any(), any(), any(), any());
-    }
 }