Merge "Delete unused camera app helper"
diff --git a/core/api/current.txt b/core/api/current.txt
index 941897c..f786e36 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -24664,11 +24664,15 @@
     field public static final int SELECTION_BEHAVIOR_GO_TO_APP = 2; // 0x2
     field public static final int SELECTION_BEHAVIOR_NONE = 0; // 0x0
     field public static final int SELECTION_BEHAVIOR_TRANSFER = 1; // 0x1
-    field public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 3; // 0x3
+    field public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 4; // 0x4
     field public static final int SUBTEXT_CUSTOM = 10000; // 0x2710
-    field public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 2; // 0x2
+    field public static final int SUBTEXT_DEVICE_LOW_POWER = 5; // 0x5
+    field public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 3; // 0x3
+    field public static final int SUBTEXT_ERROR_UNKNOWN = 1; // 0x1
     field public static final int SUBTEXT_NONE = 0; // 0x0
-    field public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 1; // 0x1
+    field public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 2; // 0x2
+    field public static final int SUBTEXT_TRACK_UNSUPPORTED = 7; // 0x7
+    field public static final int SUBTEXT_UNAUTHORIZED = 6; // 0x6
   }
 
   public static final class RouteListingPreference.Item.Builder {
@@ -32205,7 +32209,6 @@
     method public void onEarlyReportFinished();
     method public void onError(int);
     method public void onFinished();
-    method public void onFinished(@NonNull String);
     method public void onProgress(@FloatRange(from=0.0f, to=100.0f) float);
     field public static final int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; // 0x5
     field public static final int BUGREPORT_ERROR_INVALID_INPUT = 1; // 0x1
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index d6aa7fc..43d733c 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1231,6 +1231,7 @@
     field @Deprecated public static final String ACTION_STATE_USER_SETUP_COMPLETE = "android.app.action.STATE_USER_SETUP_COMPLETE";
     field @RequiresPermission(android.Manifest.permission.LAUNCH_DEVICE_MANAGER_SETUP) public static final String ACTION_UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER = "android.app.action.UPDATE_DEVICE_POLICY_MANAGEMENT_ROLE_HOLDER";
     field public static final int EXEMPT_FROM_APP_STANDBY = 0; // 0x0
+    field public static final int EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS = 1; // 0x1
     field public static final String EXTRA_FORCE_UPDATE_ROLE_HOLDER = "android.app.extra.FORCE_UPDATE_ROLE_HOLDER";
     field public static final String EXTRA_LOST_MODE_LOCATION = "android.app.extra.LOST_MODE_LOCATION";
     field public static final String EXTRA_PROFILE_OWNER_NAME = "android.app.extra.PROFILE_OWNER_NAME";
@@ -6756,6 +6757,7 @@
     field public static final int AUDIOFOCUS_FLAG_LOCK = 4; // 0x4
     field public static final int AUDIOFOCUS_FLAG_PAUSES_ON_DUCKABLE_LOSS = 2; // 0x2
     field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE = 3; // 0x3
+    field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5; // 0x5
     field public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4; // 0x4
     field public static final int DEVICE_VOLUME_BEHAVIOR_FIXED = 2; // 0x2
     field public static final int DEVICE_VOLUME_BEHAVIOR_FULL = 1; // 0x1
@@ -10073,6 +10075,10 @@
     method @RequiresPermission(android.Manifest.permission.DUMP) @WorkerThread public void startBugreport(@NonNull android.os.ParcelFileDescriptor, @Nullable android.os.ParcelFileDescriptor, @NonNull android.os.BugreportParams, @NonNull java.util.concurrent.Executor, @NonNull android.os.BugreportManager.BugreportCallback);
   }
 
+  public abstract static class BugreportManager.BugreportCallback {
+    method public void onFinished(@NonNull String);
+  }
+
   public final class BugreportParams {
     ctor public BugreportParams(int);
     ctor public BugreportParams(int, int);
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index b494a20..3fe63d8 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3877,13 +3877,24 @@
     public static final int EXEMPT_FROM_APP_STANDBY =  0;
 
     /**
+     * Prevent an app from dismissible notifications. Starting from Android U, notifications with
+     * the ongoing parameter can be dismissed by a user on an unlocked device. An app with
+     * this exemption can create non-dismissable notifications.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS =  1;
+
+    /**
      * Exemptions to platform restrictions, given to an application through
      * {@link #setApplicationExemptions(String, Set)}.
      *
      * @hide
      */
     @IntDef(prefix = { "EXEMPT_FROM_"}, value = {
-            EXEMPT_FROM_APP_STANDBY
+            EXEMPT_FROM_APP_STANDBY,
+            EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ApplicationExemptionConstants {}
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 3a61ca1..1b5c196 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -304,4 +304,9 @@
      * True if either the entire device or the user is organization managed.
      */
     public abstract boolean isUserOrganizationManaged(@UserIdInt int userId);
+
+    /**
+     * Returns whether the application exemptions feature flag is enabled.
+     */
+    public abstract boolean isApplicationExemptionsFlagEnabled();
 }
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 2d2b0fc..086b0e5 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -70,10 +70,7 @@
      * An interface describing the callback for bugreport progress and status.
      *
      * <p>Callers will receive {@link #onProgress} calls as the bugreport progresses, followed by a
-     * terminal call to either {@link #onFinished} or {@link #onError}. Note that
-     * {@link #onFinished(String)} will only be invoked when calling {@code startBugreport} with the
-     * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag set. Otherwise,
-     * {@link #onFinished()} will be invoked.
+     * terminal call to either {@link #onFinished} or {@link #onError}.
      *
      * <p>If an issue is encountered while starting the bugreport asynchronously, callers will
      * receive an {@link #onError} call without any {@link #onProgress} callbacks.
@@ -149,8 +146,7 @@
         /** Called when taking bugreport finishes successfully.
          *
          * <p>This callback will be invoked if the
-         * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is not set. Otherwise, the
-         * {@link #onFinished(String)} callback will be invoked.
+         * {@link BugreportParams#BUGREPORT_FLAG_DEFER_CONSENT} flag is not set.
          */
         public void onFinished() {}
 
@@ -161,8 +157,10 @@
          * {@link #onFinished()} callback will be invoked.
          *
          * @param bugreportFile the absolute path of the generated bugreport file.
+         * @hide
 
          */
+        @SystemApi
         public void onFinished(@NonNull String bugreportFile) {}
 
         /**
diff --git a/core/java/com/android/internal/expresslog/Histogram.java b/core/java/com/android/internal/expresslog/Histogram.java
new file mode 100644
index 0000000..fe950c4
--- /dev/null
+++ b/core/java/com/android/internal/expresslog/Histogram.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.expresslog;
+
+import android.annotation.NonNull;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+/** CounterHistogram encapsulates StatsD write API calls */
+public final class Histogram {
+
+    private final long mMetricIdHash;
+    private final BinOptions mBinOptions;
+
+    public Histogram(@NonNull String metricId, @NonNull BinOptions binOptions) {
+        mMetricIdHash = Utils.hashString(metricId);
+        mBinOptions = binOptions;
+    }
+
+    /**
+     * Logs increment sample count for automatically calculated bin
+     *
+     * @hide
+     */
+    public void logSample(float sample) {
+        final int binIndex = mBinOptions.getBinForSample(sample);
+        FrameworkStatsLog.write(FrameworkStatsLog.EXPRESS_HISTOGRAM_SAMPLE_REPORTED, mMetricIdHash,
+                /*count*/ 1, binIndex);
+    }
+
+    /** Used by CounterHistogram to map data sample to corresponding bin */
+    public interface BinOptions {
+        /**
+         * Returns bins count to be used by counter histogram
+         *
+         * @return bins count used to initialize Options, including overflow & underflow bins
+         * @hide
+         */
+        int getBinsCount();
+
+        /**
+         * @return zero based index
+         * Calculates bin index for the input sample value
+         * index == 0 stands for underflow
+         * index == getBinsCount() - 1 stands for overflow
+         * @hide
+         */
+        int getBinForSample(float sample);
+    }
+
+    /** Used by CounterHistogram to map data sample to corresponding bin for on uniform bins */
+    public static final class UniformOptions implements BinOptions {
+
+        private final int mBinCount;
+        private final float mMinValue;
+        private final float mExclusiveMaxValue;
+        private final float mBinSize;
+
+        public UniformOptions(int binCount, float minValue, float exclusiveMaxValue) {
+            if (binCount < 1) {
+                throw new IllegalArgumentException("Bin count should be positive number");
+            }
+
+            if (exclusiveMaxValue <= minValue) {
+                throw new IllegalArgumentException("Bins range invalid (maxValue < minValue)");
+            }
+
+            mMinValue = minValue;
+            mExclusiveMaxValue = exclusiveMaxValue;
+            mBinSize = (mExclusiveMaxValue - minValue) / binCount;
+
+            // Implicitly add 2 for the extra undeflow & overflow bins
+            mBinCount = binCount + 2;
+        }
+
+        @Override
+        public int getBinsCount() {
+            return mBinCount;
+        }
+
+        @Override
+        public int getBinForSample(float sample) {
+            if (sample < mMinValue) {
+                // goes to underflow
+                return 0;
+            } else if (sample >= mExclusiveMaxValue) {
+                // goes to overflow
+                return mBinCount - 1;
+            }
+            return (int) ((sample - mMinValue) / mBinSize + 1);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/expresslog/TEST_MAPPING b/core/java/com/android/internal/expresslog/TEST_MAPPING
new file mode 100644
index 0000000..c9b0cf8
--- /dev/null
+++ b/core/java/com/android/internal/expresslog/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "ExpressLogTests",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/tests/expresslog/Android.bp b/core/tests/expresslog/Android.bp
new file mode 100644
index 0000000..cab49a7
--- /dev/null
+++ b/core/tests/expresslog/Android.bp
@@ -0,0 +1,47 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_base_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+    name: "ExpressLogTests",
+
+    srcs: [
+        "src/**/*.java",
+    ],
+
+    static_libs: [
+        "androidx.test.rules",
+        "modules-utils-build",
+    ],
+
+    libs: [
+        "android.test.base",
+        "android.test.runner",
+    ],
+
+    platform_apis: true,
+    test_suites: [
+        "general-tests",
+    ],
+
+    certificate: "platform",
+}
diff --git a/core/tests/expresslog/AndroidManifest.xml b/core/tests/expresslog/AndroidManifest.xml
new file mode 100644
index 0000000..94a39e0
--- /dev/null
+++ b/core/tests/expresslog/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          android:installLocation="internalOnly"
+          package="com.android.internal.expresslog" >
+
+    <application >
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.internal.expresslog"
+            android:label="Telemetry Express Logging Helper Tests" />
+
+</manifest>
diff --git a/core/tests/expresslog/OWNERS b/core/tests/expresslog/OWNERS
new file mode 100644
index 0000000..3dc958b
--- /dev/null
+++ b/core/tests/expresslog/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 719316
+# Stats/expresslog
+file:/services/core/java/com/android/server/stats/OWNERS
diff --git a/core/tests/expresslog/TEST_MAPPING b/core/tests/expresslog/TEST_MAPPING
new file mode 100644
index 0000000..c9b0cf8
--- /dev/null
+++ b/core/tests/expresslog/TEST_MAPPING
@@ -0,0 +1,12 @@
+{
+  "presubmit": [
+    {
+      "name": "ExpressLogTests",
+      "options": [
+        {
+          "exclude-annotation": "org.junit.Ignore"
+        }
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java b/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java
new file mode 100644
index 0000000..9fa6d06
--- /dev/null
+++ b/core/tests/expresslog/src/com/android/internal/expresslog/UniformOptionsTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.expresslog;
+
+import androidx.test.filters.SmallTest;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class UniformOptionsTest {
+    private static final String TAG = UniformOptionsTest.class.getSimpleName();
+
+    @Test
+    @SmallTest
+    public void testGetBinsCount() {
+        Histogram.UniformOptions options1 = new Histogram.UniformOptions(1, 100, 1000);
+        assertEquals(3, options1.getBinsCount());
+
+        Histogram.UniformOptions options10 = new Histogram.UniformOptions(10, 100, 1000);
+        assertEquals(12, options10.getBinsCount());
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testConstructZeroBinsCount() {
+        new Histogram.UniformOptions(0, 100, 1000);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testConstructNegativeBinsCount() {
+        new Histogram.UniformOptions(-1, 100, 1000);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    @SmallTest
+    public void testConstructMaxValueLessThanMinValue() {
+        new Histogram.UniformOptions(10, 1000, 100);
+    }
+
+    @Test
+    @SmallTest
+    public void testBinIndexForRangeEqual1() {
+        Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 11);
+        for (int i = 0, bins = options.getBinsCount(); i < bins; i++) {
+            assertEquals(i, options.getBinForSample(i));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testBinIndexForRangeEqual2() {
+        Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 21);
+        for (int i = 0, bins = options.getBinsCount(); i < bins; i++) {
+            assertEquals(i, options.getBinForSample(i * 2));
+            assertEquals(i, options.getBinForSample(i * 2 - 1));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testBinIndexForRangeEqual5() {
+        Histogram.UniformOptions options = new Histogram.UniformOptions(2, 0, 10);
+        assertEquals(4, options.getBinsCount());
+        for (int i = 0; i < 2; i++) {
+            for (int sample = 0; sample < 5; sample++) {
+                assertEquals(i + 1, options.getBinForSample(i * 5 + sample));
+            }
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testBinIndexForRangeEqual10() {
+        Histogram.UniformOptions options = new Histogram.UniformOptions(10, 1, 101);
+        assertEquals(0, options.getBinForSample(0));
+        assertEquals(options.getBinsCount() - 2, options.getBinForSample(100));
+        assertEquals(options.getBinsCount() - 1, options.getBinForSample(101));
+
+        final float binSize = (101 - 1) / 10f;
+        for (int i = 1, bins = options.getBinsCount() - 1; i < bins; i++) {
+            assertEquals(i, options.getBinForSample(i * binSize));
+        }
+    }
+
+    @Test
+    @SmallTest
+    public void testBinIndexForRangeEqual90() {
+        final int binCount = 10;
+        final int minValue = 100;
+        final int maxValue = 100000;
+
+        Histogram.UniformOptions options = new Histogram.UniformOptions(binCount, minValue,
+                maxValue);
+
+        // logging underflow sample
+        assertEquals(0, options.getBinForSample(minValue - 1));
+
+        // logging overflow sample
+        assertEquals(binCount + 1, options.getBinForSample(maxValue));
+        assertEquals(binCount + 1, options.getBinForSample(maxValue + 1));
+
+        // logging min edge sample
+        assertEquals(1, options.getBinForSample(minValue));
+
+        // logging max edge sample
+        assertEquals(binCount, options.getBinForSample(maxValue - 1));
+
+        // logging single valid sample per bin
+        final int binSize = (maxValue - minValue) / binCount;
+
+        for (int i = 0; i < binCount; i++) {
+            assertEquals(i + 1, options.getBinForSample(minValue + binSize * i));
+        }
+    }
+}
diff --git a/media/java/android/media/AudioDeviceVolumeManager.java b/media/java/android/media/AudioDeviceVolumeManager.java
index 77fa9dc..bf3612d 100644
--- a/media/java/android/media/AudioDeviceVolumeManager.java
+++ b/media/java/android/media/AudioDeviceVolumeManager.java
@@ -146,13 +146,16 @@
          * @param register true for registering, false for unregistering
          * @param device device for which volume is monitored
          */
+        @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+                android.Manifest.permission.BLUETOOTH_PRIVILEGED })
         public void register(boolean register, @NonNull AudioDeviceAttributes device,
-                @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment) {
+                @NonNull List<VolumeInfo> volumes, boolean handlesVolumeAdjustment,
+                @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
             try {
                 getService().registerDeviceVolumeDispatcherForAbsoluteVolume(register,
                         this, mPackageName,
                         Objects.requireNonNull(device), Objects.requireNonNull(volumes),
-                        handlesVolumeAdjustment);
+                        handlesVolumeAdjustment, behavior);
             } catch (RemoteException e) {
                 e.rethrowFromSystemServer();
             }
@@ -234,6 +237,77 @@
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnAudioDeviceVolumeChangedListener vclistener,
             boolean handlesVolumeAdjustment) {
+        baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
+                handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+    }
+
+    /**
+     * @hide
+     * Configures a device to use absolute volume model, and registers a listener for receiving
+     * volume updates to apply on that device.
+     *
+     * Should be used instead of {@link #setDeviceAbsoluteVolumeBehavior} when there is no reliable
+     * way to set the device's volume to a percentage.
+     *
+     * @param device the audio device set to absolute volume mode
+     * @param volume the type of volume this device responds to
+     * @param executor the Executor used for receiving volume updates through the listener
+     * @param vclistener the callback for volume updates
+     */
+    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+    public void setDeviceAbsoluteVolumeAdjustOnlyBehavior(
+            @NonNull AudioDeviceAttributes device,
+            @NonNull VolumeInfo volume,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAudioDeviceVolumeChangedListener vclistener,
+            boolean handlesVolumeAdjustment) {
+        final ArrayList<VolumeInfo> volumes = new ArrayList<>(1);
+        volumes.add(volume);
+        setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior(device, volumes, executor, vclistener,
+                handlesVolumeAdjustment);
+    }
+
+    /**
+     * @hide
+     * Configures a device to use absolute volume model applied to different volume types, and
+     * registers a listener for receiving volume updates to apply on that device.
+     *
+     * Should be used instead of {@link #setDeviceAbsoluteMultiVolumeBehavior} when there is
+     * no reliable way to set the device's volume to a percentage.
+     *
+     * @param device the audio device set to absolute multi-volume mode
+     * @param volumes the list of volumes the given device responds to
+     * @param executor the Executor used for receiving volume updates through the listener
+     * @param vclistener the callback for volume updates
+     */
+    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+    public void setDeviceAbsoluteMultiVolumeAdjustOnlyBehavior(
+            @NonNull AudioDeviceAttributes device,
+            @NonNull List<VolumeInfo> volumes,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAudioDeviceVolumeChangedListener vclistener,
+            boolean handlesVolumeAdjustment) {
+        baseSetDeviceAbsoluteMultiVolumeBehavior(device, volumes, executor, vclistener,
+                handlesVolumeAdjustment, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+    }
+
+    /**
+     * Base method for configuring a device to use absolute volume behavior, or one of its variants.
+     * See {@link AudioManager#AbsoluteDeviceVolumeBehavior} for a list of allowed behaviors.
+     *
+     * @param behavior the variant of absolute device volume behavior to adopt
+     */
+    @RequiresPermission(anyOf = { android.Manifest.permission.MODIFY_AUDIO_ROUTING,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED })
+    private void baseSetDeviceAbsoluteMultiVolumeBehavior(
+            @NonNull AudioDeviceAttributes device,
+            @NonNull List<VolumeInfo> volumes,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnAudioDeviceVolumeChangedListener vclistener,
+            boolean handlesVolumeAdjustment,
+            @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
         Objects.requireNonNull(device);
         Objects.requireNonNull(volumes);
         Objects.requireNonNull(executor);
@@ -253,7 +327,8 @@
                 mDeviceVolumeListeners.removeIf(info -> info.mDevice.equalTypeAddress(device));
             }
             mDeviceVolumeListeners.add(listenerInfo);
-            mDeviceVolumeDispatcherStub.register(true, device, volumes, handlesVolumeAdjustment);
+            mDeviceVolumeDispatcherStub.register(true, device, volumes, handlesVolumeAdjustment,
+                    behavior);
         }
     }
 
@@ -375,6 +450,8 @@
                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE";
             case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
                 return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE";
+            case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
+                return "DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY";
             default:
                 return "invalid volume behavior " + behavior;
         }
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 39d965f..7de3abc3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6231,6 +6231,15 @@
     @SystemApi
     public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE = 4;
 
+    /**
+     * @hide
+     * A variant of {@link #DEVICE_VOLUME_BEHAVIOR_ABSOLUTE} where the host cannot reliably set
+     * the volume percentage of the audio device. Specifically, {@link #setStreamVolume} will have
+     * no effect, or an unreliable effect.
+     */
+    @SystemApi
+    public static final int DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 5;
+
     /** @hide */
     @IntDef({
             DEVICE_VOLUME_BEHAVIOR_VARIABLE,
@@ -6238,6 +6247,7 @@
             DEVICE_VOLUME_BEHAVIOR_FIXED,
             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+            DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeviceVolumeBehavior {}
@@ -6250,11 +6260,23 @@
             DEVICE_VOLUME_BEHAVIOR_FIXED,
             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
             DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE,
+            DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface DeviceVolumeBehaviorState {}
 
     /**
+     * Variants of absolute volume behavior that are set in {@link AudioDeviceVolumeManager}.
+     * @hide
+     */
+    @IntDef({
+            DEVICE_VOLUME_BEHAVIOR_ABSOLUTE,
+            DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AbsoluteDeviceVolumeBehavior {}
+
+    /**
      * @hide
      * Throws IAE on an invalid volume behavior value
      * @param volumeBehavior behavior value to check
@@ -6266,6 +6288,7 @@
             case DEVICE_VOLUME_BEHAVIOR_FIXED:
             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
             case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
+            case DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
                 return;
             default:
                 throw new IllegalArgumentException("Illegal volume behavior " + volumeBehavior);
@@ -6305,6 +6328,16 @@
 
     /**
      * @hide
+     * Controls whether DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY may be returned by
+     * getDeviceVolumeBehavior. If this is disabled, DEVICE_VOLUME_BEHAVIOR_FULL is returned
+     * in its place.
+     */
+    @ChangeId
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+    public static final long RETURN_DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY = 240663182L;
+
+    /**
+     * @hide
      * Returns the volume device behavior for the given audio device
      * @param device the audio device
      * @return the volume behavior for the device
@@ -6322,7 +6355,12 @@
         // communicate with service
         final IAudioService service = getService();
         try {
-            return service.getDeviceVolumeBehavior(device);
+            int behavior = service.getDeviceVolumeBehavior(device);
+            if (!CompatChanges.isChangeEnabled(RETURN_DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY)
+                    && behavior == DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY) {
+                return AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL;
+            }
+            return behavior;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 3e0356f..1c517e7 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -629,12 +629,13 @@
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
     int[] getActiveAssistantServiceUids();
 
-    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)")
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(anyOf={android.Manifest.permission.MODIFY_AUDIO_ROUTING,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
     void registerDeviceVolumeDispatcherForAbsoluteVolume(boolean register,
             in IAudioDeviceVolumeDispatcher cb,
             in String packageName,
             in AudioDeviceAttributes device, in List<VolumeInfo> volumes,
-            boolean handlesvolumeAdjustment);
+            boolean handlesvolumeAdjustment,
+            int volumeBehavior);
 
     AudioHalVersionInfo getHalVersion();
 
diff --git a/media/java/android/media/RouteListingPreference.java b/media/java/android/media/RouteListingPreference.java
index 6caedda..8e5cd95 100644
--- a/media/java/android/media/RouteListingPreference.java
+++ b/media/java/android/media/RouteListingPreference.java
@@ -300,9 +300,13 @@
                 prefix = {"SUBTEXT_"},
                 value = {
                     SUBTEXT_NONE,
+                    SUBTEXT_ERROR_UNKNOWN,
                     SUBTEXT_SUBSCRIPTION_REQUIRED,
                     SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED,
                     SUBTEXT_AD_ROUTING_DISALLOWED,
+                    SUBTEXT_DEVICE_LOW_POWER,
+                    SUBTEXT_UNAUTHORIZED,
+                    SUBTEXT_TRACK_UNSUPPORTED,
                     SUBTEXT_CUSTOM
                 })
         public @interface SubText {}
@@ -310,20 +314,40 @@
         /** The corresponding route has no associated subtext. */
         public static final int SUBTEXT_NONE = 0;
         /**
+         * The corresponding route's subtext must indicate that it is not available because of an
+         * unknown error.
+         */
+        public static final int SUBTEXT_ERROR_UNKNOWN = 1;
+        /**
          * The corresponding route's subtext must indicate that it requires a special subscription
          * in order to be available for routing.
          */
-        public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 1;
+        public static final int SUBTEXT_SUBSCRIPTION_REQUIRED = 2;
         /**
          * The corresponding route's subtext must indicate that downloaded content cannot be routed
          * to it.
          */
-        public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 2;
+        public static final int SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED = 3;
         /**
          * The corresponding route's subtext must indicate that it is not available because an ad is
          * in progress.
          */
-        public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 3;
+        public static final int SUBTEXT_AD_ROUTING_DISALLOWED = 4;
+        /**
+         * The corresponding route's subtext must indicate that it is not available because the
+         * device is in low-power mode.
+         */
+        public static final int SUBTEXT_DEVICE_LOW_POWER = 5;
+        /**
+         * The corresponding route's subtext must indicate that it is not available because the user
+         * is not authorized to route to it.
+         */
+        public static final int SUBTEXT_UNAUTHORIZED = 6;
+        /**
+         * The corresponding route's subtext must indicate that it is not available because the
+         * device does not support the current media track.
+         */
+        public static final int SUBTEXT_TRACK_UNSUPPORTED = 7;
         /**
          * The corresponding route's subtext must be obtained from {@link
          * #getCustomSubtextMessage()}.
@@ -415,10 +439,14 @@
          * <p>If this method returns {@link #SUBTEXT_CUSTOM}, then the subtext is obtained form
          * {@link #getCustomSubtextMessage()}.
          *
-         * @see #SUBTEXT_NONE
-         * @see #SUBTEXT_SUBSCRIPTION_REQUIRED
-         * @see #SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED
-         * @see #SUBTEXT_AD_ROUTING_DISALLOWED
+         * @see #SUBTEXT_NONE,
+         * @see #SUBTEXT_ERROR_UNKNOWN,
+         * @see #SUBTEXT_SUBSCRIPTION_REQUIRED,
+         * @see #SUBTEXT_DOWNLOADED_CONTENT_ROUTING_DISALLOWED,
+         * @see #SUBTEXT_AD_ROUTING_DISALLOWED,
+         * @see #SUBTEXT_DEVICE_LOW_POWER,
+         * @see #SUBTEXT_UNAUTHORIZED ,
+         * @see #SUBTEXT_TRACK_UNSUPPORTED,
          * @see #SUBTEXT_CUSTOM
          */
         @SubText
diff --git a/packages/SettingsLib/res/drawable/ic_media_avr_device.xml b/packages/SettingsLib/res/drawable/ic_media_avr_device.xml
new file mode 100644
index 0000000..3de0d84
--- /dev/null
+++ b/packages/SettingsLib/res/drawable/ic_media_avr_device.xml
@@ -0,0 +1,40 @@
+<!--
+  ~ Copyright (C) 2023 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+    <path
+        android:pathData="M18.918,7C18.963,7 19,7.037 19,7.082V14.918C19,14.963 18.963,15 18.918,15H3.082C3.037,15 3,14.963 3,14.918V7.082C3,7.037 3.037,7 3.082,7H18.918ZM18.918,5H3.082C1.932,5 1,5.932 1,7.082V14.918C1,16.068 1.932,17 3.082,17H18.918C20.068,17 21,16.068 21,14.918V7.082C21,5.932 20.068,5 18.918,5Z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M18,15H17C16.448,15 16,15.448 16,16V17C16,17.552 16.448,18 17,18H18C18.552,18 19,17.552 19,17V16C19,15.448 18.552,15 18,15Z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M5,15H4C3.448,15 3,15.448 3,16V17C3,17.552 3.448,18 4,18H5C5.552,18 6,17.552 6,17V16C6,15.448 5.552,15 5,15Z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M15.5,12.5C16.328,12.5 17,11.828 17,11C17,10.172 16.328,9.5 15.5,9.5C14.672,9.5 14,10.172 14,11C14,11.828 14.672,12.5 15.5,12.5Z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M15.5,10C16.052,10 16.5,10.448 16.5,11C16.5,11.552 16.052,12 15.5,12C14.948,12 14.5,11.552 14.5,11C14.5,10.448 14.948,10 15.5,10ZM15.5,9C14.397,9 13.5,9.897 13.5,11C13.5,12.103 14.397,13 15.5,13C16.603,13 17.5,12.103 17.5,11C17.5,9.897 16.603,9 15.5,9Z"
+        android:fillColor="#ffffff"/>
+    <path
+        android:pathData="M5,9h7v4h-7z"
+        android:fillColor="#ffffff"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
new file mode 100644
index 0000000..c38dfe3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ComplexMediaDevice.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.media;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.media.MediaRoute2Info;
+import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
+
+import com.android.settingslib.R;
+
+/**
+ * ComplexMediaDevice extends MediaDevice to represents device with signals from a number of
+ * sources.
+ */
+public class ComplexMediaDevice extends MediaDevice {
+
+    private final String mSummary = "";
+
+    ComplexMediaDevice(Context context, MediaRouter2Manager routerManager,
+            MediaRoute2Info info, String packageName,
+            RouteListingPreference.Item item) {
+        super(context, routerManager, info, packageName, item);
+    }
+
+    // MediaRoute2Info.getName was made public on API 34, but exists since API 30.
+    @SuppressWarnings("NewApi")
+    @Override
+    public String getName() {
+        return mRouteInfo.getName().toString();
+    }
+
+    @Override
+    public String getSummary() {
+        return mSummary;
+    }
+
+    @Override
+    public Drawable getIcon() {
+        return mContext.getDrawable(R.drawable.ic_media_avr_device);
+    }
+
+    @Override
+    public Drawable getIconWithoutBackground() {
+        return mContext.getDrawable(R.drawable.ic_media_avr_device);
+    }
+
+    @Override
+    public String getId() {
+        return MediaDeviceUtils.getId(mRouteInfo);
+    }
+
+    public boolean isConnected() {
+        return true;
+    }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 77e514f..85d4fab 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -22,6 +22,7 @@
 import static android.media.MediaRoute2Info.TYPE_GROUP;
 import static android.media.MediaRoute2Info.TYPE_HDMI;
 import static android.media.MediaRoute2Info.TYPE_HEARING_AID;
+import static android.media.MediaRoute2Info.TYPE_REMOTE_AUDIO_VIDEO_RECEIVER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
 import static android.media.MediaRoute2Info.TYPE_REMOTE_TV;
 import static android.media.MediaRoute2Info.TYPE_UNKNOWN;
@@ -562,6 +563,9 @@
                             route, mPackageName);
                 }
                 break;
+            case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
+                mediaDevice = new ComplexMediaDevice(mContext, mRouterManager, route,
+                        mPackageName, mPreferenceItemMap.get(route.getId()));
             default:
                 Log.w(TAG, "addMediaDevice() unknown device type : " + deviceType);
                 break;
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
index d242198..38387f1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/MediaDevice.java
@@ -31,6 +31,7 @@
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
 import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
 import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION;
+import static android.media.RouteListingPreference.Item.FLAG_ONGOING_SESSION_MANAGED;
 import static android.media.RouteListingPreference.Item.FLAG_SUGGESTED;
 import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_TRANSFER;
 import static android.media.RouteListingPreference.Item.SUBTEXT_AD_ROUTING_DISALLOWED;
@@ -257,6 +258,16 @@
     }
 
     /**
+     * Checks if device is the host for ongoing shared session, which allow user to adjust volume
+     *
+     * @return true if device is the host for ongoing shared session
+     */
+    public boolean isHostForOngoingSession() {
+        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+                && Api34Impl.isHostForOngoingSession(mItem);
+    }
+
+    /**
      * Checks if device is suggested device from application
      *
      * @return true if device is suggested device
@@ -554,6 +565,13 @@
     @RequiresApi(34)
     private static class Api34Impl {
         @DoNotInline
+        static boolean isHostForOngoingSession(RouteListingPreference.Item item) {
+            int flags = item != null ? item.getFlags() : 0;
+            return (flags & FLAG_ONGOING_SESSION) != 0
+                    && (flags & FLAG_ONGOING_SESSION_MANAGED) != 0;
+        }
+
+        @DoNotInline
         static boolean isSuggestedDevice(RouteListingPreference.Item item) {
             return item != null && (item.getFlags() & FLAG_SUGGESTED) != 0;
         }
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index b8d78fb..5aa7769 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -304,10 +304,16 @@
     ) {
         val view =
             openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground
-                ?: throw IllegalStateException(
-                    "The animateFrom dialog was not animated using " +
-                        "DialogLaunchAnimator.showFrom(View|Dialog)"
-                )
+        if (view == null) {
+            Log.w(
+                TAG,
+                "Showing dialog $dialog normally as the dialog it is shown from was not shown " +
+                    "using DialogLaunchAnimator"
+            )
+            dialog.show()
+            return
+        }
+
         showFromView(
             dialog,
             view,
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 6ff406c3..cea6a07 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -616,7 +616,11 @@
     // TODO(b259590361): Tracking bug
     val EXPERIMENTAL_FLAG = unreleasedFlag(2, "exp_flag_release")
 
-    // 2600 - keyboard shortcut
+    // 2600 - keyboard
     // TODO(b/259352579): Tracking Bug
     @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = unreleasedFlag(2600, "shortcut_list_search_layout")
+
+    // TODO(b/259428678): Tracking Bug
+    @JvmField
+    val KEYBOARD_BACKLIGHT_INDICATOR = unreleasedFlag(2601, "keyboard_backlight_indicator")
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 3b45615..ffcb8c6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -204,7 +204,7 @@
                         && mController.isAdvancedLayoutSupported() && device.hasSubtext()) {
                     boolean isActiveWithOngoingSession =
                             (device.hasOngoingSession() && currentlyConnected);
-                    boolean isHost = mController.isVolumeControlEnabled(device)
+                    boolean isHost = device.isHostForOngoingSession()
                             && isActiveWithOngoingSession;
                     if (isHost) {
                         mCurrentActivePosition = position;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 206a620..039dafb 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -800,6 +800,11 @@
     }
 
     @Override
+    public void onCarrierNetworkChange(boolean active) {
+        mHandler.post(() -> updateDialog(true /* shouldUpdateMobileNetwork */));
+    }
+
+    @Override
     @WorkerThread
     public void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
             @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 534155c..f7e7366 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -206,6 +206,8 @@
     protected boolean mHasEthernet = false;
     @VisibleForTesting
     protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor;
+    @VisibleForTesting
+    protected boolean mCarrierNetworkChangeMode;
 
     private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
             new KeyguardUpdateMonitorCallback() {
@@ -507,10 +509,13 @@
     Drawable getSignalStrengthIcon(int subId, Context context, int level, int numLevels,
             int iconType, boolean cutOut) {
         boolean isForDds = subId == mDefaultDataSubId;
+        int levelDrawable =
+                mCarrierNetworkChangeMode ? SignalDrawable.getCarrierChangeState(numLevels)
+                        : SignalDrawable.getState(level, numLevels, cutOut);
         if (isForDds) {
-            mSignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
+            mSignalDrawable.setLevel(levelDrawable);
         } else {
-            mSecondarySignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
+            mSecondarySignalDrawable.setLevel(levelDrawable);
         }
 
         // Make the network type drawable
@@ -672,10 +677,13 @@
         }
 
         int resId = Objects.requireNonNull(mapIconSets(config).get(iconKey)).dataContentDescription;
+        SignalIcon.MobileIconGroup iconGroup;
         if (isCarrierNetworkActive()) {
-            SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
-                    TelephonyIcons.CARRIER_MERGED_WIFI;
-            resId = carrierMergedWifiIconGroup.dataContentDescription;
+            iconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
+            resId = iconGroup.dataContentDescription;
+        } else if (mCarrierNetworkChangeMode) {
+            iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
+            resId = iconGroup.dataContentDescription;
         }
 
         return resId != 0
@@ -1094,7 +1102,8 @@
             TelephonyCallback.DisplayInfoListener,
             TelephonyCallback.ServiceStateListener,
             TelephonyCallback.SignalStrengthsListener,
-            TelephonyCallback.UserMobileDataStateListener {
+            TelephonyCallback.UserMobileDataStateListener,
+            TelephonyCallback.CarrierNetworkListener{
 
         private final int mSubId;
         private InternetTelephonyCallback(int subId) {
@@ -1126,6 +1135,12 @@
         public void onUserMobileDataStateChanged(boolean enabled) {
             mCallback.onUserMobileDataStateChanged(enabled);
         }
+
+        @Override
+        public void onCarrierNetworkChange(boolean active) {
+            mCarrierNetworkChangeMode = active;
+            mCallback.onCarrierNetworkChange(active);
+        }
     }
 
     private class InternetOnSubscriptionChangedListener
@@ -1295,6 +1310,8 @@
 
         void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
 
+        void onCarrierNetworkChange(boolean active);
+
         void dismissDialog();
 
         void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index 1e62fd23..316de59 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -268,6 +268,12 @@
         }
     }
 
+    @Test
+    fun showFromDialogDoesNotCrashWhenShownFromRandomDialog() {
+        val dialog = createDialogAndShowFromDialog(animateFrom = TestDialog(context))
+        dialog.dismiss()
+    }
+
     private fun createAndShowDialog(
         animator: DialogLaunchAnimator = dialogLaunchAnimator,
     ): TestDialog {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
new file mode 100644
index 0000000..1e4753e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/coroutines/FlowTest.kt
@@ -0,0 +1,24 @@
+package com.android.systemui.coroutines
+
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class FlowTest : SysuiTestCase() {
+
+    @Test
+    fun collectLastValue() = runTest {
+        val flow = flowOf(0, 1, 2)
+        val lastValue by collectLastValue(flow)
+        assertThat(lastValue).isEqualTo(2)
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index f779845..56e060d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -440,6 +440,7 @@
         when(mMediaOutputController.isSubStatusSupported()).thenReturn(true);
         when(mMediaOutputController.isAdvancedLayoutSupported()).thenReturn(true);
         when(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).thenReturn(true);
+        when(mMediaDevice1.isHostForOngoingSession()).thenReturn(true);
         when(mMediaDevice1.hasSubtext()).thenReturn(true);
         when(mMediaDevice1.getSubtext()).thenReturn(SUBTEXT_CUSTOM);
         when(mMediaDevice1.getSubtextString()).thenReturn(TEST_CUSTOM_SUBTEXT);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 62404cb..84cc977 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -941,6 +941,26 @@
         }
     }
 
+    @Test
+    public void getMobileNetworkSummary_withCarrierNetworkChange() {
+        Resources res = mock(Resources.class);
+        doReturn("Carrier network changing").when(res).getString(anyInt());
+        when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID))).thenReturn(res);
+        InternetDialogController spyController = spy(mInternetDialogController);
+        Map<Integer, TelephonyDisplayInfo> mSubIdTelephonyDisplayInfoMap =
+                spyController.mSubIdTelephonyDisplayInfoMap;
+        TelephonyDisplayInfo info = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
+                TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
+
+        mSubIdTelephonyDisplayInfoMap.put(SUB_ID, info);
+        doReturn(true).when(spyController).isMobileDataEnabled();
+        doReturn(true).when(spyController).activeNetworkIsCellular();
+        spyController.mCarrierNetworkChangeMode = true;
+        String dds = spyController.getMobileNetworkSummary(SUB_ID);
+
+        assertThat(dds).contains(mContext.getString(R.string.carrier_network_change_mode));
+    }
+
     private String getResourcesString(String name) {
         return mContext.getResources().getString(getResourcesId(name));
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
index b7a8d2e..9b4f496 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/coroutines/Flow.kt
@@ -18,6 +18,8 @@
 
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.flow.Flow
@@ -25,16 +27,35 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.runCurrent
 
-/** Collect [flow] in a new [Job] and return a getter for the last collected value. */
+/**
+ * Collect [flow] in a new [Job] and return a getter for the last collected value.
+ * ```
+ * fun myTest() = runTest {
+ *   // ...
+ *   val actual by collectLastValue(underTest.flow)
+ *   assertThat(actual).isEqualTo(expected)
+ * }
+ * ```
+ */
 fun <T> TestScope.collectLastValue(
     flow: Flow<T>,
     context: CoroutineContext = EmptyCoroutineContext,
     start: CoroutineStart = CoroutineStart.DEFAULT,
-): () -> T? {
+): FlowValue<T?> {
     var lastValue: T? = null
     backgroundScope.launch(context, start) { flow.collect { lastValue = it } }
-    return {
+    return FlowValueImpl {
         runCurrent()
         lastValue
     }
 }
+
+/** @see collectLastValue */
+interface FlowValue<T> : ReadOnlyProperty<Any?, T?> {
+    operator fun invoke(): T?
+}
+
+private class FlowValueImpl<T>(private val block: () -> T?) : FlowValue<T> {
+    override operator fun invoke(): T? = block()
+    override fun getValue(thisRef: Any?, property: KProperty<*>): T? = invoke()
+}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index c01424d..e55bddb 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -692,6 +692,7 @@
 
     // Devices where the framework sends a full scale audio signal, and controls the volume of
     // the external audio system separately.
+    // For possible volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
     Map<Integer, AbsoluteVolumeDeviceInfo> mAbsoluteVolumeDeviceInfoMap = new ArrayMap<>();
 
     /**
@@ -702,13 +703,19 @@
         private final List<VolumeInfo> mVolumeInfos;
         private final IAudioDeviceVolumeDispatcher mCallback;
         private final boolean mHandlesVolumeAdjustment;
+        private @AudioManager.AbsoluteDeviceVolumeBehavior int mDeviceVolumeBehavior;
 
-        private AbsoluteVolumeDeviceInfo(AudioDeviceAttributes device, List<VolumeInfo> volumeInfos,
-                IAudioDeviceVolumeDispatcher callback, boolean handlesVolumeAdjustment) {
+        private AbsoluteVolumeDeviceInfo(
+                AudioDeviceAttributes device,
+                List<VolumeInfo> volumeInfos,
+                IAudioDeviceVolumeDispatcher callback,
+                boolean handlesVolumeAdjustment,
+                @AudioManager.AbsoluteDeviceVolumeBehavior int behavior) {
             this.mDevice = device;
             this.mVolumeInfos = volumeInfos;
             this.mCallback = callback;
             this.mHandlesVolumeAdjustment = handlesVolumeAdjustment;
+            this.mDeviceVolumeBehavior = behavior;
         }
 
         /**
@@ -7057,7 +7064,8 @@
     public void registerDeviceVolumeDispatcherForAbsoluteVolume(boolean register,
             IAudioDeviceVolumeDispatcher cb, String packageName,
             AudioDeviceAttributes device, List<VolumeInfo> volumes,
-            boolean handlesVolumeAdjustment) {
+            boolean handlesVolumeAdjustment,
+            @AudioManager.AbsoluteDeviceVolumeBehavior int deviceVolumeBehavior) {
         // verify permissions
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
                 != PackageManager.PERMISSION_GRANTED
@@ -7073,13 +7081,16 @@
         int deviceOut = device.getInternalType();
         if (register) {
             AbsoluteVolumeDeviceInfo info = new AbsoluteVolumeDeviceInfo(
-                    device, volumes, cb, handlesVolumeAdjustment);
-            boolean volumeBehaviorChanged =
-                    removeAudioSystemDeviceOutFromFullVolumeDevices(deviceOut)
-                    | removeAudioSystemDeviceOutFromFixedVolumeDevices(deviceOut)
-                    | (addAudioSystemDeviceOutToAbsVolumeDevices(deviceOut, info) == null);
+                    device, volumes, cb, handlesVolumeAdjustment, deviceVolumeBehavior);
+            AbsoluteVolumeDeviceInfo oldInfo = mAbsoluteVolumeDeviceInfoMap.get(deviceOut);
+            boolean volumeBehaviorChanged = (oldInfo == null)
+                    || (oldInfo.mDeviceVolumeBehavior != deviceVolumeBehavior);
             if (volumeBehaviorChanged) {
-                dispatchDeviceVolumeBehavior(device, AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+                removeAudioSystemDeviceOutFromFullVolumeDevices(deviceOut);
+                removeAudioSystemDeviceOutFromFixedVolumeDevices(deviceOut);
+                addAudioSystemDeviceOutToAbsVolumeDevices(deviceOut, info);
+
+                dispatchDeviceVolumeBehavior(device, deviceVolumeBehavior);
             }
             // Update stream volumes to the given device, if specified in a VolumeInfo.
             // Mute state is not updated because it is stream-wide - the only way to mute a
@@ -7171,6 +7182,7 @@
                                 != null);
                 break;
             case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE:
+            case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY:
             case AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE:
                 throw new IllegalArgumentException("Absolute volume unsupported for now");
         }
@@ -7214,11 +7226,6 @@
         // AudioDeviceInfo.convertDeviceTypeToInternalDevice()
         final int audioSystemDeviceOut = device.getInternalType();
 
-        int setDeviceVolumeBehavior = retrieveStoredDeviceVolumeBehavior(audioSystemDeviceOut);
-        if (setDeviceVolumeBehavior != AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET) {
-            return setDeviceVolumeBehavior;
-        }
-
         // setDeviceVolumeBehavior has not been explicitly called for the device type. Deduce the
         // current volume behavior.
         if (mFullVolumeDevices.contains(audioSystemDeviceOut)) {
@@ -7230,8 +7237,11 @@
         if (mAbsVolumeMultiModeCaseDevices.contains(audioSystemDeviceOut)) {
             return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_MULTI_MODE;
         }
-        if (isAbsoluteVolumeDevice(audioSystemDeviceOut)
-                || isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
+        if (mAbsoluteVolumeDeviceInfoMap.containsKey(audioSystemDeviceOut)) {
+            return mAbsoluteVolumeDeviceInfoMap.get(audioSystemDeviceOut).mDeviceVolumeBehavior;
+        }
+
+        if (isA2dpAbsoluteVolumeDevice(audioSystemDeviceOut)
                 || AudioSystem.isLeAudioDeviceType(audioSystemDeviceOut)) {
             return AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE;
         }
@@ -10680,6 +10690,13 @@
         pw.println();
     }
 
+    private Set<Integer> getAbsoluteVolumeDevicesWithBehavior(int behavior) {
+        return mAbsoluteVolumeDeviceInfoMap.entrySet().stream()
+                .filter(entry -> entry.getValue().mDeviceVolumeBehavior == behavior)
+                .map(Map.Entry::getKey)
+                .collect(Collectors.toSet());
+    }
+
     private String dumpDeviceTypes(@NonNull Set<Integer> deviceTypes) {
         Iterator<Integer> it = deviceTypes.iterator();
         if (!it.hasNext()) {
@@ -10728,14 +10745,20 @@
         pw.print("  mNotifAliasRing="); pw.println(mNotifAliasRing);
         pw.print("  mFixedVolumeDevices="); pw.println(dumpDeviceTypes(mFixedVolumeDevices));
         pw.print("  mFullVolumeDevices="); pw.println(dumpDeviceTypes(mFullVolumeDevices));
-        pw.print("  mAbsoluteVolumeDevices.keySet()="); pw.println(dumpDeviceTypes(
-                mAbsoluteVolumeDeviceInfoMap.keySet()));
+        pw.print("  absolute volume devices="); pw.println(dumpDeviceTypes(
+                getAbsoluteVolumeDevicesWithBehavior(
+                        AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE)));
+        pw.print("  adjust-only absolute volume devices="); pw.println(dumpDeviceTypes(
+                getAbsoluteVolumeDevicesWithBehavior(
+                        AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY)));
         pw.print("  mExtVolumeController="); pw.println(mExtVolumeController);
         pw.print("  mHdmiAudioSystemClient="); pw.println(mHdmiAudioSystemClient);
         pw.print("  mHdmiPlaybackClient="); pw.println(mHdmiPlaybackClient);
         pw.print("  mHdmiTvClient="); pw.println(mHdmiTvClient);
         pw.print("  mHdmiSystemAudioSupported="); pw.println(mHdmiSystemAudioSupported);
-        pw.print("  mHdmiCecVolumeControlEnabled="); pw.println(mHdmiCecVolumeControlEnabled);
+        synchronized (mHdmiClientLock) {
+            pw.print("  mHdmiCecVolumeControlEnabled="); pw.println(mHdmiCecVolumeControlEnabled);
+        }
         pw.print("  mIsCallScreeningModeSupported="); pw.println(mIsCallScreeningModeSupported);
         pw.print("  mic mute FromSwitch=" + mMicMuteFromSwitch
                         + " FromRestrictions=" + mMicMuteFromRestrictions
@@ -12780,11 +12803,14 @@
     }
 
     /**
-     * Returns whether the input device uses absolute volume behavior. This is distinct
-     * from Bluetooth A2DP absolute volume behavior ({@link #isA2dpAbsoluteVolumeDevice}).
+     * Returns whether the input device uses absolute volume behavior, including its variants.
+     * For included volume behaviors, see {@link AudioManager.AbsoluteDeviceVolumeBehavior}.
+     *
+     * This is distinct from Bluetooth A2DP absolute volume behavior
+     * ({@link #isA2dpAbsoluteVolumeDevice}).
      */
     private boolean isAbsoluteVolumeDevice(int deviceType) {
-        return  mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType);
+        return mAbsoluteVolumeDeviceInfoMap.containsKey(deviceType);
     }
 
     /**
@@ -12888,13 +12914,15 @@
         return mFullVolumeDevices.remove(audioSystemDeviceOut);
     }
 
-    private AbsoluteVolumeDeviceInfo addAudioSystemDeviceOutToAbsVolumeDevices(
-            int audioSystemDeviceOut, AbsoluteVolumeDeviceInfo info) {
+    private void addAudioSystemDeviceOutToAbsVolumeDevices(int audioSystemDeviceOut,
+            AbsoluteVolumeDeviceInfo info) {
         if (DEBUG_VOL) {
             Log.d(TAG, "Adding DeviceType: 0x" + Integer.toHexString(audioSystemDeviceOut)
-                    + " from mAbsoluteVolumeDeviceInfoMap");
+                    + " to mAbsoluteVolumeDeviceInfoMap with behavior "
+                    + AudioDeviceVolumeManager.volumeBehaviorName(info.mDeviceVolumeBehavior)
+            );
         }
-        return mAbsoluteVolumeDeviceInfoMap.put(audioSystemDeviceOut, info);
+        mAbsoluteVolumeDeviceInfoMap.put(audioSystemDeviceOut, info);
     }
 
     private AbsoluteVolumeDeviceInfo removeAudioSystemDeviceOutFromAbsVolumeDevices(
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index cc0ecb84..0aa822b 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -579,6 +579,8 @@
 
     private static final boolean ONGOING_DISMISSAL = SystemProperties.getBoolean(
             "persist.sysui.notification.ongoing_dismissal", true);
+    @VisibleForTesting
+    protected boolean mSystemExemptFromDismissal = false;
 
     // used as a mutex for access to all active notifications & listeners
     final Object mNotificationLock = new Object();
@@ -2595,6 +2597,8 @@
         mAllowFgsDismissal = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, true);
+        mSystemExemptFromDismissal =
+                mDpm.isApplicationExemptionsFlagEnabled();
         DeviceConfig.addOnPropertiesChangedListener(
                 DeviceConfig.NAMESPACE_SYSTEMUI,
                 new HandlerExecutor(mHandler),
@@ -6801,12 +6805,20 @@
         // partition.
         boolean isSystemAppExempt = (ai.flags
                 & (ApplicationInfo.FLAG_UPDATED_SYSTEM_APP | ApplicationInfo.FLAG_SYSTEM)) > 0;
-        return isSystemAppExempt || notification.isMediaNotification() || isEnterpriseExempted();
+        return isSystemAppExempt || notification.isMediaNotification() || isEnterpriseExempted(ai);
     }
 
-    // TODO: b/266237746 Enterprise app exemptions
-    private boolean isEnterpriseExempted() {
-        return false;
+    private boolean isEnterpriseExempted(ApplicationInfo ai) {
+        // Check if the app is an organization admin app
+        // TODO(b/234609037): Replace with new DPM APIs to check if organization admin
+        if (mDpm != null && (mDpm.isActiveProfileOwner(ai.uid)
+                || mDpm.isActiveDeviceOwner(ai.uid))) {
+            return true;
+        }
+        // Check if an app has been given system exemption
+        return mSystemExemptFromDismissal && mAppOps.checkOpNoThrow(
+                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid,
+                ai.packageName) == AppOpsManager.MODE_ALLOWED;
     }
 
     private void checkRemoteViews(String pkg, String tag, int id, Notification notification) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 7797ce4..7d89c32 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -59,7 +59,6 @@
 import static android.view.WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
-import static android.view.WindowManager.LayoutParams;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
 import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
@@ -4634,24 +4633,9 @@
      */
     @VisibleForTesting
     SurfaceControl computeImeParent() {
-        if (mImeLayeringTarget != null) {
-            // Ensure changing the IME parent when the layering target that may use IME has
-            // became to the input target for preventing IME flickers.
-            // Note that:
-            // 1) For the imeLayeringTarget that may not use IME but requires IME on top
-            // of it (e.g. an overlay window with NOT_FOCUSABLE|ALT_FOCUSABLE_IM flags), we allow
-            // it to re-parent the IME on top the display to keep the legacy behavior.
-            // 2) Even though the starting window won't use IME, the associated activity
-            // behind the starting window may request the input. If so, then we should still hold
-            // the IME parent change until the activity started the input.
-            boolean imeLayeringTargetMayUseIme =
-                    LayoutParams.mayUseInputMethod(mImeLayeringTarget.mAttrs.flags)
-                    || mImeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING;
-            if (imeLayeringTargetMayUseIme && (mImeInputTarget == null
-                    || mImeLayeringTarget.mActivityRecord != mImeInputTarget.getActivityRecord())) {
-                // Do not change parent if the window hasn't requested IME.
-                return null;
-            }
+        if (!ImeTargetVisibilityPolicy.isReadyToComputeImeParent(mImeLayeringTarget,
+                mImeInputTarget)) {
+            return null;
         }
         // Attach it to app if the target is part of an app and such app is covering the entire
         // screen. If it's not covering the entire screen the IME might extend beyond the apps
diff --git a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
index 83d0f6c..49218ad 100644
--- a/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
+++ b/services/core/java/com/android/server/wm/ImeTargetVisibilityPolicy.java
@@ -17,7 +17,10 @@
 package com.android.server.wm;
 
 
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+
 import android.os.IBinder;
+import android.view.WindowManager;
 
 /**
  * A class for {@link com.android.server.inputmethod.InputMethodManagerService} to
@@ -42,4 +45,36 @@
      * @return {@code true} if success, {@code false} otherwise.
      */
     public abstract boolean updateImeParent(IBinder imeTarget, int displayId);
+
+    /**
+     * Called when {@link DisplayContent#computeImeParent()} to check if it's valid to keep
+     * computing the ime parent.
+     *
+     * @return {@code true} to keep computing the ime parent, {@code false} to defer this operation
+     */
+    public static boolean isReadyToComputeImeParent(WindowState imeLayeringTarget,
+            InputTarget imeInputTarget) {
+        if (imeLayeringTarget == null) {
+            return false;
+        }
+        // Ensure changing the IME parent when the layering target that may use IME has
+        // became to the input target for preventing IME flickers.
+        // Note that:
+        // 1) For the imeLayeringTarget that may not use IME but requires IME on top
+        // of it (e.g. an overlay window with NOT_FOCUSABLE|ALT_FOCUSABLE_IM flags), we allow
+        // it to re-parent the IME on top the display to keep the legacy behavior.
+        // 2) Even though the starting window won't use IME, the associated activity
+        // behind the starting window may request the input. If so, then we should still hold
+        // the IME parent change until the activity started the input.
+        boolean imeLayeringTargetMayUseIme =
+                WindowManager.LayoutParams.mayUseInputMethod(imeLayeringTarget.mAttrs.flags)
+                        || imeLayeringTarget.mAttrs.type == TYPE_APPLICATION_STARTING;
+
+        // Do not change parent if the window hasn't requested IME.
+        var inputAndLayeringTargetsDisagree = (imeInputTarget == null
+                || imeLayeringTarget.mActivityRecord != imeInputTarget.getActivityRecord());
+        var inputTargetStale = imeLayeringTargetMayUseIme && inputAndLayeringTargetsDisagree;
+
+        return !inputTargetStale;
+    }
 }
diff --git a/services/core/java/com/android/server/wm/LetterboxConfiguration.java b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
index 7066a33..5136670 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfiguration.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfiguration.java
@@ -20,6 +20,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ALLOW_IGNORE_ORIENTATION_REQUEST;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_CAMERA_COMPAT_TREATMENT;
+import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.LetterboxConfigurationDeviceConfig.KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY;
 
 import android.annotation.IntDef;
@@ -42,10 +43,6 @@
 
     private static final String TAG = TAG_WITH_CLASS_NAME ? "LetterboxConfiguration" : TAG_ATM;
 
-    @VisibleForTesting
-    static final String DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS =
-            "enable_compat_fake_focus";
-
     /**
      * Override of aspect ratio for fixed orientation letterboxing that is set via ADB with
      * set-fixed-orientation-letterbox-aspect-ratio or via {@link
@@ -115,12 +112,6 @@
     /** Letterboxed app window is aligned to the right side. */
     static final int LETTERBOX_VERTICAL_REACHABILITY_POSITION_BOTTOM = 2;
 
-    @VisibleForTesting
-    static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_IN = "com.android.COMPAT_FAKE_FOCUS_OPT_IN";
-    @VisibleForTesting
-    static final String PROPERTY_COMPAT_FAKE_FOCUS_OPT_OUT =
-            "com.android.COMPAT_FAKE_FOCUS_OPT_OUT";
-
     final Context mContext;
 
     // Responsible for the persistence of letterbox[Horizontal|Vertical]PositionMultiplier
@@ -317,6 +308,9 @@
         mDeviceConfig.updateFlagActiveStatus(
                 /* isActive */ true,
                 /* key */ KEY_ALLOW_IGNORE_ORIENTATION_REQUEST);
+        mDeviceConfig.updateFlagActiveStatus(
+                /* isActive */ mIsCompatFakeFocusEnabled,
+                /* key */ KEY_ENABLE_COMPAT_FAKE_FOCUS);
 
         mLetterboxConfigurationPersister = letterboxConfigurationPersister;
         mLetterboxConfigurationPersister.start();
@@ -1066,9 +1060,7 @@
 
     /** Whether fake sending focus is enabled for unfocused apps in splitscreen */
     boolean isCompatFakeFocusEnabled() {
-        return mIsCompatFakeFocusEnabled
-                && DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                        DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, true);
+        return mIsCompatFakeFocusEnabled && mDeviceConfig.getFlag(KEY_ENABLE_COMPAT_FAKE_FOCUS);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
index d004fa6..b364872 100644
--- a/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
+++ b/services/core/java/com/android/server/wm/LetterboxConfigurationDeviceConfig.java
@@ -45,6 +45,9 @@
             "allow_ignore_orientation_request";
     private static final boolean DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST = true;
 
+    static final String KEY_ENABLE_COMPAT_FAKE_FOCUS = "enable_compat_fake_focus";
+    private static final boolean DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS = true;
+
     @VisibleForTesting
     static final Map<String, Boolean> sKeyToDefaultValueMap = Map.of(
             KEY_ENABLE_CAMERA_COMPAT_TREATMENT,
@@ -52,7 +55,9 @@
             KEY_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
             DEFAULT_VALUE_ENABLE_DISPLAY_ROTATION_IMMERSIVE_APP_COMPAT_POLICY,
             KEY_ALLOW_IGNORE_ORIENTATION_REQUEST,
-            DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST
+            DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST,
+            KEY_ENABLE_COMPAT_FAKE_FOCUS,
+            DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS
     );
 
     // Whether camera compatibility treatment is enabled.
@@ -72,6 +77,11 @@
     private boolean mIsAllowIgnoreOrientationRequest =
             DEFAULT_VALUE_ALLOW_IGNORE_ORIENTATION_REQUEST;
 
+    // Whether sending compat fake focus for split screen resumed activities is enabled. This is
+    // needed because some game engines wait to get focus before drawing the content of the app
+    // which isn't guaranteed by default in multi-window modes.
+    private boolean mIsCompatFakeFocusAllowed = DEFAULT_VALUE_ENABLE_COMPAT_FAKE_FOCUS;
+
     // Set of active device configs that need to be updated in
     // DeviceConfig.OnPropertiesChangedListener#onPropertiesChanged.
     private final ArraySet<String> mActiveDeviceConfigsSet = new ArraySet<>();
@@ -117,6 +127,8 @@
                 return mIsDisplayRotationImmersiveAppCompatPolicyEnabled;
             case KEY_ALLOW_IGNORE_ORIENTATION_REQUEST:
                 return mIsAllowIgnoreOrientationRequest;
+            case KEY_ENABLE_COMPAT_FAKE_FOCUS:
+                return mIsCompatFakeFocusAllowed;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
@@ -140,6 +152,10 @@
                 mIsAllowIgnoreOrientationRequest =
                         getDeviceConfig(key, defaultValue);
                 break;
+            case KEY_ENABLE_COMPAT_FAKE_FOCUS:
+                mIsCompatFakeFocusAllowed =
+                        getDeviceConfig(key, defaultValue);
+                break;
             default:
                 throw new AssertionError("Unexpected flag name: " + key);
         }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9b26a54c..c1e7bba5 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -30,6 +30,7 @@
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.MODE_DEFAULT;
 import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_APP_STANDBY;
+import static android.app.AppOpsManager.OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
 import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_AFFILIATED;
 import static android.app.admin.DeviceAdminReceiver.ACTION_COMPLIANCE_ACKNOWLEDGEMENT_REQUIRED;
 import static android.app.admin.DeviceAdminReceiver.EXTRA_TRANSFER_OWNERSHIP_ADMIN_EXTRAS_BUNDLE;
@@ -55,6 +56,7 @@
 import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
 import static android.app.admin.DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER;
 import static android.app.admin.DevicePolicyManager.EXEMPT_FROM_APP_STANDBY;
+import static android.app.admin.DevicePolicyManager.EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS;
 import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ACCOUNT_TO_MIGRATE;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_IDS;
 import static android.app.admin.DevicePolicyManager.EXTRA_RESOURCE_TYPE;
@@ -703,6 +705,9 @@
     static {
         APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.put(
                 EXEMPT_FROM_APP_STANDBY, OPSTR_SYSTEM_EXEMPT_FROM_APP_STANDBY);
+        APPLICATION_EXEMPTION_CONSTANTS_TO_APP_OPS.put(
+                EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS,
+                OPSTR_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS);
     }
 
     /**
@@ -750,6 +755,10 @@
     private static final String HEADLESS_FLAG = "headless";
     private static final boolean DEFAULT_HEADLESS_FLAG = true;
 
+    // TODO(b/266831522) remove the flag after rollout.
+    private static final String APPLICATION_EXEMPTIONS_FLAG = "application_exemptions";
+    private static final boolean DEFAULT_APPLICATION_EXEMPTIONS_FLAG = true;
+
     /**
      * This feature flag is checked once after boot and this value us used until the next reboot to
      * avoid needing to handle the flag changing on the fly.
@@ -14193,6 +14202,14 @@
         public boolean isUserOrganizationManaged(@UserIdInt int userHandle) {
             return getDeviceStateCache().isUserOrganizationManaged(userHandle);
         }
+
+        @Override
+        public boolean isApplicationExemptionsFlagEnabled() {
+            return DeviceConfig.getBoolean(
+                    NAMESPACE_DEVICE_POLICY_MANAGER,
+                    APPLICATION_EXEMPTIONS_FLAG,
+                    DEFAULT_APPLICATION_EXEMPTIONS_FLAG);
+        }
     }
 
     private Intent createShowAdminSupportIntent(int userId) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index 38093de..7c736c7 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -40,13 +40,20 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import libcore.junit.util.compat.CoreCompatChangeRule;
+
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 
 import java.util.Collections;
 import java.util.List;
 
 public class AbsoluteVolumeBehaviorTest {
+    @Rule
+    public TestRule compatChangeRule = new CoreCompatChangeRule();
+
     private static final String TAG = "AbsoluteVolumeBehaviorTest";
 
     private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
@@ -92,7 +99,30 @@
                 new VolumeInfo.Builder(AudioManager.STREAM_MUSIC).build());
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
-                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true);
+                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+        mTestLooper.dispatchAll();
+
+        assertThat(mAudioService.getDeviceVolumeBehavior(DEVICE_SPEAKER_OUT))
+                .isEqualTo(AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+    }
+
+    /**
+     * Tests that getDeviceVolumeBehavior returns the correct volume behavior, even if
+     * a different volume behavior was previously persisted via setDeviceVolumeBehavior
+     */
+    @Test
+    public void registerDispatcherAfterSetDeviceVolumeBehavior_setsVolumeBehaviorToAbsolute() {
+        List<VolumeInfo> volumes = Collections.singletonList(
+                new VolumeInfo.Builder(AudioManager.STREAM_MUSIC).build());
+
+        mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL, mPackageName);
+        mTestLooper.dispatchAll();
+
+        mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
+                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         assertThat(mAudioService.getDeviceVolumeBehavior(DEVICE_SPEAKER_OUT))
@@ -109,7 +139,8 @@
                         .build());
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
-                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true);
+                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         assertThat(mAudioService.getStreamVolume(AudioManager.STREAM_MUSIC))
@@ -124,11 +155,13 @@
                 new VolumeInfo.Builder(AudioManager.STREAM_MUSIC).build());
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
-                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true);
+                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(false,
-                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true);
+                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         assertThat(mAudioService.getDeviceVolumeBehavior(DEVICE_SPEAKER_OUT))
@@ -147,7 +180,8 @@
                 new VolumeInfo.Builder(AudioManager.STREAM_MUSIC).build());
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
-                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true);
+                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT, volumes, true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
@@ -171,7 +205,8 @@
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
                 mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT,
-                Collections.singletonList(volumeInfo), true);
+                Collections.singletonList(volumeInfo), true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         // Set stream volume without FLAG_ABSOLUTE_VOLUME
@@ -194,7 +229,8 @@
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
                 mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT,
-                Collections.singletonList(volumeInfo), true);
+                Collections.singletonList(volumeInfo), true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         // Set stream volume with FLAG_ABSOLUTE_VOLUME
@@ -218,7 +254,8 @@
         // Register dispatcher with handlesVolumeAdjustment = true
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
                 mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT,
-                Collections.singletonList(volumeInfo), true);
+                Collections.singletonList(volumeInfo), true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         // Adjust stream volume without FLAG_ABSOLUTE_VOLUME
@@ -247,7 +284,8 @@
         // Register dispatcher with handlesVolumeAdjustment = false
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
                 mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT,
-                Collections.singletonList(volumeInfo), false);
+                Collections.singletonList(volumeInfo), false,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         // Adjust stream volume without FLAG_ABSOLUTE_VOLUME
@@ -274,7 +312,8 @@
 
         mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
                 mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT,
-                Collections.singletonList(volumeInfo), true);
+                Collections.singletonList(volumeInfo), true,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
         mTestLooper.dispatchAll();
 
         // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set
@@ -289,4 +328,38 @@
         verify(mMockDispatcher, never()).dispatchDeviceVolumeAdjusted(eq(DEVICE_SPEAKER_OUT), any(),
                 anyInt(), anyInt());
     }
+
+    @Test
+    public void switchAbsoluteVolumeBehaviorToAdjustOnly_onlyDispatchesVolumeChangeForNewListener()
+            throws RemoteException {
+        VolumeInfo volumeInfo = new VolumeInfo.Builder(AudioManager.STREAM_MUSIC)
+                .setMinVolumeIndex(0)
+                .setMaxVolumeIndex(250)
+                .setVolumeIndex(0)
+                .build();
+
+        mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
+                mMockDispatcher, mPackageName, DEVICE_SPEAKER_OUT,
+                Collections.singletonList(volumeInfo), false,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE);
+        mTestLooper.dispatchAll();
+
+        IAudioDeviceVolumeDispatcher.Stub mMockAdjustOnlyAbsoluteVolumeDispatcher =
+                mock(IAudioDeviceVolumeDispatcher.Stub.class);
+        mAudioService.registerDeviceVolumeDispatcherForAbsoluteVolume(true,
+                mMockAdjustOnlyAbsoluteVolumeDispatcher, mPackageName, DEVICE_SPEAKER_OUT,
+                Collections.singletonList(volumeInfo), false,
+                AudioManager.DEVICE_VOLUME_BEHAVIOR_ABSOLUTE_ADJUST_ONLY);
+        mTestLooper.dispatchAll();
+
+        // Set stream volume without FLAG_ABSOLUTE_VOLUME
+        mAudioService.setStreamVolume(AudioManager.STREAM_MUSIC, 15, 0, mPackageName);
+        mTestLooper.dispatchAll();
+
+        // Volume change not dispatched for absolute volume listener
+        verify(mMockDispatcher, never()).dispatchDeviceVolumeChanged(eq(DEVICE_SPEAKER_OUT), any());
+        // Volume changed dispatched for adjust-only absolute volume listener
+        verify(mMockAdjustOnlyAbsoluteVolumeDispatcher).dispatchDeviceVolumeChanged(
+                DEVICE_SPEAKER_OUT, new VolumeInfo.Builder(volumeInfo).setVolumeIndex(150).build());
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 5c69c84..38c2f40 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -316,6 +316,8 @@
     private Looper mMainLooper;
     @Mock
     private NotificationManager mMockNm;
+    @Mock
+    private DevicePolicyManagerInternal mDevicePolicyManager;
 
     @Mock
     IIntentSender pi1;
@@ -515,7 +517,7 @@
         mService.init(mWorkerHandler, mRankingHandler, mPackageManager, mPackageManagerClient,
                 mockLightsManager, mListeners, mAssistants, mConditionProviders, mCompanionMgr,
                 mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager, mGroupHelper, mAm, mAtm,
-                mAppUsageStats, mock(DevicePolicyManagerInternal.class), mUgm, mUgmInternal,
+                mAppUsageStats, mDevicePolicyManager, mUgm, mUgmInternal,
                 mAppOpsManager, mAppOpsService, mUm, mHistoryManager, mStatsManager,
                 mock(TelephonyManager.class),
                 mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class),
@@ -10291,4 +10293,74 @@
         mService.setOngoingDismissal(false);
     }
 
+    @Test
+    public void fixOrganizationAdminNotification_withOnGoingFlag_shouldBeNonDismissible()
+            throws Exception {
+        when(mDevicePolicyManager.isActiveDeviceOwner(mUid)).thenReturn(true);
+        // Given: a notification has the flag FLAG_ONGOING_EVENT set
+        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
+        mService.setOngoingDismissal(true);
+        mService.setSystemExemptFromDismissal(false);
+        Notification n = new Notification.Builder(mContext, "test")
+                .setOngoing(true)
+                .build();
+
+        // When: fix the notification with NotificationManagerService
+        mService.fixNotification(n, PKG, "tag", 9, 0);
+
+        // Then: the notification's flag FLAG_NO_DISMISS should be set
+        assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
+
+        // Avoid affecting other tests
+        mService.setOngoingDismissal(false);
+    }
+
+    @Test
+    public void fixSystemExemptAppOpNotification_withFlag_shouldBeNonDismissible()
+            throws Exception {
+        when(mAppOpsManager.checkOpNoThrow(
+                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
+                PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
+        // Given: a notification has the flag FLAG_ONGOING_EVENT set
+        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
+        mService.setOngoingDismissal(true);
+        mService.setSystemExemptFromDismissal(true);
+        Notification n = new Notification.Builder(mContext, "test")
+                .setOngoing(true)
+                .build();
+
+        // When: fix the notification with NotificationManagerService
+        mService.fixNotification(n, PKG, "tag", 9, 0);
+
+        // Then: the notification's flag FLAG_NO_DISMISS should be set
+        assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS);
+
+        // Avoid affecting other tests
+        mService.setOngoingDismissal(false);
+        mService.setSystemExemptFromDismissal(false);
+    }
+
+    @Test
+    public void fixSystemExemptAppOpNotification_withoutFlag_shouldBeNonDismissible()
+            throws Exception {
+        when(mAppOpsManager.checkOpNoThrow(
+                AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, mUid,
+                PKG)).thenReturn(AppOpsManager.MODE_ALLOWED);
+        // Given: a notification has the flag FLAG_ONGOING_EVENT set
+        // feature flag: NOTIFICATION_ONGOING_DISMISSAL is on
+        mService.setOngoingDismissal(true);
+        mService.setSystemExemptFromDismissal(false);
+        Notification n = new Notification.Builder(mContext, "test")
+                .setOngoing(true)
+                .build();
+
+        // When: fix the notification with NotificationManagerService
+        mService.fixNotification(n, PKG, "tag", 9, 0);
+
+        // Then: the notification's flag FLAG_NO_DISMISS should not be set
+        assertEquals(0, n.flags & Notification.FLAG_NO_DISMISS);
+
+        // Avoid affecting other tests
+        mService.setOngoingDismissal(false);
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
index b8c94da..1306d57 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/TestableNotificationManagerService.java
@@ -166,4 +166,8 @@
     protected void setOngoingDismissal(boolean ongoingDismissal) {
         ONGOING_DISMISSAL = ongoingDismissal;
     }
+
+    protected void setSystemExemptFromDismissal(boolean isOn) {
+        mSystemExemptFromDismissal = isOn;
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
index 12b7c9d..e1fc0cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxConfigurationTest.java
@@ -18,7 +18,6 @@
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
-import static com.android.server.wm.LetterboxConfiguration.DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_RIGHT;
@@ -26,8 +25,6 @@
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_CENTER;
 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -37,7 +34,6 @@
 
 import android.content.Context;
 import android.platform.test.annotations.Presubmit;
-import android.provider.DeviceConfig;
 
 import androidx.test.filters.SmallTest;
 
@@ -233,34 +229,6 @@
                 LetterboxConfiguration::movePositionForVerticalReachabilityToNextBottomStop);
     }
 
-    @Test
-    public void testIsCompatFakeFocusEnabledOnDevice() {
-        boolean wasFakeFocusEnabled = DeviceConfig
-                .getBoolean(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, false);
-
-        // Set runtime flag to true and build time flag to false
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
-        mLetterboxConfiguration.setIsCompatFakeFocusEnabled(false);
-        assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabled());
-
-        // Set runtime flag to false and build time flag to true
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "false", false);
-        mLetterboxConfiguration.setIsCompatFakeFocusEnabled(true);
-        assertFalse(mLetterboxConfiguration.isCompatFakeFocusEnabled());
-
-        // Set runtime flag to true so that both are enabled
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, "true", false);
-        assertTrue(mLetterboxConfiguration.isCompatFakeFocusEnabled());
-
-        DeviceConfig.setProperty(DeviceConfig.NAMESPACE_WINDOW_MANAGER,
-                DEVICE_CONFIG_KEY_ENABLE_COMPAT_FAKE_FOCUS, Boolean.toString(wasFakeFocusEnabled),
-                false);
-    }
-
     private void assertForHorizontalMove(int from, int expected, int expectedTime,
             boolean halfFoldPose, BiConsumer<LetterboxConfiguration, Boolean> move) {
         // We are in the current position