Merge "Set correct bounds when offset and expanded" into rvc-dev
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 9844120..8ba52ef 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -6032,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;
 }
 
 /**
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/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/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/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/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,