Merge "[rvc-dev] explicitly set android:exported to false" into rvc-dev
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index cc06a71..8ba52ef 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -440,6 +440,7 @@
             273 [(module) = "permissioncontroller"];
         EvsUsageStatsReported evs_usage_stats_reported = 274 [(module) = "evs"];
         AudioPowerUsageDataReported audio_power_usage_data_reported = 275;
+        TvTunerStateChanged tv_tuner_state_changed = 276 [(module) = "framework"];
         SdkExtensionStatus sdk_extension_status = 354;
 
         // StatsdStats tracks platform atoms with ids upto 500.
@@ -447,7 +448,7 @@
     }
 
     // Pulled events will start at field 10000.
-    // Next: 10083
+    // Next: 10084
     oneof pulled {
         WifiBytesTransfer wifi_bytes_transfer = 10000 [(module) = "framework"];
         WifiBytesTransferByFgBg wifi_bytes_transfer_by_fg_bg = 10001 [(module) = "framework"];
@@ -540,6 +541,8 @@
         SettingSnapshot setting_snapshot = 10080 [(module) = "framework"];
         DisplayWakeReason display_wake_reason = 10081 [(module) = "framework"];
         DataUsageBytesTransfer data_usage_bytes_transfer = 10082 [(module) = "framework"];
+        BytesTransferByTagAndMetered bytes_transfer_by_tag_and_metered =
+                10083 [(module) = "framework"];
     }
 
     // DO NOT USE field numbers above 100,000 in AOSP.
@@ -6029,6 +6032,12 @@
     optional int32 user_locked_fields = 6;
     // Indicates if the channel was deleted by the app.
     optional bool is_deleted = 7;
+    // Indicates if the channel was marked as a conversation by the app.
+    optional bool is_conversation = 8;
+    // Indicates if the channel is a conversation that was demoted by the user.
+    optional bool is_demoted_conversation = 9;
+    // Indicates if the channel is a conversation that was marked as important by the user.
+    optional bool is_important_conversation = 10;
 }
 
 /**
@@ -9168,6 +9177,28 @@
 }
 
 /**
+ * Logs when a tune occurs through device's Frontend.
+ * This is atom ID 276.
+ *
+ * Logged from:
+ *   frameworks/base/media/java/android/media/tv/tuner/Tuner.java
+ */
+message TvTunerStateChanged {
+    enum State {
+        UNKNOWN = 0;
+        TUNING = 1; // Signal is tuned
+        LOCKED = 2;    // the signal is locked
+        NOT_LOCKED = 3; // the signal isn’t locked.
+        SIGNAL_LOST = 4; // the signal was locked, but is lost now.
+        SCANNING = 5; // the signal is scanned
+        SCAN_STOPPED = 6; // the scan is stopped.
+    }
+    // The uid of the application that sent this custom atom.
+    optional int32 uid = 1 [(is_uid) = true];
+    //  new state
+    optional State state = 2;
+}
+/**
  * Logs when an app is frozen or unfrozen.
  *
  * Logged from:
@@ -9803,3 +9834,25 @@
     }
     optional AudioType type = 4;
 }
+
+/**
+  * Pulls bytes transferred over WiFi and mobile networks sliced by uid, is_metered, and tag.
+  *
+  * Pulled from:
+  *   StatsPullAtomService, which uses NetworkStatsService to query NetworkStats.
+  */
+message BytesTransferByTagAndMetered {
+    optional int32 uid = 1 [(is_uid) = true];
+
+    optional bool is_metered = 2;
+
+    optional int32 tag = 3;
+
+    optional int64 rx_bytes = 4;
+
+    optional int64 rx_packets = 5;
+
+    optional int64 tx_bytes = 6;
+
+    optional int64 tx_packets = 7;
+}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 3ce7689..be1681b 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -229,7 +229,16 @@
     void unregisterTaskStackListener(in ITaskStackListener listener);
     void setTaskResizeable(int taskId, int resizeableMode);
     void toggleFreeformWindowingMode(in IBinder token);
-    void resizeTask(int taskId, in Rect bounds, int resizeMode);
+
+    /**
+     * Resize the task with given bounds
+     *
+     * @param taskId The id of the task to set the bounds for.
+     * @param bounds The new bounds.
+     * @param resizeMode Resize mode defined as {@code ActivityTaskManager#RESIZE_MODE_*} constants.
+     * @return Return true on success. Otherwise false.
+     */
+    boolean resizeTask(int taskId, in Rect bounds, int resizeMode);
     void moveStackToDisplay(int stackId, int displayId);
     void removeStack(int stackId);
 
diff --git a/core/java/android/util/AtomicFile.java b/core/java/android/util/AtomicFile.java
index cf7ed9b..da7503d 100644
--- a/core/java/android/util/AtomicFile.java
+++ b/core/java/android/util/AtomicFile.java
@@ -29,31 +29,32 @@
 import java.util.function.Consumer;
 
 /**
- * Helper class for performing atomic operations on a file by creating a
- * backup file until a write has successfully completed.  If you need this
- * on older versions of the platform you can use
- * {@link android.support.v4.util.AtomicFile} in the v4 support library.
+ * Helper class for performing atomic operations on a file by writing to a new file and renaming it
+ * into the place of the original file after the write has successfully completed. If you need this
+ * on older versions of the platform you can use {@link androidx.core.util.AtomicFile} in AndroidX.
  * <p>
- * Atomic file guarantees file integrity by ensuring that a file has
- * been completely written and sync'd to disk before removing its backup.
- * As long as the backup file exists, the original file is considered
- * to be invalid (left over from a previous attempt to write the file).
- * </p><p>
- * Atomic file does not confer any file locking semantics.
- * Do not use this class when the file may be accessed or modified concurrently
- * by multiple threads or processes.  The caller is responsible for ensuring
- * appropriate mutual exclusion invariants whenever it accesses the file.
- * </p>
+ * Atomic file guarantees file integrity by ensuring that a file has been completely written and
+ * sync'd to disk before renaming it to the original file. Previously this is done by renaming the
+ * original file to a backup file beforehand, but this approach couldn't handle the case where the
+ * file is created for the first time. This class will also handle the backup file created by the
+ * old implementation properly.
+ * <p>
+ * Atomic file does not confer any file locking semantics. Do not use this class when the file may
+ * be accessed or modified concurrently by multiple threads or processes. The caller is responsible
+ * for ensuring appropriate mutual exclusion invariants whenever it accesses the file.
  */
 public class AtomicFile {
+    private static final String LOG_TAG = "AtomicFile";
+
     private final File mBaseName;
-    private final File mBackupName;
+    private final File mNewName;
+    private final File mLegacyBackupName;
     private final String mCommitTag;
     private long mStartTime;
 
     /**
      * Create a new AtomicFile for a file located at the given File path.
-     * The secondary backup file will be the same file path with ".bak" appended.
+     * The new file created when writing will be the same file path with ".new" appended.
      */
     public AtomicFile(File baseName) {
         this(baseName, null);
@@ -65,7 +66,8 @@
      */
     public AtomicFile(File baseName, String commitTag) {
         mBaseName = baseName;
-        mBackupName = new File(baseName.getPath() + ".bak");
+        mNewName = new File(baseName.getPath() + ".new");
+        mLegacyBackupName = new File(baseName.getPath() + ".bak");
         mCommitTag = commitTag;
     }
 
@@ -78,11 +80,12 @@
     }
 
     /**
-     * Delete the atomic file.  This deletes both the base and backup files.
+     * Delete the atomic file.  This deletes both the base and new files.
      */
     public void delete() {
         mBaseName.delete();
-        mBackupName.delete();
+        mNewName.delete();
+        mLegacyBackupName.delete();
     }
 
     /**
@@ -112,36 +115,28 @@
     public FileOutputStream startWrite(long startTime) throws IOException {
         mStartTime = startTime;
 
-        // Rename the current file so it may be used as a backup during the next read
-        if (mBaseName.exists()) {
-            if (!mBackupName.exists()) {
-                if (!mBaseName.renameTo(mBackupName)) {
-                    Log.w("AtomicFile", "Couldn't rename file " + mBaseName
-                            + " to backup file " + mBackupName);
-                }
-            } else {
-                mBaseName.delete();
+        if (mLegacyBackupName.exists()) {
+            if (!mLegacyBackupName.renameTo(mBaseName)) {
+                Log.e(LOG_TAG, "Failed to rename legacy backup file " + mLegacyBackupName
+                        + " to base file " + mBaseName);
             }
         }
-        FileOutputStream str = null;
+
         try {
-            str = new FileOutputStream(mBaseName);
+            return new FileOutputStream(mNewName);
         } catch (FileNotFoundException e) {
-            File parent = mBaseName.getParentFile();
+            File parent = mNewName.getParentFile();
             if (!parent.mkdirs()) {
-                throw new IOException("Couldn't create directory " + mBaseName);
+                throw new IOException("Failed to create directory for " + mNewName);
             }
-            FileUtils.setPermissions(
-                parent.getPath(),
-                FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-                -1, -1);
+            FileUtils.setPermissions(parent.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+                    | FileUtils.S_IXOTH, -1, -1);
             try {
-                str = new FileOutputStream(mBaseName);
+                return new FileOutputStream(mNewName);
             } catch (FileNotFoundException e2) {
-                throw new IOException("Couldn't create " + mBaseName);
+                throw new IOException("Failed to create new file " + mNewName, e2);
             }
         }
-        return str;
     }
 
     /**
@@ -151,36 +146,45 @@
      * will return the new file stream.
      */
     public void finishWrite(FileOutputStream str) {
-        if (str != null) {
-            FileUtils.sync(str);
-            try {
-                str.close();
-                mBackupName.delete();
-            } catch (IOException e) {
-                Log.w("AtomicFile", "finishWrite: Got exception:", e);
-            }
-            if (mCommitTag != null) {
-                com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
-                        mCommitTag, SystemClock.uptimeMillis() - mStartTime);
-            }
+        if (str == null) {
+            return;
+        }
+        if (!FileUtils.sync(str)) {
+            Log.e(LOG_TAG, "Failed to sync file output stream");
+        }
+        try {
+            str.close();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Failed to close file output stream", e);
+        }
+        if (!mNewName.renameTo(mBaseName)) {
+            Log.e(LOG_TAG, "Failed to rename new file " + mNewName + " to base file " + mBaseName);
+        }
+        if (mCommitTag != null) {
+            com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
+                    mCommitTag, SystemClock.uptimeMillis() - mStartTime);
         }
     }
 
     /**
      * Call when you have failed for some reason at writing to the stream
      * returned by {@link #startWrite()}.  This will close the current
-     * write stream, and roll back to the previous state of the file.
+     * write stream, and delete the new file.
      */
     public void failWrite(FileOutputStream str) {
-        if (str != null) {
-            FileUtils.sync(str);
-            try {
-                str.close();
-                mBaseName.delete();
-                mBackupName.renameTo(mBaseName);
-            } catch (IOException e) {
-                Log.w("AtomicFile", "failWrite: Got exception:", e);
-            }
+        if (str == null) {
+            return;
+        }
+        if (!FileUtils.sync(str)) {
+            Log.e(LOG_TAG, "Failed to sync file output stream");
+        }
+        try {
+            str.close();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Failed to close file output stream", e);
+        }
+        if (!mNewName.delete()) {
+            Log.e(LOG_TAG, "Failed to delete new file " + mNewName);
         }
     }
 
@@ -210,32 +214,34 @@
     }
 
     /**
-     * Open the atomic file for reading.  If there previously was an
-     * incomplete write, this will roll back to the last good data before
-     * opening for read.  You should call close() on the FileInputStream when
-     * you are done reading from it.
-     *
-     * <p>Note that if another thread is currently performing
-     * a write, this will incorrectly consider it to be in the state of a bad
-     * write and roll back, causing the new data currently being written to
-     * be dropped.  You must do your own threading protection for access to
-     * AtomicFile.
+     * Open the atomic file for reading. You should call close() on the FileInputStream when you are
+     * done reading from it.
+     * <p>
+     * You must do your own threading protection for access to AtomicFile.
      */
     public FileInputStream openRead() throws FileNotFoundException {
-        if (mBackupName.exists()) {
-            mBaseName.delete();
-            mBackupName.renameTo(mBaseName);
+        if (mLegacyBackupName.exists()) {
+            if (!mLegacyBackupName.renameTo(mBaseName)) {
+                Log.e(LOG_TAG, "Failed to rename legacy backup file " + mLegacyBackupName
+                        + " to base file " + mBaseName);
+            }
+        }
+
+        if (mNewName.exists()) {
+            if (!mNewName.delete()) {
+                Log.e(LOG_TAG, "Failed to delete outdated new file " + mNewName);
+            }
         }
         return new FileInputStream(mBaseName);
     }
 
     /**
      * @hide
-     * Checks if the original or backup file exists.
-     * @return whether the original or backup file exists.
+     * Checks if the original or legacy backup file exists.
+     * @return whether the original or legacy backup file exists.
      */
     public boolean exists() {
-        return mBaseName.exists() || mBackupName.exists();
+        return mBaseName.exists() || mLegacyBackupName.exists();
     }
 
     /**
@@ -246,8 +252,8 @@
      *     the file does not exist or an I/O error is encountered.
      */
     public long getLastModifiedTime() {
-        if (mBackupName.exists()) {
-            return mBackupName.lastModified();
+        if (mLegacyBackupName.exists()) {
+            return mLegacyBackupName.lastModified();
         }
         return mBaseName.lastModified();
     }
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3730f05..fe7b0ae 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3329,6 +3329,17 @@
     <!-- Controls the size of the back gesture inset. -->
     <dimen name="config_backGestureInset">0dp</dimen>
 
+    <!-- Array of values used in Gesture Navigation settings page to reduce/increase the back
+     gesture's inset size. These values will be multiplied into the default width, read from the
+     gesture navigation overlay package, in order to create 4 different sizes which are selectable
+     via a slider component. -->
+    <array name="config_backGestureInsetScales">
+        <item>0.75</item>
+        <item>1.00</item>
+        <item>1.33</item>
+        <item>1.66</item>
+    </array>
+
     <!-- Controls whether the navbar needs a scrim with
          {@link Window#setEnsuringNavigationBarContrastWhenTransparent}. -->
     <bool name="config_navBarNeedsScrim">true</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a9e0b58..7744e9e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2818,6 +2818,7 @@
   <java-symbol type="bool" name="config_navBarNeedsScrim" />
   <java-symbol type="bool" name="config_allowSeamlessRotationDespiteNavBarMoving" />
   <java-symbol type="dimen" name="config_backGestureInset" />
+  <java-symbol type="array" name="config_backGestureInsetScales" />
   <java-symbol type="color" name="system_bar_background_semi_transparent" />
   <java-symbol type="bool" name="config_showGesturalNavigationHints" />
 
diff --git a/core/tests/utiltests/src/android/util/AtomicFileTest.java b/core/tests/utiltests/src/android/util/AtomicFileTest.java
new file mode 100644
index 0000000..547d4d8
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/AtomicFileTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import android.app.Instrumentation;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+
+@RunWith(Parameterized.class)
+public class AtomicFileTest {
+    private static final String BASE_NAME = "base";
+    private static final String NEW_NAME = BASE_NAME + ".new";
+    private static final String LEGACY_BACKUP_NAME = BASE_NAME + ".bak";
+
+    private enum WriteAction {
+        FINISH,
+        FAIL,
+        ABORT
+    }
+
+    private static final byte[] BASE_BYTES = "base".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] EXISTING_NEW_BYTES = "unnew".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] NEW_BYTES = "new".getBytes(StandardCharsets.UTF_8);
+    private static final byte[] LEGACY_BACKUP_BYTES = "bak".getBytes(StandardCharsets.UTF_8);
+
+    // JUnit wants every parameter to be used so make it happy.
+    @Parameterized.Parameter()
+    public String mUnusedTestName;
+    @Nullable
+    @Parameterized.Parameter(1)
+    public String[] mExistingFileNames;
+    @Nullable
+    @Parameterized.Parameter(2)
+    public WriteAction mWriteAction;
+    @Nullable
+    @Parameterized.Parameter(3)
+    public byte[] mExpectedBytes;
+
+    private final Instrumentation mInstrumentation =
+            InstrumentationRegistry.getInstrumentation();
+    private final Context mContext = mInstrumentation.getContext();
+
+    private final File mDirectory = mContext.getFilesDir();
+    private final File mBaseFile = new File(mDirectory, BASE_NAME);
+    private final File mNewFile = new File(mDirectory, NEW_NAME);
+    private final File mLegacyBackupFile = new File(mDirectory, LEGACY_BACKUP_NAME);
+
+    @Parameterized.Parameters(name = "{0}")
+    public static Object[][] data() {
+        return new Object[][] {
+                { "none + none = none", null, null, null },
+                { "none + finish = new", null, WriteAction.FINISH, NEW_BYTES },
+                { "none + fail = none", null, WriteAction.FAIL, null },
+                { "none + abort = none", null, WriteAction.ABORT, null },
+                { "base + none = base", new String[] { BASE_NAME }, null, BASE_BYTES },
+                { "base + finish = new", new String[] { BASE_NAME }, WriteAction.FINISH,
+                        NEW_BYTES },
+                { "base + fail = base", new String[] { BASE_NAME }, WriteAction.FAIL, BASE_BYTES },
+                { "base + abort = base", new String[] { BASE_NAME }, WriteAction.ABORT,
+                        BASE_BYTES },
+                { "new + none = none", new String[] { NEW_NAME }, null, null },
+                { "new + finish = new", new String[] { NEW_NAME }, WriteAction.FINISH, NEW_BYTES },
+                { "new + fail = none", new String[] { NEW_NAME }, WriteAction.FAIL, null },
+                { "new + abort = none", new String[] { NEW_NAME }, WriteAction.ABORT, null },
+                { "bak + none = bak", new String[] { LEGACY_BACKUP_NAME }, null,
+                        LEGACY_BACKUP_BYTES },
+                { "bak + finish = new", new String[] { LEGACY_BACKUP_NAME }, WriteAction.FINISH,
+                        NEW_BYTES },
+                { "bak + fail = bak", new String[] { LEGACY_BACKUP_NAME }, WriteAction.FAIL,
+                        LEGACY_BACKUP_BYTES },
+                { "bak + abort = bak", new String[] { LEGACY_BACKUP_NAME }, WriteAction.ABORT,
+                        LEGACY_BACKUP_BYTES },
+                { "base & new + none = base", new String[] { BASE_NAME, NEW_NAME }, null,
+                        BASE_BYTES },
+                { "base & new + finish = new", new String[] { BASE_NAME, NEW_NAME },
+                        WriteAction.FINISH, NEW_BYTES },
+                { "base & new + fail = base", new String[] { BASE_NAME, NEW_NAME },
+                        WriteAction.FAIL, BASE_BYTES },
+                { "base & new + abort = base", new String[] { BASE_NAME, NEW_NAME },
+                        WriteAction.ABORT, BASE_BYTES },
+                { "base & bak + none = bak", new String[] { BASE_NAME, LEGACY_BACKUP_NAME }, null,
+                        LEGACY_BACKUP_BYTES },
+                { "base & bak + finish = new", new String[] { BASE_NAME, LEGACY_BACKUP_NAME },
+                        WriteAction.FINISH, NEW_BYTES },
+                { "base & bak + fail = bak", new String[] { BASE_NAME, LEGACY_BACKUP_NAME },
+                        WriteAction.FAIL, LEGACY_BACKUP_BYTES },
+                { "base & bak + abort = bak", new String[] { BASE_NAME, LEGACY_BACKUP_NAME },
+                        WriteAction.ABORT, LEGACY_BACKUP_BYTES },
+                { "new & bak + none = bak", new String[] { NEW_NAME, LEGACY_BACKUP_NAME }, null,
+                        LEGACY_BACKUP_BYTES },
+                { "new & bak + finish = new", new String[] { NEW_NAME, LEGACY_BACKUP_NAME },
+                        WriteAction.FINISH, NEW_BYTES },
+                { "new & bak + fail = bak", new String[] { NEW_NAME, LEGACY_BACKUP_NAME },
+                        WriteAction.FAIL, LEGACY_BACKUP_BYTES },
+                { "new & bak + abort = bak", new String[] { NEW_NAME, LEGACY_BACKUP_NAME },
+                        WriteAction.ABORT, LEGACY_BACKUP_BYTES },
+                { "base & new & bak + none = bak",
+                        new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME }, null,
+                        LEGACY_BACKUP_BYTES },
+                { "base & new & bak + finish = new",
+                        new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME },
+                        WriteAction.FINISH, NEW_BYTES },
+                { "base & new & bak + fail = bak",
+                        new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME }, WriteAction.FAIL,
+                        LEGACY_BACKUP_BYTES },
+                { "base & new & bak + abort = bak",
+                        new String[] { BASE_NAME, NEW_NAME, LEGACY_BACKUP_NAME }, WriteAction.ABORT,
+                        LEGACY_BACKUP_BYTES },
+        };
+    }
+
+    @Before
+    @After
+    public void deleteFiles() {
+        mBaseFile.delete();
+        mNewFile.delete();
+        mLegacyBackupFile.delete();
+    }
+
+    @Test
+    public void testAtomicFile() throws Exception {
+        if (mExistingFileNames != null) {
+            for (String fileName : mExistingFileNames) {
+                switch (fileName) {
+                    case BASE_NAME:
+                        writeBytes(mBaseFile, BASE_BYTES);
+                        break;
+                    case NEW_NAME:
+                        writeBytes(mNewFile, EXISTING_NEW_BYTES);
+                        break;
+                    case LEGACY_BACKUP_NAME:
+                        writeBytes(mLegacyBackupFile, LEGACY_BACKUP_BYTES);
+                        break;
+                    default:
+                        throw new AssertionError(fileName);
+                }
+            }
+        }
+
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        if (mWriteAction != null) {
+            try (FileOutputStream outputStream = atomicFile.startWrite()) {
+                outputStream.write(NEW_BYTES);
+                switch (mWriteAction) {
+                    case FINISH:
+                        atomicFile.finishWrite(outputStream);
+                        break;
+                    case FAIL:
+                        atomicFile.failWrite(outputStream);
+                        break;
+                    case ABORT:
+                        // Neither finishing nor failing is called upon abort.
+                        break;
+                    default:
+                        throw new AssertionError(mWriteAction);
+                }
+            }
+        }
+
+        if (mExpectedBytes != null) {
+            try (FileInputStream inputStream = atomicFile.openRead()) {
+                assertArrayEquals(mExpectedBytes, readAllBytes(inputStream));
+            }
+        } else {
+            assertThrows(FileNotFoundException.class, () -> atomicFile.openRead());
+        }
+    }
+
+    private static void writeBytes(@NonNull File file, @NonNull byte[] bytes) throws IOException {
+        try (FileOutputStream outputStream = new FileOutputStream(file)) {
+            outputStream.write(bytes);
+        }
+    }
+
+    // InputStream.readAllBytes() is introduced in Java 9. Our files are small enough so that a
+    // naive implementation is okay.
+    private static byte[] readAllBytes(@NonNull InputStream inputStream) throws IOException {
+        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
+            int b;
+            while ((b = inputStream.read()) != -1) {
+                outputStream.write(b);
+            }
+            return outputStream.toByteArray();
+        }
+    }
+
+    @NonNull
+    public static <T extends Throwable> T assertThrows(@NonNull Class<T> expectedType,
+            @NonNull ThrowingRunnable runnable) {
+        try {
+            runnable.run();
+        } catch (Throwable t) {
+            if (!expectedType.isInstance(t)) {
+                sneakyThrow(t);
+            }
+            //noinspection unchecked
+            return (T) t;
+        }
+        throw new AssertionError(String.format("Expected %s wasn't thrown",
+                expectedType.getSimpleName()));
+    }
+
+    private static <T extends Throwable> void sneakyThrow(@NonNull Throwable throwable) throws T {
+        //noinspection unchecked
+        throw (T) throwable;
+    }
+
+    private interface ThrowingRunnable {
+        void run() throws Throwable;
+    }
+}
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index bbd7399..54675d0 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -321,6 +321,21 @@
     @UnsupportedAppUsage
     private long mNativeObject;
 
+    private String convertMuxerStateCodeToString(int aState) {
+        switch (aState) {
+            case MUXER_STATE_UNINITIALIZED:
+                return "UNINITIALIZED";
+            case MUXER_STATE_INITIALIZED:
+                return "INITIALIZED";
+            case MUXER_STATE_STARTED:
+                return "STARTED";
+            case MUXER_STATE_STOPPED:
+                return "STOPPED";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
     /**
      * Constructor.
      * Creates a media muxer that writes to the specified path.
@@ -397,7 +412,7 @@
             nativeSetOrientationHint(mNativeObject, degrees);
         } else {
             throw new IllegalStateException("Can't set rotation degrees due" +
-                    " to wrong state.");
+                    " to wrong state(" + convertMuxerStateCodeToString(mState) + ")");
         }
     }
 
@@ -432,7 +447,8 @@
         if (mState == MUXER_STATE_INITIALIZED && mNativeObject != 0) {
             nativeSetLocation(mNativeObject, latitudex10000, longitudex10000);
         } else {
-            throw new IllegalStateException("Can't set location due to wrong state.");
+            throw new IllegalStateException("Can't set location due to wrong state("
+                                             + convertMuxerStateCodeToString(mState) + ")");
         }
     }
 
@@ -451,7 +467,8 @@
             nativeStart(mNativeObject);
             mState = MUXER_STATE_STARTED;
         } else {
-            throw new IllegalStateException("Can't start due to wrong state.");
+            throw new IllegalStateException("Can't start due to wrong state("
+                                             + convertMuxerStateCodeToString(mState) + ")");
         }
     }
 
@@ -462,10 +479,16 @@
      */
     public void stop() {
         if (mState == MUXER_STATE_STARTED) {
-            nativeStop(mNativeObject);
-            mState = MUXER_STATE_STOPPED;
+            try {
+                nativeStop(mNativeObject);
+            } catch (Exception e) {
+                throw e;
+            } finally {
+                mState = MUXER_STATE_STOPPED;
+            }
         } else {
-            throw new IllegalStateException("Can't stop due to wrong state.");
+            throw new IllegalStateException("Can't stop due to wrong state("
+                                             + convertMuxerStateCodeToString(mState) + ")");
         }
     }
 
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 50af60a..a458b16 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -23,6 +23,7 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.app.ActivityManager;
 import android.content.Context;
 import android.hardware.tv.tuner.V1_0.Constants;
 import android.media.tv.TvInputService;
@@ -55,6 +56,8 @@
 import android.os.Message;
 import android.util.Log;
 
+import com.android.internal.util.FrameworkStatsLog;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
@@ -208,7 +211,7 @@
     private FrontendInfo mFrontendInfo;
     private Integer mFrontendHandle;
     private int mFrontendType = FrontendSettings.TYPE_UNDEFINED;
-
+    private int mUserId;
     private Lnb mLnb;
     private Integer mLnbHandle;
     @Nullable
@@ -232,6 +235,11 @@
             new TunerResourceManager.ResourcesReclaimListener() {
                 @Override
                 public void onReclaimResources() {
+                    if (mFrontend != null) {
+                        FrameworkStatsLog
+                                .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                                    FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
+                    }
                     mHandler.sendMessage(mHandler.obtainMessage(MSG_RESOURCE_LOST));
                 }
             };
@@ -261,6 +269,8 @@
                 profile, new HandlerExecutor(mHandler), mResourceListener, clientId);
         mClientId = clientId[0];
 
+        mUserId = ActivityManager.getCurrentUser();
+
         setFrontendInfoList();
         setLnbIds();
     }
@@ -358,6 +368,9 @@
                 TunerUtils.throwExceptionForResult(res, "failed to close frontend");
             }
             mTunerResourceManager.releaseFrontend(mFrontendHandle, mClientId);
+            FrameworkStatsLog
+                    .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                        FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__UNKNOWN);
             mFrontendHandle = null;
             mFrontend = null;
         }
@@ -557,9 +570,14 @@
      */
     @Result
     public int tune(@NonNull FrontendSettings settings) {
+        Log.d(TAG, "Tune to " + settings.getFrequency());
         mFrontendType = settings.getType();
         if (checkResource(TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
             mFrontendInfo = null;
+            Log.d(TAG, "Write Stats Log for tuning.");
+            FrameworkStatsLog
+                    .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                        FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__TUNING);
             return nativeTune(settings.getType(), settings);
         }
         return RESULT_UNAVAILABLE;
@@ -602,6 +620,9 @@
             mScanCallback = scanCallback;
             mScanCallbackExecutor = executor;
             mFrontendInfo = null;
+            FrameworkStatsLog
+                    .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                        FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCANNING);
             return nativeScan(settings.getType(), settings, scanType);
         }
         return RESULT_UNAVAILABLE;
@@ -620,6 +641,10 @@
      */
     @Result
     public int cancelScanning() {
+        FrameworkStatsLog
+                .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                    FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SCAN_STOPPED);
+
         int retVal = nativeStopScan();
         mScanCallback = null;
         mScanCallbackExecutor = null;
@@ -779,12 +804,33 @@
     }
 
     private void onFrontendEvent(int eventType) {
+        Log.d(TAG, "Got event from tuning. Event type: " + eventType);
         if (mOnTunerEventExecutor != null && mOnTuneEventListener != null) {
             mOnTunerEventExecutor.execute(() -> mOnTuneEventListener.onTuneEvent(eventType));
         }
+
+        Log.d(TAG, "Wrote Stats Log for the events from tuning.");
+        if (eventType == OnTuneEventListener.SIGNAL_LOCKED) {
+            FrameworkStatsLog
+                    .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                        FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED);
+        } else if (eventType == OnTuneEventListener.SIGNAL_NO_SIGNAL) {
+            FrameworkStatsLog
+                    .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                        FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__NOT_LOCKED);
+        } else if (eventType == OnTuneEventListener.SIGNAL_LOST_LOCK) {
+            FrameworkStatsLog
+                    .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                        FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__SIGNAL_LOST);
+        }
     }
 
     private void onLocked() {
+        Log.d(TAG, "Wrote Stats Log for locked event from scanning.");
+        FrameworkStatsLog
+                .write(FrameworkStatsLog.TV_TUNER_STATE_CHANGED, mUserId,
+                    FrameworkStatsLog.TV_TUNER_STATE_CHANGED__STATE__LOCKED);
+
         if (mScanCallbackExecutor != null && mScanCallback != null) {
             mScanCallbackExecutor.execute(() -> mScanCallback.onLocked());
         }
diff --git a/media/jni/android_media_MediaMuxer.cpp b/media/jni/android_media_MediaMuxer.cpp
index 0c1e9a2..262ec76 100644
--- a/media/jni/android_media_MediaMuxer.cpp
+++ b/media/jni/android_media_MediaMuxer.cpp
@@ -26,15 +26,11 @@
 #include <unistd.h>
 #include <fcntl.h>
 
-#include <android/api-level.h>
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
-#include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaMuxer.h>
 
-extern "C" int android_get_application_target_sdk_version();
-
 namespace android {
 
 struct fields_t {
@@ -233,31 +229,11 @@
 
     status_t err = muxer->stop();
 
-    if (android_get_application_target_sdk_version() >= __ANDROID_API_R__) {
-        switch (err) {
-            case OK:
-                break;
-            case ERROR_IO: {
-                jniThrowException(env, "java/lang/UncheckedIOException",
-                                  "Muxer stopped unexpectedly");
-                return;
-            }
-            case ERROR_MALFORMED: {
-                jniThrowException(env, "java/io/IOError",
-                                  "Failure of reading or writing operation");
-                return;
-            }
-            default: {
-                jniThrowException(env, "java/lang/IllegalStateException",
-                                  "Failed to stop the muxer");
-                return;
-            }
-        }
-    } else {
-        if (err != OK) {
-            jniThrowException(env, "java/lang/IllegalStateException", "Failed to stop the muxer");
-            return;
-        }
+    if (err != OK) {
+        ALOGE("Error during stop:%d", err);
+        jniThrowException(env, "java/lang/IllegalStateException",
+                    "Error during stop(), muxer would have stopped already");
+        return;
     }
 }
 
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e42e438..3dbf5a4 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1376,4 +1376,7 @@
 
     <!-- A content description for work profile app [CHAR LIMIT=35] -->
     <string name="accessibility_work_profile_app_description">Work <xliff:g id="app_name" example="Camera">%s</xliff:g></string>
+
+    <!-- Name of the 3.5mm and usb audio device. [CHAR LIMIT=40] -->
+    <string name="media_transfer_wired_usb_device_name">Wired headphone</string>
 </resources>
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 8ea5ff1..9a3ae1b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -61,11 +61,11 @@
         switch (mRouteInfo.getType()) {
             case TYPE_WIRED_HEADSET:
             case TYPE_WIRED_HEADPHONES:
-                name = mContext.getString(R.string.media_transfer_wired_device_name);
-                break;
             case TYPE_USB_DEVICE:
             case TYPE_USB_HEADSET:
             case TYPE_USB_ACCESSORY:
+                name = mContext.getString(R.string.media_transfer_wired_usb_device_name);
+                break;
             case TYPE_DOCK:
             case TYPE_HDMI:
                 name = mRouteInfo.getName();
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
index 47f6fe3..421e5c0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/PhoneMediaDeviceTest.java
@@ -100,12 +100,12 @@
         when(mInfo.getName()).thenReturn(deviceName);
 
         assertThat(mPhoneMediaDevice.getName())
-                .isEqualTo(mContext.getString(R.string.media_transfer_wired_device_name));
+                .isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name));
 
         when(mInfo.getType()).thenReturn(TYPE_USB_DEVICE);
 
         assertThat(mPhoneMediaDevice.getName())
-                .isEqualTo(deviceName);
+                .isEqualTo(mContext.getString(R.string.media_transfer_wired_usb_device_name));
 
         when(mInfo.getType()).thenReturn(TYPE_BUILTIN_SPEAKER);
 
diff --git a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
index e4ae7c1..46396e3 100644
--- a/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_action_chip.xml
@@ -20,23 +20,29 @@
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:layout_marginEnd="@dimen/screenshot_action_chip_margin_right"
+    android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical"
     android:layout_gravity="center"
-    android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
-    android:background="@drawable/action_chip_background"
-    android:alpha="0.0"
-    android:gravity="center">
-    <ImageView
-        android:id="@+id/screenshot_action_chip_icon"
-        android:layout_width="@dimen/screenshot_action_chip_icon_size"
-        android:layout_height="@dimen/screenshot_action_chip_icon_size"
-        android:layout_marginStart="@dimen/screenshot_action_chip_padding_start"
-        android:layout_marginEnd="@dimen/screenshot_action_chip_padding_middle"/>
-    <TextView
-        android:id="@+id/screenshot_action_chip_text"
+    android:gravity="center"
+    android:alpha="0.0">
+    <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end"
-        android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
-        android:textSize="@dimen/screenshot_action_chip_text_size"
-        android:textColor="@color/global_screenshot_button_text"/>
+        android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
+        android:background="@drawable/action_chip_background"
+        android:gravity="center">
+        <ImageView
+            android:id="@+id/screenshot_action_chip_icon"
+            android:layout_width="@dimen/screenshot_action_chip_icon_size"
+            android:layout_height="@dimen/screenshot_action_chip_icon_size"
+            android:layout_marginStart="@dimen/screenshot_action_chip_padding_start"
+            android:layout_marginEnd="@dimen/screenshot_action_chip_padding_middle"/>
+        <TextView
+            android:id="@+id/screenshot_action_chip_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="@dimen/screenshot_action_chip_padding_end"
+            android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
+            android:textSize="@dimen/screenshot_action_chip_text_size"
+            android:textColor="@color/global_screenshot_button_text"/>
+    </LinearLayout>
 </com.android.systemui.screenshot.ScreenshotActionChip>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a3d32c1..31edcf6 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -314,13 +314,14 @@
     <dimen name="screenshot_dismiss_button_margin">8dp</dimen>
     <dimen name="screenshot_action_container_offset_y">32dp</dimen>
     <dimen name="screenshot_action_container_corner_radius">10dp</dimen>
-    <dimen name="screenshot_action_container_padding_vertical">16dp</dimen>
+    <dimen name="screenshot_action_container_padding_vertical">6dp</dimen>
     <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
     <dimen name="screenshot_action_container_padding_left">96dp</dimen>
     <dimen name="screenshot_action_container_padding_right">8dp</dimen>
     <!-- Radius of the chip background on global screenshot actions -->
     <dimen name="screenshot_button_corner_radius">20dp</dimen>
     <dimen name="screenshot_action_chip_margin_right">8dp</dimen>
+    <dimen name="screenshot_action_chip_margin_vertical">10dp</dimen>
     <dimen name="screenshot_action_chip_padding_vertical">7dp</dimen>
     <dimen name="screenshot_action_chip_icon_size">18dp</dimen>
     <dimen name="screenshot_action_chip_padding_start">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 236fa2d..f970152 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -18,7 +18,6 @@
 
 import android.app.ActivityView
 import android.app.Dialog
-import android.content.ComponentName
 import android.content.Intent
 import android.provider.Settings
 import android.view.View
@@ -58,17 +57,13 @@
             launchIntent.putExtra(EXTRA_USE_PANEL, true)
 
             // Apply flags to make behaviour match documentLaunchMode=always.
-            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT)
             launchIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK)
 
             view.startActivity(launchIntent)
         }
 
         override fun onActivityViewDestroyed(view: ActivityView) {}
-
-        override fun onTaskCreated(taskId: Int, componentName: ComponentName) {}
-
-        override fun onTaskRemovalStarted(taskId: Int) {}
     }
 
     init {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index 5e36704..6572937 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -34,6 +34,7 @@
 import com.android.systemui.statusbar.phone.BiometricUnlockController;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.wakelock.DelayedWakeLock;
@@ -56,6 +57,7 @@
     private final ProximitySensor mProximitySensor;
     private final DelayedWakeLock.Builder mDelayedWakeLockBuilder;
     private final Handler mHandler;
+    private final DelayableExecutor mDelayableExecutor;
     private final BiometricUnlockController mBiometricUnlockController;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final DozeHost mDozeHost;
@@ -68,6 +70,7 @@
             DockManager dockManager, @Nullable IWallpaperManager wallpaperManager,
             ProximitySensor proximitySensor,
             DelayedWakeLock.Builder delayedWakeLockBuilder, Handler handler,
+            DelayableExecutor delayableExecutor,
             BiometricUnlockController biometricUnlockController,
             BroadcastDispatcher broadcastDispatcher, DozeHost dozeHost) {
         mFalsingManager = falsingManager;
@@ -83,6 +86,7 @@
         mProximitySensor = proximitySensor;
         mDelayedWakeLockBuilder = delayedWakeLockBuilder;
         mHandler = handler;
+        mDelayableExecutor = delayableExecutor;
         mBiometricUnlockController = biometricUnlockController;
         mBroadcastDispatcher = broadcastDispatcher;
         mDozeHost = dozeHost;
@@ -107,8 +111,8 @@
                 new DozePauser(mHandler, machine, mAlarmManager, mDozeParameters.getPolicy()),
                 new DozeFalsingManagerAdapter(mFalsingManager),
                 createDozeTriggers(dozeService, mAsyncSensorManager, mDozeHost,
-                        mAlarmManager, config, mDozeParameters, mHandler, wakeLock, machine,
-                        mDockManager, mDozeLog),
+                        mAlarmManager, config, mDozeParameters, mDelayableExecutor, wakeLock,
+                        machine, mDockManager, mDozeLog),
                 createDozeUi(dozeService, mDozeHost, wakeLock, machine, mHandler,
                         mAlarmManager, mDozeParameters, mDozeLog),
                 new DozeScreenState(wrappedService, mHandler, mDozeHost, mDozeParameters,
@@ -135,11 +139,11 @@
 
     private DozeTriggers createDozeTriggers(Context context, AsyncSensorManager sensorManager,
             DozeHost host, AlarmManager alarmManager, AmbientDisplayConfiguration config,
-            DozeParameters params, Handler handler, WakeLock wakeLock, DozeMachine machine,
-            DockManager dockManager, DozeLog dozeLog) {
+            DozeParameters params, DelayableExecutor delayableExecutor, WakeLock wakeLock,
+            DozeMachine machine, DockManager dockManager, DozeLog dozeLog) {
         boolean allowPulseTriggers = true;
         return new DozeTriggers(context, machine, host, alarmManager, config, params,
-                sensorManager, handler, wakeLock, allowPulseTriggers, dockManager,
+                sensorManager, delayableExecutor, wakeLock, allowPulseTriggers, dockManager,
                 mProximitySensor, dozeLog, mBroadcastDispatcher);
 
     }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
index e1081cd..78f8f67 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java
@@ -101,7 +101,8 @@
 
     public DozeSensors(Context context, AlarmManager alarmManager, AsyncSensorManager sensorManager,
             DozeParameters dozeParameters, AmbientDisplayConfiguration config, WakeLock wakeLock,
-            Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog) {
+            Callback callback, Consumer<Boolean> proxCallback, DozeLog dozeLog,
+            ProximitySensor proximitySensor) {
         mContext = context;
         mAlarmManager = alarmManager;
         mSensorManager = sensorManager;
@@ -111,6 +112,7 @@
         mProxCallback = proxCallback;
         mResolver = mContext.getContentResolver();
         mCallback = callback;
+        mProximitySensor = proximitySensor;
 
         boolean alwaysOn = mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT);
         mSensors = new TriggerSensor[] {
@@ -173,7 +175,6 @@
                         dozeLog),
         };
 
-        mProximitySensor = new ProximitySensor(context.getResources(), sensorManager);
         setProxListening(false);  // Don't immediately start listening when we register.
         mProximitySensor.register(
                 proximityEvent -> {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 3510e07..6a55014 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -26,7 +26,6 @@
 import android.content.res.Configuration;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.metrics.LogMaker;
-import android.os.Handler;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.format.Formatter;
@@ -43,6 +42,7 @@
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.Assert;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.wakelock.WakeLock;
@@ -152,9 +152,9 @@
 
     public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
             AlarmManager alarmManager, AmbientDisplayConfiguration config,
-            DozeParameters dozeParameters, AsyncSensorManager sensorManager, Handler handler,
-            WakeLock wakeLock, boolean allowPulseTriggers, DockManager dockManager,
-            ProximitySensor proximitySensor,
+            DozeParameters dozeParameters, AsyncSensorManager sensorManager,
+            DelayableExecutor delayableExecutor, WakeLock wakeLock, boolean allowPulseTriggers,
+            DockManager dockManager, ProximitySensor proximitySensor,
             DozeLog dozeLog, BroadcastDispatcher broadcastDispatcher) {
         mContext = context;
         mMachine = machine;
@@ -165,10 +165,10 @@
         mWakeLock = wakeLock;
         mAllowPulseTriggers = allowPulseTriggers;
         mDozeSensors = new DozeSensors(context, alarmManager, mSensorManager, dozeParameters,
-                config, wakeLock, this::onSensor, this::onProximityFar, dozeLog);
+                config, wakeLock, this::onSensor, this::onProximityFar, dozeLog, proximitySensor);
         mUiModeManager = mContext.getSystemService(UiModeManager.class);
         mDockManager = dockManager;
-        mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, handler);
+        mProxCheck = new ProximitySensor.ProximityCheck(proximitySensor, delayableExecutor);
         mDozeLog = dozeLog;
         mBroadcastDispatcher = broadcastDispatcher;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
index 13516a9..7f7e108 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java
@@ -170,9 +170,9 @@
         private final @AnimationType int mAnimationType;
         private final Rect mDestinationBounds = new Rect();
 
-        private T mStartValue;
+        protected T mCurrentValue;
+        protected T mStartValue;
         private T mEndValue;
-        private T mCurrentValue;
         private PipAnimationCallback mPipAnimationCallback;
         private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
                 mSurfaceControlTransactionFactory;
@@ -288,7 +288,6 @@
          */
         void updateEndValue(T endValue) {
             mEndValue = endValue;
-            mStartValue = mCurrentValue;
         }
 
         SurfaceControl.Transaction newSurfaceControlTransaction() {
@@ -337,6 +336,12 @@
                     tx.show(leash);
                     tx.apply();
                 }
+
+                @Override
+                void updateEndValue(Float endValue) {
+                    super.updateEndValue(endValue);
+                    mStartValue = mCurrentValue;
+                }
             };
         }
 
@@ -392,6 +397,14 @@
                     getSurfaceTransactionHelper().resetScale(tx, leash, getDestinationBounds())
                             .crop(tx, leash, getDestinationBounds());
                 }
+
+                @Override
+                void updateEndValue(Rect endValue) {
+                    super.updateEndValue(endValue);
+                    if (mStartValue != null && mCurrentValue != null) {
+                        mStartValue.set(mCurrentValue);
+                    }
+                }
             };
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index d0ff2ff9..af9dd57 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -431,13 +431,21 @@
             } else {
                 final float offsetBufferPx = BOTTOM_OFFSET_BUFFER_DP
                         * mContext.getResources().getDisplayMetrics().density;
-                final Rect toMovementBounds = mMenuState == MENU_STATE_FULL && willResizeMenu()
+                final boolean isExpanded = mMenuState == MENU_STATE_FULL && willResizeMenu();
+                final Rect toMovementBounds = isExpanded
                         ? new Rect(expandedMovementBounds)
                         : new Rect(normalMovementBounds);
                 final int prevBottom = mMovementBounds.bottom - mMovementBoundsExtraOffsets;
                 final int toBottom = toMovementBounds.bottom < toMovementBounds.top
                         ? toMovementBounds.bottom
                         : toMovementBounds.bottom - extraOffset;
+
+                if (isExpanded) {
+                    curBounds.set(mExpandedBounds);
+                    mSnapAlgorithm.applySnapFraction(curBounds, toMovementBounds,
+                            mSavedSnapFraction);
+                }
+
                 if ((Math.min(prevBottom, toBottom) - offsetBufferPx) <= curBounds.top
                         && curBounds.top <= (Math.max(prevBottom, toBottom) + offsetBufferPx)) {
                     mMotionHelper.animateToOffset(curBounds, toBottom - curBounds.top);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 6f68ee8..f2d2eb3 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -703,9 +703,7 @@
                 mScreenshotPreview.buildLayer();
                 mScreenshotAnimation.start();
             });
-
         });
-
     }
 
     private AnimatorSet createScreenshotDropInAnimation(int width, int height, Rect bounds) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
index 44b20e5..b5209bb 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
@@ -22,8 +22,8 @@
 import android.graphics.drawable.Icon;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.widget.FrameLayout;
 import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.TextView;
 
 import com.android.systemui.R;
@@ -31,7 +31,7 @@
 /**
  * View for a chip with an icon and text.
  */
-public class ScreenshotActionChip extends LinearLayout {
+public class ScreenshotActionChip extends FrameLayout {
 
     private static final String TAG = "ScreenshotActionChip";
 
diff --git a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
index 378dde2..708b5a7 100644
--- a/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
+++ b/packages/SystemUI/src/com/android/systemui/util/sensors/ProximitySensor.java
@@ -21,16 +21,17 @@
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
-import android.os.Handler;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.systemui.R;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Consumer;
 
 import javax.inject.Inject;
@@ -49,8 +50,9 @@
     private String mTag = null;
     @VisibleForTesting ProximityEvent mLastEvent;
     private int mSensorDelay = SensorManager.SENSOR_DELAY_NORMAL;
-    private boolean mPaused;
+    @VisibleForTesting protected boolean mPaused;
     private boolean mRegistered;
+    private final AtomicBoolean mAlerting = new AtomicBoolean();
 
     private SensorEventListener mSensorEventListener = new SensorEventListener() {
         @Override
@@ -217,8 +219,12 @@
 
     /** Update all listeners with the last value this class received from the sensor. */
     public void alertListeners() {
+        if (mAlerting.getAndSet(true)) {
+            return;
+        }
         mListeners.forEach(proximitySensorListener ->
                 proximitySensorListener.onSensorEvent(mLastEvent));
+        mAlerting.set(false);
     }
 
     private void onSensorEvent(SensorEvent event) {
@@ -239,14 +245,14 @@
     public static class ProximityCheck implements Runnable {
 
         private final ProximitySensor mSensor;
-        private final Handler mHandler;
+        private final DelayableExecutor mDelayableExecutor;
         private List<Consumer<Boolean>> mCallbacks = new ArrayList<>();
 
         @Inject
-        public ProximityCheck(ProximitySensor sensor, Handler handler) {
+        public ProximityCheck(ProximitySensor sensor, DelayableExecutor delayableExecutor) {
             mSensor = sensor;
             mSensor.setTag("prox_check");
-            mHandler = handler;
+            mDelayableExecutor = delayableExecutor;
             mSensor.pause();
             ProximitySensorListener listener = proximityEvent -> {
                 mCallbacks.forEach(
@@ -280,7 +286,7 @@
             mCallbacks.add(callback);
             if (!mSensor.isRegistered()) {
                 mSensor.resume();
-                mHandler.postDelayed(this, timeoutMs);
+                mDelayableExecutor.executeDelayed(this, timeoutMs);
             }
         }
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 50b7af2..e0049d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -19,6 +19,7 @@
 import static junit.framework.TestCase.assertFalse;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -29,9 +30,8 @@
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
-import static java.lang.Thread.sleep;
-
 import android.app.AppOpsManager;
+import android.os.Looper;
 import android.os.UserHandle;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
@@ -229,12 +229,7 @@
     @Test
     public void testActiveOpNotRemovedAfterNoted() throws InterruptedException {
         // Replaces the timeout delay with 5 ms
-        AppOpsControllerImpl.H testHandler = mController.new H(mTestableLooper.getLooper()) {
-            @Override
-            public void scheduleRemoval(AppOpItem item, long timeToRemoval) {
-                super.scheduleRemoval(item, 5L);
-            }
-        };
+        TestHandler testHandler = new TestHandler(mTestableLooper.getLooper());
 
         mController.addCallback(new int[]{AppOpsManager.OP_FINE_LOCATION}, mCallback);
         mController.setBGHandler(testHandler);
@@ -245,6 +240,10 @@
         mController.onOpNoted(AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME,
                 AppOpsManager.MODE_ALLOWED);
 
+        // Check that we "scheduled" the removal. Don't actually schedule until we are ready to
+        // process messages at a later time.
+        assertNotNull(testHandler.mDelayScheduled);
+
         mTestableLooper.processAllMessages();
         List<AppOpItem> list = mController.getActiveAppOps();
         verify(mCallback).onActiveStateChanged(
@@ -253,8 +252,8 @@
         // Duplicates are not removed between active and noted
         assertEquals(2, list.size());
 
-        sleep(10L);
-
+        // Now is later, so we can schedule delayed messages.
+        testHandler.scheduleDelayed();
         mTestableLooper.processAllMessages();
 
         verify(mCallback, never()).onActiveStateChanged(
@@ -321,4 +320,24 @@
         verify(mCallback).onActiveStateChanged(
                 AppOpsManager.OP_FINE_LOCATION, TEST_UID, TEST_PACKAGE_NAME, true);
     }
+
+    private class TestHandler extends AppOpsControllerImpl.H {
+        TestHandler(Looper looper) {
+            mController.super(looper);
+        }
+
+        Runnable mDelayScheduled;
+
+        void scheduleDelayed() {
+            if (mDelayScheduled != null) {
+                mDelayScheduled.run();
+                mDelayScheduled = null;
+            }
+        }
+
+        @Override
+        public void scheduleRemoval(AppOpItem item, long timeToRemoval) {
+            mDelayScheduled = () -> super.scheduleRemoval(item, 0L);
+        }
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
index 317500c..a567536 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeSensorsTest.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.plugins.SensorManagerPlugin.Sensor.TYPE_WAKE_LOCK_SCREEN;
 
+import static org.junit.Assert.assertFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -46,6 +47,7 @@
 import com.android.systemui.plugins.SensorManagerPlugin;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.util.sensors.AsyncSensorManager;
+import com.android.systemui.util.sensors.ProximitySensor;
 import com.android.systemui.util.wakelock.WakeLock;
 
 import org.junit.Before;
@@ -82,7 +84,7 @@
     @Mock
     private DozeLog mDozeLog;
     @Mock
-    private Sensor mProximitySensor;
+    private ProximitySensor mProximitySensor;
     private SensorManagerPlugin.SensorEventListener mWakeLockScreenListener;
     private TestableLooper mTestableLooper;
     private DozeSensors mDozeSensors;
@@ -93,7 +95,6 @@
         mTestableLooper = TestableLooper.get(this);
         when(mAmbientDisplayConfiguration.getWakeLockScreenDebounce()).thenReturn(5000L);
         when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
-        when(mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY)).thenReturn(mProximitySensor);
         doAnswer(invocation -> {
             ((Runnable) invocation.getArgument(0)).run();
             return null;
@@ -103,10 +104,9 @@
 
     @Test
     public void testRegisterProx() {
-        // We should not register with the sensor manager initially.
-        verify(mSensorManager, never()).registerListener(any(), any(Sensor.class), anyInt());
+        assertFalse(mProximitySensor.isRegistered());
         mDozeSensors.setProxListening(true);
-        verify(mSensorManager).registerListener(any(), any(Sensor.class), anyInt());
+        verify(mProximitySensor).resume();
     }
 
     @Test
@@ -169,7 +169,8 @@
 
         TestableDozeSensors() {
             super(getContext(), mAlarmManager, mSensorManager, mDozeParameters,
-                    mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog);
+                    mAmbientDisplayConfiguration, mWakeLock, mCallback, mProxCallback, mDozeLog,
+                    mProximitySensor);
             for (TriggerSensor sensor : mSensors) {
                 if (sensor instanceof PluginSensor
                         && ((PluginSensor) sensor).mPluginSensor.getType()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index debc9d6..73aaeff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -31,7 +31,6 @@
 import android.hardware.Sensor;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
-import android.os.Looper;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
@@ -42,10 +41,12 @@
 import com.android.systemui.broadcast.BroadcastDispatcher;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.sensors.FakeProximitySensor;
 import com.android.systemui.util.sensors.FakeSensorManager;
 import com.android.systemui.util.sensors.ProximitySensor;
+import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.util.wakelock.WakeLockFake;
 
@@ -75,6 +76,7 @@
     private FakeSensorManager mSensors;
     private Sensor mTapSensor;
     private FakeProximitySensor mProximitySensor;
+    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
 
     @Before
     public void setUp() throws Exception {
@@ -89,7 +91,7 @@
         mProximitySensor = new FakeProximitySensor(getContext().getResources(), asyncSensorManager);
 
         mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
-                asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
+                asyncSensorManager, mFakeExecutor, wakeLock, true,
                 mDockManager, mProximitySensor, mock(DozeLog.class), mBroadcastDispatcher);
         waitForSensorManager();
     }
@@ -111,9 +113,8 @@
         verify(mMachine, never()).requestState(any());
         verify(mMachine, never()).requestPulse(anyInt());
 
-        captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
-        waitForSensorManager();
         mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
+        captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
         mProximitySensor.alertListeners();
 
         verify(mMachine).requestPulse(anyInt());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
index 31d884c..bd697fe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/FakeProximitySensor.java
@@ -20,6 +20,7 @@
 
 public class FakeProximitySensor extends ProximitySensor {
     private boolean mAvailable;
+    private boolean mRegistered;
 
     public FakeProximitySensor(Resources resources, AsyncSensorManager sensorManager) {
         super(resources, sensorManager);
@@ -35,17 +36,22 @@
     }
 
     @Override
+    public boolean isRegistered() {
+        return mRegistered;
+    }
+
+    @Override
     public boolean getSensorAvailable() {
         return mAvailable;
     }
 
     @Override
     protected void registerInternal() {
-        // no-op
+        mRegistered = !mPaused;
     }
 
     @Override
     protected void unregisterInternal() {
-        // no-op
+        mRegistered = false;
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
new file mode 100644
index 0000000..7221095
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximityCheckTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util.sensors;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.function.Consumer;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ProximityCheckTest extends SysuiTestCase {
+
+    private FakeProximitySensor mFakeProximitySensor;
+    private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
+
+    private TestableCallback mTestableCallback = new TestableCallback();
+
+    private ProximitySensor.ProximityCheck mProximityCheck;
+
+    @Before
+    public void setUp() throws Exception {
+        AsyncSensorManager asyncSensorManager =
+                new AsyncSensorManager(new FakeSensorManager(mContext), null, new Handler());
+        mFakeProximitySensor = new FakeProximitySensor(mContext.getResources(), asyncSensorManager);
+
+        mProximityCheck = new ProximitySensor.ProximityCheck(mFakeProximitySensor, mFakeExecutor);
+    }
+
+    @Test
+    public void testCheck() {
+        mProximityCheck.check(100, mTestableCallback);
+
+        assertNull(mTestableCallback.mLastResult);
+
+        mFakeProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 0));
+        mFakeProximitySensor.alertListeners();
+
+        assertTrue(mTestableCallback.mLastResult);
+    }
+
+    @Test
+    public void testTimeout() {
+        mProximityCheck.check(100, mTestableCallback);
+
+        assertTrue(mFakeProximitySensor.isRegistered());
+
+        mFakeExecutor.advanceClockToNext();
+        mFakeExecutor.runAllReady();
+
+        assertFalse(mFakeProximitySensor.isRegistered());
+    }
+
+    private static class TestableCallback implements Consumer<Boolean> {
+        Boolean mLastResult;
+        @Override
+        public void accept(Boolean result) {
+            mLastResult = result;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
index 526fba7..914790b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/sensors/ProximitySensorTest.java
@@ -219,6 +219,26 @@
         waitForSensorManager();
     }
 
+    @Test
+    public void testPreventRecursiveAlert() {
+        TestableListener listenerA = new TestableListener() {
+            @Override
+            public void onSensorEvent(ProximitySensor.ProximityEvent proximityEvent) {
+                super.onSensorEvent(proximityEvent);
+                if (mCallCount < 2) {
+                    mProximitySensor.alertListeners();
+                }
+            }
+        };
+
+        mProximitySensor.register(listenerA);
+
+        mProximitySensor.alertListeners();
+
+        assertEquals(1, listenerA.mCallCount);
+    }
+
+
     class TestableListener implements ProximitySensor.ProximitySensorListener {
         ProximitySensor.ProximityEvent mLastEvent;
         int mCallCount = 0;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 37f1ad1..4ace676 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -337,6 +337,7 @@
 import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.TriFunction;
+import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.server.AlarmManagerInternal;
 import com.android.server.AttributeCache;
 import com.android.server.DeviceIdleInternal;
@@ -18831,28 +18832,39 @@
         @Override
         public int checkContentProviderUriPermission(Uri uri, int userId,
                 int callingUid, int modeFlags) {
-            // We can find ourselves needing to check Uri permissions while
-            // already holding the WM lock, which means reaching back here for
-            // the AM lock would cause an inversion. The WM team has requested
-            // that we use the strategy below instead of shifting where Uri
-            // grants are calculated.
+            final Object wmLock = mActivityTaskManager.getGlobalLock();
+            if (Thread.currentThread().holdsLock(wmLock)
+                    && !Thread.currentThread().holdsLock(ActivityManagerService.this)) {
+                // We can find ourselves needing to check Uri permissions while already holding the
+                // WM lock, which means reaching back here for the AM lock would cause an inversion.
+                // The WM team has requested that we use the strategy below instead of shifting
+                // where Uri grants are calculated.
+                synchronized (wmLock) {
+                    final int[] result = new int[1];
+                    final Message msg = PooledLambda.obtainMessage(
+                            LocalService::checkContentProviderUriPermission,
+                            this, uri, userId, callingUid, modeFlags, wmLock, result);
+                    mHandler.sendMessage(msg);
+                    try {
+                        wmLock.wait();
+                    } catch (InterruptedException ignore) {
 
-            // Since we could also arrive here while holding the AM lock, we
-            // can't always delegate the call through the handler, and we need
-            // to delicately dance between the deadlocks.
-            if (Thread.currentThread().holdsLock(ActivityManagerService.this)) {
+                    }
+                    return result[0];
+                }
+            } else {
                 return ActivityManagerService.this.checkContentProviderUriPermission(uri,
                         userId, callingUid, modeFlags);
-            } else {
-                final CompletableFuture<Integer> res = new CompletableFuture<>();
-                mHandler.post(() -> {
-                    res.complete(ActivityManagerService.this.checkContentProviderUriPermission(uri,
-                            userId, callingUid, modeFlags));
-                });
-                try {
-                    return res.get();
-                } catch (InterruptedException | ExecutionException e) {
-                    throw new RuntimeException(e);
+            }
+        }
+
+        void checkContentProviderUriPermission(
+                Uri uri, int userId, int callingUid, int modeFlags, Object wmLock, int[] result) {
+            synchronized (ActivityManagerService.this) {
+                synchronized (wmLock) {
+                    result[0] = ActivityManagerService.this.checkContentProviderUriPermission(
+                                    uri, userId, callingUid, modeFlags);
+                    wmLock.notify();
                 }
             }
         }
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index f9d204f..43e3a04 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -45,6 +45,7 @@
 
 import java.io.FileOutputStream;
 import java.io.FileReader;
+import java.io.IOException;
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -114,6 +115,14 @@
     }
     private PropertyChangedCallbackForTest mTestCallback;
 
+    // This interface is for functions related to the Process object that need a different
+    // implementation in the tests as we are not creating real processes when testing compaction.
+    @VisibleForTesting
+    interface ProcessDependencies {
+        long[] getRss(int pid);
+        void performCompaction(String action, int pid) throws IOException;
+    }
+
     // Handler constants.
     static final int COMPACT_PROCESS_SOME = 1;
     static final int COMPACT_PROCESS_FULL = 2;
@@ -215,13 +224,16 @@
     @VisibleForTesting final Set<Integer> mProcStateThrottle;
 
     // Handler on which compaction runs.
-    private Handler mCompactionHandler;
+    @VisibleForTesting
+    Handler mCompactionHandler;
     private Handler mFreezeHandler;
 
     // Maps process ID to last compaction statistics for processes that we've fully compacted. Used
     // when evaluating throttles that we only consider for "full" compaction, so we don't store
-    // data for "some" compactions.
-    private Map<Integer, LastCompactionStats> mLastCompactionStats =
+    // data for "some" compactions. Uses LinkedHashMap to ensure insertion order is kept and
+    // facilitate removal of the oldest entry.
+    @VisibleForTesting
+    LinkedHashMap<Integer, LastCompactionStats> mLastCompactionStats =
             new LinkedHashMap<Integer, LastCompactionStats>() {
                 @Override
                 protected boolean removeEldestEntry(Map.Entry eldest) {
@@ -233,17 +245,20 @@
     private int mFullCompactionCount;
     private int mPersistentCompactionCount;
     private int mBfgsCompactionCount;
+    private final ProcessDependencies mProcessDependencies;
 
     public CachedAppOptimizer(ActivityManagerService am) {
-        mAm = am;
-        mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
-                THREAD_PRIORITY_FOREGROUND, true);
-        mProcStateThrottle = new HashSet<>();
+        this(am, null, new DefaultProcessDependencies());
     }
 
     @VisibleForTesting
-    CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback) {
-        this(am);
+    CachedAppOptimizer(ActivityManagerService am, PropertyChangedCallbackForTest callback,
+            ProcessDependencies processDependencies) {
+        mAm = am;
+        mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
+            THREAD_PRIORITY_FOREGROUND, true);
+        mProcStateThrottle = new HashSet<>();
+        mProcessDependencies = processDependencies;
         mTestCallback = callback;
     }
 
@@ -659,7 +674,8 @@
         }
     }
 
-    private static final class LastCompactionStats {
+    @VisibleForTesting
+    static final class LastCompactionStats {
         private final long[] mRssAfterCompaction;
 
         LastCompactionStats(long[] rss) {
@@ -712,9 +728,7 @@
 
                         lastCompactAction = proc.lastCompactAction;
                         lastCompactTime = proc.lastCompactTime;
-                        // remove rather than get so that insertion order will be updated when we
-                        // put the post-compaction stats back into the map.
-                        lastCompactionStats = mLastCompactionStats.remove(pid);
+                        lastCompactionStats = mLastCompactionStats.get(pid);
                     }
 
                     if (pid == 0) {
@@ -806,7 +820,7 @@
                         return;
                     }
 
-                    long[] rssBefore = Process.getRss(pid);
+                    long[] rssBefore = mProcessDependencies.getRss(pid);
                     long anonRssBefore = rssBefore[2];
 
                     if (rssBefore[0] == 0 && rssBefore[1] == 0 && rssBefore[2] == 0
@@ -863,16 +877,13 @@
                         default:
                             break;
                     }
-
                     try {
                         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Compact "
                                 + ((pendingAction == COMPACT_PROCESS_SOME) ? "some" : "full")
                                 + ": " + name);
                         long zramFreeKbBefore = Debug.getZramFreeKb();
-                        FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim");
-                        fos.write(action.getBytes());
-                        fos.close();
-                        long[] rssAfter = Process.getRss(pid);
+                        mProcessDependencies.performCompaction(action, pid);
+                        long[] rssAfter = mProcessDependencies.getRss(pid);
                         long end = SystemClock.uptimeMillis();
                         long time = end - start;
                         long zramFreeKbAfter = Debug.getZramFreeKb();
@@ -882,7 +893,6 @@
                                 rssAfter[2] - rssBefore[2], rssAfter[3] - rssBefore[3], time,
                                 lastCompactAction, lastCompactTime, lastOomAdj, procState,
                                 zramFreeKbBefore, zramFreeKbAfter - zramFreeKbBefore);
-
                         // Note that as above not taking mPhenoTypeFlagLock here to avoid locking
                         // on every single compaction for a flag that will seldom change and the
                         // impact of reading the wrong value here is low.
@@ -894,14 +904,14 @@
                                     lastOomAdj, ActivityManager.processStateAmToProto(procState),
                                     zramFreeKbBefore, zramFreeKbAfter);
                         }
-
                         synchronized (mAm) {
                             proc.lastCompactTime = end;
                             proc.lastCompactAction = pendingAction;
                         }
-
                         if (action.equals(COMPACT_ACTION_FULL)
                                 || action.equals(COMPACT_ACTION_ANON)) {
+                            // Remove entry and insert again to update insertion order.
+                            mLastCompactionStats.remove(pid);
                             mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
                         }
                     } catch (Exception e) {
@@ -1018,4 +1028,23 @@
             }
         }
     }
+
+    /**
+     * Default implementation for ProcessDependencies, public vor visibility to OomAdjuster class.
+     */
+    private static final class DefaultProcessDependencies implements ProcessDependencies {
+        // Get memory RSS from process.
+        @Override
+        public long[] getRss(int pid) {
+            return Process.getRss(pid);
+        }
+
+        // Compact process.
+        @Override
+        public void performCompaction(String action, int pid) throws IOException {
+            try (FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim")) {
+                fos.write(action.getBytes());
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index a099606..b9cd43d 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -51,6 +51,11 @@
     private static final float MAX_GRAD = 1.0f;
     private static final float SHORT_TERM_MODEL_THRESHOLD_RATIO = 0.6f;
 
+    // Constant that ensures that each step of the curve can increase by up to at least
+    // MIN_PERMISSABLE_INCREASE. Otherwise when the brightness is set to 0, the curve will never
+    // increase and will always be 0.
+    private static final float MIN_PERMISSABLE_INCREASE =  0.004f;
+
     protected boolean mLoggingEnabled;
 
     private static final Plog PLOG = Plog.createSystemPlog(TAG);
@@ -400,7 +405,9 @@
         for (int i = idx+1; i < lux.length; i++) {
             float currLux = lux[i];
             float currBrightness = brightness[i];
-            float maxBrightness = prevBrightness * permissibleRatio(currLux, prevLux);
+            float maxBrightness = MathUtils.max(
+                    prevBrightness * permissibleRatio(currLux, prevLux),
+                    prevBrightness + MIN_PERMISSABLE_INCREASE);
             float newBrightness = MathUtils.constrain(
                     currBrightness, prevBrightness, maxBrightness);
             if (newBrightness == currBrightness) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
index a435f1e..53205ad 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderServiceProxy.java
@@ -60,7 +60,7 @@
     private Connection mActiveConnection;
     private boolean mConnectionReady;
 
-    private RouteDiscoveryPreference mPendingDiscoveryPreference = null;
+    private RouteDiscoveryPreference mLastDiscoveryPreference = null;
 
     MediaRoute2ProviderServiceProxy(@NonNull Context context, @NonNull ComponentName componentName,
             int userId) {
@@ -98,11 +98,10 @@
 
     @Override
     public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
+        mLastDiscoveryPreference = discoveryPreference;
         if (mConnectionReady) {
             mActiveConnection.updateDiscoveryPreference(discoveryPreference);
             updateBinding();
-        } else {
-            mPendingDiscoveryPreference = discoveryPreference;
         }
     }
 
@@ -277,9 +276,8 @@
     private void onConnectionReady(Connection connection) {
         if (mActiveConnection == connection) {
             mConnectionReady = true;
-            if (mPendingDiscoveryPreference != null) {
-                updateDiscoveryPreference(mPendingDiscoveryPreference);
-                mPendingDiscoveryPreference = null;
+            if (mLastDiscoveryPreference != null) {
+                updateDiscoveryPreference(mLastDiscoveryPreference);
             }
         }
     }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index fdee9f8..d7e7247 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -45,7 +45,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.text.TextUtils;
-import android.util.Log;
+import android.util.Slog;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -58,7 +58,8 @@
 // TODO: check thread safety. We may need to use lock to protect variables.
 class SystemMediaRoute2Provider extends MediaRoute2Provider {
     private static final String TAG = "MR2SystemProvider";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    // TODO(b/156996903): Revert it when releasing the framework.
+    private static final boolean DEBUG = true;
 
     static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
     static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
@@ -269,7 +270,11 @@
                 builder.addRoute(route);
             }
         }
-        setProviderState(builder.build());
+        MediaRoute2ProviderInfo providerInfo = builder.build();
+        setProviderState(providerInfo);
+        if (DEBUG) {
+            Slog.d(TAG, "Updating system provider info : " + providerInfo);
+        }
     }
 
     /**
@@ -327,6 +332,9 @@
             if (Objects.equals(oldSessionInfo, newSessionInfo)) {
                 return false;
             } else {
+                if (DEBUG) {
+                    Slog.d(TAG, "Updating system routing session info : " + newSessionInfo);
+                }
                 mSessionInfos.clear();
                 mSessionInfos.add(newSessionInfo);
                 mDefaultSessionInfo = new RoutingSessionInfo.Builder(
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 38c65f1..9d56d81 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -1936,6 +1936,9 @@
                     event.writeInt(channel.getImportance());
                     event.writeInt(channel.getUserLockedFields());
                     event.writeBoolean(channel.isDeleted());
+                    event.writeBoolean(channel.getConversationId() != null);
+                    event.writeBoolean(channel.isDemoted());
+                    event.writeBoolean(channel.isImportantConversation());
                     events.add(event.build());
                 }
             }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3e587bf..d73d872 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -13221,7 +13221,9 @@
 
     private void enforceCanSetPackagesSuspendedAsUser(String callingPackage, int callingUid,
             int userId, String callingMethod) {
-        if (callingUid == Process.ROOT_UID || callingUid == Process.SYSTEM_UID) {
+        if (callingUid == Process.ROOT_UID
+                // Need to compare app-id to allow system dialogs access on secondary users
+                || UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 7d49f78..163504c 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -124,6 +124,7 @@
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.os.RoSystemProperties;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IntPair;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
@@ -421,6 +422,10 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) {
+            return;
+        }
+
         mContext.getSystemService(PermissionControllerManager.class).dump(fd, args);
     }
 
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 3336697..f075790 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -301,10 +301,7 @@
                 }
                 if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(action)) {
                     String packageName = intent.getData().getSchemeSpecificPart();
-                    if (LOCAL_LOGV) {
-                        Slog.v(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED"
-                                + " pkg=" + packageName);
-                    }
+                    Slog.i(TAG, "broadcast=ACTION_PACKAGE_FULLY_REMOVED pkg=" + packageName);
                     onPackageFullyRemoved(packageName);
                 }
             }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0598680..b5b82d3 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3589,7 +3589,7 @@
 
     @Override
     ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom,
-            WindowContainer boundary) {
+            ActivityRecord boundary) {
         return callback.test(this) ? this : null;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 2f868d9..9b9b613 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -2708,7 +2708,9 @@
      */
     @Nullable
     private ActivityRecord getOccludingActivityAbove(ActivityRecord activity) {
-        return getActivity((ar) -> ar.occludesParent(), true /* traverseTopToBottom */, activity);
+        ActivityRecord top = getActivity((ar) -> ar.occludesParent(),
+                true /* traverseTopToBottom */, activity);
+        return top != activity ? top : null;
     }
 
     boolean willActivityBeVisible(IBinder token) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index dfa3fe0..c28d47c 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -19,7 +19,6 @@
 import static android.app.ActivityManager.START_SUCCESS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL;
 
@@ -193,9 +192,7 @@
         final ActivityStack homeStack;
         try {
             // Make sure home stack exists on display area.
-            // TODO(b/153624902): Replace with TaskDisplayArea#getOrCreateRootHomeTask()
-            homeStack = taskDisplayArea.getOrCreateStack(WINDOWING_MODE_UNDEFINED,
-                    ACTIVITY_TYPE_HOME, ON_TOP);
+            homeStack = taskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
         } finally {
             mSupervisor.endDeferResume();
         }
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 78e4237..fdbb2b2 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3319,7 +3319,7 @@
     }
 
     @Override
-    public void resizeTask(int taskId, Rect bounds, int resizeMode) {
+    public boolean resizeTask(int taskId, Rect bounds, int resizeMode) {
         mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "resizeTask()");
         long ident = Binder.clearCallingIdentity();
         try {
@@ -3328,10 +3328,11 @@
                         MATCH_TASK_IN_STACKS_ONLY);
                 if (task == null) {
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
-                    return;
+                    return false;
                 }
                 if (!task.getWindowConfiguration().canResizeTask()) {
-                    throw new IllegalArgumentException("resizeTask not allowed on task=" + task);
+                    Slog.w(TAG, "resizeTask not allowed on task=" + task);
+                    return false;
                 }
 
                 // Reparent the task to the right stack if necessary
@@ -3339,7 +3340,7 @@
 
                 // After reparenting (which only resizes the task to the stack bounds), resize the
                 // task to the actual bounds provided
-                task.resize(bounds, resizeMode, preserveWindow);
+                return task.resize(bounds, resizeMode, preserveWindow);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 0b2bd81..85c439c 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -4700,9 +4700,11 @@
     boolean supportsSystemDecorations() {
         return (mWmService.mDisplayWindowSettings.shouldShowSystemDecorsLocked(this)
                 || (mDisplay.getFlags() & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0
-                || (mWmService.mForceDesktopModeOnExternalDisplays && !isUntrustedVirtualDisplay()))
+                || mWmService.mForceDesktopModeOnExternalDisplays)
                 // VR virtual display will be used to run and render 2D app within a VR experience.
-                && mDisplayId != mWmService.mVr2dDisplayId;
+                && mDisplayId != mWmService.mVr2dDisplayId
+                // Do not show system decorations on untrusted virtual display.
+                && !isUntrustedVirtualDisplay();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 888a6e9..0ecde72 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1369,7 +1369,7 @@
         calculateDefaultMinimalSizeOfResizeableTasks();
 
         final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
-        defaultTaskDisplayArea.getOrCreateRootHomeTask();
+        defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
         positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
                 false /* includingParents */);
     }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 37a4c1f..6ce36f1 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -1461,16 +1461,23 @@
         return mChildren.get(index);
     }
 
+    @Nullable
+    ActivityStack getOrCreateRootHomeTask() {
+        return getOrCreateRootHomeTask(false /* onTop */);
+    }
+
     /**
      * Returns the existing home stack or creates and returns a new one if it should exist for the
      * display.
+     * @param onTop Only be used when there is no existing home stack. If true the home stack will
+     *              be created at the top of the display, else at the bottom.
      */
     @Nullable
-    ActivityStack getOrCreateRootHomeTask() {
+    ActivityStack getOrCreateRootHomeTask(boolean onTop) {
         ActivityStack homeTask = getRootHomeTask();
         if (homeTask == null && mDisplayContent.supportsSystemDecorations()
                 && !mDisplayContent.isUntrustedVirtualDisplay()) {
-            homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, false /* onTop */);
+            homeTask = createStack(WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, onTop);
         }
         return homeTask;
     }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 7bfddd7..e2023ae 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1415,11 +1415,12 @@
     }
 
     ActivityRecord getActivity(Predicate<ActivityRecord> callback, boolean traverseTopToBottom,
-            WindowContainer boundary) {
+            ActivityRecord boundary) {
         if (traverseTopToBottom) {
             for (int i = mChildren.size() - 1; i >= 0; --i) {
                 final WindowContainer wc = mChildren.get(i);
-                if (wc == boundary) return null;
+                // TODO(b/156986561): Improve the correctness of the boundary check.
+                if (wc == boundary) return boundary;
 
                 final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary);
                 if (r != null) {
@@ -1430,7 +1431,8 @@
             final int count = mChildren.size();
             for (int i = 0; i < count; i++) {
                 final WindowContainer wc = mChildren.get(i);
-                if (wc == boundary) return null;
+                // TODO(b/156986561): Improve the correctness of the boundary check.
+                if (wc == boundary) return boundary;
 
                 final ActivityRecord r = wc.getActivity(callback, traverseTopToBottom, boundary);
                 if (r != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
index e5ec1f7..96a44a4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/CachedAppOptimizerTest.java
@@ -16,14 +16,23 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+
 import static com.android.server.am.ActivityManagerService.Injector;
 import static com.android.server.am.CachedAppOptimizer.compactActionIntToString;
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
+import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManagerInternal;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.MessageQueue;
 import android.os.Process;
 import android.platform.test.annotations.Presubmit;
 import android.provider.DeviceConfig;
@@ -31,9 +40,11 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
 import com.android.server.appop.AppOpsService;
 import com.android.server.testables.TestableDeviceConfig;
+import com.android.server.wm.ActivityTaskManagerService;
 
 import org.junit.After;
 import org.junit.Assume;
@@ -45,6 +56,7 @@
 import org.mockito.junit.MockitoJUnitRunner;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.HashSet;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -68,25 +80,36 @@
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     private CountDownLatch mCountDown;
+    private ActivityManagerService mAms;
+    private Context mContext;
+    private TestInjector mInjector;
+    private TestProcessDependencies mProcessDependencies;
+
+    @Mock
+    private PackageManagerInternal mPackageManagerInt;
 
     @Rule
-    public TestableDeviceConfig.TestableDeviceConfigRule
+    public final TestableDeviceConfig.TestableDeviceConfigRule
             mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+    @Rule
+    public final ApplicationExitInfoTest.ServiceThreadRule
+            mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule();
 
     @Before
     public void setUp() {
         mHandlerThread = new HandlerThread("");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
-
         mThread = new ServiceThread("TestServiceThread", Process.THREAD_PRIORITY_DEFAULT,
                 true /* allowIo */);
         mThread.start();
-
-        ActivityManagerService ams = new ActivityManagerService(
-                new TestInjector(InstrumentationRegistry.getInstrumentation().getContext()),
-                mThread);
-        mCachedAppOptimizerUnderTest = new CachedAppOptimizer(ams,
+        mContext = InstrumentationRegistry.getInstrumentation().getContext();
+        mInjector = new TestInjector(mContext);
+        mAms = new ActivityManagerService(
+                new TestInjector(mContext), mServiceThreadRule.getThread());
+        doReturn(new ComponentName("", "")).when(mPackageManagerInt).getSystemUiServiceComponent();
+        mProcessDependencies = new TestProcessDependencies();
+        mCachedAppOptimizerUnderTest = new CachedAppOptimizer(mAms,
                 new CachedAppOptimizer.PropertyChangedCallbackForTest() {
                     @Override
                     public void onPropertyChanged() {
@@ -94,7 +117,9 @@
                             mCountDown.countDown();
                         }
                     }
-                });
+                }, mProcessDependencies);
+        LocalServices.removeServiceForTest(PackageManagerInternal.class);
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInt);
     }
 
     @After
@@ -104,6 +129,19 @@
         mCountDown = null;
     }
 
+    private ProcessRecord makeProcessRecord(int pid, int uid, int packageUid, String processName,
+            String packageName) {
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = packageName;
+        ProcessRecord app = new ProcessRecord(mAms, ai, processName, uid);
+        app.pid = pid;
+        app.info.uid = packageUid;
+        // Exact value does not mater, it can be any state for which compaction is allowed.
+        app.setProcState = PROCESS_STATE_BOUND_FOREGROUND_SERVICE;
+        app.setAdj = 905;
+        return app;
+    }
+
     @Test
     public void init_setsDefaults() {
         mCachedAppOptimizerUnderTest.init();
@@ -197,7 +235,7 @@
                 CachedAppOptimizer.DEFAULT_USE_FREEZER);
         DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 CachedAppOptimizer.KEY_USE_FREEZER, CachedAppOptimizer.DEFAULT_USE_FREEZER
-                ?  "false" : "true" , false);
+                        ? "false" : "true", false);
 
         // Then calling init will read and set that flag.
         mCachedAppOptimizerUnderTest.init();
@@ -790,6 +828,174 @@
                 .containsExactlyElementsIn(expected);
     }
 
+    @Test
+    public void processWithDeltaRSSTooSmall_notFullCompacted() throws Exception {
+        // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS
+        // throttle to 12000.
+        mCachedAppOptimizerUnderTest.init();
+        setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
+        setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_DELTA_RSS_THROTTLE_KB, "12000", false);
+        initActivityManagerService();
+
+        // Simulate RSS anon memory larger than throttle.
+        long[] rssBefore1 =
+                new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 10000, /*anonRSS*/ 12000, /*swap*/
+                        10000};
+        long[] rssAfter1 =
+                new long[]{/*totalRSS*/ 9000, /*fileRSS*/ 9000, /*anonRSS*/ 11000, /*swap*/9000};
+        // Delta between rssAfter1 and rssBefore2 is below threshold (500).
+        long[] rssBefore2 =
+                new long[]{/*totalRSS*/ 9500, /*fileRSS*/ 9500, /*anonRSS*/ 11500, /*swap*/9500};
+        long[] rssAfter2 =
+                new long[]{/*totalRSS*/ 8000, /*fileRSS*/ 8000, /*anonRSS*/ 9000, /*swap*/8000};
+        // Delta between rssAfter1 and rssBefore3 is above threshold (13000).
+        long[] rssBefore3 =
+                new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 18000, /*anonRSS*/ 13000, /*swap*/ 7000};
+        long[] rssAfter3 =
+                new long[]{/*totalRSS*/ 10000, /*fileRSS*/ 11000, /*anonRSS*/ 10000, /*swap*/ 6000};
+        long[] valuesAfter = {};
+        // Process that passes properties.
+        int pid = 1;
+        ProcessRecord processRecord = makeProcessRecord(pid, 2, 3, "p1", "app1");
+
+        // GIVEN we simulate RSS memory before above thresholds and it is the first time 'p1' is
+        // compacted.
+        mProcessDependencies.setRss(rssBefore1);
+        mProcessDependencies.setRssAfterCompaction(rssAfter1); //
+        // WHEN we try to run compaction
+        mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+        waitForHandler();
+        // THEN process IS compacted.
+        assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
+        valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
+                pid).getRssAfterCompaction();
+        assertThat(valuesAfter).isEqualTo(rssAfter1);
+
+        // WHEN delta is below threshold (500).
+        mProcessDependencies.setRss(rssBefore2);
+        mProcessDependencies.setRssAfterCompaction(rssAfter2);
+        // This is to avoid throttle of compacting too soon.
+        processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000;
+        // WHEN we try to run compaction.
+        mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+        waitForHandler();
+        // THEN process IS NOT compacted - values after compaction for process 1 should remain the
+        // same as from the last compaction.
+        assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
+        valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
+                pid).getRssAfterCompaction();
+        assertThat(valuesAfter).isEqualTo(rssAfter1);
+
+        // WHEN delta is above threshold (13000).
+        mProcessDependencies.setRss(rssBefore3);
+        mProcessDependencies.setRssAfterCompaction(rssAfter3);
+        // This is to avoid throttle of compacting too soon.
+        processRecord.lastCompactTime = processRecord.lastCompactTime - 10_000;
+        // WHEN we try to run compaction
+        mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+        waitForHandler();
+        // THEN process IS compacted - values after compaction for process 1 should be updated.
+        assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
+        valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
+                pid).getRssAfterCompaction();
+        assertThat(valuesAfter).isEqualTo(rssAfter3);
+
+    }
+
+    @Test
+    public void processWithAnonRSSTooSmall_notFullCompacted() throws Exception {
+        // Initialize CachedAppOptimizer and set flags to (1) enable compaction, (2) set RSS
+        // throttle to 8000.
+        mCachedAppOptimizerUnderTest.init();
+        setFlag(CachedAppOptimizer.KEY_USE_COMPACTION, "true", true);
+        setFlag(CachedAppOptimizer.KEY_COMPACT_FULL_RSS_THROTTLE_KB, "8000", false);
+        initActivityManagerService();
+
+        // Simulate RSS anon memory larger than throttle.
+        long[] rssBelowThreshold =
+                new long[]{/*Total RSS*/ 10000, /*File RSS*/ 10000, /*Anon RSS*/ 7000, /*Swap*/
+                        10000};
+        long[] rssBelowThresholdAfter =
+                new long[]{/*Total RSS*/ 9000, /*File RSS*/ 7000, /*Anon RSS*/ 4000, /*Swap*/
+                        8000};
+        long[] rssAboveThreshold =
+                new long[]{/*Total RSS*/ 10000, /*File RSS*/ 10000, /*Anon RSS*/ 9000, /*Swap*/
+                        10000};
+        long[] rssAboveThresholdAfter =
+                new long[]{/*Total RSS*/ 8000, /*File RSS*/ 9000, /*Anon RSS*/ 6000, /*Swap*/5000};
+        // Process that passes properties.
+        int pid = 1;
+        ProcessRecord processRecord =
+                makeProcessRecord(pid, 2, 3, "p1",
+                        "app1");
+
+        // GIVEN we simulate RSS memory before below threshold.
+        mProcessDependencies.setRss(rssBelowThreshold);
+        mProcessDependencies.setRssAfterCompaction(rssBelowThresholdAfter);
+        // WHEN we try to run compaction
+        mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+        waitForHandler();
+        // THEN process IS NOT compacted.
+        assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNull();
+
+        // GIVEN we simulate RSS memory before above threshold.
+        mProcessDependencies.setRss(rssAboveThreshold);
+        mProcessDependencies.setRssAfterCompaction(rssAboveThresholdAfter);
+        // WHEN we try to run compaction
+        mCachedAppOptimizerUnderTest.compactAppFull(processRecord);
+        waitForHandler();
+        // THEN process IS compacted.
+        assertThat(mCachedAppOptimizerUnderTest.mLastCompactionStats.get(pid)).isNotNull();
+        long[] valuesAfter = mCachedAppOptimizerUnderTest.mLastCompactionStats.get(
+                pid).getRssAfterCompaction();
+        assertThat(valuesAfter).isEqualTo(rssAboveThresholdAfter);
+    }
+
+
+    private void setFlag(String key, String value, boolean defaultValue) throws Exception {
+        mCountDown = new CountDownLatch(1);
+        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, key, value, defaultValue);
+        assertThat(mCountDown.await(5, TimeUnit.SECONDS)).isTrue();
+    }
+
+    private void waitForHandler() {
+        Idle idle = new Idle();
+        mCachedAppOptimizerUnderTest.mCompactionHandler.getLooper().getQueue().addIdleHandler(idle);
+        mCachedAppOptimizerUnderTest.mCompactionHandler.post(() -> { });
+        idle.waitForIdle();
+    }
+
+    private void initActivityManagerService() {
+        mAms = new ActivityManagerService(mInjector, mServiceThreadRule.getThread());
+        mAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
+        mAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
+        mAms.mAtmInternal = spy(mAms.mActivityTaskManager.getAtmInternal());
+        mAms.mPackageManagerInt = mPackageManagerInt;
+    }
+
+    private static final class Idle implements MessageQueue.IdleHandler {
+        private boolean mIdle;
+
+        @Override
+        public boolean queueIdle() {
+            synchronized (this) {
+                mIdle = true;
+                notifyAll();
+            }
+            return false;
+        }
+
+        public synchronized void waitForIdle() {
+            while (!mIdle) {
+                try {
+                    // Wait with a timeout of 10s.
+                    wait(10000);
+                } catch (InterruptedException e) {
+                }
+            }
+        }
+    }
+
     private class TestInjector extends Injector {
 
         TestInjector(Context context) {
@@ -806,4 +1012,29 @@
             return mHandler;
         }
     }
+
+    // Test implementation for ProcessDependencies.
+    private static final class TestProcessDependencies
+            implements CachedAppOptimizer.ProcessDependencies {
+        private long[] mRss;
+        private long[] mRssAfterCompaction;
+
+        @Override
+        public long[] getRss(int pid) {
+            return mRss;
+        }
+
+        @Override
+        public void performCompaction(String action, int pid) throws IOException {
+            mRss = mRssAfterCompaction;
+        }
+
+        public void setRss(long[] newValues) {
+            mRss = newValues;
+        }
+
+        public void setRssAfterCompaction(long[] newValues) {
+            mRssAfterCompaction = newValues;
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index efa25bd..320dacf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -1582,6 +1582,41 @@
                 "s2");
     }
 
+    public void testCachedShortcuts_canPassShortcutLimit() {
+        // Change the max number of shortcuts.
+        mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
+
+        runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+            assertTrue(mManager.setDynamicShortcuts(list(makeLongLivedShortcut("s1"),
+                    makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
+                    makeLongLivedShortcut("s4"))));
+        });
+
+        // Cache All
+        runWithCaller(LAUNCHER_1, USER_0, () -> {
+            mLauncherApps.cacheShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
+                    HANDLE_USER_0);
+        });
+
+        setCaller(CALLING_PACKAGE_1);
+
+        // Get dynamic shortcuts
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
+                "s1", "s2", "s3", "s4");
+        // Get cached shortcuts
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
+                "s1", "s2", "s3", "s4");
+
+        assertTrue(mManager.setDynamicShortcuts(makeShortcuts("sx1", "sx2", "sx3", "sx4")));
+
+        // Get dynamic shortcuts
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_DYNAMIC),
+                "sx1", "sx2", "sx3", "sx4");
+        // Get cached shortcuts
+        assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED),
+                "s1", "s2", "s3", "s4");
+    }
+
     // === Test for launcher side APIs ===
 
     public void testGetShortcuts() {
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index f4e5d56..078c21e 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -81,6 +81,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Pair;
+import android.util.StatsEvent;
 import android.util.Xml;
 
 import androidx.test.InstrumentationRegistry;
@@ -89,6 +90,7 @@
 import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 
+
 import org.json.JSONArray;
 import org.json.JSONObject;
 import org.junit.Before;
@@ -2996,6 +2998,31 @@
                 PKG_O, UID_O, parent.getId(), conversationId, false, false), conversationId);
     }
 
+
+    @Test
+    public void testPullConversationNotificationChannel() {
+        String conversationId = "friend";
+
+        NotificationChannel parent =
+                new NotificationChannel("parent", "messages", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_O, UID_O, parent, true, false);
+
+        String channelId = String.format(
+                CONVERSATION_CHANNEL_ID_FORMAT, parent.getId(), conversationId);
+        NotificationChannel friend = new NotificationChannel(channelId,
+                "messages", IMPORTANCE_DEFAULT);
+        friend.setConversationId(parent.getId(), conversationId);
+        mHelper.createNotificationChannel(PKG_O, UID_O, friend, true, false);
+        ArrayList<StatsEvent> events = new ArrayList<>();
+        mHelper.pullPackageChannelPreferencesStats(events);
+        boolean found = false;
+        for (StatsEvent event : events) {
+            // TODO(b/153195691): inspect the content once it is possible to do so
+            found = true;
+        }
+        assertTrue("conversation was not in the pull", found);
+    }
+
     @Test
     public void testGetNotificationChannel_conversationProvidedByNotCustomizedYet() {
         String conversationId = "friend";
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index bb6f154..b35b323 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -536,13 +536,16 @@
 
         // Assign permission to special system apps
         assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
-                PHONE_PACKAGE_NAME);
+                PHONE_PACKAGE_NAME, true);
         assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
-                BLUETOOTH_PACKAGE_NAME);
+                BLUETOOTH_PACKAGE_NAME, true);
         assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
-                MMS_SERVICE_PACKAGE_NAME);
+                MMS_SERVICE_PACKAGE_NAME, true);
         assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
-                TELEPHONY_PROVIDER_PACKAGE_NAME);
+                TELEPHONY_PROVIDER_PACKAGE_NAME, true);
+        // CellbroadcastReceiver is a mainline module thus skip signature match.
+        assignExclusiveSmsPermissionsToSystemApp(context, packageManager, appOps,
+                CellBroadcastUtils.getDefaultCellBroadcastReceiverPackageName(context), false);
 
         // Give AppOps permission to UID 1001 which contains multiple
         // apps, all of them should be able to write to telephony provider.
@@ -744,17 +747,23 @@
      * @param packageManager The package manager instance
      * @param appOps The AppOps manager instance
      * @param packageName The package name of the system app
+     * @param sigatureMatch whether to check signature match
      */
     private static void assignExclusiveSmsPermissionsToSystemApp(Context context,
-            PackageManager packageManager, AppOpsManager appOps, String packageName) {
+            PackageManager packageManager, AppOpsManager appOps, String packageName,
+            boolean sigatureMatch) {
         // First check package signature matches the caller's package signature.
         // Since this class is only used internally by the system, this check makes sure
         // the package signature matches system signature.
-        final int result = packageManager.checkSignatures(context.getPackageName(), packageName);
-        if (result != PackageManager.SIGNATURE_MATCH) {
-            Log.e(LOG_TAG, packageName + " does not have system signature");
-            return;
+        if (sigatureMatch) {
+            final int result = packageManager.checkSignatures(context.getPackageName(),
+                    packageName);
+            if (result != PackageManager.SIGNATURE_MATCH) {
+                Log.e(LOG_TAG, packageName + " does not have system signature");
+                return;
+            }
         }
+
         try {
             PackageInfo info = packageManager.getPackageInfo(packageName, 0);
             int mode = appOps.unsafeCheckOp(AppOpsManager.OPSTR_WRITE_SMS, info.applicationInfo.uid,