Merge "Add APIs to control the external display state" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index e394857..7005349 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -19,7 +19,7 @@
     // Add java_aconfig_libraries to here to add them to the core framework
     srcs: [
         ":android.security.flags-aconfig-java{.generated_srcjars}",
-        ":com.android.hardware.camera2-aconfig-java{.generated_srcjars}",
+        ":camera_platform_flags_core_java_lib{.generated_srcjars}",
         ":com.android.window.flags.window-aconfig-java{.generated_srcjars}",
         ":com.android.text.flags-aconfig-java{.generated_srcjars}",
     ],
@@ -34,15 +34,9 @@
 }
 
 // Camera
-aconfig_declarations {
-    name: "com.android.hardware.camera2-aconfig",
-    package: "com.android.hardware.camera2",
-    srcs: ["core/java/android/hardware/camera2/camera_platform.aconfig"],
-}
-
 java_aconfig_library {
-    name: "com.android.hardware.camera2-aconfig-java",
-    aconfig_declarations: "com.android.hardware.camera2-aconfig",
+    name: "camera_platform_flags_core_java_lib",
+    aconfig_declarations: "camera_platform_flags",
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 3dd69e4..1665cc1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -223,6 +223,7 @@
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.am.MemInfoDumpProto;
 
+import dalvik.annotation.optimization.NeverCompile;
 import dalvik.system.AppSpecializationHooks;
 import dalvik.system.CloseGuard;
 import dalvik.system.VMDebug;
@@ -1496,6 +1497,7 @@
             }
         }
 
+        @NeverCompile
         @Override
         public void dumpMemInfo(ParcelFileDescriptor pfd, Debug.MemoryInfo mem, boolean checkin,
                 boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
@@ -1510,6 +1512,7 @@
             }
         }
 
+        @NeverCompile
         private void dumpMemInfo(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
                 boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly, boolean dumpUnreachable) {
             long nativeMax = Debug.getNativeHeapSize() / 1024;
@@ -1666,6 +1669,7 @@
             }
         }
 
+        @NeverCompile
         @Override
         public void dumpMemInfoProto(ParcelFileDescriptor pfd, Debug.MemoryInfo mem,
                 boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
@@ -1679,6 +1683,7 @@
             }
         }
 
+        @NeverCompile
         private void dumpMemInfo(ProtoOutputStream proto, Debug.MemoryInfo memInfo,
                 boolean dumpFullInfo, boolean dumpDalvik,
                 boolean dumpSummaryOnly, boolean dumpUnreachable) {
@@ -3020,6 +3025,7 @@
         pw.println(String.format(format, objs));
     }
 
+    @NeverCompile
     public static void dumpMemInfoTable(PrintWriter pw, Debug.MemoryInfo memInfo, boolean checkin,
             boolean dumpFullInfo, boolean dumpDalvik, boolean dumpSummaryOnly,
             int pid, String processName,
@@ -3349,6 +3355,7 @@
     /**
      * Dump mem info data to proto.
      */
+    @NeverCompile
     public static void dumpMemInfoTable(ProtoOutputStream proto, Debug.MemoryInfo memInfo,
             boolean dumpDalvik, boolean dumpSummaryOnly,
             long nativeMax, long nativeAllocated, long nativeFree,
diff --git a/core/java/android/hardware/camera2/camera_platform.aconfig b/core/java/android/hardware/camera2/camera_platform.aconfig
deleted file mode 100644
index 67f6300..0000000
--- a/core/java/android/hardware/camera2/camera_platform.aconfig
+++ /dev/null
@@ -1,8 +0,0 @@
-package: "com.android.hardware.camera2"
-
-flag {
-     namespace: "camera_platform"
-     name: "initial_test_flag"
-     description: "Flag infrastructure test flag"
-     bug: "292631208"
-}
diff --git a/core/java/android/hardware/radio/UniqueProgramIdentifier.aidl b/core/java/android/hardware/radio/UniqueProgramIdentifier.aidl
new file mode 100644
index 0000000..2ed2bcc
--- /dev/null
+++ b/core/java/android/hardware/radio/UniqueProgramIdentifier.aidl
@@ -0,0 +1,20 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+/** @hide */
+parcelable UniqueProgramIdentifier;
diff --git a/core/java/android/hardware/radio/UniqueProgramIdentifier.java b/core/java/android/hardware/radio/UniqueProgramIdentifier.java
new file mode 100644
index 0000000..ea8948e
--- /dev/null
+++ b/core/java/android/hardware/radio/UniqueProgramIdentifier.java
@@ -0,0 +1,163 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Stream;
+
+/**
+ * Identifier that can uniquely identifies a program.
+ *
+ * This is a transport class used for internal communication between
+ * Broadcast Radio Service and Radio Manager. Do not use it directly.
+ *
+ * @hide
+ */
+public final class UniqueProgramIdentifier implements Parcelable {
+
+    @NonNull private final ProgramSelector.Identifier mPrimaryId;
+    @NonNull private final ProgramSelector.Identifier[] mCriticalSecondaryIds;
+
+    /**
+     * Check whether some secondary identifier is needed to uniquely specify a program for
+     * a given primary identifier type
+     *
+     * @param type primary identifier type {@link ProgramSelector.IdentifierType}
+     * @return whether some secondary identifier is needed to uniquely specify a program.
+     */
+    public static boolean requireCriticalSecondaryIds(@ProgramSelector.IdentifierType int type) {
+        return type == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT || type
+                == ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT;
+    }
+
+    public UniqueProgramIdentifier(ProgramSelector selector) {
+        Objects.requireNonNull(selector, "Program selector can not be null");
+        mPrimaryId = selector.getPrimaryId();
+        switch (mPrimaryId.getType()) {
+            case ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT:
+            case ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT:
+                ProgramSelector.Identifier ensembleId = null;
+                ProgramSelector.Identifier frequencyId = null;
+                ProgramSelector.Identifier[] secondaryIds = selector.getSecondaryIds();
+                for (int i = 0; i < secondaryIds.length; i++) {
+                    if (ensembleId == null && secondaryIds[i].getType()
+                            == ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE) {
+                        ensembleId = selector.getSecondaryIds()[i];
+                    } else if (frequencyId == null && secondaryIds[i].getType()
+                            == ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY) {
+                        frequencyId = secondaryIds[i];
+                    }
+                    if (ensembleId != null && frequencyId != null) {
+                        break;
+                    }
+                }
+                if (ensembleId == null) {
+                    if (frequencyId == null) {
+                        mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
+                    } else {
+                        mCriticalSecondaryIds = new ProgramSelector.Identifier[]{frequencyId};
+                    }
+                } else if (frequencyId == null) {
+                    mCriticalSecondaryIds = new ProgramSelector.Identifier[]{ensembleId};
+                } else {
+                    mCriticalSecondaryIds = new ProgramSelector.Identifier[]{ensembleId,
+                            frequencyId};
+                }
+                break;
+            default:
+                mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
+        }
+
+    }
+
+    public UniqueProgramIdentifier(ProgramSelector.Identifier primaryId) {
+        mPrimaryId = primaryId;
+        mCriticalSecondaryIds = new ProgramSelector.Identifier[]{};
+    }
+
+    @NonNull
+    public ProgramSelector.Identifier getPrimaryId() {
+        return mPrimaryId;
+    }
+
+    @NonNull
+    public List<ProgramSelector.Identifier> getCriticalSecondaryIds() {
+        return List.of(mCriticalSecondaryIds);
+    }
+
+    @NonNull
+    @Override
+    public String toString() {
+        return new StringBuilder("UniqueProgramIdentifier(primary=").append(mPrimaryId)
+                .append(", criticalSecondary=")
+                .append(Arrays.toString(mCriticalSecondaryIds)).append(")")
+                .toString();
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrimaryId, Arrays.hashCode(mCriticalSecondaryIds));
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) return true;
+        if (!(obj instanceof UniqueProgramIdentifier)) return false;
+        UniqueProgramIdentifier other = (UniqueProgramIdentifier) obj;
+        return other.mPrimaryId.equals(mPrimaryId)
+                && Arrays.equals(other.mCriticalSecondaryIds, mCriticalSecondaryIds);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    private UniqueProgramIdentifier(Parcel in) {
+        mPrimaryId = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
+        mCriticalSecondaryIds = in.createTypedArray(ProgramSelector.Identifier.CREATOR);
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeTypedObject(mPrimaryId, 0);
+        dest.writeTypedArray(mCriticalSecondaryIds, 0);
+        if (Stream.of(mCriticalSecondaryIds).anyMatch(Objects::isNull)) {
+            throw new IllegalArgumentException(
+                    "criticalSecondaryIds list must not contain nulls");
+        }
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<UniqueProgramIdentifier> CREATOR =
+            new Parcelable.Creator<UniqueProgramIdentifier>() {
+                public UniqueProgramIdentifier createFromParcel(Parcel in) {
+                    return new UniqueProgramIdentifier(in);
+                }
+
+                public UniqueProgramIdentifier[] newArray(int size) {
+                    return new UniqueProgramIdentifier[size];
+                }
+            };
+}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java
index ae43a1c..b1cf9c2 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/ProgramSelectorTest.java
@@ -437,8 +437,8 @@
 
     @Test
     public void writeToParcel_forProgramSelector() {
-        ProgramSelector selectorExpected =
-                getFmSelector(/* secondaryIds= */ null, /* vendorIds= */ null);
+        ProgramSelector selectorExpected = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
         Parcel parcel = Parcel.obtain();
 
         selectorExpected.writeToParcel(parcel, /* flags= */ 0);
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/UniqueProgramIdentifierTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/UniqueProgramIdentifierTest.java
new file mode 100644
index 0000000..b36367b
--- /dev/null
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/UniqueProgramIdentifierTest.java
@@ -0,0 +1,187 @@
+/**
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.radio;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+import com.google.common.truth.Expect;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+public final class UniqueProgramIdentifierTest {
+    private static final ProgramSelector.Identifier FM_IDENTIFIER = new ProgramSelector.Identifier(
+            ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 88_500);
+
+    private static final ProgramSelector.Identifier DAB_DMB_SID_EXT_IDENTIFIER_1 =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
+                    /* value= */ 0xA000000111L);
+    private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
+                    /* value= */ 0x1001);
+    private static final ProgramSelector.Identifier DAB_FREQUENCY_IDENTIFIER =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
+                    /* value= */ 220352);
+    private static final ProgramSelector.Identifier DAB_SCID_IDENTIFIER =
+            new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SCID,
+                    /* value= */ 0x101);
+
+    @Rule
+    public final Expect expect = Expect.create();
+
+    @Test
+    public void getPrimaryId_forUniqueProgramIdentifier() {
+        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+
+        expect.withMessage("Primary id of DAB unique identifier")
+                .that(dabIdentifier.getPrimaryId()).isEqualTo(DAB_DMB_SID_EXT_IDENTIFIER_1);
+    }
+
+    @Test
+    public void getCriticalSecondaryIds_forDabUniqueProgramIdentifier() {
+        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER, DAB_SCID_IDENTIFIER},
+                /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+
+        expect.withMessage("Critical secondary ids of DAB unique identifier")
+                .that(dabIdentifier.getCriticalSecondaryIds()).containsExactly(
+                        DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER);
+    }
+
+    @Test
+    public void getCriticalSecondaryIds_forFmUniqueProgramIdentifier() {
+        UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(
+                new ProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, FM_IDENTIFIER,
+                        new ProgramSelector.Identifier[]{new ProgramSelector.Identifier(
+                                ProgramSelector.IDENTIFIER_TYPE_RDS_PI, /* value= */ 0x1003)},
+                        /* vendorIds= */ null));
+
+        expect.withMessage("Empty critical secondary id list of FM unique identifier")
+                .that(fmUniqueIdentifier.getCriticalSecondaryIds()).isEmpty();
+    }
+
+    @Test
+    public void toString_forUniqueProgramIdentifier() {
+        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+
+        String identifierString = dabIdentifier.toString();
+
+        expect.withMessage("Primary id in DAB unique identifier")
+                .that(identifierString).contains(DAB_DMB_SID_EXT_IDENTIFIER_1.toString());
+        expect.withMessage("Ensemble id in DAB unique identifier")
+                .that(identifierString).contains(DAB_ENSEMBLE_IDENTIFIER.toString());
+        expect.withMessage("Frequency id in DAB unique identifier")
+                .that(identifierString).contains(DAB_FREQUENCY_IDENTIFIER.toString());
+    }
+
+    @Test
+    public void hashCode_withTheSameUniqueProgramIdentifier_equals() {
+        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+        ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_FREQUENCY_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER}, /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+        UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);
+
+        expect.withMessage("Hash code of the same DAB unique identifiers")
+                .that(dabIdentifier1.hashCode()).isEqualTo(dabIdentifier2.hashCode());
+    }
+
+    @Test
+    public void equals_withIdsForUniqueProgramIdentifier_returnsTrue() {
+        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+        ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_FREQUENCY_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER}, /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+        UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);
+
+        expect.withMessage("The same DAB unique identifiers")
+                .that(dabIdentifier1).isEqualTo(dabIdentifier2);
+    }
+
+    @Test
+    public void equals_withDifferentPrimaryIdsForUniqueProgramIdentifier_returnsFalse() {
+        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+        UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(FM_IDENTIFIER);
+
+        expect.withMessage("Unique identifier with different primary ids")
+                .that(dabIdentifier1).isNotEqualTo(fmUniqueIdentifier);
+    }
+
+    @Test
+    public void equals_withDifferentSecondaryIdsForUniqueProgramIdentifier_returnsFalse() {
+        ProgramSelector dabSelector1 = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+        ProgramSelector.Identifier dabFreqIdentifier2 = new ProgramSelector.Identifier(
+                ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, /* value= */ 222064);
+        ProgramSelector dabSelector2 = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, dabFreqIdentifier2}, /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier1 = new UniqueProgramIdentifier(dabSelector1);
+        UniqueProgramIdentifier dabIdentifier2 = new UniqueProgramIdentifier(dabSelector2);
+
+        expect.withMessage("DAB unique identifier with different secondary ids")
+                .that(dabIdentifier1).isNotEqualTo(dabIdentifier2);
+    }
+
+    @Test
+    public void describeContents_forUniqueProgramIdentifier() {
+        UniqueProgramIdentifier fmUniqueIdentifier = new UniqueProgramIdentifier(FM_IDENTIFIER);
+
+        expect.withMessage("FM unique identifier contents")
+                .that(fmUniqueIdentifier.describeContents()).isEqualTo(0);
+    }
+
+    @Test
+    public void newArray_forUniqueProgramIdentifier() {
+        int createArraySize = 3;
+        UniqueProgramIdentifier[] identifiers = UniqueProgramIdentifier.CREATOR.newArray(
+                createArraySize);
+
+        expect.withMessage("Unique identifiers").that(identifiers).hasLength(createArraySize);
+    }
+
+    @Test
+    public void writeToParcel_forUniqueProgramIdentifier() {
+        ProgramSelector dabSelector = getDabSelector(new ProgramSelector.Identifier[]{
+                DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER}, /* vendorIds= */ null);
+        UniqueProgramIdentifier dabIdentifier = new UniqueProgramIdentifier(dabSelector);
+        Parcel parcel = Parcel.obtain();
+
+        dabIdentifier.writeToParcel(parcel, /* flags= */ 0);
+        parcel.setDataPosition(0);
+
+        UniqueProgramIdentifier identifierFromParcel = UniqueProgramIdentifier.CREATOR
+                .createFromParcel(parcel);
+        expect.withMessage("Unique identifier created from parcel")
+                .that(identifierFromParcel).isEqualTo(dabIdentifier);
+    }
+
+    private ProgramSelector getDabSelector(@Nullable ProgramSelector.Identifier[] secondaryIds,
+            @Nullable long[] vendorIds) {
+        return new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, DAB_DMB_SID_EXT_IDENTIFIER_1,
+                secondaryIds, vendorIds);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
index 4dc98e4..050d1e9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MaximizeMenu.kt
@@ -141,13 +141,13 @@
     private fun setupMaximizeMenu() {
         val maximizeMenuView = maximizeMenu?.mWindowViewHost?.view ?: return
 
-        maximizeMenuView.findViewById<Button>(
+        maximizeMenuView.requireViewById<Button>(
                 R.id.maximize_menu_maximize_button
         ).setOnClickListener(onClickListener)
-        maximizeMenuView.findViewById<Button>(
+        maximizeMenuView.requireViewById<Button>(
                 R.id.maximize_menu_snap_right_button
         ).setOnClickListener(onClickListener)
-        maximizeMenuView.findViewById<Button>(
+        maximizeMenuView.requireViewById<Button>(
                 R.id.maximize_menu_snap_left_button
         ).setOnClickListener(onClickListener)
     }
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index b472982..2077af8 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -135,7 +135,10 @@
 aconfig_declarations {
     name: "systemui_aconfig_flags",
     package: "com.android.systemui.aconfig",
-    srcs: ["src/com/android/systemui/aconfig/systemui.aconfig"],
+    srcs: [
+        "src/com/android/systemui/aconfig/systemui.aconfig",
+        "src/com/android/systemui/accessibility/aconfig/accessibility.aconfig",
+    ],
 }
 
 java_aconfig_library {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/aconfig/accessibility.aconfig b/packages/SystemUI/src/com/android/systemui/accessibility/aconfig/accessibility.aconfig
new file mode 100644
index 0000000..91c5551
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/aconfig/accessibility.aconfig
@@ -0,0 +1,7 @@
+package: "com.android.systemui.aconfig"
+flag {
+    name: "floating_menu_overlaps_nav_bars_flag"
+    namespace: "accessibility"
+    description: "Adjusts bounds to allow the floating menu to render on top of navigation bars."
+    bug: "283768342"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 47770fa..f29077d 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -38,6 +38,7 @@
 import androidx.recyclerview.widget.RecyclerViewAccessibilityDelegate;
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.systemui.aconfig.Flags;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -284,6 +285,22 @@
     void updateMenuMoveToTucked(boolean isMoveToTucked) {
         mIsMoveToTucked = isMoveToTucked;
         mMenuViewModel.updateMenuMoveToTucked(isMoveToTucked);
+
+        if (Flags.floatingMenuOverlapsNavBarsFlag()) {
+            if (isMoveToTucked) {
+                final float halfWidth = getMenuWidth() / 2.0f;
+                final boolean isOnLeftSide = mMenuAnimationController.isOnLeftSide();
+                final Rect clipBounds = new Rect(
+                        (int) (!isOnLeftSide ? 0 : halfWidth),
+                        0,
+                        (int) (!isOnLeftSide ? halfWidth : getMenuWidth()),
+                        getMenuHeight()
+                );
+                setClipBounds(clipBounds);
+            } else {
+                setClipBounds(null);
+            }
+        }
     }
 
 
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index 3cd250f..3822936 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -35,6 +35,7 @@
 import androidx.annotation.DimenRes;
 
 import com.android.systemui.R;
+import com.android.systemui.aconfig.Flags;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -154,8 +155,10 @@
         final int margin = getMenuMargin();
         final Rect draggableBounds = new Rect(getWindowAvailableBounds());
 
-        // Initializes start position for mapping the translation of the menu view.
-        draggableBounds.offsetTo(/* newLeft= */ 0, /* newTop= */ 0);
+        if (!Flags.floatingMenuOverlapsNavBarsFlag()) {
+            // Initializes start position for mapping the translation of the menu view.
+            draggableBounds.offsetTo(/* newLeft= */ 0, /* newTop= */ 0);
+        }
 
         draggableBounds.top += margin;
         draggableBounds.right -= getMenuWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
index c52ecc5..cc18c30 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -24,6 +24,7 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
+import com.android.systemui.aconfig.Flags;
 import com.android.systemui.util.settings.SecureSettings;
 
 /**
@@ -77,8 +78,15 @@
         params.receiveInsetsIgnoringZOrder = true;
         params.privateFlags |= PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION;
         params.windowAnimations = android.R.style.Animation_Translucent;
-        params.setFitInsetsTypes(
-                WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+        // Insets are configured to allow the menu to display over navigation and system bars.
+        if (Flags.floatingMenuOverlapsNavBarsFlag()) {
+            params.setFitInsetsTypes(0);
+            params.layoutInDisplayCutoutMode =
+                    WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        } else {
+            params.setFitInsetsTypes(
+                    WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout());
+        }
         return params;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
index 6c5cc48..a7ecf38 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/CredentialPasswordView.kt
@@ -52,7 +52,7 @@
                     titleView.ellipsize = TextUtils.TruncateAt.MARQUEE
                     titleView.marqueeRepeatLimit = -1
                     // select to enable marquee unless a screen reader is enabled
-                    titleView.isSelected = accessibilityManager.shouldMarquee()
+                    titleView.isSelected = accessibilityManager?.shouldMarquee() ?: false
                 } else {
                     titleView.isSingleLine = false
                     titleView.ellipsize = null
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
index 7859fa0..8dd25bc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/BaseScreenSharePermissionDialog.kt
@@ -121,7 +121,7 @@
 
     private fun createOptionsView(@LayoutRes layoutId: Int?) {
         if (layoutId == null) return
-        val stub = findViewById<View>(R.id.options_stub) as ViewStub
+        val stub = requireViewById<View>(R.id.options_stub) as ViewStub
         stub.layoutResource = layoutId
         stub.inflate()
     }
@@ -144,8 +144,8 @@
     override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
         val inflater = LayoutInflater.from(parent.context)
         val view = inflater.inflate(R.layout.screen_share_dialog_spinner_item_text, parent, false)
-        val titleTextView = view.findViewById<TextView>(android.R.id.text1)
-        val errorTextView = view.findViewById<TextView>(android.R.id.text2)
+        val titleTextView = view.requireViewById<TextView>(android.R.id.text1)
+        val errorTextView = view.requireViewById<TextView>(android.R.id.text2)
         titleTextView.text = getItem(position)
         errorTextView.text = options[position].spinnerDisabledText
         if (isEnabled(position)) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
index 08b5d2b..16091b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotDetectionControllerTest.kt
@@ -170,8 +170,8 @@
     private class ComponentInfoFlagMatcher(
         @PackageManager.ComponentInfoFlagsBits val mask: Int, val value: Int
     ): ArgumentMatcher<PackageManager.ComponentInfoFlags> {
-        override fun matches(flags: PackageManager.ComponentInfoFlags): Boolean {
-            return (mask.toLong() and flags.value) == value.toLong()
+        override fun matches(flags: PackageManager.ComponentInfoFlags?): Boolean {
+            return flags != null && (mask.toLong() and flags.value) == value.toLong()
         }
 
         override fun toString(): String{
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
index 69575a9..7e0632b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/util/mockito/KotlinMockitoHelpers.kt
@@ -36,7 +36,7 @@
  *
  * Generic T is nullable because implicitly bounded by Any?.
  */
-fun <T> eq(obj: T): T = Mockito.eq<T>(obj)
+fun <T> eq(obj: T): T = Mockito.eq<T>(obj) ?: obj
 
 /**
  * Returns Mockito.any() as nullable type to avoid java.lang.IllegalStateException when
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 714ff68..b941aaf 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -193,6 +193,7 @@
         "power_optimization_flags_lib",
         "notification_flags_lib",
         "pm_flags_lib",
+        "camera_platform_flags_core_java_lib",
     ],
     javac_shard_size: 50,
     javacflags: [