Merge changes from topic "nscobie-hwui_unit_tests"

* changes:
  Tweak GraphicsStats#findRootPath to accept various valid locations
  Enable hwui_unit_tests in presubmit for libs/hwui
  Fix GraphicsStats#findRootPath and disable CacheManager#trimMemory tests
  Change hwui_unit_tests (and benchmarks) to push to non-root writable dir
diff --git a/BROADCASTS_OWNERS b/BROADCASTS_OWNERS
new file mode 100644
index 0000000..1ea610b
--- /dev/null
+++ b/BROADCASTS_OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 316181
+ctate@android.com
+jsharkey@android.com
+jsharkey@google.com
+sudheersai@google.com
+yamasani@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/core/api/current.txt b/core/api/current.txt
index ff9c7b1..d132706 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -17088,8 +17088,8 @@
     method public boolean flush(android.hardware.SensorEventListener);
     method public static float getAltitude(float, float);
     method public static void getAngleChange(float[], float[], float[]);
-    method public android.hardware.Sensor getDefaultSensor(int);
-    method public android.hardware.Sensor getDefaultSensor(int, boolean);
+    method @Nullable public android.hardware.Sensor getDefaultSensor(int);
+    method @Nullable public android.hardware.Sensor getDefaultSensor(int, boolean);
     method public java.util.List<android.hardware.Sensor> getDynamicSensorList(int);
     method public static float getInclination(float[]);
     method public static float[] getOrientation(float[], float[]);
diff --git a/core/java/android/content/OWNERS b/core/java/android/content/OWNERS
index 1c9713d..2cde539 100644
--- a/core/java/android/content/OWNERS
+++ b/core/java/android/content/OWNERS
@@ -13,3 +13,4 @@
 per-file LocusId* = file:/core/java/android/service/contentcapture/OWNERS
 per-file ComponentCallbacksController = file:/services/core/java/com/android/server/wm/OWNERS
 per-file ComponentCallbacksController = charlesccchen@google.com
+per-file Broadcast* = file:/BROADCASTS_OWNERS
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index 3b6a564..d2a2f12 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -16,3 +16,6 @@
 # Buffers
 per-file HardwareBuffer* = file:/graphics/java/android/graphics/OWNERS
 per-file DataSpace* = file:/graphics/java/android/graphics/OWNERS
+
+# OverlayProperties
+per-file OverlayProperties* = file:/graphics/java/android/graphics/OWNERS
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index dec424c..6d8c4a9 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -16,6 +16,7 @@
 
 package android.hardware;
 
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -496,7 +497,7 @@
      * @see #getSensorList(int)
      * @see Sensor
      */
-    public Sensor getDefaultSensor(int type) {
+    public @Nullable Sensor getDefaultSensor(int type) {
         // TODO: need to be smarter, for now, just return the 1st sensor
         List<Sensor> l = getSensorList(type);
         boolean wakeUpSensor = false;
@@ -544,7 +545,7 @@
      *         and the application has the necessary permissions, or null otherwise.
      * @see Sensor#isWakeUpSensor()
      */
-    public Sensor getDefaultSensor(int type, boolean wakeUp) {
+    public @Nullable Sensor getDefaultSensor(int type, boolean wakeUp) {
         List<Sensor> l = getSensorList(type);
         for (Sensor sensor : l) {
             if (sensor.isWakeUpSensor() == wakeUp) {
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 468e604..5abe677 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -88,8 +88,8 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map;
 import java.util.List;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
@@ -1027,6 +1027,19 @@
         return fixedFaceRectangles;
     }
 
+    private boolean setLensShadingMap(LensShadingMap lensShadingMap) {
+        if (lensShadingMap == null) {
+            return false;
+        }
+        float[] lsmArray = new float[lensShadingMap.getGainFactorCount()];
+        lensShadingMap.copyGainFactors(lsmArray, 0);
+        setBase(CaptureResult.STATISTICS_LENS_SHADING_MAP, lsmArray);
+
+        Size s = new Size(lensShadingMap.getRowCount(), lensShadingMap.getColumnCount());
+        setBase(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE, s);
+        return true;
+    }
+
     private LensShadingMap getLensShadingMap() {
         float[] lsmArray = getBase(CaptureResult.STATISTICS_LENS_SHADING_MAP);
         Size s = get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE);
@@ -1851,6 +1864,13 @@
                 metadata.setAERegions(value);
             }
         });
+        sSetCommandMap.put(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
+                new SetCommand() {
+                    @Override
+                    public <T> void setValue(CameraMetadataNative metadata, T value) {
+                        metadata.setLensShadingMap((LensShadingMap) value);
+                    }
+                });
     }
 
     private boolean setAvailableFormats(int[] value) {
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index 60d8cac..7c2e518 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -108,6 +108,34 @@
     }
 
     /**
+     * This is meant to be called by UsbRequest's queue() in order to synchronize on
+     * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueRequest(UsbRequest request, ByteBuffer buffer, int length) {
+        synchronized (mLock) {
+            if (!isOpen()) {
+                return false;
+            }
+
+            return request.queueIfConnectionOpen(buffer, length);
+        }
+    }
+
+    /**
+     * This is meant to be called by UsbRequest's queue() in order to synchronize on
+     * UsbDeviceConnection's mLock to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueRequest(UsbRequest request, @Nullable ByteBuffer buffer) {
+        synchronized (mLock) {
+            if (!isOpen()) {
+                return false;
+            }
+
+            return request.queueIfConnectionOpen(buffer);
+        }
+    }
+
+    /**
      * Releases all system resources related to the device.
      * Once the object is closed it cannot be used again.
      * The client must call {@link UsbManager#openDevice} again
diff --git a/core/java/android/hardware/usb/UsbRequest.java b/core/java/android/hardware/usb/UsbRequest.java
index 6ac5e8d..beb0f8d 100644
--- a/core/java/android/hardware/usb/UsbRequest.java
+++ b/core/java/android/hardware/usb/UsbRequest.java
@@ -113,11 +113,13 @@
      * Releases all resources related to this request.
      */
     public void close() {
-        if (mNativeContext != 0) {
-            mEndpoint = null;
-            mConnection = null;
-            native_close();
-            mCloseGuard.close();
+        synchronized (mLock) {
+            if (mNativeContext != 0) {
+                mEndpoint = null;
+                mConnection = null;
+                native_close();
+                mCloseGuard.close();
+            }
         }
     }
 
@@ -191,10 +193,32 @@
      */
     @Deprecated
     public boolean queue(ByteBuffer buffer, int length) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new NullPointerException("invalid connection");
+        }
+
+        // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+        // the connection being closed while queueing.
+        return connection.queueRequest(this, buffer, length);
+    }
+
+    /**
+     * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+     * there, to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueIfConnectionOpen(ByteBuffer buffer, int length) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null || !connection.isOpen()) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new NullPointerException("invalid connection");
+        }
+
         boolean out = (mEndpoint.getDirection() == UsbConstants.USB_DIR_OUT);
         boolean result;
 
-        if (mConnection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
+        if (connection.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.P
                 && length > MAX_USBFS_BUFFER_SIZE) {
             length = MAX_USBFS_BUFFER_SIZE;
         }
@@ -243,6 +267,28 @@
      * @return true if the queueing operation succeeded
      */
     public boolean queue(@Nullable ByteBuffer buffer) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new IllegalStateException("invalid connection");
+        }
+
+        // Calling into the underlying UsbDeviceConnection to synchronize on its lock, to prevent
+        // the connection being closed while queueing.
+        return connection.queueRequest(this, buffer);
+    }
+
+    /**
+     * This is meant to be called from UsbDeviceConnection after synchronizing using the lock over
+     * there, to prevent the connection being closed while queueing.
+     */
+    /* package */ boolean queueIfConnectionOpen(@Nullable ByteBuffer buffer) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null || !connection.isOpen()) {
+            // The expected exception by CTS Verifier - USB Device test
+            throw new IllegalStateException("invalid connection");
+        }
+
         // Request need to be initialized
         Preconditions.checkState(mNativeContext != 0, "request is not initialized");
 
@@ -260,7 +306,7 @@
                 mIsUsingNewQueue = true;
                 wasQueued = native_queue(null, 0, 0);
             } else {
-                if (mConnection.getContext().getApplicationInfo().targetSdkVersion
+                if (connection.getContext().getApplicationInfo().targetSdkVersion
                         < Build.VERSION_CODES.P) {
                     // Can only send/receive MAX_USBFS_BUFFER_SIZE bytes at once
                     Preconditions.checkArgumentInRange(buffer.remaining(), 0, MAX_USBFS_BUFFER_SIZE,
@@ -363,11 +409,12 @@
      * @return true if cancelling succeeded
      */
     public boolean cancel() {
-        if (mConnection == null) {
+        UsbDeviceConnection connection = mConnection;
+        if (connection == null) {
             return false;
         }
 
-        return mConnection.cancelRequest(this);
+        return connection.cancelRequest(this);
     }
 
     /**
@@ -382,7 +429,8 @@
      * @return true if cancelling succeeded.
      */
     /* package */ boolean cancelIfOpen() {
-        if (mNativeContext == 0 || (mConnection != null && !mConnection.isOpen())) {
+        UsbDeviceConnection connection = mConnection;
+        if (mNativeContext == 0 || (connection != null && !connection.isOpen())) {
             Log.w(TAG,
                     "Detected attempt to cancel a request on a connection which isn't open");
             return false;
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 935374d1..911aaf3 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -122,7 +122,7 @@
         Binder token;
 
         public NfcActivityState(Activity activity) {
-            if (activity.getWindow().isDestroyed()) {
+            if (activity.isDestroyed()) {
                 throw new IllegalStateException("activity is already destroyed");
             }
             // Check if activity is resumed right now, as we will not
diff --git a/core/java/android/nfc/TechListParcel.java b/core/java/android/nfc/TechListParcel.java
index 90645dd..9f01559 100644
--- a/core/java/android/nfc/TechListParcel.java
+++ b/core/java/android/nfc/TechListParcel.java
@@ -53,7 +53,7 @@
             int count = source.readInt();
             String[][] techLists = new String[count][];
             for (int i = 0; i < count; i++) {
-                techLists[i] = source.readStringArray();
+                techLists[i] = source.createStringArray();
             }
             return new TechListParcel(techLists);
         }
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7db6130..b377c92 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3344,9 +3344,26 @@
                     }
                 }
 
-                // Fetch all flags for the namespace at once for caching purposes
-                Bundle b = cp.call(cr.getAttributionSource(),
-                        mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+                Bundle b;
+                // b/252663068: if we're in system server and the caller did not call
+                // clearCallingIdentity, the read would fail due to mismatched AttributionSources.
+                // TODO(b/256013480): remove this bypass after fixing the callers in system server.
+                if (namespace.equals(DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER)
+                        && Settings.isInSystemServer()
+                        && Binder.getCallingUid() != Process.myUid()) {
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        // Fetch all flags for the namespace at once for caching purposes
+                        b = cp.call(cr.getAttributionSource(),
+                                mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+                } else {
+                    // Fetch all flags for the namespace at once for caching purposes
+                    b = cp.call(cr.getAttributionSource(),
+                            mProviderHolder.mUri.getAuthority(), mCallListCommand, null, args);
+                }
                 if (b == null) {
                     // Invalid response, return an empty map
                     return keyValues;
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 5ff9263..ac46be7 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4803,6 +4803,13 @@
                 "phone_number_source_ims";
 
         /**
+         * TelephonyProvider column name for last used TP - message Reference
+         *
+         * @hide
+         */
+        public static final String COLUMN_TP_MESSAGE_REF = "tp_message_ref";
+
+        /**
          * TelephonyProvider column name for the device's preferred usage setting.
          *
          * @hide
diff --git a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
index 5f4a9cd..473134e 100644
--- a/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
+++ b/core/java/com/android/internal/app/chooser/DisplayResolveInfo.java
@@ -172,14 +172,14 @@
 
     @Override
     public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) {
-        prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
+        TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, userId);
         activity.startActivityAsCaller(mResolvedIntent, options, false, userId);
         return true;
     }
 
     @Override
     public boolean startAsUser(Activity activity, Bundle options, UserHandle user) {
-        prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
+        TargetInfo.prepareIntentForCrossProfileLaunch(mResolvedIntent, user.getIdentifier());
         activity.startActivityAsUser(mResolvedIntent, options, user);
         return false;
     }
@@ -224,13 +224,6 @@
         }
     };
 
-    private static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
-        final int currentUserId = UserHandle.myUserId();
-        if (targetUserId != currentUserId) {
-            intent.fixUris(currentUserId);
-        }
-    }
-
     private DisplayResolveInfo(Parcel in) {
         mDisplayLabel = in.readCharSequence();
         mExtendedInfo = in.readCharSequence();
diff --git a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
index 264e4f7..4b9b7cb 100644
--- a/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/SelectableTargetInfo.java
@@ -232,6 +232,7 @@
         }
         intent.setComponent(mChooserTarget.getComponentName());
         intent.putExtras(mChooserTarget.getIntentExtras());
+        TargetInfo.prepareIntentForCrossProfileLaunch(intent, userId);
 
         // Important: we will ignore the target security checks in ActivityManager
         // if and only if the ChooserTarget's target package is the same package
diff --git a/core/java/com/android/internal/app/chooser/TargetInfo.java b/core/java/com/android/internal/app/chooser/TargetInfo.java
index f56ab17..7bb7ddc 100644
--- a/core/java/com/android/internal/app/chooser/TargetInfo.java
+++ b/core/java/com/android/internal/app/chooser/TargetInfo.java
@@ -130,4 +130,15 @@
      * @return true if this target should be pinned to the front by the request of the user
      */
     boolean isPinned();
+
+    /**
+     * Fix the URIs in {@code intent} if cross-profile sharing is required. This should be called
+     * before launching the intent as another user.
+     */
+    static void prepareIntentForCrossProfileLaunch(Intent intent, int targetUserId) {
+        final int currentUserId = UserHandle.myUserId();
+        if (targetUserId != currentUserId) {
+            intent.fixUris(currentUserId);
+        }
+    }
 }
diff --git a/core/tests/coretests/src/android/hardware/camera2/OWNERS b/core/tests/coretests/src/android/hardware/camera2/OWNERS
new file mode 100644
index 0000000..f48a95c
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/camera2/OWNERS
@@ -0,0 +1 @@
+include platform/frameworks/av:/camera/OWNERS
diff --git a/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java b/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java
new file mode 100644
index 0000000..a38c040
--- /dev/null
+++ b/core/tests/coretests/src/android/hardware/camera2/impl/CaptureMetadataNativeTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.impl;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.LensShadingMap;
+import android.util.Size;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.Arrays;
+
+@SmallTest
+@RunWith(JUnit4.class)
+/** Tests for {@link CameraMetadataNative} class. */
+public class CaptureMetadataNativeTest {
+
+    @Test
+    public void setLensShadingMap() {
+        final Size s = new Size(10, 10);
+        // 4 x rows x columns
+        final float[] elements = new float[400];
+        Arrays.fill(elements, 42);
+
+        final LensShadingMap lensShadingMap =
+                new LensShadingMap(elements, s.getHeight(), s.getWidth());
+        CameraMetadataNative captureResults = new CameraMetadataNative();
+        captureResults.set(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP, lensShadingMap);
+
+        final LensShadingMap output =
+                captureResults.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+
+        assertThat(output).isEqualTo(lensShadingMap);
+    }
+}
diff --git a/core/tests/nfctests/Android.bp b/core/tests/nfctests/Android.bp
index 335cea1..c74600b 100644
--- a/core/tests/nfctests/Android.bp
+++ b/core/tests/nfctests/Android.bp
@@ -27,6 +27,7 @@
         "androidx.test.ext.junit",
         "androidx.test.rules",
         "mockito-target-minus-junit4",
+        "truth-prebuilt",
     ],
     libs: [
         "android.test.runner",
diff --git a/core/tests/nfctests/src/android/nfc/TechListParcelTest.java b/core/tests/nfctests/src/android/nfc/TechListParcelTest.java
new file mode 100644
index 0000000..a12bbbc
--- /dev/null
+++ b/core/tests/nfctests/src/android/nfc/TechListParcelTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.nfc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(AndroidJUnit4.class)
+public class TechListParcelTest {
+
+    private static final String[] TECH_LIST_1 = new String[] { "tech1.1", "tech1.2" };
+    private static final String[] TECH_LIST_2 = new String[] { "tech2.1" };
+    private static final String[] TECH_LIST_EMPTY = new String[] {};
+
+    @Test
+    public void testWriteParcel() {
+        TechListParcel techListParcel = new TechListParcel(TECH_LIST_1, TECH_LIST_2);
+
+        Parcel parcel = Parcel.obtain();
+        techListParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        TechListParcel actualTechList =
+                TechListParcel.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(actualTechList.getTechLists().length).isEqualTo(2);
+        assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_1)).isTrue();
+        assertThat(Arrays.equals(actualTechList.getTechLists()[1], TECH_LIST_2)).isTrue();
+    }
+
+    @Test
+    public void testWriteParcelArrayEmpty() {
+        TechListParcel techListParcel = new TechListParcel();
+
+        Parcel parcel = Parcel.obtain();
+        techListParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        TechListParcel actualTechList =
+                TechListParcel.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(actualTechList.getTechLists().length).isEqualTo(0);
+    }
+
+    @Test
+    public void testWriteParcelElementEmpty() {
+        TechListParcel techListParcel = new TechListParcel(TECH_LIST_EMPTY);
+
+        Parcel parcel = Parcel.obtain();
+        techListParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        TechListParcel actualTechList =
+                TechListParcel.CREATOR.createFromParcel(parcel);
+        parcel.recycle();
+
+        assertThat(actualTechList.getTechLists().length).isEqualTo(1);
+        assertThat(Arrays.equals(actualTechList.getTechLists()[0], TECH_LIST_EMPTY)).isTrue();
+    }
+
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 8ca1607..ff42fb5 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -342,10 +342,6 @@
         <permission name="android.permission.SET_WALLPAPER_COMPONENT" />
         <permission name="android.permission.SET_WALLPAPER_DIM_AMOUNT" />
         <permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
-        <!-- Permission required for CTS test - TrustTestCases -->
-        <permission name="android.permission.PROVIDE_TRUST_AGENT" />
-        <permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
-        <permission name="android.permission.TRUST_LISTENER" />
         <!-- Permissions required for Incremental CTS tests -->
         <permission name="com.android.permission.USE_INSTALLER_V2"/>
         <permission name="android.permission.LOADER_USAGE_STATS"/>
diff --git a/libs/incident/Android.bp b/libs/incident/Android.bp
index ff1714d..a996700 100644
--- a/libs/incident/Android.bp
+++ b/libs/incident/Android.bp
@@ -60,6 +60,7 @@
         ":libincident_aidl",
         "src/IncidentReportArgs.cpp",
     ],
+    min_sdk_version: "29",
 }
 
 cc_library_shared {
diff --git a/media/java/android/media/ImageUtils.java b/media/java/android/media/ImageUtils.java
index 2f1a36c..4957300 100644
--- a/media/java/android/media/ImageUtils.java
+++ b/media/java/android/media/ImageUtils.java
@@ -255,10 +255,10 @@
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW_PRIVATE: // round estimate, real size is unknown
             case ImageFormat.DEPTH16:
-            case ImageFormat.YCBCR_P010:
                 estimatedBytePerPixel = 2.0;
                 break;
             case PixelFormat.RGB_888:
+            case ImageFormat.YCBCR_P010:
                 estimatedBytePerPixel = 3.0;
                 break;
             case PixelFormat.RGBA_8888:
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 845975d..c8187b8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -35,7 +35,6 @@
 import android.os.ParcelUuid;
 import android.os.SystemClock;
 import android.text.TextUtils;
-import android.util.EventLog;
 import android.util.Log;
 import android.util.LruCache;
 import android.util.Pair;
@@ -1020,14 +1019,7 @@
         if (BluetoothUuid.containsAnyUuid(uuids, PbapServerProfile.PBAB_CLIENT_UUIDS)) {
             // The pairing dialog now warns of phone-book access for paired devices.
             // No separate prompt is displayed after pairing.
-            if (mDevice.getPhonebookAccessPermission() == BluetoothDevice.ACCESS_UNKNOWN) {
-                if (BluetoothUtils.isDeviceClassMatched(mDevice,
-                        BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE)
-                        || BluetoothUtils.isDeviceClassMatched(mDevice,
-                        BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET)) {
-                    EventLog.writeEvent(0x534e4554, "138529441", -1, "");
-                }
-            }
+            mDevice.getPhonebookAccessPermission();
         }
     }
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index fd7554f..cd667ca 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -48,6 +48,7 @@
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FrameworkStatsLog;
 
@@ -376,9 +377,11 @@
             Setting newSetting = new Setting(name, oldSetting.getValue(), null,
                     oldSetting.getPackageName(), oldSetting.getTag(), false,
                     oldSetting.getId());
+            int newSize = getNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), 0,
+                    oldValue, newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+            checkNewMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
             mSettings.put(name, newSetting);
-            updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), oldValue,
-                    newSetting.getValue(), oldDefaultValue, newSetting.getDefaultValue());
+            updateMemoryUsagePerPackageLocked(newSetting.getPackageName(), newSize);
             scheduleWriteIfNeededLocked();
         }
     }
@@ -410,6 +413,13 @@
         Setting oldState = mSettings.get(name);
         String oldValue = (oldState != null) ? oldState.value : null;
         String oldDefaultValue = (oldState != null) ? oldState.defaultValue : null;
+        String newDefaultValue = makeDefault ? value : oldDefaultValue;
+
+        int newSize = getNewMemoryUsagePerPackageLocked(packageName,
+                oldValue == null ? name.length() : 0 /* deltaKeySize */,
+                oldValue, value, oldDefaultValue, newDefaultValue);
+        checkNewMemoryUsagePerPackageLocked(packageName, newSize);
+
         Setting newState;
 
         if (oldState != null) {
@@ -430,8 +440,7 @@
 
         addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
 
-        updateMemoryUsagePerPackageLocked(packageName, oldValue, value,
-                oldDefaultValue, newState.getDefaultValue());
+        updateMemoryUsagePerPackageLocked(packageName, newSize);
 
         scheduleWriteIfNeededLocked();
 
@@ -552,13 +561,18 @@
         }
 
         Setting oldState = mSettings.remove(name);
+        if (oldState == null) {
+            return false;
+        }
+        int newSize = getNewMemoryUsagePerPackageLocked(oldState.packageName,
+                -name.length() /* deltaKeySize */,
+                oldState.value, null, oldState.defaultValue, null);
 
         FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
                 /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
                 FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
 
-        updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
-                null, oldState.defaultValue, null);
+        updateMemoryUsagePerPackageLocked(oldState.packageName, newSize);
 
         addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
 
@@ -575,20 +589,23 @@
         }
 
         Setting setting = mSettings.get(name);
+        if (setting == null) {
+            return false;
+        }
 
         Setting oldSetting = new Setting(setting);
         String oldValue = setting.getValue();
         String oldDefaultValue = setting.getDefaultValue();
 
+        int newSize = getNewMemoryUsagePerPackageLocked(setting.packageName, 0, oldValue,
+                oldDefaultValue, oldDefaultValue, oldDefaultValue);
+        checkNewMemoryUsagePerPackageLocked(setting.packageName, newSize);
+
         if (!setting.reset()) {
             return false;
         }
 
-        String newValue = setting.getValue();
-        String newDefaultValue = setting.getDefaultValue();
-
-        updateMemoryUsagePerPackageLocked(setting.packageName, oldValue,
-                newValue, oldDefaultValue, newDefaultValue);
+        updateMemoryUsagePerPackageLocked(setting.packageName, newSize);
 
         addHistoricalOperationLocked(HISTORICAL_OPERATION_RESET, oldSetting);
 
@@ -696,38 +713,49 @@
     }
 
     @GuardedBy("mLock")
-    private void updateMemoryUsagePerPackageLocked(String packageName, String oldValue,
-            String newValue, String oldDefaultValue, String newDefaultValue) {
-        if (mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED) {
+    private boolean isExemptFromMemoryUsageCap(String packageName) {
+        return mMaxBytesPerAppPackage == MAX_BYTES_PER_APP_PACKAGE_UNLIMITED
+                || SYSTEM_PACKAGE_NAME.equals(packageName);
+    }
+
+    @GuardedBy("mLock")
+    private void checkNewMemoryUsagePerPackageLocked(String packageName, int newSize)
+            throws IllegalStateException {
+        if (isExemptFromMemoryUsageCap(packageName)) {
             return;
         }
-
-        if (SYSTEM_PACKAGE_NAME.equals(packageName)) {
-            return;
-        }
-
-        final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
-        final int newValueSize = (newValue != null) ? newValue.length() : 0;
-        final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
-        final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
-        final int deltaSize = newValueSize + newDefaultValueSize
-                - oldValueSize - oldDefaultValueSize;
-
-        Integer currentSize = mPackageToMemoryUsage.get(packageName);
-        final int newSize = Math.max((currentSize != null)
-                ? currentSize + deltaSize : deltaSize, 0);
-
         if (newSize > mMaxBytesPerAppPackage) {
             throw new IllegalStateException("You are adding too many system settings. "
                     + "You should stop using system settings for app specific data"
                     + " package: " + packageName);
         }
+    }
 
+    @GuardedBy("mLock")
+    private int getNewMemoryUsagePerPackageLocked(String packageName, int deltaKeySize,
+            String oldValue, String newValue, String oldDefaultValue, String newDefaultValue) {
+        if (isExemptFromMemoryUsageCap(packageName)) {
+            return 0;
+        }
+        final Integer currentSize = mPackageToMemoryUsage.get(packageName);
+        final int oldValueSize = (oldValue != null) ? oldValue.length() : 0;
+        final int newValueSize = (newValue != null) ? newValue.length() : 0;
+        final int oldDefaultValueSize = (oldDefaultValue != null) ? oldDefaultValue.length() : 0;
+        final int newDefaultValueSize = (newDefaultValue != null) ? newDefaultValue.length() : 0;
+        final int deltaSize = deltaKeySize + newValueSize + newDefaultValueSize
+                - oldValueSize - oldDefaultValueSize;
+        return Math.max((currentSize != null) ? currentSize + deltaSize : deltaSize, 0);
+    }
+
+    @GuardedBy("mLock")
+    private void updateMemoryUsagePerPackageLocked(String packageName, int newSize) {
+        if (isExemptFromMemoryUsageCap(packageName)) {
+            return;
+        }
         if (DEBUG) {
             Slog.i(LOG_TAG, "Settings for package: " + packageName
                     + " size: " + newSize + " bytes.");
         }
-
         mPackageToMemoryUsage.put(packageName, newSize);
     }
 
@@ -1556,4 +1584,11 @@
         }
         return false;
     }
+
+    @VisibleForTesting
+    public int getMemoryUsage(String packageName) {
+        synchronized (mLock) {
+            return mPackageToMemoryUsage.getOrDefault(packageName, 0);
+        }
+    }
 }
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
index 69eb713..f6d4329 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsStateTest.java
@@ -20,6 +20,8 @@
 import android.util.TypedXmlSerializer;
 import android.util.Xml;
 
+import com.google.common.base.Strings;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -276,4 +278,132 @@
         settingsState.setVersionLocked(SettingsState.SETTINGS_VERSION_NEW_ENCODING);
         return settingsState;
     }
+
+    public void testInsertSetting_memoryUsage() {
+        SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+                SettingsState.MAX_BYTES_PER_APP_PACKAGE_UNLIMITED, Looper.getMainLooper());
+        // No exception should be thrown when there is no cap
+        settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                null, false, "p1");
+        settingsState.deleteSettingLocked(SETTING_NAME);
+
+        settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+                SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+        // System package doesn't have memory usage limit
+        settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                null, false, SYSTEM_PACKAGE);
+        settingsState.deleteSettingLocked(SETTING_NAME);
+
+        // Should not throw if usage is under the cap
+        settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 19975),
+                null, false, "p1");
+        settingsState.deleteSettingLocked(SETTING_NAME);
+        try {
+            settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                    null, false, "p1");
+            fail("Should throw because it exceeded per package memory usage");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("p1"));
+        }
+        try {
+            settingsState.insertSettingLocked(SETTING_NAME, Strings.repeat("A", 20001),
+                    null, false, "p1");
+            fail("Should throw because it exceeded per package memory usage");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("p1"));
+        }
+        assertTrue(settingsState.getSettingLocked(SETTING_NAME).isNull());
+        try {
+            settingsState.insertSettingLocked(Strings.repeat("A", 20001), "",
+                    null, false, "p1");
+            fail("Should throw because it exceeded per package memory usage");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+        }
+    }
+
+    public void testMemoryUsagePerPackage() {
+        SettingsState settingsState = new SettingsState(getContext(), mLock, mSettingsFile, 1,
+                SettingsState.MAX_BYTES_PER_APP_PACKAGE_LIMITED, Looper.getMainLooper());
+
+        // Test inserting one key with default
+        final String testKey1 = SETTING_NAME;
+        final String testValue1 = Strings.repeat("A", 100);
+        settingsState.insertSettingLocked(testKey1, testValue1, null, true, TEST_PACKAGE);
+        int expectedMemUsage = testKey1.length() + testValue1.length()
+                + testValue1.length() /* size for default */;
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test inserting another key
+        final String testKey2 = SETTING_NAME + "2";
+        settingsState.insertSettingLocked(testKey2, testValue1, null, false, TEST_PACKAGE);
+        expectedMemUsage += testKey2.length() + testValue1.length();
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test updating first key with new default
+        final String testValue2 = Strings.repeat("A", 300);
+        settingsState.insertSettingLocked(testKey1, testValue2, null, true, TEST_PACKAGE);
+        expectedMemUsage += (testValue2.length() - testValue1.length()) * 2;
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test updating first key without new default
+        final String testValue3 = Strings.repeat("A", 50);
+        settingsState.insertSettingLocked(testKey1, testValue3, null, false, TEST_PACKAGE);
+        expectedMemUsage -= testValue2.length() - testValue3.length();
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test updating second key
+        settingsState.insertSettingLocked(testKey2, testValue2, null, false, TEST_PACKAGE);
+        expectedMemUsage -= testValue1.length() - testValue2.length();
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test resetting key
+        settingsState.resetSettingLocked(testKey1);
+        expectedMemUsage += testValue2.length() - testValue3.length();
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test resetting default value
+        settingsState.resetSettingDefaultValueLocked(testKey1);
+        expectedMemUsage -= testValue2.length();
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test deletion
+        settingsState.deleteSettingLocked(testKey2);
+        expectedMemUsage -= testValue2.length() + testKey2.length() /* key is deleted too */;
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test another package with a different key
+        final String testPackage2 = TEST_PACKAGE + "2";
+        final String testKey3 = SETTING_NAME + "3";
+        settingsState.insertSettingLocked(testKey3, testValue1, null, true, testPackage2);
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+        final int expectedMemUsage2 = testKey3.length() + testValue1.length() * 2;
+        assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+
+        // Test system package
+        settingsState.insertSettingLocked(testKey1, testValue1, null, true, SYSTEM_PACKAGE);
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+        assertEquals(expectedMemUsage2, settingsState.getMemoryUsage(testPackage2));
+        assertEquals(0, settingsState.getMemoryUsage(SYSTEM_PACKAGE));
+
+        // Test invalid value
+        try {
+            settingsState.insertSettingLocked(testKey1, Strings.repeat("A", 20001), null, false,
+                    TEST_PACKAGE);
+            fail("Should throw because it exceeded per package memory usage");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+        }
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+
+        // Test invalid key
+        try {
+            settingsState.insertSettingLocked(Strings.repeat("A", 20001), "", null, false,
+                    TEST_PACKAGE);
+            fail("Should throw because it exceeded per package memory usage");
+        } catch (IllegalStateException ex) {
+            assertTrue(ex.getMessage().contains("You are adding too many system settings"));
+        }
+        assertEquals(expectedMemUsage, settingsState.getMemoryUsage(TEST_PACKAGE));
+    }
 }
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 979e9ea..f13d961 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -578,11 +578,6 @@
     <!-- Permission required for CTS test - PeopleManagerTest -->
     <uses-permission android:name="android.permission.READ_PEOPLE_DATA" />
 
-    <!-- Permissions required for CTS test - TrustTestCases -->
-    <uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
-    <uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
-    <uses-permission android:name="android.permission.TRUST_LISTENER" />
-
     <!-- Permission required for CTS test - CtsTaskFpsCallbackTestCases -->
     <uses-permission android:name="android.permission.ACCESS_FPS_COUNTER" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
index ded466a..2727c83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelControllerBase.java
@@ -23,8 +23,8 @@
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.res.Configuration;
+import android.content.res.Configuration.Orientation;
 import android.metrics.LogMaker;
-import android.util.Log;
 import android.view.View;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -75,6 +75,7 @@
 
     @Nullable
     private Consumer<Boolean> mMediaVisibilityChangedListener;
+    @Orientation
     private int mLastOrientation;
     private String mCachedSpecs = "";
     @Nullable
@@ -88,21 +89,16 @@
             new QSPanel.OnConfigurationChangedListener() {
                 @Override
                 public void onConfigurationChange(Configuration newConfig) {
+                    mQSLogger.logOnConfigurationChanged(
+                        /* lastOrientation= */ mLastOrientation,
+                        /* newOrientation= */ newConfig.orientation,
+                        /* containerName= */ mView.getDumpableTag());
+
                     mShouldUseSplitNotificationShade =
-                            LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
-                    // Logging to aid the investigation of b/216244185.
-                    Log.d(TAG,
-                            "onConfigurationChange: "
-                                    + "mShouldUseSplitNotificationShade="
-                                    + mShouldUseSplitNotificationShade + ", "
-                                    + "newConfig.windowConfiguration="
-                                    + newConfig.windowConfiguration);
-                    mQSLogger.logOnConfigurationChanged(mLastOrientation, newConfig.orientation,
-                            mView.getDumpableTag());
-                    if (newConfig.orientation != mLastOrientation) {
-                        mLastOrientation = newConfig.orientation;
-                        switchTileLayout(false);
-                    }
+                        LargeScreenUtils.shouldUseSplitNotificationShade(getResources());
+                    mLastOrientation = newConfig.orientation;
+
+                    switchTileLayoutIfNeeded();
                     onConfigurationChanged();
                 }
             };
@@ -334,6 +330,10 @@
         }
     }
 
+    private void switchTileLayoutIfNeeded() {
+        switchTileLayout(/* force= */ false);
+    }
+
     boolean switchTileLayout(boolean force) {
         /* Whether or not the panel currently contains a media player. */
         boolean horizontal = shouldUseHorizontalLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
index 76025ab7..0446165 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ImageTransformState.java
@@ -53,7 +53,9 @@
             return true;
         }
         if (otherState instanceof ImageTransformState) {
-            return mIcon != null && mIcon.sameAs(((ImageTransformState) otherState).getIcon());
+            final Icon otherIcon = ((ImageTransformState) otherState).mIcon;
+            return mIcon == otherIcon || (mIcon != null && otherIcon != null && mIcon.sameAs(
+                    otherIcon));
         }
         return false;
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
index 3cad2a0..b847ad0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerBaseTest.java
@@ -277,7 +277,7 @@
 
         // Then the layout changes
         assertThat(mController.shouldUseHorizontalLayout()).isTrue();
-        verify(mHorizontalLayoutListener).run(); // not invoked
+        verify(mHorizontalLayoutListener).run();
 
         // When it is rotated back to portrait
         mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT;
@@ -300,4 +300,24 @@
         verify(mQSTile).refreshState();
         verify(mOtherTile, never()).refreshState();
     }
+
+    @Test
+    public void configurationChange_onlySplitShadeConfigChanges_horizontalLayoutStatusUpdated() {
+        // Preconditions for horizontal layout
+        when(mMediaHost.getVisible()).thenReturn(true);
+        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(false);
+        mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE;
+        mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener);
+        mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+        assertThat(mController.shouldUseHorizontalLayout()).isTrue();
+        reset(mHorizontalLayoutListener);
+
+        // Only split shade status changes
+        when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true);
+        mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration);
+
+        // Horizontal layout is updated accordingly.
+        assertThat(mController.shouldUseHorizontalLayout()).isFalse();
+        verify(mHorizontalLayoutListener).run();
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 2a4bcb0..1e9c3b7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -657,25 +657,27 @@
                     userState.mBindingServices.removeIf(filter);
                     userState.mCrashedServices.removeIf(filter);
                     final Iterator<ComponentName> it = userState.mEnabledServices.iterator();
+                    boolean anyServiceRemoved = false;
                     while (it.hasNext()) {
                         final ComponentName comp = it.next();
                         final String compPkg = comp.getPackageName();
                         if (compPkg.equals(packageName)) {
                             it.remove();
-                            // Update the enabled services setting.
-                            persistComponentNamesToSettingLocked(
-                                    Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
-                                    userState.mEnabledServices, userId);
-                            // Update the touch exploration granted services setting.
                             userState.mTouchExplorationGrantedServices.remove(comp);
-                            persistComponentNamesToSettingLocked(
-                                    Settings.Secure.
-                                    TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
-                                    userState.mTouchExplorationGrantedServices, userId);
-                            onUserStateChangedLocked(userState);
-                            return;
+                            anyServiceRemoved = true;
                         }
                     }
+                    if (anyServiceRemoved) {
+                        // Update the enabled services setting.
+                        persistComponentNamesToSettingLocked(
+                                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
+                                userState.mEnabledServices, userId);
+                        // Update the touch exploration granted services setting.
+                        persistComponentNamesToSettingLocked(
+                                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+                                userState.mTouchExplorationGrantedServices, userId);
+                        onUserStateChangedLocked(userState);
+                    }
                 }
             }
 
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 38237fa..34b7a8a 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -573,12 +573,6 @@
                 }
             }
 
-            if (onClickIntent != null) {
-                views.setOnClickPendingIntent(android.R.id.background,
-                        PendingIntent.getActivity(mContext, 0, onClickIntent,
-                                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE));
-            }
-
             Icon icon = appInfo.icon != 0
                     ? Icon.createWithResource(appInfo.packageName, appInfo.icon)
                     : Icon.createWithResource(mContext, android.R.drawable.sym_def_app_icon);
@@ -590,6 +584,12 @@
             for (int j = 0; j < widgetCount; j++) {
                 Widget widget = provider.widgets.get(j);
                 if (targetWidget != null && targetWidget != widget) continue;
+                if (onClickIntent != null) {
+                    views.setOnClickPendingIntent(android.R.id.background,
+                            PendingIntent.getActivity(mContext, widget.appWidgetId, onClickIntent,
+                                    PendingIntent.FLAG_UPDATE_CURRENT
+                                       | PendingIntent.FLAG_IMMUTABLE));
+                }
                 if (widget.replaceWithMaskedViewsLocked(views)) {
                     scheduleNotifyUpdateAppWidgetLocked(widget, widget.getEffectiveViewsLocked());
                 }
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9268fc0..bf71e6b 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -154,8 +154,8 @@
         "android.hardware.health-translate-java",
         "android.hardware.light-V1-java",
         "android.hardware.tv.cec-V1.1-java",
-        "android.hardware.tv.cec-V1-java",
-        "android.hardware.tv.hdmi-V1-java",
+        "android.hardware.tv.hdmi.cec-V1-java",
+        "android.hardware.tv.hdmi.connection-V1-java",
         "android.hardware.weaver-V1.0-java",
         "android.hardware.biometrics.face-V1.0-java",
         "android.hardware.biometrics.fingerprint-V2.3-java",
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index da209f0..5cdcd42 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -41,3 +41,5 @@
 # Multiuser
 per-file User* = file:/MULTIUSER_OWNERS
 
+# Broadcasts
+per-file Broadcast* = file:/BROADCASTS_OWNERS
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index bda60ff..8624ee0 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -379,11 +379,16 @@
                 resolvedType = key.requestResolvedType;
             }
 
-            // Apply any launch flags from the ActivityOptions. This is to ensure that the caller
-            // can specify a consistent launch mode even if the PendingIntent is immutable
+            // Apply any launch flags from the ActivityOptions. This is used only by SystemUI
+            // to ensure that we can launch the pending intent with a consistent launch mode even
+            // if the provided PendingIntent is immutable (ie. to force an activity to launch into
+            // a new task, or to launch multiple instances if supported by the app)
             final ActivityOptions opts = ActivityOptions.fromBundle(options);
             if (opts != null) {
-                finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+                // TODO(b/254490217): Move this check into SafeActivityOptions
+                if (controller.mAtmInternal.isCallerRecents(Binder.getCallingUid())) {
+                    finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
+                }
             }
 
             // Extract options before clearing calling identity
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index bf0052d..dbba9ee 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -19,16 +19,16 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiPortInfo;
-import android.hardware.tv.cec.CecMessage;
-import android.hardware.tv.cec.IHdmiCec;
-import android.hardware.tv.cec.IHdmiCecCallback;
 import android.hardware.tv.cec.V1_0.HotplugEvent;
 import android.hardware.tv.cec.V1_0.IHdmiCec.getPhysicalAddressCallback;
 import android.hardware.tv.cec.V1_0.OptionKey;
 import android.hardware.tv.cec.V1_0.Result;
 import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.hardware.tv.hdmi.IHdmi;
-import android.hardware.tv.hdmi.IHdmiCallback;
+import android.hardware.tv.hdmi.cec.CecMessage;
+import android.hardware.tv.hdmi.cec.IHdmiCec;
+import android.hardware.tv.hdmi.cec.IHdmiCecCallback;
+import android.hardware.tv.hdmi.connection.IHdmiConnection;
+import android.hardware.tv.hdmi.connection.IHdmiConnectionCallback;
 import android.icu.util.IllformedLocaleException;
 import android.icu.util.ULocale;
 import android.os.Binder;
@@ -178,7 +178,7 @@
         if (controller != null) {
             return controller;
         }
-        HdmiLogger.warning("Unable to use CEC and HDMI AIDL HALs");
+        HdmiLogger.warning("Unable to use CEC and HDMI Connection AIDL HALs");
 
         controller = createWithNativeWrapper(service, new NativeWrapperImpl11(), atomWriter);
         if (controller != null) {
@@ -872,14 +872,14 @@
     private static final class NativeWrapperImplAidl
             implements NativeWrapper, IBinder.DeathRecipient {
         private IHdmiCec mHdmiCec;
-        private IHdmi mHdmi;
+        private IHdmiConnection mHdmiConnection;
         @Nullable private HdmiCecCallback mCallback;
 
         private final Object mLock = new Object();
 
         @Override
         public String nativeInit() {
-            return connectToHal() ? mHdmiCec.toString() + " " + mHdmi.toString() : null;
+            return connectToHal() ? mHdmiCec.toString() + " " + mHdmiConnection.toString() : null;
         }
 
         boolean connectToHal() {
@@ -896,15 +896,15 @@
                 HdmiLogger.error("Couldn't link to death : ", e);
             }
 
-            mHdmi =
-                    IHdmi.Stub.asInterface(
-                            ServiceManager.getService(IHdmi.DESCRIPTOR + "/default"));
-            if (mHdmi == null) {
-                HdmiLogger.error("Could not initialize HDMI AIDL HAL");
+            mHdmiConnection =
+                    IHdmiConnection.Stub.asInterface(
+                            ServiceManager.getService(IHdmiConnection.DESCRIPTOR + "/default"));
+            if (mHdmiConnection == null) {
+                HdmiLogger.error("Could not initialize HDMI Connection AIDL HAL");
                 return false;
             }
             try {
-                mHdmi.asBinder().linkToDeath(this, 0);
+                mHdmiConnection.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
                 HdmiLogger.error("Couldn't link to death : ", e);
             }
@@ -915,8 +915,8 @@
         public void binderDied() {
             // One of the services died, try to reconnect to both.
             mHdmiCec.asBinder().unlinkToDeath(this, 0);
-            mHdmi.asBinder().unlinkToDeath(this, 0);
-            HdmiLogger.error("HDMI or CEC service died, reconnecting");
+            mHdmiConnection.asBinder().unlinkToDeath(this, 0);
+            HdmiLogger.error("HDMI Connection or CEC service died, reconnecting");
             connectToHal();
             // Reconnect the callback
             if (mCallback != null) {
@@ -935,7 +935,7 @@
             }
             try {
                 // Create an AIDL callback that can callback onHotplugEvent
-                mHdmi.setCallback(new HdmiCallbackAidl(callback));
+                mHdmiConnection.setCallback(new HdmiConnectionCallbackAidl(callback));
             } catch (RemoteException e) {
                 HdmiLogger.error("Couldn't initialise tv.hdmi callback : ", e);
             }
@@ -1052,10 +1052,11 @@
         @Override
         public HdmiPortInfo[] nativeGetPortInfos() {
             try {
-                android.hardware.tv.hdmi.HdmiPortInfo[] hdmiPortInfos = mHdmi.getPortInfo();
+                android.hardware.tv.hdmi.connection.HdmiPortInfo[] hdmiPortInfos =
+                        mHdmiConnection.getPortInfo();
                 HdmiPortInfo[] hdmiPortInfo = new HdmiPortInfo[hdmiPortInfos.length];
                 int i = 0;
-                for (android.hardware.tv.hdmi.HdmiPortInfo portInfo : hdmiPortInfos) {
+                for (android.hardware.tv.hdmi.connection.HdmiPortInfo portInfo : hdmiPortInfos) {
                     hdmiPortInfo[i] =
                             new HdmiPortInfo(
                                     portInfo.portId,
@@ -1076,7 +1077,7 @@
         @Override
         public boolean nativeIsConnected(int port) {
             try {
-                return mHdmi.isConnected(port);
+                return mHdmiConnection.isConnected(port);
             } catch (RemoteException e) {
                 HdmiLogger.error("Failed to get connection info : ", e);
                 return false;
@@ -1580,10 +1581,10 @@
         }
     }
 
-    private static final class HdmiCallbackAidl extends IHdmiCallback.Stub {
+    private static final class HdmiConnectionCallbackAidl extends IHdmiConnectionCallback.Stub {
         private final HdmiCecCallback mHdmiCecCallback;
 
-        HdmiCallbackAidl(HdmiCecCallback hdmiCecCallback) {
+        HdmiConnectionCallbackAidl(HdmiCecCallback hdmiCecCallback) {
             mHdmiCecCallback = hdmiCecCallback;
         }
 
@@ -1594,12 +1595,12 @@
 
         @Override
         public synchronized String getInterfaceHash() throws android.os.RemoteException {
-            return IHdmiCallback.Stub.HASH;
+            return IHdmiConnectionCallback.Stub.HASH;
         }
 
         @Override
         public int getInterfaceVersion() throws android.os.RemoteException {
-            return IHdmiCallback.Stub.VERSION;
+            return IHdmiConnectionCallback.Stub.VERSION;
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index 4d525da..9b42cfc 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -36,6 +36,7 @@
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.os.Handler;
 import android.os.PowerManager;
 import android.os.SystemClock;
@@ -103,12 +104,12 @@
 
     /**
      * Number of boots until we consider the escrow data to be stale for the purposes of metrics.
-     * <p>
-     * If the delta between the current boot number and the boot number stored when the mechanism
+     *
+     * <p>If the delta between the current boot number and the boot number stored when the mechanism
      * was armed is under this number and the escrow mechanism fails, we report it as a failure of
      * the mechanism.
-     * <p>
-     * If the delta over this number and escrow fails, we will not report the metric as failed
+     *
+     * <p>If the delta over this number and escrow fails, we will not report the metric as failed
      * since there most likely was some other issue if the device rebooted several times before
      * getting to the escrow restore code.
      */
@@ -120,8 +121,11 @@
      */
     private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_COUNT = 3;
     private static final int DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS = 30;
+
     // 3 minutes. It's enough for the default 3 retries with 30 seconds interval
-    private static final int DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS = 180_000;
+    private static final int DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS = 180_000;
+    // 5 seconds. An extension of the overall RoR timeout to account for overhead.
+    private static final int DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS = 5000;
 
     @IntDef(prefix = {"ERROR_"}, value = {
             ERROR_NONE,
@@ -133,6 +137,7 @@
             ERROR_PROVIDER_MISMATCH,
             ERROR_KEYSTORE_FAILURE,
             ERROR_NO_NETWORK,
+            ERROR_TIMEOUT_EXHAUSTED,
     })
     @Retention(RetentionPolicy.SOURCE)
     @interface RebootEscrowErrorCode {
@@ -147,6 +152,7 @@
     static final int ERROR_PROVIDER_MISMATCH = 6;
     static final int ERROR_KEYSTORE_FAILURE = 7;
     static final int ERROR_NO_NETWORK = 8;
+    static final int ERROR_TIMEOUT_EXHAUSTED = 9;
 
     private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE;
 
@@ -168,6 +174,15 @@
     /** Notified when mRebootEscrowReady changes. */
     private RebootEscrowListener mRebootEscrowListener;
 
+    /** Set when unlocking reboot escrow times out. */
+    private boolean mRebootEscrowTimedOut = false;
+
+    /**
+     * Set when {@link #loadRebootEscrowDataWithRetry} is called to ensure the function is only
+     * called once.
+     */
+    private boolean mLoadEscrowDataWithRetry = false;
+
     /**
      * Hold this lock when checking or generating the reboot escrow key.
      */
@@ -192,6 +207,7 @@
 
     PowerManager.WakeLock mWakeLock;
 
+    private ConnectivityManager.NetworkCallback mNetworkCallback;
 
     interface Callbacks {
         boolean isUserSecure(int userId);
@@ -246,6 +262,11 @@
                     "server_based_ror_enabled", false);
         }
 
+        public boolean waitForInternet() {
+            return DeviceConfig.getBoolean(
+                    DeviceConfig.NAMESPACE_OTA, "wait_for_internet_ror", false);
+        }
+
         public boolean isNetworkConnected() {
             final ConnectivityManager connectivityManager =
                     mContext.getSystemService(ConnectivityManager.class);
@@ -263,6 +284,38 @@
                             NetworkCapabilities.NET_CAPABILITY_VALIDATED);
         }
 
+        /**
+         * Request network with internet connectivity with timeout.
+         *
+         * @param networkCallback callback to be executed if connectivity manager exists.
+         * @return true if success
+         */
+        public boolean requestNetworkWithInternet(
+                ConnectivityManager.NetworkCallback networkCallback) {
+            final ConnectivityManager connectivityManager =
+                    mContext.getSystemService(ConnectivityManager.class);
+            if (connectivityManager == null) {
+                return false;
+            }
+            NetworkRequest request =
+                    new NetworkRequest.Builder()
+                            .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                            .build();
+
+            connectivityManager.requestNetwork(
+                    request, networkCallback, getLoadEscrowTimeoutMillis());
+            return true;
+        }
+
+        public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) {
+            final ConnectivityManager connectivityManager =
+                    mContext.getSystemService(ConnectivityManager.class);
+            if (connectivityManager == null) {
+                return;
+            }
+            connectivityManager.unregisterNetworkCallback(networkCallback);
+        }
+
         public Context getContext() {
             return mContext;
         }
@@ -318,6 +371,16 @@
                     DEFAULT_LOAD_ESCROW_DATA_RETRY_INTERVAL_SECONDS);
         }
 
+        @VisibleForTesting
+        public int getLoadEscrowTimeoutMillis() {
+            return DEFAULT_LOAD_ESCROW_BASE_TIMEOUT_MILLIS;
+        }
+
+        @VisibleForTesting
+        public int getWakeLockTimeoutMillis() {
+            return getLoadEscrowTimeoutMillis() + DEFAULT_LOAD_ESCROW_TIMEOUT_EXTENSION_MILLIS;
+        }
+
         public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount,
                 int escrowDurationInSeconds, int vbmetaDigestStatus,
                 int durationSinceBootCompleteInSeconds) {
@@ -351,13 +414,37 @@
         mKeyStoreManager = injector.getKeyStoreManager();
     }
 
-    private void onGetRebootEscrowKeyFailed(List<UserInfo> users, int attemptCount) {
+    /** Wrapper function to set error code serialized through handler, */
+    private void setLoadEscrowDataErrorCode(@RebootEscrowErrorCode int value, Handler handler) {
+        if (mInjector.waitForInternet()) {
+            mInjector.post(
+                    handler,
+                    () -> {
+                        mLoadEscrowDataErrorCode = value;
+                    });
+        } else {
+            mLoadEscrowDataErrorCode = value;
+        }
+    }
+
+    /** Wrapper function to compare and set error code serialized through handler. */
+    private void compareAndSetLoadEscrowDataErrorCode(
+            @RebootEscrowErrorCode int expectedValue,
+            @RebootEscrowErrorCode int newValue,
+            Handler handler) {
+        if (expectedValue == mLoadEscrowDataErrorCode) {
+            setLoadEscrowDataErrorCode(newValue, handler);
+        }
+    }
+
+    private void onGetRebootEscrowKeyFailed(
+            List<UserInfo> users, int attemptCount, Handler retryHandler) {
         Slog.w(TAG, "Had reboot escrow data for users, but no key; removing escrow storage.");
         for (UserInfo user : users) {
             mStorage.removeRebootEscrow(user.id);
         }
 
-        onEscrowRestoreComplete(false, attemptCount);
+        onEscrowRestoreComplete(false, attemptCount, retryHandler);
     }
 
     void loadRebootEscrowDataIfAvailable(Handler retryHandler) {
@@ -380,39 +467,130 @@
         mWakeLock = mInjector.getWakeLock();
         if (mWakeLock != null) {
             mWakeLock.setReferenceCounted(false);
-            mWakeLock.acquire(DEFAULT_WAKE_LOCK_TIMEOUT_MILLIS);
+            mWakeLock.acquire(mInjector.getWakeLockTimeoutMillis());
+        }
+
+        if (mInjector.waitForInternet()) {
+            // Timeout to stop retrying same as the wake lock timeout.
+            mInjector.postDelayed(
+                    retryHandler,
+                    () -> {
+                        mRebootEscrowTimedOut = true;
+                    },
+                    mInjector.getLoadEscrowTimeoutMillis());
+
+            mInjector.post(
+                    retryHandler,
+                    () -> loadRebootEscrowDataOnInternet(retryHandler, users, rebootEscrowUsers));
+            return;
         }
 
         mInjector.post(retryHandler, () -> loadRebootEscrowDataWithRetry(
                 retryHandler, 0, users, rebootEscrowUsers));
     }
 
-    void scheduleLoadRebootEscrowDataOrFail(Handler retryHandler, int attemptNumber,
-            List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
+    void scheduleLoadRebootEscrowDataOrFail(
+            Handler retryHandler,
+            int attemptNumber,
+            List<UserInfo> users,
+            List<UserInfo> rebootEscrowUsers) {
         Objects.requireNonNull(retryHandler);
 
         final int retryLimit = mInjector.getLoadEscrowDataRetryLimit();
         final int retryIntervalInSeconds = mInjector.getLoadEscrowDataRetryIntervalSeconds();
 
-        if (attemptNumber < retryLimit) {
+        if (attemptNumber < retryLimit && !mRebootEscrowTimedOut) {
             Slog.i(TAG, "Scheduling loadRebootEscrowData retry number: " + attemptNumber);
             mInjector.postDelayed(retryHandler, () -> loadRebootEscrowDataWithRetry(
-                    retryHandler, attemptNumber, users, rebootEscrowUsers),
+                            retryHandler, attemptNumber, users, rebootEscrowUsers),
                     retryIntervalInSeconds * 1000);
             return;
         }
 
+        if (mInjector.waitForInternet()) {
+            if (mRebootEscrowTimedOut) {
+                Slog.w(TAG, "Failed to load reboot escrow data within timeout");
+                compareAndSetLoadEscrowDataErrorCode(
+                        ERROR_NONE, ERROR_TIMEOUT_EXHAUSTED, retryHandler);
+            } else {
+                Slog.w(
+                        TAG,
+                        "Failed to load reboot escrow data after " + attemptNumber + " attempts");
+                compareAndSetLoadEscrowDataErrorCode(
+                        ERROR_NONE, ERROR_RETRY_COUNT_EXHAUSTED, retryHandler);
+            }
+            onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
+            return;
+        }
+
         Slog.w(TAG, "Failed to load reboot escrow data after " + attemptNumber + " attempts");
         if (mInjector.serverBasedResumeOnReboot() && !mInjector.isNetworkConnected()) {
             mLoadEscrowDataErrorCode = ERROR_NO_NETWORK;
         } else {
             mLoadEscrowDataErrorCode = ERROR_RETRY_COUNT_EXHAUSTED;
         }
-        onGetRebootEscrowKeyFailed(users, attemptNumber);
+        onGetRebootEscrowKeyFailed(users, attemptNumber, retryHandler);
     }
 
-    void loadRebootEscrowDataWithRetry(Handler retryHandler, int attemptNumber,
-            List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
+    void loadRebootEscrowDataOnInternet(
+            Handler retryHandler, List<UserInfo> users, List<UserInfo> rebootEscrowUsers) {
+
+        // HAL-Based RoR does not require network connectivity.
+        if (!mInjector.serverBasedResumeOnReboot()) {
+            loadRebootEscrowDataWithRetry(
+                    retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers);
+            return;
+        }
+
+        mNetworkCallback =
+                new ConnectivityManager.NetworkCallback() {
+                    @Override
+                    public void onAvailable(Network network) {
+                        compareAndSetLoadEscrowDataErrorCode(
+                                ERROR_NO_NETWORK, ERROR_NONE, retryHandler);
+
+                        if (!mLoadEscrowDataWithRetry) {
+                            mLoadEscrowDataWithRetry = true;
+                            // Only kickoff retry mechanism on first onAvailable call.
+                            loadRebootEscrowDataWithRetry(
+                                    retryHandler,
+                                    /* attemptNumber = */ 0,
+                                    users,
+                                    rebootEscrowUsers);
+                        }
+                    }
+
+                    @Override
+                    public void onUnavailable() {
+                        Slog.w(TAG, "Failed to connect to network within timeout");
+                        compareAndSetLoadEscrowDataErrorCode(
+                                ERROR_NONE, ERROR_NO_NETWORK, retryHandler);
+                        onGetRebootEscrowKeyFailed(users, /* attemptCount= */ 0, retryHandler);
+                    }
+
+                    @Override
+                    public void onLost(Network lostNetwork) {
+                        // TODO(b/231660348): If network is lost, wait for network to become
+                        // available again.
+                        Slog.w(TAG, "Network lost, still attempting to load escrow key.");
+                        compareAndSetLoadEscrowDataErrorCode(
+                                ERROR_NONE, ERROR_NO_NETWORK, retryHandler);
+                    }
+                };
+
+        // Fallback to retrying without waiting for internet on failure.
+        boolean success = mInjector.requestNetworkWithInternet(mNetworkCallback);
+        if (!success) {
+            loadRebootEscrowDataWithRetry(
+                    retryHandler, /* attemptNumber = */ 0, users, rebootEscrowUsers);
+        }
+    }
+
+    void loadRebootEscrowDataWithRetry(
+            Handler retryHandler,
+            int attemptNumber,
+            List<UserInfo> users,
+            List<UserInfo> rebootEscrowUsers) {
         // Fetch the key from keystore to decrypt the escrow data & escrow key; this key is
         // generated before reboot. Note that we will clear the escrow key even if the keystore key
         // is null.
@@ -423,7 +601,7 @@
 
         RebootEscrowKey escrowKey;
         try {
-            escrowKey = getAndClearRebootEscrowKey(kk);
+            escrowKey = getAndClearRebootEscrowKey(kk, retryHandler);
         } catch (IOException e) {
             Slog.i(TAG, "Failed to load escrow key, scheduling retry.", e);
             scheduleLoadRebootEscrowDataOrFail(retryHandler, attemptNumber + 1, users,
@@ -438,12 +616,12 @@
                         ? RebootEscrowProviderInterface.TYPE_SERVER_BASED
                         : RebootEscrowProviderInterface.TYPE_HAL;
                 if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) {
-                    mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH;
+                    setLoadEscrowDataErrorCode(ERROR_PROVIDER_MISMATCH, retryHandler);
                 } else {
-                    mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY;
+                    setLoadEscrowDataErrorCode(ERROR_LOAD_ESCROW_KEY, retryHandler);
                 }
             }
-            onGetRebootEscrowKeyFailed(users, attemptNumber + 1);
+            onGetRebootEscrowKeyFailed(users, attemptNumber + 1, retryHandler);
             return;
         }
 
@@ -454,10 +632,10 @@
             allUsersUnlocked &= restoreRebootEscrowForUser(user.id, escrowKey, kk);
         }
 
-        if (!allUsersUnlocked && mLoadEscrowDataErrorCode == ERROR_NONE) {
-            mLoadEscrowDataErrorCode = ERROR_UNLOCK_ALL_USERS;
+        if (!allUsersUnlocked) {
+            compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNLOCK_ALL_USERS, retryHandler);
         }
-        onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1);
+        onEscrowRestoreComplete(allUsersUnlocked, attemptNumber + 1, retryHandler);
     }
 
     private void clearMetricsStorage() {
@@ -497,7 +675,8 @@
                 .REBOOT_ESCROW_RECOVERY_REPORTED__VBMETA_DIGEST_STATUS__MISMATCH;
     }
 
-    private void reportMetricOnRestoreComplete(boolean success, int attemptCount) {
+    private void reportMetricOnRestoreComplete(
+            boolean success, int attemptCount, Handler retryHandler) {
         int serviceType = mInjector.serverBasedResumeOnReboot()
                 ? FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__SERVER_BASED
                 : FrameworkStatsLog.REBOOT_ESCROW_RECOVERY_REPORTED__TYPE__HAL;
@@ -511,52 +690,69 @@
         }
 
         int vbmetaDigestStatus = getVbmetaDigestStatusOnRestoreComplete();
-        if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) {
-            mLoadEscrowDataErrorCode = ERROR_UNKNOWN;
+        if (!success) {
+            compareAndSetLoadEscrowDataErrorCode(ERROR_NONE, ERROR_UNKNOWN, retryHandler);
         }
 
-        Slog.i(TAG, "Reporting RoR recovery metrics, success: " + success + ", service type: "
-                + serviceType + ", error code: " + mLoadEscrowDataErrorCode);
+        Slog.i(
+                TAG,
+                "Reporting RoR recovery metrics, success: "
+                        + success
+                        + ", service type: "
+                        + serviceType
+                        + ", error code: "
+                        + mLoadEscrowDataErrorCode);
         // TODO(179105110) report the duration since boot complete.
-        mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount,
-                escrowDurationInSeconds, vbmetaDigestStatus, -1);
+        mInjector.reportMetric(
+                success,
+                mLoadEscrowDataErrorCode,
+                serviceType,
+                attemptCount,
+                escrowDurationInSeconds,
+                vbmetaDigestStatus,
+                -1);
 
-        mLoadEscrowDataErrorCode = ERROR_NONE;
+        setLoadEscrowDataErrorCode(ERROR_NONE, retryHandler);
     }
 
-    private void onEscrowRestoreComplete(boolean success, int attemptCount) {
+    private void onEscrowRestoreComplete(boolean success, int attemptCount, Handler retryHandler) {
         int previousBootCount = mStorage.getInt(REBOOT_ESCROW_ARMED_KEY, -1, USER_SYSTEM);
 
         int bootCountDelta = mInjector.getBootCount() - previousBootCount;
         if (success || (previousBootCount != -1 && bootCountDelta <= BOOT_COUNT_TOLERANCE)) {
-            reportMetricOnRestoreComplete(success, attemptCount);
+            reportMetricOnRestoreComplete(success, attemptCount, retryHandler);
         }
-
         // Clear the old key in keystore. A new key will be generated by new RoR requests.
         mKeyStoreManager.clearKeyStoreEncryptionKey();
         // Clear the saved reboot escrow provider
         mInjector.clearRebootEscrowProvider();
         clearMetricsStorage();
 
+        if (mNetworkCallback != null) {
+            mInjector.stopRequestingNetwork(mNetworkCallback);
+        }
+
         if (mWakeLock != null) {
             mWakeLock.release();
         }
     }
 
-    private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk) throws IOException {
+    private RebootEscrowKey getAndClearRebootEscrowKey(SecretKey kk, Handler retryHandler)
+            throws IOException {
         RebootEscrowProviderInterface rebootEscrowProvider =
                 mInjector.createRebootEscrowProviderIfNeeded();
         if (rebootEscrowProvider == null) {
-            Slog.w(TAG,
+            Slog.w(
+                    TAG,
                     "Had reboot escrow data for users, but RebootEscrowProvider is unavailable");
-            mLoadEscrowDataErrorCode = ERROR_NO_PROVIDER;
+            setLoadEscrowDataErrorCode(ERROR_NO_PROVIDER, retryHandler);
             return null;
         }
 
         // Server based RoR always need the decryption key from keystore.
         if (rebootEscrowProvider.getType() == RebootEscrowProviderInterface.TYPE_SERVER_BASED
                 && kk == null) {
-            mLoadEscrowDataErrorCode = ERROR_KEYSTORE_FAILURE;
+            setLoadEscrowDataErrorCode(ERROR_KEYSTORE_FAILURE, retryHandler);
             return null;
         }
 
@@ -870,6 +1066,9 @@
         pw.print("mRebootEscrowListener=");
         pw.println(mRebootEscrowListener);
 
+        pw.print("mLoadEscrowDataErrorCode=");
+        pw.println(mLoadEscrowDataErrorCode);
+
         boolean keySet;
         synchronized (mKeyGenerationLock) {
             keySet = mPendingRebootEscrowKey != null;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7468d32..b6bdd4c 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1977,34 +1977,39 @@
             return (haystack & needle) != 0;
         }
 
-        public boolean isInLockDownMode() {
-            return mIsInLockDownMode;
+        // Return whether the user is in lockdown mode.
+        // If the flag is not set, we assume the user is not in lockdown.
+        public boolean isInLockDownMode(int userId) {
+            return mUserInLockDownMode.get(userId, false);
         }
 
         @Override
         public synchronized void onStrongAuthRequiredChanged(int userId) {
             boolean userInLockDownModeNext = containsFlag(getStrongAuthForUser(userId),
                     STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
-            mUserInLockDownMode.put(userId, userInLockDownModeNext);
-            boolean isInLockDownModeNext = mUserInLockDownMode.indexOfValue(true) != -1;
 
-            if (mIsInLockDownMode == isInLockDownModeNext) {
+            // Nothing happens if the lockdown mode of userId keeps the same.
+            if (userInLockDownModeNext == isInLockDownMode(userId)) {
                 return;
             }
 
-            if (isInLockDownModeNext) {
-                cancelNotificationsWhenEnterLockDownMode();
+            // When the lockdown mode is changed, we perform the following steps.
+            // If the userInLockDownModeNext is true, all the function calls to
+            // notifyPostedLocked and notifyRemovedLocked will not be executed.
+            // The cancelNotificationsWhenEnterLockDownMode calls notifyRemovedLocked
+            // and postNotificationsWhenExitLockDownMode calls notifyPostedLocked.
+            // So we shall call cancelNotificationsWhenEnterLockDownMode before
+            // we set mUserInLockDownMode as true.
+            // On the other hand, if the userInLockDownModeNext is false, we shall call
+            // postNotificationsWhenExitLockDownMode after we put false into mUserInLockDownMode
+            if (userInLockDownModeNext) {
+                cancelNotificationsWhenEnterLockDownMode(userId);
             }
 
-            // When the mIsInLockDownMode is true, both notifyPostedLocked and
-            // notifyRemovedLocked will be dismissed. So we shall call
-            // cancelNotificationsWhenEnterLockDownMode before we set mIsInLockDownMode
-            // as true and call postNotificationsWhenExitLockDownMode after we set
-            // mIsInLockDownMode as false.
-            mIsInLockDownMode = isInLockDownModeNext;
+            mUserInLockDownMode.put(userId, userInLockDownModeNext);
 
-            if (!isInLockDownModeNext) {
-                postNotificationsWhenExitLockDownMode();
+            if (!userInLockDownModeNext) {
+                postNotificationsWhenExitLockDownMode(userId);
             }
         }
     }
@@ -4953,16 +4958,7 @@
             }
             enforcePolicyAccess(Binder.getCallingUid(), "addAutomaticZenRule");
 
-            // If the caller is system, take the package name from the rule's owner rather than
-            // from the caller's package.
-            String rulePkg = pkg;
-            if (isCallingUidSystem()) {
-                if (automaticZenRule.getOwner() != null) {
-                    rulePkg = automaticZenRule.getOwner().getPackageName();
-                }
-            }
-
-            return mZenModeHelper.addAutomaticZenRule(rulePkg, automaticZenRule,
+            return mZenModeHelper.addAutomaticZenRule(pkg, automaticZenRule,
                     "addAutomaticZenRule");
         }
 
@@ -9713,11 +9709,14 @@
         }
     }
 
-    private void cancelNotificationsWhenEnterLockDownMode() {
+    private void cancelNotificationsWhenEnterLockDownMode(int userId) {
         synchronized (mNotificationLock) {
             int numNotifications = mNotificationList.size();
             for (int i = 0; i < numNotifications; i++) {
                 NotificationRecord rec = mNotificationList.get(i);
+                if (rec.getUser().getIdentifier() != userId) {
+                    continue;
+                }
                 mListeners.notifyRemovedLocked(rec, REASON_CANCEL_ALL,
                         rec.getStats());
             }
@@ -9725,14 +9724,23 @@
         }
     }
 
-    private void postNotificationsWhenExitLockDownMode() {
+    private void postNotificationsWhenExitLockDownMode(int userId) {
         synchronized (mNotificationLock) {
             int numNotifications = mNotificationList.size();
+            // Set the delay to spread out the burst of notifications.
+            long delay = 0;
             for (int i = 0; i < numNotifications; i++) {
                 NotificationRecord rec = mNotificationList.get(i);
-                mListeners.notifyPostedLocked(rec, rec);
+                if (rec.getUser().getIdentifier() != userId) {
+                    continue;
+                }
+                mHandler.postDelayed(() -> {
+                    synchronized (mNotificationLock) {
+                        mListeners.notifyPostedLocked(rec, rec);
+                    }
+                }, delay);
+                delay += 20;
             }
-
         }
     }
 
@@ -9911,6 +9919,9 @@
 
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
+            if (isInLockDownMode(record.getUser().getIdentifier())) {
+                continue;
+            }
             if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) {
                 continue;
             }
@@ -9952,8 +9963,8 @@
                 rankings.toArray(new NotificationListenerService.Ranking[0]));
     }
 
-    boolean isInLockDownMode() {
-        return mStrongAuthTracker.isInLockDownMode();
+    boolean isInLockDownMode(int userId) {
+        return mStrongAuthTracker.isInLockDownMode(userId);
     }
 
     boolean hasCompanionDevice(ManagedServiceInfo info) {
@@ -11015,7 +11026,7 @@
         @GuardedBy("mNotificationLock")
         void notifyPostedLocked(NotificationRecord r, NotificationRecord old,
                 boolean notifyAllListeners) {
-            if (isInLockDownMode()) {
+            if (isInLockDownMode(r.getUser().getIdentifier())) {
                 return;
             }
 
@@ -11121,7 +11132,7 @@
         @GuardedBy("mNotificationLock")
         public void notifyRemovedLocked(NotificationRecord r, int reason,
                 NotificationStats notificationStats) {
-            if (isInLockDownMode()) {
+            if (isInLockDownMode(r.getUser().getIdentifier())) {
                 return;
             }
 
@@ -11170,10 +11181,6 @@
          */
         @GuardedBy("mNotificationLock")
         public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
-            if (isInLockDownMode()) {
-                return;
-            }
-
             boolean isHiddenRankingUpdate = changedHiddenNotifications != null
                     && changedHiddenNotifications.size() > 0;
             // TODO (b/73052211): if the ranking update changed the notification type,
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 4c23ab8..d426679 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -326,7 +326,7 @@
 
     public String addAutomaticZenRule(String pkg, AutomaticZenRule automaticZenRule,
             String reason) {
-        if (!ZenModeConfig.SYSTEM_AUTHORITY.equals(pkg)) {
+        if (!isSystemRule(automaticZenRule)) {
             PackageItemInfo component = getServiceInfo(automaticZenRule.getOwner());
             if (component == null) {
                 component = getActivityInfo(automaticZenRule.getConfigurationActivity());
@@ -582,6 +582,11 @@
         }
     }
 
+    private boolean isSystemRule(AutomaticZenRule rule) {
+        return rule.getOwner() != null
+                && ZenModeConfig.SYSTEM_AUTHORITY.equals(rule.getOwner().getPackageName());
+    }
+
     private ServiceInfo getServiceInfo(ComponentName owner) {
         Intent queryIntent = new Intent();
         queryIntent.setComponent(owner);
diff --git a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
index 1407530..e056151 100644
--- a/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
+++ b/services/core/java/com/android/server/pm/dex/ArtStatsLogUtils.java
@@ -298,7 +298,9 @@
                     dexMetadataType,
                     apkType,
                     ISA_MAP.getOrDefault(isa,
-                            ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN));
+                            ArtStatsLog.ART_DATUM_REPORTED__ISA__ART_ISA_UNKNOWN),
+                    ArtStatsLog.ART_DATUM_REPORTED__GC__ART_GC_COLLECTOR_TYPE_UNKNOWN,
+                    ArtStatsLog.ART_DATUM_REPORTED__UFFD_SUPPORT__ART_UFFD_SUPPORT_UNKNOWN);
         }
     }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 014d580..554e269 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -644,8 +644,8 @@
             Permission bp = mRegistry.getPermission(info.name);
             added = bp == null;
             int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
+            enforcePermissionCapLocked(info, tree);
             if (added) {
-                enforcePermissionCapLocked(info, tree);
                 bp = new Permission(info.name, tree.getPackageName(), Permission.TYPE_DYNAMIC);
             } else if (!bp.isDynamic()) {
                 throw new SecurityException("Not allowed to modify non-dynamic permission "
@@ -2136,6 +2136,46 @@
     }
 
     /**
+     * If the package was below api 23, got the SYSTEM_ALERT_WINDOW permission automatically, and
+     * then updated past api 23, and the app does not satisfy any of the other SAW permission flags,
+     * the permission should be revoked.
+     *
+     * @param newPackage The new package that was installed
+     * @param oldPackage The old package that was updated
+     */
+    private void revokeSystemAlertWindowIfUpgradedPast23(
+            @NonNull AndroidPackage newPackage,
+            @NonNull AndroidPackage oldPackage) {
+        if (oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.M
+                || newPackage.getTargetSdkVersion() < Build.VERSION_CODES.M
+                || !newPackage.getRequestedPermissions()
+                .contains(Manifest.permission.SYSTEM_ALERT_WINDOW)) {
+            return;
+        }
+
+        Permission saw;
+        synchronized (mLock) {
+            saw = mRegistry.getPermission(Manifest.permission.SYSTEM_ALERT_WINDOW);
+        }
+        final PackageStateInternal ps =
+                mPackageManagerInt.getPackageStateInternal(newPackage.getPackageName());
+        if (shouldGrantPermissionByProtectionFlags(newPackage, ps, saw, new ArraySet<>())
+                || shouldGrantPermissionBySignature(newPackage, saw)) {
+            return;
+        }
+        for (int userId : getAllUserIds()) {
+            try {
+                revokePermissionFromPackageForUser(newPackage.getPackageName(),
+                        Manifest.permission.SYSTEM_ALERT_WINDOW, false, userId,
+                        mDefaultPermissionCallback);
+            } catch (IllegalStateException | SecurityException e) {
+                Log.e(TAG, "unable to revoke SYSTEM_ALERT_WINDOW for "
+                        + newPackage.getPackageName() + " user " + userId, e);
+            }
+        }
+    }
+
+    /**
      * We might auto-grant permissions if any permission of the group is already granted. Hence if
      * the group of a granted permission changes we need to revoke it to avoid having permissions of
      * the new group auto-granted.
@@ -4661,6 +4701,7 @@
                 if (hasOldPkg) {
                     revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg);
                     revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg);
+                    revokeSystemAlertWindowIfUpgradedPast23(pkg, oldPkg);
                 }
                 if (hasPermissionDefinitionChanges) {
                     revokeRuntimePermissionsIfPermissionDefinitionChangedInternal(
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index ed4ba0d..7f8f406 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3202,8 +3202,7 @@
             }
             final PowerGroup powerGroup = mPowerGroups.get(groupId);
             wakefulness = powerGroup.getWakefulnessLocked();
-            if ((wakefulness == WAKEFULNESS_DREAMING || wakefulness == WAKEFULNESS_DOZING) &&
-                    powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
+            if (powerGroup.isSandmanSummonedLocked() && powerGroup.isReadyLocked()) {
                 startDreaming = canDreamLocked(powerGroup) || canDozeLocked(powerGroup);
                 powerGroup.setSandmanSummonedLocked(/* isSandmanSummoned= */ false);
             } else {
diff --git a/services/core/java/com/android/server/security/rkp/TEST_MAPPING b/services/core/java/com/android/server/security/rkp/TEST_MAPPING
new file mode 100644
index 0000000..e983968
--- /dev/null
+++ b/services/core/java/com/android/server/security/rkp/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "RemoteProvisioningServiceTests"
+    }
+  ]
+}
+
diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java
index fd6c974..b160af6a 100644
--- a/services/core/java/com/android/server/wm/AppTaskImpl.java
+++ b/services/core/java/com/android/server/wm/AppTaskImpl.java
@@ -98,7 +98,7 @@
                     throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
                 }
                 return mService.getRecentTasks().createRecentTaskInfo(task,
-                        false /* stripExtras */);
+                        false /* stripExtras */, true /* getTasksAllowed */);
             } finally {
                 Binder.restoreCallingIdentity(origId);
             }
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index 4860762..1fc061b 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -976,7 +976,7 @@
                 continue;
             }
 
-            res.add(createRecentTaskInfo(task, true /* stripExtras */));
+            res.add(createRecentTaskInfo(task, true /* stripExtras */, getTasksAllowed));
         }
         return res;
     }
@@ -1895,7 +1895,8 @@
     /**
      * Creates a new RecentTaskInfo from a Task.
      */
-    ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras) {
+    ActivityManager.RecentTaskInfo createRecentTaskInfo(Task tr, boolean stripExtras,
+            boolean getTasksAllowed) {
         final ActivityManager.RecentTaskInfo rti = new ActivityManager.RecentTaskInfo();
         // If the recent Task is detached, we consider it will be re-attached to the default
         // TaskDisplayArea because we currently only support recent overview in the default TDA.
@@ -1907,6 +1908,9 @@
         rti.id = rti.isRunning ? rti.taskId : INVALID_TASK_ID;
         rti.persistentId = rti.taskId;
         rti.lastSnapshotData.set(tr.mLastTaskSnapshotData);
+        if (!getTasksAllowed) {
+            Task.trimIneffectiveInfo(tr, rti);
+        }
 
         // Fill in organized child task info for the task created by organizer.
         if (tr.mCreatedByOrganizer) {
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 33f019e..4e339f1 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -177,6 +177,10 @@
         }
         // Fill in some deprecated values
         rti.id = rti.taskId;
+
+        if (!mAllowed) {
+            Task.trimIneffectiveInfo(task, rti);
+        }
         return rti;
     }
 }
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index d6f295e..8993840 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3447,6 +3447,27 @@
         info.isSleeping = shouldSleepActivities();
     }
 
+    /**
+     * Removes the activity info if the activity belongs to a different uid, which is
+     * different from the app that hosts the task.
+     */
+    static void trimIneffectiveInfo(Task task, TaskInfo info) {
+        final ActivityRecord baseActivity = task.getActivity(r -> !r.finishing,
+                false /* traverseTopToBottom */);
+        final int baseActivityUid =
+                baseActivity != null ? baseActivity.getUid() : task.effectiveUid;
+
+        if (info.topActivityInfo != null
+                && task.effectiveUid != info.topActivityInfo.applicationInfo.uid) {
+            info.topActivity = null;
+            info.topActivityInfo = null;
+        }
+
+        if (task.effectiveUid != baseActivityUid) {
+            info.baseActivity = null;
+        }
+    }
+
     @Nullable PictureInPictureParams getPictureInPictureParams() {
         final Task topTask = getTopMostTask();
         if (topTask == null) return null;
diff --git a/services/tests/RemoteProvisioningServiceTests/Android.bp b/services/tests/RemoteProvisioningServiceTests/Android.bp
new file mode 100644
index 0000000..075680a
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/Android.bp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 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
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "RemoteProvisioningServiceTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "android.security.rkp_aidl-java",
+        "androidx.test.core",
+        "androidx.test.ext.junit",
+        "androidx.test.rules",
+        "mockito-target",
+        "service-rkp.impl",
+        "services.core",
+        "truth-prebuilt",
+    ],
+    test_suites: [
+        "device-tests",
+    ],
+    platform_apis: true,
+}
diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml
new file mode 100644
index 0000000..7c12e189
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 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.security.rkp.test">
+
+    <application android:testOnly="true">
+        <uses-library android:name="android.test.runner"/>
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.security.rkp.test"
+                     android:label="Remote Provisioning System Service Tests"/>
+</manifest>
diff --git a/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml
new file mode 100644
index 0000000..bf86fc8
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/AndroidTest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Runs Frameworks RemoteProvisioningService Tests.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-instrumentation" />
+    <option name="test-tag" value="RemoteProvisioningServiceTests" />
+
+    <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="install-arg" value="-t" />
+        <option name="test-file-name" value="RemoteProvisioningServiceTests.apk" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="com.android.server.security.rkp.test" />
+        <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+        <option name="exclude-annotation" value="androidx.test.filters.FlakyTest" />
+    </test>
+</configuration>
diff --git a/services/tests/RemoteProvisioningServiceTests/OWNERS b/services/tests/RemoteProvisioningServiceTests/OWNERS
new file mode 100644
index 0000000..348f940
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:master:/core/java/android/security/rkp/OWNERS
diff --git a/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
new file mode 100644
index 0000000..470f2be
--- /dev/null
+++ b/services/tests/RemoteProvisioningServiceTests/src/com/android/server/security/rkp/RemoteProvisioningRegistrationTest.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2022 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.security.rkp;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.AdditionalAnswers.answerVoid;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.argThat;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.CancellationSignal;
+import android.os.OperationCanceledException;
+import android.os.OutcomeReceiver;
+import android.security.rkp.IGetKeyCallback;
+import android.security.rkp.service.RegistrationProxy;
+import android.security.rkp.service.RemotelyProvisionedKey;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.VoidAnswer4;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.concurrent.Executor;
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksServicesTests:RemoteProvisioningRegistrationTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RemoteProvisioningRegistrationTest {
+    private RegistrationProxy mRegistrationProxy;
+    private RemoteProvisioningRegistration mRegistration;
+
+    @Before
+    public void setUp() {
+        mRegistrationProxy = mock(RegistrationProxy.class);
+        mRegistration = new RemoteProvisioningRegistration(mRegistrationProxy, Runnable::run);
+    }
+
+    // answerVoid wrapper with explicit types, avoiding long signatures when mocking getKeyAsync.
+    static Answer<Void> answerGetKeyAsync(
+            VoidAnswer4<Integer, CancellationSignal, Executor,
+                    OutcomeReceiver<RemotelyProvisionedKey, Exception>> answer) {
+        return answerVoid(answer);
+    }
+
+    // matcher helper, making it easier to match the different key types
+    private android.security.rkp.RemotelyProvisionedKey matches(
+            RemotelyProvisionedKey expectedKey) {
+        return argThat((android.security.rkp.RemotelyProvisionedKey key) ->
+                Arrays.equals(key.keyBlob, expectedKey.getKeyBlob())
+                        && Arrays.equals(key.encodedCertChain, expectedKey.getEncodedCertChain())
+        );
+    }
+
+    @Test
+    public void getKeySuccess() throws Exception {
+        RemotelyProvisionedKey expectedKey = mock(RemotelyProvisionedKey.class);
+        doAnswer(
+                answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+                        executor.execute(() -> receiver.onResult(expectedKey))))
+                .when(mRegistrationProxy).getKeyAsync(eq(42), any(), any(), any());
+
+        IGetKeyCallback callback = mock(IGetKeyCallback.class);
+        mRegistration.getKey(42, callback);
+        verify(callback).onSuccess(matches(expectedKey));
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void getKeyHandlesError() throws Exception {
+        Exception expectedException = new Exception("oops!");
+        doAnswer(
+                answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+                        executor.execute(() -> receiver.onError(expectedException))))
+                .when(mRegistrationProxy).getKeyAsync(eq(0), any(), any(), any());
+        IGetKeyCallback callback = mock(IGetKeyCallback.class);
+        mRegistration.getKey(0, callback);
+        verify(callback).onError(eq(expectedException.getMessage()));
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void getKeyCancelDuringProxyOperation() throws Exception {
+        IGetKeyCallback callback = mock(IGetKeyCallback.class);
+        doAnswer(
+                answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> {
+                    mRegistration.cancelGetKey(callback);
+                    assertThat(cancelSignal.isCanceled()).isTrue();
+                    executor.execute(() -> receiver.onError(new OperationCanceledException()));
+                }))
+                .when(mRegistrationProxy).getKeyAsync(eq(Integer.MAX_VALUE), any(), any(), any());
+
+        mRegistration.getKey(Integer.MAX_VALUE, callback);
+        verify(callback).onCancel();
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void cancelGetKeyWithInvalidCallback() throws Exception {
+        IGetKeyCallback callback = mock(IGetKeyCallback.class);
+        assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+    }
+
+    @Test
+    public void getKeyRejectsDuplicateCallback() throws Exception {
+        IGetKeyCallback callback = mock(IGetKeyCallback.class);
+        doAnswer(
+                answerGetKeyAsync((keyId, cancelSignal, executor, receiver) -> {
+                    assertThrows(IllegalArgumentException.class, () ->
+                            mRegistration.getKey(0, callback));
+                    executor.execute(() -> receiver.onResult(mock(RemotelyProvisionedKey.class)));
+                }))
+                .when(mRegistrationProxy).getKeyAsync(anyInt(), any(), any(), any());
+
+        mRegistration.getKey(0, callback);
+        verify(callback, times(1)).onSuccess(any());
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void getKeyCancelAfterCompleteFails() throws Exception {
+        IGetKeyCallback callback = mock(IGetKeyCallback.class);
+        doAnswer(
+                answerGetKeyAsync((keyId, cancelSignal, executor, receiver) ->
+                        executor.execute(() ->
+                                receiver.onResult(mock(RemotelyProvisionedKey.class))
+                        )))
+                .when(mRegistrationProxy).getKeyAsync(eq(Integer.MIN_VALUE), any(), any(), any());
+
+        mRegistration.getKey(Integer.MIN_VALUE, callback);
+        verify(callback).onSuccess(any());
+        assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void getKeyCatchesExceptionFromProxy() throws Exception {
+        Exception expectedException = new RuntimeException("oops! bad input!");
+        doThrow(expectedException)
+                .when(mRegistrationProxy)
+                .getKeyAsync(anyInt(), any(), any(), any());
+
+        IGetKeyCallback callback = mock(IGetKeyCallback.class);
+        mRegistration.getKey(0, callback);
+        verify(callback).onError(eq(expectedException.getMessage()));
+        assertThrows(IllegalArgumentException.class, () -> mRegistration.cancelGetKey(callback));
+        verifyNoMoreInteractions(callback);
+    }
+
+    @Test
+    public void storeUpgradedKeySuccess() throws Exception {
+        // TODO(b/262748535)
+    }
+
+    @Test
+    public void storeUpgradedKeyFails() throws Exception {
+        // TODO(b/262748535)
+    }
+
+    @Test
+    public void storeUpgradedCatchesExceptionFromProxy() throws Exception {
+        // TODO(b/262748535)
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index b01c1c8..ce6bd6c 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -50,6 +50,8 @@
 import android.content.ContextWrapper;
 import android.content.pm.UserInfo;
 import android.hardware.rebootescrow.IRebootEscrow;
+import android.net.ConnectivityManager;
+import android.net.Network;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.RemoteException;
@@ -72,6 +74,7 @@
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
@@ -113,6 +116,7 @@
     private RebootEscrowManager mService;
     private SecretKey mAesKey;
     private MockInjector mMockInjector;
+    private Handler mHandler;
 
     public interface MockableRebootEscrowInjected {
         int getBootCount();
@@ -132,6 +136,9 @@
         private final RebootEscrowKeyStoreManager mKeyStoreManager;
         private boolean mServerBased;
         private RebootEscrowProviderInterface mRebootEscrowProviderInUse;
+        private ConnectivityManager.NetworkCallback mNetworkCallback;
+        private Consumer<ConnectivityManager.NetworkCallback> mNetworkConsumer;
+        private boolean mWaitForInternet;
 
         MockInjector(Context context, UserManager userManager,
                 IRebootEscrow rebootEscrow,
@@ -142,6 +149,7 @@
             mRebootEscrow = rebootEscrow;
             mServiceConnection = null;
             mServerBased = false;
+            mWaitForInternet = false;
             RebootEscrowProviderHalImpl.Injector halInjector =
                     new RebootEscrowProviderHalImpl.Injector() {
                         @Override
@@ -164,6 +172,7 @@
             mServiceConnection = serviceConnection;
             mRebootEscrow = null;
             mServerBased = true;
+            mWaitForInternet = false;
             RebootEscrowProviderServerBasedImpl.Injector injector =
                     new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection) {
                         @Override
@@ -199,11 +208,33 @@
         }
 
         @Override
+        public boolean waitForInternet() {
+            return mWaitForInternet;
+        }
+
+        public void setWaitForNetwork(boolean waitForNetworkEnabled) {
+            mWaitForInternet = waitForNetworkEnabled;
+        }
+
+        @Override
         public boolean isNetworkConnected() {
             return false;
         }
 
         @Override
+        public boolean requestNetworkWithInternet(
+                ConnectivityManager.NetworkCallback networkCallback) {
+            mNetworkCallback = networkCallback;
+            mNetworkConsumer.accept(networkCallback);
+            return true;
+        }
+
+        @Override
+        public void stopRequestingNetwork(ConnectivityManager.NetworkCallback networkCallback) {
+            mNetworkCallback = null;
+        }
+
+        @Override
         public RebootEscrowProviderInterface createRebootEscrowProviderIfNeeded() {
             mRebootEscrowProviderInUse = mDefaultRebootEscrowProvider;
             return mRebootEscrowProviderInUse;
@@ -242,6 +273,12 @@
         }
 
         @Override
+        public int getLoadEscrowTimeoutMillis() {
+            // Timeout in 3 seconds.
+            return 3000;
+        }
+
+        @Override
         public String getVbmetaDigest(boolean other) {
             return other ? "" : "fake digest";
         }
@@ -291,6 +328,9 @@
         mMockInjector = new MockInjector(mContext, mUserManager, mRebootEscrow,
                 mKeyStoreManager, mStorage, mInjected);
         mService = new RebootEscrowManager(mMockInjector, mCallbacks, mStorage);
+        HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
+        thread.start();
+        mHandler = new Handler(thread.getLooper());
     }
 
     private void setServerBasedRebootEscrowProvider() throws Exception {
@@ -462,7 +502,7 @@
 
     @Test
     public void loadRebootEscrowDataIfAvailable_NothingAvailable_Success() throws Exception {
-        mService.loadRebootEscrowDataIfAvailable(null);
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
     }
 
     @Test
@@ -499,7 +539,7 @@
                 eq(20), eq(0) /* vbmeta status */, anyInt());
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> keyByteCaptor.getValue());
 
-        mService.loadRebootEscrowDataIfAvailable(null);
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
         verify(mRebootEscrow).retrieveKey();
         assertTrue(metricsSuccessCaptor.getValue());
         verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
@@ -531,9 +571,16 @@
         // pretend reboot happens here
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
-                eq(0) /* error code */, eq(2) /* Server based */, eq(1) /* attempt count */,
-                anyInt(), eq(0) /* vbmeta status */, anyInt());
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        eq(0) /* error code */,
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
 
         when(mServiceConnection.unwrap(any(), anyLong()))
                 .thenAnswer(invocation -> invocation.getArgument(0));
@@ -569,15 +616,23 @@
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
-                metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
-                eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
 
         when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class);
         mService.loadRebootEscrowDataIfAvailable(null);
         verify(mServiceConnection).unwrap(any(), anyLong());
         assertFalse(metricsSuccessCaptor.getValue());
-        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
                 metricsErrorCodeCaptor.getValue());
     }
 
@@ -606,18 +661,24 @@
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
         ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
-                metricsErrorCodeCaptor.capture(), eq(2) /* Server based */,
-                eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt());
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(2) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
         when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
 
-        HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
-        thread.start();
-        mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
         // Sleep 5s for the retry to complete
         Thread.sleep(5 * 1000);
         assertFalse(metricsSuccessCaptor.getValue());
-        assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
                 metricsErrorCodeCaptor.getValue());
     }
 
@@ -645,16 +706,22 @@
         // pretend reboot happens here
         when(mInjected.getBootCount()).thenReturn(1);
         ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
-        doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(),
-                anyInt(), anyInt(), eq(2) /* attempt count */, anyInt(), anyInt(), anyInt());
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        anyInt(),
+                        anyInt(),
+                        eq(2) /* attempt count */,
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
 
         when(mServiceConnection.unwrap(any(), anyLong()))
                 .thenThrow(new IOException())
                 .thenAnswer(invocation -> invocation.getArgument(0));
 
-        HandlerThread thread = new HandlerThread("RebootEscrowManagerTest");
-        thread.start();
-        mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper()));
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
         // Sleep 5s for the retry to complete
         Thread.sleep(5 * 1000);
         verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
@@ -663,6 +730,447 @@
     }
 
     @Test
+    public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternet_success()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        eq(0) /* error code */,
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+
+        // load escrow data
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        Network mockNetwork = mock(Network.class);
+        mMockInjector.mNetworkConsumer =
+                (callback) -> {
+                    callback.onAvailable(mockNetwork);
+                };
+
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        assertTrue(metricsSuccessCaptor.getValue());
+        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_serverBasedWaitForInternetRemoteException_Failure()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+
+        // load escrow data
+        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class);
+        Network mockNetwork = mock(Network.class);
+        mMockInjector.mNetworkConsumer =
+                (callback) -> {
+                    callback.onAvailable(mockNetwork);
+                };
+
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
+                metricsErrorCodeCaptor.getValue());
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_waitForInternet_networkUnavailable()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(0) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+
+        // Network is not available within timeout.
+        mMockInjector.mNetworkConsumer = ConnectivityManager.NetworkCallback::onUnavailable;
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
+                metricsErrorCodeCaptor.getValue());
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_waitForInternet_networkLost() throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(2) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+
+        // Network is available, then lost.
+        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException());
+        Network mockNetwork = mock(Network.class);
+        mMockInjector.mNetworkConsumer =
+                (callback) -> {
+                    callback.onAvailable(mockNetwork);
+                    callback.onLost(mockNetwork);
+                };
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        // Sleep 5s for the retry to complete
+        Thread.sleep(5 * 1000);
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_NO_NETWORK),
+                metricsErrorCodeCaptor.getValue());
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_waitForInternet_networkAvailableWithDelay()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+
+        // load escrow data
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        // network available after 1 sec
+        Network mockNetwork = mock(Network.class);
+        mMockInjector.mNetworkConsumer =
+                (callback) -> {
+                    try {
+                        Thread.sleep(1000);
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
+                    callback.onAvailable(mockNetwork);
+                };
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        assertTrue(metricsSuccessCaptor.getValue());
+        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_waitForInternet_timeoutExhausted()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(1) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+
+        // load reboot escrow data
+        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class);
+        Network mockNetwork = mock(Network.class);
+        // wait past timeout
+        mMockInjector.mNetworkConsumer =
+                (callback) -> {
+                    try {
+                        Thread.sleep(3500);
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException(e);
+                    }
+                    callback.onAvailable(mockNetwork);
+                };
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        verify(mServiceConnection).unwrap(any(), anyLong());
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_TIMEOUT_EXHAUSTED),
+                metricsErrorCodeCaptor.getValue());
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_serverBasedWaitForNetwork_retryCountExhausted()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        metricsErrorCodeCaptor.capture(),
+                        eq(2) /* Server based */,
+                        eq(2) /* attempt count */,
+                        anyInt(),
+                        eq(0) /* vbmeta status */,
+                        anyInt());
+
+        when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(new IOException());
+        Network mockNetwork = mock(Network.class);
+        mMockInjector.mNetworkConsumer =
+                (callback) -> {
+                    callback.onAvailable(mockNetwork);
+                };
+
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        // Sleep 5s for the retry to complete
+        Thread.sleep(5 * 1000);
+        verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
+        assertFalse(metricsSuccessCaptor.getValue());
+        assertEquals(
+                Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED),
+                metricsErrorCodeCaptor.getValue());
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
+    public void loadRebootEscrowDataIfAvailable_ServerBasedWaitForInternet_RetrySuccess()
+            throws Exception {
+        setServerBasedRebootEscrowProvider();
+        mMockInjector.setWaitForNetwork(true);
+
+        when(mInjected.getBootCount()).thenReturn(0);
+        RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+        mService.setRebootEscrowListener(mockListener);
+        mService.prepareRebootEscrow();
+
+        clearInvocations(mServiceConnection);
+        mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+        verify(mockListener).onPreparedForReboot(eq(true));
+        verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+        // Use x -> x for both wrap & unwrap functions.
+        when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        assertEquals(ARM_REBOOT_ERROR_NONE, mService.armRebootEscrowIfNeeded());
+        verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+        assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+        // pretend reboot happens here
+        when(mInjected.getBootCount()).thenReturn(1);
+        ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+        doNothing()
+                .when(mInjected)
+                .reportMetric(
+                        metricsSuccessCaptor.capture(),
+                        anyInt(),
+                        anyInt(),
+                        eq(2) /* attempt count */,
+                        anyInt(),
+                        anyInt(),
+                        anyInt());
+
+        when(mServiceConnection.unwrap(any(), anyLong()))
+                .thenThrow(new IOException())
+                .thenAnswer(invocation -> invocation.getArgument(0));
+        Network mockNetwork = mock(Network.class);
+        mMockInjector.mNetworkConsumer =
+                (callback) -> {
+                    callback.onAvailable(mockNetwork);
+                };
+
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
+        // Sleep 5s for the retry to complete
+        Thread.sleep(5 * 1000);
+        verify(mServiceConnection, times(2)).unwrap(any(), anyLong());
+        assertTrue(metricsSuccessCaptor.getValue());
+        verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+        assertNull(mMockInjector.mNetworkCallback);
+    }
+
+    @Test
     public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
         when(mInjected.getBootCount()).thenReturn(0);
 
@@ -687,7 +1195,7 @@
         when(mInjected.getBootCount()).thenReturn(10);
         when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
 
-        mService.loadRebootEscrowDataIfAvailable(null);
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
         verify(mRebootEscrow).retrieveKey();
         verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
                 anyInt(), anyInt(), anyInt());
@@ -715,7 +1223,7 @@
         when(mInjected.getBootCount()).thenReturn(10);
         when(mRebootEscrow.retrieveKey()).thenReturn(new byte[32]);
 
-        mService.loadRebootEscrowDataIfAvailable(null);
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
         verify(mInjected, never()).reportMetric(anyBoolean(), anyInt(), anyInt(), anyInt(),
                 anyInt(), anyInt(), anyInt());
     }
@@ -753,7 +1261,7 @@
         // Trigger a vbmeta digest mismatch
         mStorage.setString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
                 "non sense value", USER_SYSTEM);
-        mService.loadRebootEscrowDataIfAvailable(null);
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
         verify(mInjected).reportMetric(eq(true), eq(0) /* error code */, eq(1) /* HAL based */,
                 eq(1) /* attempt count */, anyInt(), eq(2) /* vbmeta status */, anyInt());
         assertEquals(mStorage.getString(RebootEscrowManager.REBOOT_ESCROW_KEY_VBMETA_DIGEST,
@@ -790,7 +1298,7 @@
                 eq(1) /* attempt count */, anyInt(), anyInt(), anyInt());
 
         when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null);
-        mService.loadRebootEscrowDataIfAvailable(null);
+        mService.loadRebootEscrowDataIfAvailable(mHandler);
         verify(mRebootEscrow).retrieveKey();
         assertFalse(metricsSuccessCaptor.getValue());
         assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY),
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
index c5131c8..57e403c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -435,63 +435,112 @@
 
     @Test
     public void testNotifyPostedLockedInLockdownMode() {
-        NotificationRecord r = mock(NotificationRecord.class);
-        NotificationRecord old = mock(NotificationRecord.class);
+        NotificationRecord r0 = mock(NotificationRecord.class);
+        NotificationRecord old0 = mock(NotificationRecord.class);
+        UserHandle uh0 = mock(UserHandle.class);
 
-        // before the lockdown mode
-        when(mNm.isInLockDownMode()).thenReturn(false);
-        mListeners.notifyPostedLocked(r, old, true);
-        mListeners.notifyPostedLocked(r, old, false);
-        verify(r, atLeast(2)).getSbn();
+        NotificationRecord r1 = mock(NotificationRecord.class);
+        NotificationRecord old1 = mock(NotificationRecord.class);
+        UserHandle uh1 = mock(UserHandle.class);
 
-        // in the lockdown mode
-        reset(r);
-        reset(old);
-        when(mNm.isInLockDownMode()).thenReturn(true);
-        mListeners.notifyPostedLocked(r, old, true);
-        mListeners.notifyPostedLocked(r, old, false);
-        verify(r, never()).getSbn();
-    }
+        // Neither user0 and user1 is in the lockdown mode
+        when(r0.getUser()).thenReturn(uh0);
+        when(uh0.getIdentifier()).thenReturn(0);
+        when(mNm.isInLockDownMode(0)).thenReturn(false);
 
-    @Test
-    public void testnotifyRankingUpdateLockedInLockdownMode() {
-        List chn = mock(List.class);
+        when(r1.getUser()).thenReturn(uh1);
+        when(uh1.getIdentifier()).thenReturn(1);
+        when(mNm.isInLockDownMode(1)).thenReturn(false);
 
-        // before the lockdown mode
-        when(mNm.isInLockDownMode()).thenReturn(false);
-        mListeners.notifyRankingUpdateLocked(chn);
-        verify(chn, atLeast(1)).size();
+        mListeners.notifyPostedLocked(r0, old0, true);
+        mListeners.notifyPostedLocked(r0, old0, false);
+        verify(r0, atLeast(2)).getSbn();
 
-        // in the lockdown mode
-        reset(chn);
-        when(mNm.isInLockDownMode()).thenReturn(true);
-        mListeners.notifyRankingUpdateLocked(chn);
-        verify(chn, never()).size();
+        mListeners.notifyPostedLocked(r1, old1, true);
+        mListeners.notifyPostedLocked(r1, old1, false);
+        verify(r1, atLeast(2)).getSbn();
+
+        // Reset
+        reset(r0);
+        reset(old0);
+        reset(r1);
+        reset(old1);
+
+        // Only user 0 is in the lockdown mode
+        when(r0.getUser()).thenReturn(uh0);
+        when(uh0.getIdentifier()).thenReturn(0);
+        when(mNm.isInLockDownMode(0)).thenReturn(true);
+
+        when(r1.getUser()).thenReturn(uh1);
+        when(uh1.getIdentifier()).thenReturn(1);
+        when(mNm.isInLockDownMode(1)).thenReturn(false);
+
+        mListeners.notifyPostedLocked(r0, old0, true);
+        mListeners.notifyPostedLocked(r0, old0, false);
+        verify(r0, never()).getSbn();
+
+        mListeners.notifyPostedLocked(r1, old1, true);
+        mListeners.notifyPostedLocked(r1, old1, false);
+        verify(r1, atLeast(2)).getSbn();
     }
 
     @Test
     public void testNotifyRemovedLockedInLockdownMode() throws NoSuchFieldException {
-        NotificationRecord r = mock(NotificationRecord.class);
-        NotificationStats rs = mock(NotificationStats.class);
+        NotificationRecord r0 = mock(NotificationRecord.class);
+        NotificationStats rs0 = mock(NotificationStats.class);
+        UserHandle uh0 = mock(UserHandle.class);
+
+        NotificationRecord r1 = mock(NotificationRecord.class);
+        NotificationStats rs1 = mock(NotificationStats.class);
+        UserHandle uh1 = mock(UserHandle.class);
+
         StatusBarNotification sbn = mock(StatusBarNotification.class);
         FieldSetter.setField(mNm,
                 NotificationManagerService.class.getDeclaredField("mHandler"),
                 mock(NotificationManagerService.WorkerHandler.class));
 
-        // before the lockdown mode
-        when(mNm.isInLockDownMode()).thenReturn(false);
-        when(r.getSbn()).thenReturn(sbn);
-        mListeners.notifyRemovedLocked(r, 0, rs);
-        mListeners.notifyRemovedLocked(r, 0, rs);
-        verify(r, atLeast(2)).getSbn();
+        // Neither user0 and user1 is in the lockdown mode
+        when(r0.getUser()).thenReturn(uh0);
+        when(uh0.getIdentifier()).thenReturn(0);
+        when(mNm.isInLockDownMode(0)).thenReturn(false);
+        when(r0.getSbn()).thenReturn(sbn);
 
-        // in the lockdown mode
-        reset(r);
-        reset(rs);
-        when(mNm.isInLockDownMode()).thenReturn(true);
-        when(r.getSbn()).thenReturn(sbn);
-        mListeners.notifyRemovedLocked(r, 0, rs);
-        mListeners.notifyRemovedLocked(r, 0, rs);
-        verify(r, never()).getSbn();
+        when(r1.getUser()).thenReturn(uh1);
+        when(uh1.getIdentifier()).thenReturn(1);
+        when(mNm.isInLockDownMode(1)).thenReturn(false);
+        when(r1.getSbn()).thenReturn(sbn);
+
+        mListeners.notifyRemovedLocked(r0, 0, rs0);
+        mListeners.notifyRemovedLocked(r0, 0, rs0);
+        verify(r0, atLeast(2)).getSbn();
+
+        mListeners.notifyRemovedLocked(r1, 0, rs1);
+        mListeners.notifyRemovedLocked(r1, 0, rs1);
+        verify(r1, atLeast(2)).getSbn();
+
+        // Reset
+        reset(r0);
+        reset(rs0);
+        reset(r1);
+        reset(rs1);
+
+        // Only user 0 is in the lockdown mode
+        when(r0.getUser()).thenReturn(uh0);
+        when(uh0.getIdentifier()).thenReturn(0);
+        when(mNm.isInLockDownMode(0)).thenReturn(true);
+        when(r0.getSbn()).thenReturn(sbn);
+
+        when(r1.getUser()).thenReturn(uh1);
+        when(uh1.getIdentifier()).thenReturn(1);
+        when(mNm.isInLockDownMode(1)).thenReturn(false);
+        when(r1.getSbn()).thenReturn(sbn);
+
+        mListeners.notifyRemovedLocked(r0, 0, rs0);
+        mListeners.notifyRemovedLocked(r0, 0, rs0);
+        verify(r0, never()).getSbn();
+
+        mListeners.notifyRemovedLocked(r1, 0, rs1);
+        mListeners.notifyRemovedLocked(r1, 0, rs1);
+        verify(r1, atLeast(2)).getSbn();
     }
 }
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 4d1c786..dd43a6b 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -174,6 +174,7 @@
 import android.service.notification.ConversationChannelWrapper;
 import android.service.notification.NotificationListenerFilter;
 import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
 import android.service.notification.ZenPolicy;
@@ -7604,43 +7605,6 @@
     }
 
     @Test
-    public void testAddAutomaticZenRule_systemCallTakesPackageFromOwner() throws Exception {
-        mService.isSystemUid = true;
-        ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
-        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
-                .thenReturn(true);
-        mService.setZenHelper(mockZenModeHelper);
-        ComponentName owner = new ComponentName("android", "ProviderName");
-        ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
-        boolean isEnabled = true;
-        AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
-                zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
-        mBinderService.addAutomaticZenRule(rule, "com.android.settings");
-
-        // verify that zen mode helper gets passed in a package name of "android"
-        verify(mockZenModeHelper).addAutomaticZenRule(eq("android"), eq(rule), anyString());
-    }
-
-    @Test
-    public void testAddAutomaticZenRule_nonSystemCallTakesPackageFromArg() throws Exception {
-        mService.isSystemUid = false;
-        ZenModeHelper mockZenModeHelper = mock(ZenModeHelper.class);
-        when(mConditionProviders.isPackageOrComponentAllowed(anyString(), anyInt()))
-                .thenReturn(true);
-        mService.setZenHelper(mockZenModeHelper);
-        ComponentName owner = new ComponentName("android", "ProviderName");
-        ZenPolicy zenPolicy = new ZenPolicy.Builder().allowAlarms(true).build();
-        boolean isEnabled = true;
-        AutomaticZenRule rule = new AutomaticZenRule("test", owner, owner, mock(Uri.class),
-                zenPolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, isEnabled);
-        mBinderService.addAutomaticZenRule(rule, "another.package");
-
-        // verify that zen mode helper gets passed in the package name from the arg, not the owner
-        verify(mockZenModeHelper).addAutomaticZenRule(
-                eq("another.package"), eq(rule), anyString());
-    }
-
-    @Test
     public void testAreNotificationsEnabledForPackage() throws Exception {
         mBinderService.areNotificationsEnabledForPackage(mContext.getPackageName(),
                 mUid);
@@ -9837,10 +9801,10 @@
         mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
         mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
-        assertTrue(mStrongAuthTracker.isInLockDownMode());
-        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+        assertTrue(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
+        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(mContext.getUserId());
         mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
-        assertFalse(mStrongAuthTracker.isInLockDownMode());
+        assertFalse(mStrongAuthTracker.isInLockDownMode(mContext.getUserId()));
     }
 
     @Test
@@ -9856,8 +9820,8 @@
         // when entering the lockdown mode, cancel the 2 notifications.
         mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
                 STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
-        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
-        assertTrue(mStrongAuthTracker.isInLockDownMode());
+        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+        assertTrue(mStrongAuthTracker.isInLockDownMode(0));
 
         // the notifyRemovedLocked function is called twice due to REASON_CANCEL_ALL.
         ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
@@ -9866,10 +9830,46 @@
 
         // exit lockdown mode.
         mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
-        mStrongAuthTracker.onStrongAuthRequiredChanged(mContext.getUserId());
+        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+        assertFalse(mStrongAuthTracker.isInLockDownMode(0));
 
         // the notifyPostedLocked function is called twice.
-        verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+        verify(mWorkerHandler, times(2)).postDelayed(any(Runnable.class), anyLong());
+        //verify(mListeners, times(2)).notifyPostedLocked(any(), any());
+    }
+
+    @Test
+    public void testMakeRankingUpdateLockedInLockDownMode() {
+        // post 2 notifications from a same package
+        NotificationRecord pkgA = new NotificationRecord(mContext,
+                generateSbn("a", 1000, 9, 0), mTestNotificationChannel);
+        mService.addNotification(pkgA);
+        NotificationRecord pkgB = new NotificationRecord(mContext,
+                generateSbn("a", 1000, 9, 1), mTestNotificationChannel);
+        mService.addNotification(pkgB);
+
+        mService.setIsVisibleToListenerReturnValue(true);
+        NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null);
+        assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
+
+        // when only user 0 entering the lockdown mode, its notification will be suppressed.
+        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(
+                STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
+        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+        assertTrue(mStrongAuthTracker.isInLockDownMode(0));
+        assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+        nru = mService.makeRankingUpdateLocked(null);
+        assertEquals(1, nru.getRankingMap().getOrderedKeys().length);
+
+        // User 0 exits lockdown mode. Its notification will be resumed.
+        mStrongAuthTracker.setGetStrongAuthForUserReturnValue(0);
+        mStrongAuthTracker.onStrongAuthRequiredChanged(0);
+        assertFalse(mStrongAuthTracker.isInLockDownMode(0));
+        assertFalse(mStrongAuthTracker.isInLockDownMode(1));
+
+        nru = mService.makeRankingUpdateLocked(null);
+        assertEquals(2, nru.getRankingMap().getOrderedKeys().length);
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index b49e5cb..8cf74fb 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -19,10 +19,12 @@
 import android.companion.ICompanionDeviceManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.service.notification.StatusBarNotification;
 
 import androidx.annotation.Nullable;
 
 import com.android.internal.logging.InstanceIdSequence;
+import com.android.server.notification.ManagedServices.ManagedServiceInfo;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -37,6 +39,9 @@
     @Nullable
     NotificationAssistantAccessGrantedCallback mNotificationAssistantAccessGrantedCallback;
 
+    @Nullable
+    Boolean mIsVisibleToListenerReturnValue = null;
+
     TestableNotificationManagerService(Context context, NotificationRecordLogger logger,
             InstanceIdSequence notificationInstanceIdSequence) {
         super(context, logger, notificationInstanceIdSequence);
@@ -119,6 +124,19 @@
         mShowReviewPermissionsNotification = setting;
     }
 
+    protected void setIsVisibleToListenerReturnValue(boolean value) {
+        mIsVisibleToListenerReturnValue = value;
+    }
+
+    @Override
+    boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
+            ManagedServiceInfo listener) {
+        if (mIsVisibleToListenerReturnValue != null) {
+            return mIsVisibleToListenerReturnValue;
+        }
+        return super.isVisibleToListener(sbn, notificationType, listener);
+    }
+
     public class StrongAuthTrackerFake extends NotificationManagerService.StrongAuthTracker {
         private int mGetStrongAuthForUserReturnValue = 0;
         StrongAuthTrackerFake(Context context) {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 2ccdcaa..4550b56 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -1672,36 +1672,6 @@
     }
 
     @Test
-    public void testAddAutomaticZenRule_claimedSystemOwner() {
-        // Make sure anything that claims to have a "system" owner but not actually part of the
-        // system package still gets limited on number of rules
-        for (int i = 0; i < RULE_LIMIT_PER_PACKAGE; i++) {
-            ScheduleInfo si = new ScheduleInfo();
-            si.startHour = i;
-            AutomaticZenRule zenRule = new AutomaticZenRule("name" + i,
-                    new ComponentName("android", "ScheduleConditionProvider" + i),
-                    null, // configuration activity
-                    ZenModeConfig.toScheduleConditionId(si),
-                    new ZenPolicy.Builder().build(),
-                    NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-            String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
-            assertNotNull(id);
-        }
-        try {
-            AutomaticZenRule zenRule = new AutomaticZenRule("name",
-                    new ComponentName("android", "ScheduleConditionProviderFinal"),
-                    null, // configuration activity
-                    ZenModeConfig.toScheduleConditionId(new ScheduleInfo()),
-                    new ZenPolicy.Builder().build(),
-                    NotificationManager.INTERRUPTION_FILTER_PRIORITY, true);
-            String id = mZenModeHelperSpy.addAutomaticZenRule("pkgname", zenRule, "test");
-            fail("allowed too many rules to be created");
-        } catch (IllegalArgumentException e) {
-            // yay
-        }
-    }
-
-    @Test
     public void testAddAutomaticZenRule_CA() {
         AutomaticZenRule zenRule = new AutomaticZenRule("name",
                 null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index adf694c..0462e1b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -30,6 +30,7 @@
 import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.os.Process.NOBODY_UID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -1220,20 +1221,34 @@
 
     @Test
     public void testCreateRecentTaskInfo_detachedTask() {
-        final Task task = createTaskBuilder(".Task").setCreateActivity(true).build();
+        final Task task = createTaskBuilder(".Task").build();
+        new ActivityBuilder(mSupervisor.mService)
+                .setTask(task)
+                .setUid(NOBODY_UID)
+                .setComponent(getUniqueComponentName())
+                .build();
         final TaskDisplayArea tda = task.getDisplayArea();
 
         assertTrue(task.isAttached());
         assertTrue(task.supportsMultiWindow());
 
-        RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true);
+        RecentTaskInfo info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+                true /* getTasksAllowed */);
 
         assertTrue(info.supportsMultiWindow);
 
+        info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+                false /* getTasksAllowed */);
+
+        assertTrue(info.topActivity == null);
+        assertTrue(info.topActivityInfo == null);
+        assertTrue(info.baseActivity == null);
+
         // The task can be put in split screen even if it is not attached now.
         task.removeImmediately();
 
-        info = mRecentTasks.createRecentTaskInfo(task, true);
+        info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+                true /* getTasksAllowed */);
 
         assertTrue(info.supportsMultiWindow);
 
@@ -1242,7 +1257,8 @@
         doReturn(false).when(tda).supportsNonResizableMultiWindow();
         doReturn(false).when(task).isResizeable();
 
-        info = mRecentTasks.createRecentTaskInfo(task, true);
+        info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+                true /* getTasksAllowed */);
 
         assertFalse(info.supportsMultiWindow);
 
@@ -1250,7 +1266,8 @@
         // the device supports it.
         doReturn(true).when(tda).supportsNonResizableMultiWindow();
 
-        info = mRecentTasks.createRecentTaskInfo(task, true);
+        info = mRecentTasks.createRecentTaskInfo(task, true /* stripExtras */,
+                true /* getTasksAllowed */);
 
         assertTrue(info.supportsMultiWindow);
     }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 13550f0..f373f6e 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -478,6 +478,14 @@
     public static final String SUBSCRIPTION_TYPE = SimInfo.COLUMN_SUBSCRIPTION_TYPE;
 
     /**
+     * TelephonyProvider column name for last used TP - message Reference
+     * <P>Type: INTEGER (int)</P> with -1 as default value
+     * TP - Message Reference valid range [0 - 255]
+     * @hide
+     */
+    public static final String TP_MESSAGE_REF = SimInfo.COLUMN_TP_MESSAGE_REF;
+
+    /**
      * TelephonyProvider column name data_enabled_override_rules.
      * It's a list of rules for overriding data enabled settings. The syntax is
      * For example, "mms=nonDefault" indicates enabling data for mms in non-default subscription.
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 3379ce5..b32f046 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -1109,6 +1109,7 @@
         sb.append(", ").append(mCarrierId);
         sb.append(", ").append(mSkip464Xlat);
         sb.append(", ").append(mAlwaysOn);
+        sb.append(", ").append(Objects.hash(mUser, mPassword));
         return sb.toString();
     }
 
@@ -1297,8 +1298,6 @@
                 other.mLingeringNetworkTypeBitmask)
                 && Objects.equals(this.mProfileId, other.mProfileId)
                 && Objects.equals(this.mPersistent, other.mPersistent)
-                && Objects.equals(this.mMvnoType, other.mMvnoType)
-                && Objects.equals(this.mMvnoMatchData, other.mMvnoMatchData)
                 && Objects.equals(this.mApnSetId, other.mApnSetId)
                 && Objects.equals(this.mCarrierId, other.mCarrierId)
                 && Objects.equals(this.mSkip464Xlat, other.mSkip464Xlat)
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index b51e8d3d..7a5bf06 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -249,7 +249,6 @@
                 ENCODING_UNKNOWN, 0, 0);
     }
 
-
     /**
      * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
      *
@@ -272,7 +271,7 @@
             boolean statusReportRequested, byte[] header, int encoding,
             int languageTable, int languageShiftTable) {
         return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
-            header, encoding, languageTable, languageShiftTable, -1);
+            header, encoding, languageTable, languageShiftTable, -1, 0);
     }
 
     /**
@@ -297,6 +296,32 @@
             String destinationAddress, String message,
             boolean statusReportRequested, byte[] header, int encoding,
             int languageTable, int languageShiftTable, int validityPeriod) {
+        return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested, header,
+                encoding, languageTable, languageShiftTable, validityPeriod, 0);
+    }
+
+    /**
+     * Gets an SMS-SUBMIT PDU for a destination address and a message using the specified encoding.
+     *
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param message string representation of the message payload.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @param header a byte array containing the data for the User Data Header.
+     * @param encoding encoding defined by constants in
+     *                 com.android.internal.telephony.SmsConstants.ENCODING_*
+     * @param languageTable
+     * @param languageShiftTable
+     * @param validityPeriod Validity Period of the message in Minutes.
+     * @param messageRef TP Message Reference number
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     * @hide
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, String message,
+            boolean statusReportRequested, byte[] header, int encoding,
+            int languageTable, int languageShiftTable, int validityPeriod, int messageRef) {
 
         // Perform null parameter checks.
         if (message == null || destinationAddress == null) {
@@ -350,7 +375,7 @@
 
         ByteArrayOutputStream bo = getSubmitPduHead(
                 scAddress, destinationAddress, mtiByte,
-                statusReportRequested, ret);
+                statusReportRequested, ret, messageRef);
 
         // Skip encoding pdu if error occurs when create pdu head and the error will be handled
         // properly later on encodedMessage correctness check.
@@ -496,7 +521,7 @@
             String destinationAddress, String message,
             boolean statusReportRequested, int validityPeriod) {
         return getSubmitPdu(scAddress, destinationAddress, message, statusReportRequested,
-                null, ENCODING_UNKNOWN, 0, 0, validityPeriod);
+                null, ENCODING_UNKNOWN, 0, 0, validityPeriod, 0);
     }
 
     /**
@@ -507,12 +532,13 @@
      * @param destinationPort the port to deliver the message to at the destination.
      * @param data the data for the message.
      * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @param messageRef TP Message Reference number
      * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
      *         encoded message. Returns null on encode error.
      */
     public static SubmitPdu getSubmitPdu(String scAddress,
             String destinationAddress, int destinationPort, byte[] data,
-            boolean statusReportRequested) {
+            boolean statusReportRequested, int messageRef) {
 
         SmsHeader.PortAddrs portAddrs = new SmsHeader.PortAddrs();
         portAddrs.destPort = destinationPort;
@@ -533,7 +559,7 @@
         SubmitPdu ret = new SubmitPdu();
         ByteArrayOutputStream bo = getSubmitPduHead(
                 scAddress, destinationAddress, (byte) 0x41, /* TP-MTI=SMS-SUBMIT, TP-UDHI=true */
-                statusReportRequested, ret);
+                statusReportRequested, ret, messageRef);
         // Skip encoding pdu if error occurs when create pdu head and the error will be handled
         // properly later on encodedMessage correctness check.
         if (bo == null) return ret;
@@ -559,6 +585,24 @@
     }
 
     /**
+     * Gets an SMS-SUBMIT PDU for a data message to a destination address &amp; port.
+     *
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param destinationPort the port to deliver the message to at the destination.
+     * @param data the data for the message.
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @return a <code>SubmitPdu</code> containing the encoded SC address if applicable and the
+     *         encoded message. Returns null on encode error.
+     */
+    public static SubmitPdu getSubmitPdu(String scAddress,
+            String destinationAddress, int destinationPort, byte[] data,
+            boolean statusReportRequested) {
+        return getSubmitPdu(scAddress, destinationAddress, destinationPort, data,
+                statusReportRequested, 0);
+    }
+
+    /**
      * Creates the beginning of a SUBMIT PDU.
      *
      * This is the part of the SUBMIT PDU that is common to the two versions of
@@ -576,6 +620,28 @@
     private static ByteArrayOutputStream getSubmitPduHead(
             String scAddress, String destinationAddress, byte mtiByte,
             boolean statusReportRequested, SubmitPdu ret) {
+        return getSubmitPduHead(scAddress, destinationAddress, mtiByte, statusReportRequested, ret,
+                0);
+    }
+
+    /**
+     * Creates the beginning of a SUBMIT PDU.
+     *
+     * This is the part of the SUBMIT PDU that is common to the two versions of
+     * {@link #getSubmitPdu}, one of which takes a byte array and the other of which takes a
+     * <code>String</code>.
+     *
+     * @param scAddress Service Centre address. Null means use default.
+     * @param destinationAddress the address of the destination for the message.
+     * @param mtiByte
+     * @param statusReportRequested indicates whether a report is reuested for this message.
+     * @param ret <code>SubmitPdu</code>.
+     * @param messageRef TP Message Reference number
+     * @return a byte array of the beginning of a SUBMIT PDU. Null for invalid destinationAddress.
+     */
+    private static ByteArrayOutputStream getSubmitPduHead(
+            String scAddress, String destinationAddress, byte mtiByte,
+            boolean statusReportRequested, SubmitPdu ret, int messageRef) {
         ByteArrayOutputStream bo = new ByteArrayOutputStream(
                 MAX_USER_DATA_BYTES + 40);
 
@@ -596,7 +662,7 @@
         bo.write(mtiByte);
 
         // space for TP-Message-Reference
-        bo.write(0);
+        bo.write(messageRef);
 
         byte[] daBytes;