Merge "Add dynamic item in output switcher if it is available"
diff --git a/api/system-current.txt b/api/system-current.txt
index 5ef042f..9cf0926 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -5725,7 +5725,7 @@
     method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
   }
 
-  public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+  public final class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
     method public int getBandwidthCapability();
     method public int getCodeRateCapability();
     method public int getGuardIntervalCapability();
@@ -5734,7 +5734,7 @@
     method public int getTransmissionModeCapability();
   }
 
-  public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+  public final class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
     method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
     method public int getBandwidth();
     method public int getCodeRate();
@@ -5797,6 +5797,7 @@
   public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
     method @NonNull public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder();
     method public int getAnnex();
+    method public int getBandwidth();
     method public long getInnerFec();
     method public int getModulation();
     method public int getOuterFec();
@@ -5808,6 +5809,11 @@
     field public static final int ANNEX_B = 2; // 0x2
     field public static final int ANNEX_C = 4; // 0x4
     field public static final int ANNEX_UNDEFINED = 0; // 0x0
+    field public static final int BANDWIDTH_5MHZ = 1; // 0x1
+    field public static final int BANDWIDTH_6MHZ = 2; // 0x2
+    field public static final int BANDWIDTH_7MHZ = 4; // 0x4
+    field public static final int BANDWIDTH_8MHZ = 8; // 0x8
+    field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
     field public static final int MODULATION_AUTO = 1; // 0x1
     field public static final int MODULATION_MOD_128QAM = 16; // 0x10
     field public static final int MODULATION_MOD_16QAM = 2; // 0x2
@@ -5837,6 +5843,7 @@
   public static class DvbcFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setBandwidth(int);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
@@ -6130,17 +6137,17 @@
     method public int getBer();
     method @NonNull public int[] getBers();
     method @NonNull public int[] getCodeRates();
+    method @NonNull public int[] getExtendedModulations();
     method public int getFreqOffset();
     method public int getGuardInterval();
     method public int getHierarchy();
     method public long getInnerFec();
     method @NonNull public int[] getInterleaving();
-    method @NonNull public int[] getIsdbtSegment();
+    method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
     method @NonNull public boolean[] getLayerErrors();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
-    method @NonNull public int[] getModulationsExt();
     method public int getPer();
     method public int getPerBer();
     method public int getPlpId();
@@ -6149,7 +6156,7 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
-    method public int getSystemId();
+    method @IntRange(from=0, to=65535) public int getSystemId();
     method public int getTransmissionMode();
     method @NonNull public int[] getTsDataRate();
     method public int getUec();
@@ -9478,6 +9485,7 @@
     field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
     field public static final String NAMESPACE_PERMISSIONS = "permissions";
     field public static final String NAMESPACE_PRIVACY = "privacy";
+    field public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
     field public static final String NAMESPACE_ROLLBACK = "rollback";
     field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
     field public static final String NAMESPACE_RUNTIME = "runtime";
diff --git a/api/test-current.txt b/api/test-current.txt
index edc422d..67b496b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1802,6 +1802,7 @@
     method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
     method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
     method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
     method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
     method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index eea50be..b37f738 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -5665,7 +5665,7 @@
     method @NonNull public android.media.tv.tuner.frontend.AtscFrontendSettings.Builder setModulation(int);
   }
 
-  public class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
+  public final class DtmbFrontendCapabilities extends android.media.tv.tuner.frontend.FrontendCapabilities {
     method public int getBandwidthCapability();
     method public int getCodeRateCapability();
     method public int getGuardIntervalCapability();
@@ -5674,7 +5674,7 @@
     method public int getTransmissionModeCapability();
   }
 
-  public class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
+  public final class DtmbFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
     method @NonNull public static android.media.tv.tuner.frontend.DtmbFrontendSettings.Builder builder();
     method public int getBandwidth();
     method public int getCodeRate();
@@ -5737,6 +5737,7 @@
   public class DvbcFrontendSettings extends android.media.tv.tuner.frontend.FrontendSettings {
     method @NonNull public static android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder builder();
     method public int getAnnex();
+    method public int getBandwidth();
     method public long getInnerFec();
     method public int getModulation();
     method public int getOuterFec();
@@ -5748,6 +5749,11 @@
     field public static final int ANNEX_B = 2; // 0x2
     field public static final int ANNEX_C = 4; // 0x4
     field public static final int ANNEX_UNDEFINED = 0; // 0x0
+    field public static final int BANDWIDTH_5MHZ = 1; // 0x1
+    field public static final int BANDWIDTH_6MHZ = 2; // 0x2
+    field public static final int BANDWIDTH_7MHZ = 4; // 0x4
+    field public static final int BANDWIDTH_8MHZ = 8; // 0x8
+    field public static final int BANDWIDTH_UNDEFINED = 0; // 0x0
     field public static final int MODULATION_AUTO = 1; // 0x1
     field public static final int MODULATION_MOD_128QAM = 16; // 0x10
     field public static final int MODULATION_MOD_16QAM = 2; // 0x2
@@ -5777,6 +5783,7 @@
   public static class DvbcFrontendSettings.Builder {
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings build();
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setAnnex(int);
+    method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setBandwidth(int);
     method @IntRange(from=1) @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setFrequency(int);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setInnerFec(long);
     method @NonNull public android.media.tv.tuner.frontend.DvbcFrontendSettings.Builder setModulation(int);
@@ -6070,17 +6077,17 @@
     method public int getBer();
     method @NonNull public int[] getBers();
     method @NonNull public int[] getCodeRates();
+    method @NonNull public int[] getExtendedModulations();
     method public int getFreqOffset();
     method public int getGuardInterval();
     method public int getHierarchy();
     method public long getInnerFec();
     method @NonNull public int[] getInterleaving();
-    method @NonNull public int[] getIsdbtSegment();
+    method @IntRange(from=0, to=255) @NonNull public int[] getIsdbtSegment();
     method @NonNull public boolean[] getLayerErrors();
     method public int getLnbVoltage();
     method public int getMer();
     method public int getModulation();
-    method @NonNull public int[] getModulationsExt();
     method public int getPer();
     method public int getPerBer();
     method public int getPlpId();
@@ -6089,7 +6096,7 @@
     method public int getSnr();
     method public int getSpectralInversion();
     method public int getSymbolRate();
-    method public int getSystemId();
+    method @IntRange(from=0, to=65535) public int getSystemId();
     method public int getTransmissionMode();
     method @NonNull public int[] getTsDataRate();
     method public int getUec();
@@ -8329,6 +8336,7 @@
     field public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
     field public static final String NAMESPACE_PERMISSIONS = "permissions";
     field public static final String NAMESPACE_PRIVACY = "privacy";
+    field public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
     field public static final String NAMESPACE_ROLLBACK = "rollback";
     field public static final String NAMESPACE_ROLLBACK_BOOT = "rollback_boot";
     field public static final String NAMESPACE_RUNTIME = "runtime";
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
new file mode 100644
index 0000000..a16bb4f
--- /dev/null
+++ b/core/java/android/content/pm/OWNERS
@@ -0,0 +1 @@
+per-file PackageParser.java = chiuwinson@google.com
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 118524d..1c90d23 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -141,8 +141,14 @@
  * <li>All installations must contain a single base APK.
  * </ul>
  *
+ * @deprecated This class is mostly unused and no new changes should be added to it. Use
+ * {@link android.content.pm.parsing.ParsingPackageUtils} and related parsing v2 infrastructure in
+ * the core/services parsing subpackages. Or for a quick parse of a provided APK, use
+ * {@link PackageManager#getPackageArchiveInfo(String, int)}.
+ *
  * @hide
  */
+@Deprecated
 public class PackageParser {
 
     public static final boolean DEBUG_JAR = false;
diff --git a/core/java/android/content/pm/parsing/OWNERS b/core/java/android/content/pm/parsing/OWNERS
new file mode 100644
index 0000000..8049d5c
--- /dev/null
+++ b/core/java/android/content/pm/parsing/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 36137
+
+chiuwinson@google.com
+patb@google.com
+toddke@google.com
diff --git a/core/java/android/hardware/usb/AccessoryFilter.java b/core/java/android/hardware/usb/AccessoryFilter.java
index 8345ff3..98e8c92 100644
--- a/core/java/android/hardware/usb/AccessoryFilter.java
+++ b/core/java/android/hardware/usb/AccessoryFilter.java
@@ -102,7 +102,7 @@
     public boolean matches(UsbAccessory acc) {
         if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
         if (mModel != null && !acc.getModel().equals(mModel)) return false;
-        return !(mVersion != null && !acc.getVersion().equals(mVersion));
+        return !(mVersion != null && !mVersion.equals(acc.getVersion()));
     }
 
     /**
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index 0a4e867..97c9f4b 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -251,6 +251,15 @@
     public static final String NAMESPACE_PACKAGE_MANAGER_SERVICE = "package_manager_service";
 
     /**
+     * Namespace for features related to the Profcollect native Service.
+     * These features are applied at reboot.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String NAMESPACE_PROFCOLLECT_NATIVE_BOOT = "profcollect_native_boot";
+
+    /**
      * Namespace for Rollback flags that are applied immediately.
      *
      * @hide
diff --git a/core/java/com/android/internal/app/ChooserFlags.java b/core/java/com/android/internal/app/ChooserFlags.java
index 3e26679..1a93f1b 100644
--- a/core/java/com/android/internal/app/ChooserFlags.java
+++ b/core/java/com/android/internal/app/ChooserFlags.java
@@ -33,7 +33,7 @@
      */
     public static final boolean USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS =
             DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, true);
+                SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, false);
 
     /**
      * Whether to use {@link AppPredictionManager} to query for direct share targets (as opposed to
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f44ebf3..833924c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -63,7 +63,6 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.pip.phone.PipMenuActivityController;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipUpdateThread;
@@ -137,7 +136,6 @@
     private final PipBoundsState mPipBoundsState;
     private final PipBoundsHandler mPipBoundsHandler;
     private final PipMenuActivityController mMenuActivityController;
-    private final SystemWindows mSystemWindows;
     private final PipAnimationController mPipAnimationController;
     private final PipUiEventLogger mPipUiEventLoggerLogger;
     private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
@@ -270,14 +268,12 @@
             Optional<SplitScreen> splitScreenOptional,
             @NonNull DisplayController displayController,
             @NonNull PipUiEventLogger pipUiEventLogger,
-            @NonNull ShellTaskOrganizer shellTaskOrganizer,
-            @NonNull SystemWindows systemWindows) {
+            @NonNull ShellTaskOrganizer shellTaskOrganizer) {
         mMainHandler = new Handler(Looper.getMainLooper());
         mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
         mPipBoundsState = pipBoundsState;
         mPipBoundsHandler = boundsHandler;
         mMenuActivityController = menuActivityController;
-        mSystemWindows = systemWindows;
         mEnterExitAnimationDuration = context.getResources()
                 .getInteger(R.integer.config_pipResizeAnimationDuration);
         mSurfaceTransactionHelper = surfaceTransactionHelper;
@@ -499,9 +495,13 @@
         }
 
         if (mShouldIgnoreEnteringPipTransition) {
+            final Rect destinationBounds = mPipBoundsState.getBounds();
             // animation is finished in the Launcher and here we directly apply the final touch.
-            applyEnterPipSyncTransaction(mPipBoundsState.getBounds(),
-                    () -> sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP));
+            applyEnterPipSyncTransaction(destinationBounds, () -> {
+                // ensure menu's settled in its final bounds first
+                finishResizeForMenu(destinationBounds);
+                sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
+            });
             mShouldIgnoreEnteringPipTransition = false;
             return;
         }
@@ -995,16 +995,21 @@
             return;
         } else if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
             // TODO: Synchronize this correctly in #applyEnterPipSyncTransaction
-            runOnMainHandler(() -> {
-                mMenuActivityController.movePipMenu(null, null, destinationBounds);
-                mMenuActivityController.updateMenuBounds(destinationBounds);
-            });
+            finishResizeForMenu(destinationBounds);
             return;
         }
 
         WindowContainerTransaction wct = new WindowContainerTransaction();
         prepareFinishResizeTransaction(destinationBounds, direction, tx, wct);
         applyFinishBoundsResize(wct, direction);
+        finishResizeForMenu(destinationBounds);
+    }
+
+    private void finishResizeForMenu(Rect destinationBounds) {
+        if (mMenuActivityController == null) {
+            if (DEBUG) Log.d(TAG, "mMenuActivityController is null");
+            return;
+        }
         runOnMainHandler(() -> {
             mMenuActivityController.movePipMenu(null, null, destinationBounds);
             mMenuActivityController.updateMenuBounds(destinationBounds);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
index a525265..24144b2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuActivityController.java
@@ -305,8 +305,8 @@
     }
 
     private boolean maybeCreateSyncApplier() {
-        if (mPipMenuView == null) {
-            Log.v(TAG, "Not going to move PiP since the menu is not created.");
+        if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+            Log.v(TAG, "Not going to move PiP, either menu or its parent is not created.");
             return false;
         }
 
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index ccc6797..39381c6 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -41,7 +41,6 @@
 
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.pip.phone.PipMenuActivityController;
 import com.android.wm.shell.splitscreen.SplitScreen;
 
@@ -69,7 +68,6 @@
     @Mock private PipUiEventLogger mMockPipUiEventLogger;
     @Mock private Optional<SplitScreen> mMockOptionalSplitScreen;
     @Mock private ShellTaskOrganizer mMockShellTaskOrganizer;
-    @Mock private SystemWindows mSystemWindows;
     private PipBoundsState mPipBoundsState;
 
     private ComponentName mComponent1;
@@ -84,7 +82,7 @@
         mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState,
                 mMockPipBoundsHandler, mMenuActivityController, mMockPipSurfaceTransactionHelper,
                 mMockOptionalSplitScreen, mMockdDisplayController, mMockPipUiEventLogger,
-                mMockShellTaskOrganizer, mSystemWindows));
+                mMockShellTaskOrganizer));
         preparePipTaskOrg();
     }
 
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 1d06e28..a2861c2 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -943,7 +943,7 @@
          * with named endpoint channels. The samples in the frame correspond to the
          * named set bits in the channel position mask, in ascending bit order.
          * See {@link #setChannelIndexMask(int)} to specify channels
-         * based on endpoint numbered channels. This <a href="#channelPositionMask>description of
+         * based on endpoint numbered channels. This <a href="#channelPositionMask">description of
          * channel position masks</a> covers the concept in more details.
          * @param channelMask describes the configuration of the audio channels.
          *    <p> For output, the channelMask can be an OR-ed combination of
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
index 9fc3a23..dd386b0 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendCapabilities.java
@@ -25,7 +25,7 @@
  * @hide
  */
 @SystemApi
-public class DtmbFrontendCapabilities extends FrontendCapabilities {
+public final class DtmbFrontendCapabilities extends FrontendCapabilities {
     private final int mModulationCap;
     private final int mTransmissionModeCap;
     private final int mGuardIntervalCap;
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
index 2c3fe6a..d85e60d 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -34,7 +34,7 @@
  * @hide
  */
 @SystemApi
-public class DtmbFrontendSettings extends FrontendSettings {
+public final class DtmbFrontendSettings extends FrontendSettings {
 
     /** @hide */
     @IntDef(flag = true,
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index e6968bb..a611da6 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -223,6 +223,40 @@
     public static final int TIME_INTERLEAVE_MODE_128_4 = android.hardware.tv.tuner.V1_1.Constants
             .FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
 
+    /** @hide */
+    @IntDef(flag = true,
+            prefix = "BANDWIDTH_",
+            value = {BANDWIDTH_UNDEFINED, BANDWIDTH_5MHZ, BANDWIDTH_6MHZ, BANDWIDTH_7MHZ,
+                    BANDWIDTH_8MHZ})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Bandwidth {}
+
+    /**
+     * Bandwidth undefined.
+     */
+    public static final int BANDWIDTH_UNDEFINED =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.UNDEFINED;
+    /**
+     * 5 MHz bandwidth.
+     */
+    public static final int BANDWIDTH_5MHZ =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_5MHZ;
+    /**
+     * 6 MHz bandwidth.
+     */
+    public static final int BANDWIDTH_6MHZ =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_6MHZ;
+    /**
+     * 7 MHz bandwidth.
+     */
+    public static final int BANDWIDTH_7MHZ =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_7MHZ;
+    /**
+     * 8 MHz bandwidth.
+     */
+    public static final int BANDWIDTH_8MHZ =
+            android.hardware.tv.tuner.V1_1.Constants.FrontendDvbcBandwidth.BANDWIDTH_8MHZ;
+
 
     private final int mModulation;
     private final long mInnerFec;
@@ -232,9 +266,11 @@
     private final int mSpectralInversion;
     // Dvbc time interleave mode is only supported in Tuner 1.1 or higher.
     private final int mInterleaveMode;
+    // Dvbc bandwidth is only supported in Tuner 1.1 or higher.
+    private final int mBandwidth;
 
     private DvbcFrontendSettings(int frequency, int modulation, long innerFec, int symbolRate,
-            int outerFec, int annex, int spectralInversion, int interleaveMode) {
+            int outerFec, int annex, int spectralInversion, int interleaveMode, int bandwidth) {
         super(frequency);
         mModulation = modulation;
         mInnerFec = innerFec;
@@ -243,6 +279,7 @@
         mAnnex = annex;
         mSpectralInversion = spectralInversion;
         mInterleaveMode = interleaveMode;
+        mBandwidth = bandwidth;
     }
 
     /**
@@ -293,6 +330,13 @@
     public int getTimeInterleaveMode() {
         return mInterleaveMode;
     }
+    /**
+     * Gets Bandwidth.
+     */
+    @Bandwidth
+    public int getBandwidth() {
+        return mBandwidth;
+    }
 
     /**
      * Creates a builder for {@link DvbcFrontendSettings}.
@@ -314,6 +358,7 @@
         private int mAnnex = ANNEX_UNDEFINED;
         private int mSpectralInversion = FrontendSettings.FRONTEND_SPECTRAL_INVERSION_UNDEFINED;
         private int mInterleaveMode = TIME_INTERLEAVE_MODE_UNDEFINED;
+        private int mBandwidth = BANDWIDTH_UNDEFINED;
 
         private Builder() {
         }
@@ -407,6 +452,23 @@
             }
             return this;
         }
+        /**
+         * Set the Bandwidth.
+         *
+         * <p>This API is only supported by Tuner HAL 1.1 or higher. Unsupported version would cause
+         * no-op. Use {@link TunerVersionChecker.getTunerVersion()} to check the version.
+         *
+         * @param bandwidth the value to set as the bandwidth. Default value is
+         * {@link #BANDWIDTH_UNDEFINED}.
+         */
+        @NonNull
+        public Builder setBandwidth(@Bandwidth int bandwidth) {
+            if (TunerVersionChecker.checkHigherOrEqualVersionTo(
+                        TunerVersionChecker.TUNER_VERSION_1_1, "setBandwidth")) {
+                mBandwidth = bandwidth;
+            }
+            return this;
+        }
 
         /**
          * Builds a {@link DvbcFrontendSettings} object.
@@ -414,7 +476,7 @@
         @NonNull
         public DvbcFrontendSettings build() {
             return new DvbcFrontendSettings(mFrequency, mModulation, mInnerFec, mSymbolRate,
-                mOuterFec, mAnnex, mSpectralInversion, mInterleaveMode);
+                mOuterFec, mAnnex, mSpectralInversion, mInterleaveMode, mBandwidth);
         }
     }
 
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index dd9347c..c93e079 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -17,6 +17,7 @@
 package android.media.tv.tuner.frontend;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.hardware.tv.tuner.V1_0.Constants;
@@ -634,7 +635,7 @@
     }
 
     /**
-     * Gets an array of BERS status.
+     * Gets an array of extended bit error ratio status.
      *
      * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
@@ -650,12 +651,14 @@
     }
 
     /**
-     * Gets an array of code rates status.
+     * Gets an array of code rates status. The {@link FrontendSettings.InnerFec} would be used to
+     * show the code rate.
      *
      * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
      */
     @NonNull
+    @FrontendSettings.InnerFec
     public int[] getCodeRates() {
         TunerVersionChecker.checkHigherOrEqualVersionTo(
                 TunerVersionChecker.TUNER_VERSION_1_1, "getCodeRates status");
@@ -714,7 +717,8 @@
     }
 
     /**
-     * Gets UEC status.
+     * Gets the Uncorrectable Error Counts of the frontend's Physical Layer Pipe (PLP) since the
+     * last tune operation.
      *
      * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
@@ -729,11 +733,12 @@
     }
 
     /**
-     * Gets system id status.
+     * Gets the current DVB-T2 system id status.
      *
      * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
      */
+    @IntRange(from = 0, to = 0xffff)
     public int getSystemId() {
         TunerVersionChecker.checkHigherOrEqualVersionTo(
                 TunerVersionChecker.TUNER_VERSION_1_1, "getSystemId status");
@@ -744,13 +749,14 @@
     }
 
     /**
-     * Gets an array of interleaving status. Array value should be withink {@link
+     * Gets an array of interleaving status. Array value should be within {@link
      * FrontendInterleaveMode}.
      *
      * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
      */
     @NonNull
+    @FrontendInterleaveMode
     public int[] getInterleaving() {
         TunerVersionChecker.checkHigherOrEqualVersionTo(
                 TunerVersionChecker.TUNER_VERSION_1_1, "getInterleaving status");
@@ -761,12 +767,13 @@
     }
 
     /**
-     * Gets an array of isdbt segment status.
+     * Gets an array of the segments status in ISDB-T Specification of all the channels.
      *
      * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
      */
     @NonNull
+    @IntRange(from = 0, to = 0xff)
     public int[] getIsdbtSegment() {
         TunerVersionChecker.checkHigherOrEqualVersionTo(
                 TunerVersionChecker.TUNER_VERSION_1_1, "getIsdbtSegment status");
@@ -777,7 +784,7 @@
     }
 
     /**
-     * Gets an array of TS data rate status.
+     * Gets an array of the Transport Stream Data Rate in BPS of the current channel.
      *
      * <p>This status query is only supported by Tuner HAL 1.1 or higher. Use
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
@@ -800,9 +807,9 @@
      * {@link TunerVersionChecker.getTunerVersion()} to check the version.
      */
     @NonNull
-    public int[] getModulationsExt() {
+    public int[] getExtendedModulations() {
         TunerVersionChecker.checkHigherOrEqualVersionTo(
-                TunerVersionChecker.TUNER_VERSION_1_1, "getModulationsExt status");
+                TunerVersionChecker.TUNER_VERSION_1_1, "getExtendedModulations status");
         if (mModulationsExt == null) {
             throw new IllegalStateException();
         }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 1be0d44..27e1992 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -140,6 +140,7 @@
 using ::android::hardware::tv::tuner::V1_1::FrontendAnalogSettingsExt1_1;
 using ::android::hardware::tv::tuner::V1_1::FrontendBandwidth;
 using ::android::hardware::tv::tuner::V1_1::FrontendCableTimeInterleaveMode;
+using ::android::hardware::tv::tuner::V1_1::FrontendDvbcBandwidth;
 using ::android::hardware::tv::tuner::V1_1::FrontendDvbsScanType;
 using ::android::hardware::tv::tuner::V1_1::FrontendDvbcSettingsExt1_1;
 using ::android::hardware::tv::tuner::V1_1::FrontendDvbsSettingsExt1_1;
@@ -2725,9 +2726,13 @@
     FrontendCableTimeInterleaveMode interleaveMode =
             static_cast<FrontendCableTimeInterleaveMode>(
                     env->GetIntField(settings, env->GetFieldID(clazz, "mInterleaveMode", "I")));
+    FrontendDvbcBandwidth bandwidth =
+            static_cast<FrontendDvbcBandwidth>(
+                    env->GetIntField(settings, env->GetFieldID(clazz, "mBandwidth", "I")));
 
     FrontendDvbcSettingsExt1_1 dvbcExt1_1 {
         .interleaveMode = interleaveMode,
+        .bandwidth = bandwidth,
     };
     settingsExt1_1.settingExt.dvbc(dvbcExt1_1);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index 64a2aca..e40185c 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -32,6 +32,7 @@
 import android.view.View;
 import android.view.ViewConfiguration;
 import android.view.WindowManager;
+import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
@@ -67,7 +68,7 @@
     private final int mTapTimeout = ViewConfiguration.getTapTimeout();
     private final int mTouchSlop;
     private int mMagnificationMode = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
-    private final WindowManager.LayoutParams mParams;
+    private final LayoutParams mParams;
     private boolean mIsVisible = false;
 
     MagnificationModeSwitch(Context context) {
@@ -80,7 +81,7 @@
         mAccessibilityManager = mContext.getSystemService(AccessibilityManager.class);
         mWindowManager = (WindowManager) mContext.getSystemService(
                 Context.WINDOW_SERVICE);
-        mParams = createLayoutParams();
+        mParams = createLayoutParams(context);
         mImageView = imageView;
         mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         applyResourcesValues();
@@ -221,11 +222,22 @@
     }
 
     void onConfigurationChanged(int configDiff) {
-        if ((configDiff & ActivityInfo.CONFIG_DENSITY) == 0) {
+        if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
+            applyResourcesValues();
+            mImageView.setImageResource(getIconResId(mMagnificationMode));
             return;
         }
-        applyResourcesValues();
-        mImageView.setImageResource(getIconResId(mMagnificationMode));
+        if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+            updateAccessibilityWindowTitle();
+            return;
+        }
+    }
+
+    private void updateAccessibilityWindowTitle() {
+        mParams.accessibilityTitle = getAccessibilityWindowTitle(mContext);
+        if (mIsVisible) {
+            mWindowManager.updateViewLayout(mImageView, mParams);
+        }
     }
 
     private void toggleMagnificationMode() {
@@ -261,14 +273,19 @@
                 : R.drawable.ic_open_in_new_fullscreen;
     }
 
-    private static WindowManager.LayoutParams createLayoutParams() {
-        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
-                WindowManager.LayoutParams.WRAP_CONTENT,
-                WindowManager.LayoutParams.WRAP_CONTENT,
-                WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+    private static LayoutParams createLayoutParams(Context context) {
+        final LayoutParams params = new LayoutParams(
+                LayoutParams.WRAP_CONTENT,
+                LayoutParams.WRAP_CONTENT,
+                LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+                LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSPARENT);
         params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
+        params.accessibilityTitle = getAccessibilityWindowTitle(context);
         return params;
     }
+
+    private static String getAccessibilityWindowTitle(Context context) {
+        return context.getString(com.android.internal.R.string.android_system_label);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
index e9e453b..98424be 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnification.java
@@ -53,7 +53,8 @@
         CommandQueue.Callbacks {
     private static final String TAG = "WindowMagnification";
     private static final int CONFIG_MASK =
-            ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION;
+            ActivityInfo.CONFIG_DENSITY | ActivityInfo.CONFIG_ORIENTATION
+                    | ActivityInfo.CONFIG_LOCALE;
 
     @VisibleForTesting
     protected WindowMagnificationAnimationController mWindowMagnificationAnimationController;
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 340ca04..fd89baa 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -16,7 +16,7 @@
 
 package com.android.systemui.accessibility;
 
-import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+import static android.view.WindowManager.LayoutParams;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
@@ -254,9 +254,18 @@
             }
         } else if ((configDiff & ActivityInfo.CONFIG_ORIENTATION) != 0) {
             onRotate();
+        } else if ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0) {
+            updateAccessibilityWindowTitleIfNeeded();
         }
     }
 
+    private void updateAccessibilityWindowTitleIfNeeded() {
+        if (!isWindowVisible()) return;
+        LayoutParams params = (LayoutParams) mMirrorView.getLayoutParams();
+        params.accessibilityTitle = getAccessibilityWindowTitle();
+        mWm.updateViewLayout(mMirrorView, params);
+    }
+
     /** Handles MirrorWindow position when the navigation bar mode changed. */
     public void onNavigationModeChanged(int mode) {
         mNavBarMode = mode;
@@ -290,8 +299,8 @@
             return;
         }
         // The rect of MirrorView is going to be transformed.
-        WindowManager.LayoutParams params =
-                (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+        LayoutParams params =
+                (LayoutParams) mMirrorView.getLayoutParams();
         mTmpRect.set(params.x, params.y, params.x + params.width, params.y + params.height);
         final RectF transformedRect = new RectF(mTmpRect);
         matrix.mapRect(transformedRect);
@@ -313,17 +322,18 @@
         int windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
         int windowHeight = mMagnificationFrame.height() + 2 * mMirrorSurfaceMargin;
 
-        WindowManager.LayoutParams params = new WindowManager.LayoutParams(
+        LayoutParams params = new LayoutParams(
                 windowWidth, windowHeight,
-                WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
-                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+                LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
+                LayoutParams.FLAG_NOT_TOUCH_MODAL
+                        | LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSPARENT);
         params.gravity = Gravity.TOP | Gravity.LEFT;
         params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
         params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
-        params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
+        params.layoutInDisplayCutoutMode = LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
         params.setTitle(mContext.getString(R.string.magnification_window_title));
+        params.accessibilityTitle = getAccessibilityWindowTitle();
 
         mMirrorView = LayoutInflater.from(mContext).inflate(R.layout.window_magnifier_view, null);
         mMirrorSurfaceView = mMirrorView.findViewById(R.id.surface_view);
@@ -369,6 +379,10 @@
         return regionInsideDragBorder;
     }
 
+    private String getAccessibilityWindowTitle() {
+        return mResources.getString(com.android.internal.R.string.android_system_label);
+    }
+
     private void showControls() {
         if (mMirrorWindowControl != null) {
             mMirrorWindowControl.showControl();
@@ -432,8 +446,8 @@
         }
         final int maxMirrorViewX = mDisplaySize.x - mMirrorView.getWidth();
         final int maxMirrorViewY = mDisplaySize.y - mMirrorView.getHeight() - mNavGestureHeight;
-        WindowManager.LayoutParams params =
-                (WindowManager.LayoutParams) mMirrorView.getLayoutParams();
+        LayoutParams params =
+                (LayoutParams) mMirrorView.getLayoutParams();
         params.x = mMagnificationFrame.left - mMirrorSurfaceMargin;
         params.y = mMagnificationFrame.top - mMirrorSurfaceMargin;
         // If nav bar mode supports swipe-up gesture, the Y position of mirror view should not
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index bbabaf42..a59c876 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -22,7 +22,6 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.WindowManagerShellWrapper;
 import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsHandler;
 import com.android.wm.shell.pip.PipBoundsState;
@@ -107,10 +106,9 @@
             PipBoundsHandler pipBoundsHandler,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
-            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
-            SystemWindows systemWindows) {
+            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
         return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
                 null /* menuActivityController */, pipSurfaceTransactionHelper, splitScreenOptional,
-                displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows);
+                displayController, pipUiEventLogger, shellTaskOrganizer);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
index 1ca04af..0f8fb7b 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java
@@ -131,10 +131,9 @@
             PipMenuActivityController menuActivityController,
             PipSurfaceTransactionHelper pipSurfaceTransactionHelper,
             Optional<SplitScreen> splitScreenOptional, DisplayController displayController,
-            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer,
-            SystemWindows systemWindows) {
+            PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) {
         return new PipTaskOrganizer(context, pipBoundsState, pipBoundsHandler,
                 menuActivityController, pipSurfaceTransactionHelper, splitScreenOptional,
-                displayController, pipUiEventLogger, shellTaskOrganizer, systemWindows);
+                displayController, pipUiEventLogger, shellTaskOrganizer);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index c923515..a0ae35f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -287,6 +287,33 @@
         assertShowFadingAnimation(FADE_OUT_ALPHA);
     }
 
+    @Test
+    public void showButton_hasAccessibilityWindowTitle() {
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_WINDOW);
+
+        ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+                WindowManager.LayoutParams.class);
+        verify(mWindowManager).addView(eq(mSpyImageView), paramsArgumentCaptor.capture());
+        assertEquals(getContext().getResources().getString(
+                com.android.internal.R.string.android_system_label),
+                paramsArgumentCaptor.getValue().accessibilityTitle);
+    }
+
+    @Test
+    public void onLocaleChanged_buttonIsShowing_updateA11yWindowTitle() {
+        final String newA11yWindowTitle = "new a11y window title";
+        mMagnificationModeSwitch.showButton(ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN);
+
+        getContext().getOrCreateTestableResources().addOverride(
+                com.android.internal.R.string.android_system_label, newA11yWindowTitle);
+        mMagnificationModeSwitch.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+
+        ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+                WindowManager.LayoutParams.class);
+        verify(mWindowManager).updateViewLayout(eq(mSpyImageView), paramsArgumentCaptor.capture());
+        assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
+    }
+
     private void assertModeUnchanged(int expectedMode) {
         final int actualMode = Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE, 0);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index f1606c5..a39bc70 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -41,6 +41,7 @@
 import android.content.res.Resources;
 import android.os.Handler;
 import android.testing.AndroidTestingRunner;
+import android.testing.TestableResources;
 import android.view.Display;
 import android.view.Surface;
 import android.view.SurfaceControl;
@@ -114,8 +115,7 @@
         when(mTransaction.remove(any())).thenReturn(mTransaction);
         when(mTransaction.setGeometry(any(), any(), any(),
                 anyInt())).thenReturn(mTransaction);
-        mResources = Mockito.spy(mContext.getResources());
-        when(mContext.getResources()).thenReturn(mResources);
+        mResources = getContext().getOrCreateTestableResources().getResources();
         mWindowMagnificationController = new WindowMagnificationController(mContext,
                 mHandler, mSfVsyncFrameProvider,
                 mMirrorWindowControl, mTransaction, mWindowMagnifierCallback);
@@ -222,7 +222,6 @@
         assertEquals(Surface.ROTATION_90, mWindowMagnificationController.mRotation);
     }
 
-
     @Test
     public void onDensityChanged_enabled_updateDimensionsAndResetWindowMagnification() {
         mInstrumentation.runOnMainSync(() -> {
@@ -310,4 +309,41 @@
 
         verify(mWindowManager).updateViewLayout(eq(mMirrorView), any());
     }
+
+    @Test
+    public void enableWindowMagnification_hasA11yWindowTitle() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+                WindowManager.LayoutParams.class);
+        verify(mWindowManager).addView(eq(mMirrorView), paramsArgumentCaptor.capture());
+        assertEquals(getContext().getResources().getString(
+                com.android.internal.R.string.android_system_label),
+                paramsArgumentCaptor.getValue().accessibilityTitle);
+    }
+
+    @Test
+    public void onLocaleChanged_enabled_updateA11yWindowTitle() {
+        final String newA11yWindowTitle = "new a11y window title";
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+        final TestableResources testableResources = getContext().getOrCreateTestableResources();
+        testableResources.addOverride(com.android.internal.R.string.android_system_label,
+                newA11yWindowTitle);
+        when(mContext.getResources()).thenReturn(testableResources.getResources());
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onConfigurationChanged(ActivityInfo.CONFIG_LOCALE);
+        });
+
+        ArgumentCaptor<WindowManager.LayoutParams> paramsArgumentCaptor = ArgumentCaptor.forClass(
+                WindowManager.LayoutParams.class);
+        verify(mWindowManager).updateViewLayout(eq(mMirrorView), paramsArgumentCaptor.capture());
+        assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
+    }
 }
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index 60e59e3..28afcbb 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -85,6 +85,7 @@
         DeviceConfig.NAMESPACE_INTELLIGENCE_CONTENT_SUGGESTIONS,
         DeviceConfig.NAMESPACE_MEDIA_NATIVE,
         DeviceConfig.NAMESPACE_NETD_NATIVE,
+        DeviceConfig.NAMESPACE_PROFCOLLECT_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE,
         DeviceConfig.NAMESPACE_RUNTIME_NATIVE_BOOT,
         DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index cfc58a5..52fc93f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4306,7 +4306,7 @@
     /** @see AudioManager#playSoundEffect(int, float) */
     public void playSoundEffectVolume(int effectType, float volume) {
         // do not try to play the sound effect if the system stream is muted
-        if (isStreamMutedByRingerOrZenMode(STREAM_SYSTEM)) {
+        if (isStreamMute(STREAM_SYSTEM)) {
             return;
         }
 
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 88867fc..a4ae9c8 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.camera;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.BroadcastReceiver;
@@ -39,6 +40,7 @@
 import android.util.ArraySet;
 import android.util.Slog;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.internal.util.FrameworkStatsLog;
@@ -48,6 +50,8 @@
 import com.android.server.SystemService.TargetUser;
 import com.android.server.wm.WindowManagerInternal;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -79,10 +83,19 @@
 
     // Handler message codes
     private static final int MSG_SWITCH_USER = 1;
+    private static final int MSG_NOTIFY_DEVICE_STATE = 2;
 
     private static final int RETRY_DELAY_TIME = 20; //ms
     private static final int RETRY_TIMES = 60;
 
+    @IntDef(flag = true, prefix = { "DEVICE_STATE_" }, value = {
+            ICameraService.DEVICE_STATE_BACK_COVERED,
+            ICameraService.DEVICE_STATE_FRONT_COVERED,
+            ICameraService.DEVICE_STATE_FOLDED
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    @interface DeviceStateFlags {}
+
     // Maximum entries to keep in usage history before dumping out
     private static final int MAX_USAGE_HISTORY = 100;
 
@@ -94,6 +107,15 @@
     private final Object mLock = new Object();
     private Set<Integer> mEnabledCameraUsers;
     private int mLastUser;
+    // The current set of device state flags. May be different from mLastReportedDeviceState if the
+    // native camera service has not been notified of the change.
+    @GuardedBy("mLock")
+    @DeviceStateFlags
+    private int mDeviceState;
+    // The most recent device state flags reported to the native camera server.
+    @GuardedBy("mLock")
+    @DeviceStateFlags
+    private int mLastReportedDeviceState;
 
     private ICameraService mCameraServiceRaw;
 
@@ -185,6 +207,7 @@
                 return;
             }
             notifySwitchWithRetries(RETRY_TIMES);
+            notifyDeviceStateWithRetries(RETRY_TIMES);
         }
 
         @Override
@@ -218,12 +241,55 @@
         mLogWriterService.allowCoreThreadTimeOut(true);
     }
 
+    /**
+     * Sets the device state bits set within {@code deviceStateFlags} leaving all other bits the
+     * same.
+     * <p>
+     * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}.
+     *
+     * @param deviceStateFlags a bitmask of the device state bits that should be set.
+     *
+     * @see #clearDeviceStateFlags(int)
+     */
+    public void setDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
+            mDeviceState |= deviceStateFlags;
+            if (mDeviceState != mLastReportedDeviceState) {
+                notifyDeviceStateWithRetriesLocked(RETRY_TIMES);
+            }
+        }
+    }
+
+    /**
+     * Clears the device state bits set within {@code deviceStateFlags} leaving all other bits the
+     * same.
+     * <p>
+     * Calling requires permission {@link android.Manifest.permission#CAMERA_SEND_SYSTEM_EVENTS}.
+     *
+     * @param deviceStateFlags a bitmask of the device state bits that should be cleared.
+     *
+     * @see #setDeviceStateFlags(int)
+     */
+    public void clearDeviceStateFlags(@DeviceStateFlags int deviceStateFlags) {
+        synchronized (mLock) {
+            mHandler.removeMessages(MSG_NOTIFY_DEVICE_STATE);
+            mDeviceState &= ~deviceStateFlags;
+            if (mDeviceState != mLastReportedDeviceState) {
+                notifyDeviceStateWithRetriesLocked(RETRY_TIMES);
+            }
+        }
+    }
+
     @Override
     public boolean handleMessage(Message msg) {
         switch(msg.what) {
             case MSG_SWITCH_USER: {
                 notifySwitchWithRetries(msg.arg1);
             } break;
+            case MSG_NOTIFY_DEVICE_STATE: {
+                notifyDeviceStateWithRetries(msg.arg1);
+            } break;
             default: {
                 Slog.e(TAG, "CameraServiceProxy error, invalid message: " + msg.what);
             } break;
@@ -386,6 +452,25 @@
         }
     }
 
+    @Nullable
+    private ICameraService getCameraServiceRawLocked() {
+        if (mCameraServiceRaw == null) {
+            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
+            if (cameraServiceBinder == null) {
+                return null;
+            }
+            try {
+                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not link to death of native camera service");
+                return null;
+            }
+
+            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+        }
+        return mCameraServiceRaw;
+    }
+
     private void switchUserLocked(int userHandle) {
         Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
         mLastUser = userHandle;
@@ -431,20 +516,10 @@
     private boolean notifyCameraserverLocked(int eventType, Set<Integer> updatedUserHandles) {
         // Forward the user switch event to the native camera service running in the cameraserver
         // process.
-        if (mCameraServiceRaw == null) {
-            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
-            if (cameraServiceBinder == null) {
-                Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
-                return false; // Camera service not active, cannot evict user clients.
-            }
-            try {
-                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Could not link to death of native camera service");
-                return false;
-            }
-
-            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
+        ICameraService cameraService = getCameraServiceRawLocked();
+        if (cameraService == null) {
+            Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
+            return false;
         }
 
         try {
@@ -457,6 +532,43 @@
         return true;
     }
 
+    private void notifyDeviceStateWithRetries(int retries) {
+        synchronized (mLock) {
+            notifyDeviceStateWithRetriesLocked(retries);
+        }
+    }
+
+    private void notifyDeviceStateWithRetriesLocked(int retries) {
+        if (notifyDeviceStateChangeLocked(mDeviceState)) {
+            return;
+        }
+        if (retries <= 0) {
+            return;
+        }
+        Slog.i(TAG, "Could not notify camera service of device state change, retrying...");
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_NOTIFY_DEVICE_STATE, retries - 1,
+                0, null), RETRY_DELAY_TIME);
+    }
+
+    private boolean notifyDeviceStateChangeLocked(@DeviceStateFlags int deviceState) {
+        // Forward the state to the native camera service running in the cameraserver process.
+        ICameraService cameraService = getCameraServiceRawLocked();
+        if (cameraService == null) {
+            Slog.w(TAG, "Could not notify cameraserver, camera service not available.");
+            return false;
+        }
+
+        try {
+            mCameraServiceRaw.notifyDeviceStateChange(deviceState);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not notify cameraserver, remote exception: " + e);
+            // Not much we can do if camera service is dead.
+            return false;
+        }
+        mLastReportedDeviceState = deviceState;
+        return true;
+    }
+
     private void updateActivityCount(String cameraId, int newCameraState, int facing,
             String clientName, int apiLevel) {
         synchronized(mLock) {
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
index 7d76628..e656143 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageValidator.java
@@ -343,6 +343,78 @@
         return true;
     }
 
+    /**
+     * Check if the given value is a valid day of month. A valid value is one which falls within the
+     * range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     *
+     * @param value day of month
+     * @return true if the day of month is valid
+     */
+    private boolean isValidDayOfMonth(int value) {
+        return isWithinRange(value, 1, 31);
+    }
+
+    /**
+     * Check if the given value is a valid month of year. A valid value is one which falls within
+     * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     *
+     * @param value month of year
+     * @return true if the month of year is valid
+     */
+    private boolean isValidMonthOfYear(int value) {
+        return isWithinRange(value, 1, 12);
+    }
+
+    /**
+     * Check if the given value is a valid hour. A valid value is one which falls within the range
+     * description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     *
+     * @param value hour
+     * @return true if the hour is valid
+     */
+    private boolean isValidHour(int value) {
+        return isWithinRange(value, 0, 23);
+    }
+
+    /**
+     * Check if the given value is a valid minute. A valid value is one which falls within the range
+     * description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     *
+     * @param value minute
+     * @return true if the minute is valid
+     */
+    private boolean isValidMinute(int value) {
+        return isWithinRange(value, 0, 59);
+    }
+
+    /**
+     * Check if the given value is a valid duration hours. A valid value is one which falls within
+     * the range description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     *
+     * @param value duration hours
+     * @return true if the duration hours is valid
+     */
+    private boolean isValidDurationHours(int value) {
+        return isWithinRange(value, 0, 99);
+    }
+
+    /**
+     * Check if the given value is a valid recording sequence. A valid value is adheres to range
+     * description defined in CEC 1.4 Specification : Operand Descriptions (Section 17)
+     *
+     * @param value recording sequence
+     * @return true if the given recording sequence is valid
+     */
+    private boolean isValidRecordingSequence(int value) {
+        value = value & 0xFF;
+        // Validate bit 7 is set to zero
+        if ((value & 0x80) != 0x00) {
+            return false;
+        }
+        // Validate than not more than one bit is set
+        return (Integer.bitCount(value) <= 1);
+    }
+
     private class PhysicalAddressValidator implements ParameterValidator {
         @Override
         public int isValid(byte[] params) {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b532fa1..9a60afb 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3254,9 +3254,6 @@
 
     boolean hideCurrentInputLocked(IBinder windowToken, int flags, ResultReceiver resultReceiver,
             @SoftInputShowHideReason int reason) {
-        if (mCurClient == null || mCurClient.curSession == null) {
-            return false;
-        }
         if ((flags&InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
                 && (mShowExplicitlyRequested || mShowForced)) {
             if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b3f49ad..925d3cc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -545,6 +545,7 @@
     static final int SCAN_AS_SYSTEM_EXT = 1 << 21;
     static final int SCAN_AS_ODM = 1 << 22;
     static final int SCAN_AS_APK_IN_APEX = 1 << 23;
+    static final int SCAN_EXPECTED_BETTER = 1 << 24;
 
     @IntDef(flag = true, prefix = { "SCAN_" }, value = {
             SCAN_NO_DEX,
@@ -804,11 +805,12 @@
     final SparseIntArray mIsolatedOwners = new SparseIntArray();
 
     /**
-     * Tracks new system packages [received in an OTA] that we expect to
-     * find updated user-installed versions. Keys are package name, values
-     * are package location.
+     * Tracks packages that we expect to find updated versions of on disk.
+     * Keys are package name, values are package location and package version code.
+     *
+     * @see #expectBetter(String, File, long)
      */
-    final private ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
+    private final ArrayMap<String, List<Pair<File, Long>>> mExpectingBetter = new ArrayMap<>();
 
     /**
      * Tracks existing packages prior to receiving an OTA. Keys are package name.
@@ -3349,7 +3351,7 @@
                                     + ", versionCode=" + ps.versionCode
                                     + "; scanned versionCode=" + scannedPkg.getLongVersionCode());
                             removePackageLI(scannedPkg, true);
-                            mExpectingBetter.put(ps.name, ps.getPath());
+                            expectBetter(ps.name, ps.getPath(), ps.versionCode);
                         }
 
                         continue;
@@ -3379,7 +3381,8 @@
                             // We're expecting that the system app should remain disabled, but add
                             // it to expecting better to recover in case the data version cannot
                             // be scanned.
-                            mExpectingBetter.put(disabledPs.name, disabledPs.getPath());
+                            expectBetter(disabledPs.name, disabledPs.getPath(),
+                                    disabledPs.versionCode);
                         }
                     }
                 }
@@ -3480,38 +3483,48 @@
                 for (int i = 0; i < mExpectingBetter.size(); i++) {
                     final String packageName = mExpectingBetter.keyAt(i);
                     if (!mPackages.containsKey(packageName)) {
-                        final File scanFile = mExpectingBetter.valueAt(i);
-
                         logCriticalInfo(Log.WARN, "Expected better " + packageName
                                 + " but never showed up; reverting to system");
 
-                        @ParseFlags int reparseFlags = 0;
-                        @ScanFlags int rescanFlags = 0;
-                        for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
-                            final ScanPartition partition = mDirsToScanAsSystem.get(i1);
-                            if (partition.containsPrivApp(scanFile)) {
-                                reparseFlags = systemParseFlags;
-                                rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
-                                        | partition.scanFlag;
-                                break;
-                            }
-                            if (partition.containsApp(scanFile)) {
-                                reparseFlags = systemParseFlags;
-                                rescanFlags = systemScanFlags | partition.scanFlag;
-                                break;
-                            }
-                        }
-                        if (rescanFlags == 0) {
-                            Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
-                            continue;
-                        }
-                        mSettings.enableSystemPackageLPw(packageName);
+                        final List<Pair<File, Long>> scanFiles = mExpectingBetter.valueAt(i);
+                        // Sort ascending and iterate backwards to take highest version code
+                        Collections.sort(scanFiles,
+                                (first, second) -> Long.compare(first.second, second.second));
+                        for (int index = scanFiles.size() - 1; index >= 0; index--) {
+                            File scanFile = scanFiles.get(index).first;
 
-                        try {
-                            scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
-                        } catch (PackageManagerException e) {
-                            Slog.e(TAG, "Failed to parse original system package: "
-                                    + e.getMessage());
+                            @ParseFlags int reparseFlags = 0;
+                            @ScanFlags int rescanFlags = 0;
+                            for (int i1 = mDirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
+                                final ScanPartition partition = mDirsToScanAsSystem.get(i1);
+                                if (partition.containsPrivApp(scanFile)) {
+                                    reparseFlags = systemParseFlags;
+                                    rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
+                                            | partition.scanFlag;
+                                    break;
+                                }
+                                if (partition.containsApp(scanFile)) {
+                                    reparseFlags = systemParseFlags;
+                                    rescanFlags = systemScanFlags | partition.scanFlag;
+                                    break;
+                                }
+                            }
+                            if (rescanFlags == 0) {
+                                Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);
+                                continue;
+                            }
+                            mSettings.enableSystemPackageLPw(packageName);
+
+                            rescanFlags |= SCAN_EXPECTED_BETTER;
+
+                            try {
+                                scanPackageTracedLI(scanFile, reparseFlags, rescanFlags, 0, null);
+                                // Take first success and break out of for loop
+                                break;
+                            } catch (PackageManagerException e) {
+                                Slog.e(TAG, "Failed to parse original system package: "
+                                        + e.getMessage());
+                            }
                         }
                     }
                 }
@@ -3901,6 +3914,33 @@
     }
 
     /**
+     * Mark a package as skipped during initial scan, expecting a more up to date version to be
+     * available on the scan of a higher priority partition. This can be either a system partition
+     * or the data partition.
+     *
+     * If for some reason that newer version cannot be scanned successfully, the data structure
+     * created here will be used to backtrack in the scanning process to try and take the highest
+     * version code of the package left on disk that scans successfully.
+     *
+     * This can occur if an OTA adds a new system package which the user has already installed an
+     * update on data for. Or if the device image includes multiple versions of the same package,
+     * for cases where the maintainer of a higher priority partition wants to update an app on
+     * a lower priority partition before shipping a device to users.
+     *
+     * @param pkgName the package name identifier to queue under
+     * @param codePath the path to re-scan if needed
+     * @param knownVersionCode the version of the package so that the set of files can be sorted
+     */
+    private void expectBetter(String pkgName, File codePath, long knownVersionCode) {
+        List<Pair<File, Long>> pairs = mExpectingBetter.get(pkgName);
+        if (pairs == null) {
+            pairs = new ArrayList<>(0);
+            mExpectingBetter.put(pkgName, pairs);
+        }
+        pairs.add(Pair.create(codePath, knownVersionCode));
+    }
+
+    /**
      * Extract, install and enable a stub package.
      * <p>If the compressed file can not be extracted / installed for any reason, the stub
      * APK will be installed and the package will be disabled. To recover from this situation,
@@ -11271,7 +11311,23 @@
                 isUpdatedSystemApp = disabledPkgSetting != null;
             }
             applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);
-            assertPackageIsValid(parsedPackage, parseFlags, scanFlags);
+            try {
+                assertPackageIsValid(parsedPackage, pkgSetting, parseFlags, scanFlags);
+            } catch (PackageManagerException e) {
+                if (e.error == INSTALL_FAILED_VERSION_DOWNGRADE
+                        && ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0)
+                        && ((scanFlags & SCAN_BOOTING) != 0)) {
+                    if (pkgSetting != null && pkgSetting.getPkg() == null) {
+                        // If a package for the pkgSetting hasn't already been found, this is
+                        // skipping a downgrade on a lower priority partition, and so a later scan
+                        // is expected to fill the package.
+                        expectBetter(pkgSetting.name, new File(parsedPackage.getPath()),
+                                parsedPackage.getLongVersionCode());
+                    }
+                }
+
+                throw e;
+            }
 
             SharedUserSetting sharedUserSetting = null;
             if (parsedPackage.getSharedUserId() != null) {
@@ -12123,9 +12179,9 @@
      *
      * @throws PackageManagerException If the package fails any of the validation checks
      */
-    private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
-            final @ScanFlags int scanFlags)
-                    throws PackageManagerException {
+    private void assertPackageIsValid(AndroidPackage pkg,
+            @Nullable PackageSetting existingPkgSetting, final @ParseFlags int parseFlags,
+            final @ScanFlags int scanFlags) throws PackageManagerException {
         if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
             assertCodePolicy(pkg);
         }
@@ -12140,11 +12196,11 @@
         // after OTA.
         final boolean isUserInstall = (scanFlags & SCAN_BOOTING) == 0;
         final boolean isFirstBootOrUpgrade = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;
+        String pkgName = pkg.getPackageName();
         if ((isUserInstall || isFirstBootOrUpgrade)
-                && mApexManager.isApexPackage(pkg.getPackageName())) {
+                && mApexManager.isApexPackage(pkgName)) {
             throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
-                    pkg.getPackageName()
-                            + " is an APEX package and can't be installed as an APK.");
+                    pkgName + " is an APEX package and can't be installed as an APK.");
         }
 
         // Make sure we're not adding any bogus keyset info
@@ -12153,7 +12209,7 @@
 
         synchronized (mLock) {
             // The special "android" package can only be defined once
-            if (pkg.getPackageName().equals("android")) {
+            if (pkgName.equals("android")) {
                 if (mAndroidApplication != null) {
                     Slog.w(TAG, "*************************************************");
                     Slog.w(TAG, "Core android package being redefined.  Skipping.");
@@ -12164,12 +12220,46 @@
                 }
             }
 
-            // A package name must be unique; don't allow duplicates
-            if ((scanFlags & SCAN_NEW_INSTALL) == 0
-                    && mPackages.containsKey(pkg.getPackageName())) {
-                throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
-                        "Application package " + pkg.getPackageName()
-                        + " already installed.  Skipping duplicate.");
+            final long newLongVersionCode = pkg.getLongVersionCode();
+            if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+                boolean runDuplicateCheck = false;
+
+                // It's possible to re-scan a package if an updated system app was expected, but
+                // no update on /data could be found. To avoid infinitely looping, a flag is passed
+                // in when re-scanning and this first branch is skipped if the flag is set.
+                if ((scanFlags & SCAN_EXPECTED_BETTER) == 0 && existingPkgSetting != null) {
+                    long existingLongVersionCode = existingPkgSetting.versionCode;
+                    if (newLongVersionCode <= existingLongVersionCode) {
+                        // Must check that real name is equivalent, as it's possible to downgrade
+                        // version code if the package is actually a different package taking over
+                        // a package name through <original-package/>. It is assumed that this
+                        // migration is one time, one way, and that there is no failsafe if this
+                        // doesn't hold true.
+                        if (Objects.equals(existingPkgSetting.realName, pkg.getRealPackage())) {
+                            if (newLongVersionCode != existingLongVersionCode) {
+                                throw new PackageManagerException(
+                                        INSTALL_FAILED_VERSION_DOWNGRADE,
+                                        "Ignoring lower version " + newLongVersionCode
+                                                + " for package " + pkgName
+                                                + " with expected version "
+                                                + existingLongVersionCode);
+                            }
+                        }
+                    } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0
+                            && (scanFlags & SCAN_BOOTING) != 0) {
+                        // During system boot scan, if there's already a package known, but this
+                        // package is higher version, use it instead, ignoring the duplicate check.
+                        // This will store the higher version in the setting object, and the above
+                        // branch/exception will cause future scans to skip the lower versions.
+                        runDuplicateCheck = false;
+                    }
+                }
+
+                if (runDuplicateCheck && mPackages.containsKey(pkgName)) {
+                    throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
+                            "Application package " + pkgName
+                                    + " already installed.  Skipping duplicate.");
+                }
             }
 
             if (pkg.isStaticSharedLibrary()) {
@@ -12289,8 +12379,8 @@
                         }
                     }
                 }
-                if (pkg.getLongVersionCode() < minVersionCode
-                        || pkg.getLongVersionCode() > maxVersionCode) {
+                if (newLongVersionCode < minVersionCode
+                        || newLongVersionCode > maxVersionCode) {
                     throw new PackageManagerException("Static shared"
                             + " lib version codes must be ordered as lib versions");
                 }
@@ -12305,11 +12395,10 @@
             // to the user-installed location. If we don't allow this change, any newer,
             // user-installed version of the application will be ignored.
             if ((scanFlags & SCAN_REQUIRE_KNOWN) != 0) {
-                if (mExpectingBetter.containsKey(pkg.getPackageName())) {
-                    Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package "
-                            + pkg.getPackageName());
+                if (mExpectingBetter.containsKey(pkgName)) {
+                    Slog.w(TAG, "Relax SCAN_REQUIRE_KNOWN requirement for package " + pkgName);
                 } else {
-                    PackageSetting known = mSettings.getPackageLPr(pkg.getPackageName());
+                    PackageSetting known = mSettings.getPackageLPr(pkgName);
                     if (known != null) {
                         if (DEBUG_PACKAGE_SCANNING) {
                             Log.d(TAG, "Examining " + pkg.getPath()
@@ -12317,14 +12406,14 @@
                         }
                         if (!pkg.getPath().equals(known.getPathString())) {
                             throw new PackageManagerException(INSTALL_FAILED_PACKAGE_CHANGED,
-                                    "Application package " + pkg.getPackageName()
+                                    "Application package " + pkgName
                                     + " found at " + pkg.getPath()
                                     + " but expected at " + known.getPathString()
                                     + "; ignoring.");
                         }
                     } else {
                         throw new PackageManagerException(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
-                                "Application package " + pkg.getPackageName()
+                                "Application package " + pkgName
                                 + " not found; ignoring.");
                     }
                 }
@@ -12347,7 +12436,7 @@
                             INSTALL_FAILED_PROCESS_NOT_DEFINED,
                             "Can't install because application tag's process attribute "
                                     + pkg.getProcessName()
-                                    + " (in package " + pkg.getPackageName()
+                                    + " (in package " + pkgName
                                     + ") is not included in the <processes> list");
                 }
                 assertPackageProcesses(pkg, pkg.getActivities(), procs, "activity");
@@ -12371,7 +12460,7 @@
                             pkg.getSigningDetails().signatures)) {
                         throw new PackageManagerException("Apps that share a user with a " +
                                 "privileged app must themselves be marked as privileged. " +
-                                pkg.getPackageName() + " shares privileged user " +
+                                pkgName + " shares privileged user " +
                                 pkg.getSharedUserId() + ".");
                     }
                 }
@@ -12388,21 +12477,21 @@
                         // upgraded.
                         Objects.requireNonNull(mOverlayConfig,
                                 "Parsing non-system dir before overlay configs are initialized");
-                        if (!mOverlayConfig.isMutable(pkg.getPackageName())) {
+                        if (!mOverlayConfig.isMutable(pkgName)) {
                             throw new PackageManagerException("Overlay "
-                                    + pkg.getPackageName()
+                                    + pkgName
                                     + " is static and cannot be upgraded.");
                         }
                     } else {
                         if ((scanFlags & SCAN_AS_VENDOR) != 0) {
                             if (pkg.getTargetSdkVersion() < getVendorPartitionVersion()) {
-                                Slog.w(TAG, "System overlay " + pkg.getPackageName()
+                                Slog.w(TAG, "System overlay " + pkgName
                                         + " targets an SDK below the required SDK level of vendor"
                                         + " overlays (" + getVendorPartitionVersion() + ")."
                                         + " This will become an install error in a future release");
                             }
                         } else if (pkg.getTargetSdkVersion() < Build.VERSION.SDK_INT) {
-                            Slog.w(TAG, "System overlay " + pkg.getPackageName()
+                            Slog.w(TAG, "System overlay " + pkgName
                                     + " targets an SDK below the required SDK level of system"
                                     + " overlays (" + Build.VERSION.SDK_INT + ")."
                                     + " This will become an install error in a future release");
@@ -12418,7 +12507,7 @@
                         if (!comparePackageSignatures(platformPkgSetting,
                                 pkg.getSigningDetails().signatures)) {
                             throw new PackageManagerException("Overlay "
-                                    + pkg.getPackageName()
+                                    + pkgName
                                     + " must target Q or later, "
                                     + "or be signed with the platform certificate");
                         }
@@ -12440,7 +12529,7 @@
                                 // check reference signature
                                 if (mOverlayConfigSignaturePackage == null) {
                                     throw new PackageManagerException("Overlay "
-                                            + pkg.getPackageName() + " and target "
+                                            + pkgName + " and target "
                                             + pkg.getOverlayTarget() + " signed with"
                                             + " different certificates, and the overlay lacks"
                                             + " <overlay android:targetName>");
@@ -12450,7 +12539,7 @@
                                 if (!comparePackageSignatures(refPkgSetting,
                                         pkg.getSigningDetails().signatures)) {
                                     throw new PackageManagerException("Overlay "
-                                            + pkg.getPackageName() + " signed with a different "
+                                            + pkgName + " signed with a different "
                                             + "certificate than both the reference package and "
                                             + "target " + pkg.getOverlayTarget() + ", and the "
                                             + "overlay lacks <overlay android:targetName>");
@@ -12470,7 +12559,7 @@
                 if (pkg.getSigningDetails().signatureSchemeVersion < minSignatureSchemeVersion) {
                     throw new PackageManagerException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
                             "No signature found in package of version " + minSignatureSchemeVersion
-                                    + " or newer for package " + pkg.getPackageName());
+                                    + " or newer for package " + pkgName);
                 }
             }
         }
diff --git a/services/core/java/com/android/server/pm/parsing/PackageCacher.java b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
index 74ec161..3463daf 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageCacher.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageCacher.java
@@ -57,7 +57,16 @@
      * Returns the cache key for a specified {@code packageFile} and {@code flags}.
      */
     private String getCacheKey(File packageFile, int flags) {
-        StringBuilder sb = new StringBuilder(packageFile.getName());
+        StringBuilder sb = new StringBuilder();
+
+        // To support packages with the same file name across partitions, use the partition name
+        // as a prefix. The cache should only be used for cases where the file paths have been
+        // established using the unique partition names, without canonicalization, so any links
+        // which would point to the same partition name should be handled separately.
+        String cachePrefix = packageFile.toPath().getName(0).toString();
+        sb.append(cachePrefix);
+        sb.append('-');
+        sb.append(packageFile.getName());
         sb.append('-');
         sb.append(flags);
 
diff --git a/services/core/java/com/android/server/pm/parsing/PackageParser2.java b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
index 851ddd1..46d31d9 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageParser2.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageParser2.java
@@ -135,7 +135,7 @@
     }
 
     /**
-     * TODO(b/135203078): Document new package parsing
+     * TODO(b/155493909): Document new package parsing
      */
     @AnyThread
     public ParsedPackage parsePackage(File packageFile, int flags, boolean useCaches)
diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java
index f8e26fc..b007a75 100644
--- a/services/core/java/com/android/server/policy/DisplayFoldController.java
+++ b/services/core/java/com/android/server/policy/DisplayFoldController.java
@@ -16,8 +16,10 @@
 
 package com.android.server.policy;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.Rect;
+import android.hardware.ICameraService;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -26,11 +28,13 @@
 import android.os.Handler;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
+import android.util.Slog;
 import android.view.DisplayInfo;
 import android.view.IDisplayFoldListener;
 
 import com.android.server.DisplayThread;
 import com.android.server.LocalServices;
+import com.android.server.camera.CameraServiceProxy;
 import com.android.server.wm.WindowManagerInternal;
 
 /**
@@ -38,11 +42,13 @@
  * TODO(b/126160895): Move DisplayFoldController from PhoneWindowManager to DisplayPolicy.
  */
 class DisplayFoldController {
-
     private static final String TAG = "DisplayFoldController";
 
     private final WindowManagerInternal mWindowManagerInternal;
     private final DisplayManagerInternal mDisplayManagerInternal;
+    // Camera service proxy can be disabled through a config.
+    @Nullable
+    private final CameraServiceProxy mCameraServiceProxy;
     private final int mDisplayId;
     private final Handler mHandler;
 
@@ -58,10 +64,12 @@
     private final DisplayFoldDurationLogger mDurationLogger = new DisplayFoldDurationLogger();
 
     DisplayFoldController(WindowManagerInternal windowManagerInternal,
-            DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea,
+            DisplayManagerInternal displayManagerInternal,
+            @Nullable CameraServiceProxy cameraServiceProxy, int displayId, Rect foldedArea,
             Handler handler) {
         mWindowManagerInternal = windowManagerInternal;
         mDisplayManagerInternal = displayManagerInternal;
+        mCameraServiceProxy = cameraServiceProxy;
         mDisplayId = displayId;
         mFoldedArea = new Rect(foldedArea);
         mHandler = handler;
@@ -116,6 +124,16 @@
             }
         }
 
+        if (mCameraServiceProxy != null) {
+            if (folded) {
+                mCameraServiceProxy.setDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+            } else {
+                mCameraServiceProxy.clearDeviceStateFlags(ICameraService.DEVICE_STATE_FOLDED);
+            }
+        } else {
+            Slog.w(TAG, "Camera service unavailable to toggle folded state.");
+        }
+
         mDurationLogger.setDeviceFolded(folded);
         mDurationLogger.logFocusedAppWithFoldState(folded, mFocusedApp);
         mFolded = folded;
@@ -193,8 +211,13 @@
     }
 
     static DisplayFoldController create(Context context, int displayId) {
+        final WindowManagerInternal windowManagerService =
+                LocalServices.getService(WindowManagerInternal.class);
         final DisplayManagerInternal displayService =
                 LocalServices.getService(DisplayManagerInternal.class);
+        final CameraServiceProxy cameraServiceProxy =
+                LocalServices.getService(CameraServiceProxy.class);
+
         final String configFoldedArea = context.getResources().getString(
                 com.android.internal.R.string.config_foldedArea);
         final Rect foldedArea;
@@ -204,7 +227,7 @@
             foldedArea = Rect.unflattenFromString(configFoldedArea);
         }
 
-        return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class),
-                displayService, displayId, foldedArea, DisplayThread.getHandler());
+        return new DisplayFoldController(windowManagerService, displayService, cameraServiceProxy,
+                displayId, foldedArea, DisplayThread.getHandler());
     }
 }
diff --git a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
index e72c185..f9ef994 100644
--- a/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
+++ b/services/core/java/com/android/server/rollback/WatchdogRollbackLogger.java
@@ -36,6 +36,7 @@
 import android.content.pm.VersionedPackage;
 import android.content.rollback.PackageRollbackInfo;
 import android.content.rollback.RollbackInfo;
+import android.os.SystemProperties;
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Slog;
@@ -216,6 +217,34 @@
                     failingPackageName,
                     new byte[]{});
         }
+
+        logTestProperties(logPackage, type, rollbackReason, failingPackageName);
+    }
+
+    /**
+     * Writes properties which will be used by rollback tests to check if particular rollback
+     * events have occurred.
+     *
+     * persist.sys.rollbacktest.enabled: true if rollback tests are running
+     * persist.sys.rollbacktest.EVENT_TYPE: true if a particular rollback event has occurred
+     *   ex: persist.sys.rollbacktest.ROLLBACK_INITIATE is true if ROLLBACK_INITIATE has happened
+     * persist.sys.rollbacktest.EVENT_TYPE.logPackage: the package to associate the rollback with
+     * persist.sys.rollbacktest.EVENT_TYPE.rollbackReason: the reason Watchdog triggered a rollback
+     * persist.sys.rollbacktest.EVENT_TYPE.failedPackageName: the failing package or process which
+     *   triggered the rollback
+     */
+    private static void logTestProperties(@Nullable VersionedPackage logPackage, int type,
+            int rollbackReason, @NonNull String failingPackageName) {
+        // This property should be on only during the tests
+        final String prefix = "persist.sys.rollbacktest.";
+        if (!SystemProperties.getBoolean(prefix + "enabled", false)) {
+            return;
+        }
+        String key = prefix + rollbackTypeToString(type);
+        SystemProperties.set(key, String.valueOf(true));
+        SystemProperties.set(key + ".logPackage", logPackage != null ? logPackage.toString() : "");
+        SystemProperties.set(key + ".rollbackReason", rollbackReasonToString(rollbackReason));
+        SystemProperties.set(key + ".failedPackageName", failingPackageName);
     }
 
     @VisibleForTesting
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 03edc58..267c9b7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1266,6 +1266,12 @@
             inputManager = new InputManagerService(context);
             t.traceEnd();
 
+            if (!disableCameraService) {
+                t.traceBegin("StartCameraServiceProxy");
+                mSystemServiceManager.startService(CameraServiceProxy.class);
+                t.traceEnd();
+            }
+
             t.traceBegin("StartWindowManagerService");
             // WMS needs sensor service ready
             ConcurrentUtils.waitForFutureNoInterrupt(mSensorServiceStart, START_SENSOR_SERVICE);
@@ -2200,12 +2206,6 @@
             t.traceEnd();
         }
 
-        if (!disableCameraService) {
-            t.traceBegin("StartCameraServiceProxy");
-            mSystemServiceManager.startService(CameraServiceProxy.class);
-            t.traceEnd();
-        }
-
         if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_EMBEDDED)) {
             t.traceBegin("StartIoTSystemService");
             mSystemServiceManager.startService(IOT_SERVICE_CLASS);
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
index e17358d..7ae2fe0 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/FactoryPackageTest.kt
@@ -17,11 +17,8 @@
 class FactoryPackageTest : BaseHostJUnit4Test() {
 
     companion object {
-        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
-
-        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
-        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
         private const val DEVICE_SIDE = "PackageManagerServiceDeviceSideTests.apk"
+        private const val DEVICE_SIDE_PKG_NAME = "com.android.server.pm.test.deviceside"
 
         @get:ClassRule
         val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -40,7 +37,8 @@
     @Before
     @After
     fun removeApk() {
-        device.uninstallPackage(TEST_PKG_NAME)
+        HostUtils.deleteAllTestPackages(device, preparer)
+        device.uninstallPackage(DEVICE_SIDE_PKG_NAME)
         device.deleteFile(filePath.parent.toString())
         device.reboot()
     }
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
index 9399030..f0b60f4 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/HostUtils.kt
@@ -23,6 +23,15 @@
 import java.io.File
 import java.io.FileOutputStream
 
+internal const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
+internal const val VERSION_STUB = "PackageManagerTestAppStub.apk"
+internal const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
+internal const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
+internal const val VERSION_THREE = "PackageManagerTestAppVersion3.apk"
+internal const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk"
+internal const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk"
+internal const val VERSION_OVERRIDE = "PackageManagerTestAppOriginalOverride.apk"
+
 internal fun SystemPreparer.pushApk(javaResourceName: String, partition: Partition) =
         pushResourceFile(javaResourceName, HostUtils.makePathForApk(javaResourceName, partition)
                 .toString())
@@ -89,12 +98,25 @@
 
 internal object HostUtils {
 
-    fun getDataDir(device: ITestDevice, pkgName: String) =
-            device.executeShellCommand("dumpsys package $pkgName")
-                    .lineSequence()
-                    .map(String::trim)
-                    .single { it.startsWith("dataDir=") }
-                    .removePrefix("dataDir=")
+    /**
+     * Since most of the tests use the same test APKs, consolidate the logic for deleting them
+     * before and after a test runs. This also ensures that a failing test doesn't leave an APK on
+     * device that could spill over to another test when developing locally.
+     *
+     * Iterates all partitions since different tests use different partitions.
+     */
+    fun deleteAllTestPackages(device: ITestDevice, preparer: SystemPreparer) {
+        Partition.values().forEach { partition ->
+            device.uninstallPackage(TEST_PKG_NAME)
+            preparer.deleteApkFolders(partition, VERSION_ONE, VERSION_TWO, VERSION_THREE,
+                    VERSION_THREE_INVALID, VERSION_FOUR, VERSION_OVERRIDE)
+        }
+
+        // TODO: There is an optimization that can be made here by hooking into the SystemPreparer's
+        //  reboot rule, avoiding a reboot cycle by doing the delete in line the built in @After
+        //  reboot.
+        preparer.reboot()
+    }
 
     fun makePathForApk(fileName: String, partition: Partition) =
             makePathForApk(File(fileName), partition)
@@ -136,14 +158,35 @@
             }
             .map(String::trim)
 
+    fun getDataDir(device: ITestDevice, pkgName: String) =
+            packageSection(device, pkgName)
+                    .singleOrNull { it.startsWith("dataDir=") }
+                    ?.removePrefix("dataDir=")
+
+    /** Return all code paths for a package. This will include hidden system package code paths. */
     fun getCodePaths(device: ITestDevice, pkgName: String) =
-            device.executeShellCommand("pm dump $pkgName")
-                    .lineSequence()
-                    .map(String::trim)
+            (packageSection(device, pkgName) +
+                    packageSection(device, pkgName, "Hidden system packages"))
                     .filter { it.startsWith("codePath=") }
                     .map { it.removePrefix("codePath=") }
                     .toList()
 
+    fun getVersionCode(device: ITestDevice, pkgName: String) =
+            packageSection(device, pkgName)
+                    .filter { it.startsWith("versionCode=") }
+                    .map { it.removePrefix("versionCode=") }
+                    .map { it.takeWhile { !it.isWhitespace() } }
+                    .map { it.toInt() }
+                    .firstOrNull()
+
+    fun getPrivateFlags(device: ITestDevice, pkgName: String) =
+            packageSection(device, pkgName)
+                    .filter { it.startsWith("privateFlags=") }
+                    .map { it.removePrefix("privateFlags=[ ") }
+                    .map { it.removeSuffix(" ]") }
+                    .map { it.split(" ") }
+                    .firstOrNull()
+
     private fun userIdLineSequence(device: ITestDevice, pkgName: String) =
             packageSection(device, pkgName)
                     .filter { it.startsWith("User ") }
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
index 37c999c..8594706 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/InvalidNewSystemAppTest.kt
@@ -33,12 +33,6 @@
 class InvalidNewSystemAppTest : BaseHostJUnit4Test() {
 
     companion object {
-        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
-        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
-        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
-        private const val VERSION_THREE_INVALID = "PackageManagerTestAppVersion3Invalid.apk"
-        private const val VERSION_FOUR = "PackageManagerTestAppVersion4.apk"
-
         @get:ClassRule
         val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
     }
@@ -55,7 +49,7 @@
     @Before
     @After
     fun removeApk() {
-        device.uninstallPackage(TEST_PKG_NAME)
+        HostUtils.deleteAllTestPackages(device, preparer)
         preparer.deleteFile(filePath.parent.toString())
                 .reboot()
     }
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
index 4becae6..0c5816b 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/OriginalPackageMigrationTest.kt
@@ -34,10 +34,6 @@
 
     companion object {
         private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
-        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
-        private const val VERSION_TWO = "PackageManagerTestAppVersion2.apk"
-        private const val VERSION_THREE = "PackageManagerTestAppVersion3.apk"
-        private const val NEW_PKG = "PackageManagerTestAppOriginalOverride.apk"
 
         @get:ClassRule
         val deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
@@ -54,9 +50,7 @@
     @Before
     @After
     fun deleteApkFolders() {
-        preparer.deleteApkFolders(Partition.SYSTEM, VERSION_ONE, VERSION_TWO, VERSION_THREE,
-                NEW_PKG)
-                .reboot()
+        HostUtils.deleteAllTestPackages(device, preparer)
     }
 
     @Test
@@ -89,10 +83,10 @@
         device.pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
 
         preparer.deleteApkFolders(Partition.SYSTEM, apk)
-                .pushApk(NEW_PKG, Partition.SYSTEM)
+                .pushApk(VERSION_OVERRIDE, Partition.SYSTEM)
                 .reboot()
 
-        assertCodePath(NEW_PKG)
+        assertCodePath(VERSION_OVERRIDE)
 
         // And then reading the data contents back
         assertThat(device.pullFileContents(
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt
new file mode 100644
index 0000000..8d789e0
--- /dev/null
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemAppScanPriorityTest.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.test
+
+import com.android.internal.util.test.SystemPreparer
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.ClassRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.RuleChain
+import org.junit.rules.TemporaryFolder
+import org.junit.runner.RunWith
+import java.io.File
+
+/**
+ * Pushes APKs onto various system partitions to verify that multiple versions result in the
+ * highest version being scanned. Also tries to upgrade/replace these APKs which should result
+ * in a version upgrade on reboot.
+ *
+ * This will also verify that APKs under the same folder/file name across different partitions
+ * are parsed as separate entities and don't get combined under the same cache entry.
+ *
+ * Known limitations:
+ * - Does not verify that v1 isn't scanned. It's possible to introduce a bug that upgrades the
+ *     system on every reboot from v1 -> v2, as this isn't easily visible after scan has finished.
+ *     This would also have to successfully preserve the app data though, which seems unlikely.
+ * - This takes a very long time to run. 105 seconds for the first test case, up to 60 seconds for
+ *     each following case. It's theoretically possible to parallelize these tests so that each
+ *     method is run by installing all the apps under different names, requiring only 3 reboots to
+ *     fully verify, rather than 3 * numTestCases.
+ */
+@RunWith(DeviceJUnit4ClassRunner::class)
+class SystemAppScanPriorityTest : BaseHostJUnit4Test() {
+
+    companion object {
+        @get:ClassRule
+        var deviceRebootRule = SystemPreparer.TestRuleDelegate(true)
+    }
+
+    private val tempFolder = TemporaryFolder()
+    private val preparer: SystemPreparer = SystemPreparer(tempFolder,
+            SystemPreparer.RebootStrategy.FULL, deviceRebootRule) { this.device }
+
+    private var firstReboot = true
+
+    @Rule
+    @JvmField
+    val rules = RuleChain.outerRule(tempFolder).around(preparer)!!
+
+    @Before
+    @After
+    fun deleteFiles() {
+        HostUtils.deleteAllTestPackages(device, preparer)
+    }
+
+    @Before
+    fun resetFirstReboot() {
+        firstReboot = true
+    }
+
+    @Test
+    fun takeHigherPriority() {
+        preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+                .pushFile(VERSION_TWO, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.PRODUCT)
+    }
+
+    @Test
+    fun takeLowerPriority() {
+        preparer.pushFile(VERSION_TWO, Partition.VENDOR)
+                .pushFile(VERSION_ONE, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.VENDOR)
+    }
+
+    @Test
+    fun upgradeToHigherOnLowerPriority() {
+        preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+                .pushFile(VERSION_TWO, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.PRODUCT)
+
+        preparer.pushFile(VERSION_THREE, Partition.VENDOR)
+                .rebootForTest()
+
+        assertVersionAndPartition(3, Partition.VENDOR)
+    }
+
+    @Test
+    fun upgradeToNewerOnHigherPriority() {
+        preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+                .pushFile(VERSION_TWO, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.PRODUCT)
+
+        preparer.pushFile(VERSION_THREE, Partition.SYSTEM_EXT)
+                .rebootForTest()
+
+        assertVersionAndPartition(3, Partition.SYSTEM_EXT)
+    }
+
+    @Test
+    fun replaceNewerOnLowerPriority() {
+        preparer.pushFile(VERSION_TWO, Partition.VENDOR)
+                .pushFile(VERSION_ONE, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.VENDOR)
+
+        preparer.pushFile(VERSION_THREE, Partition.VENDOR)
+                .rebootForTest()
+
+        assertVersionAndPartition(3, Partition.VENDOR)
+    }
+
+    @Test
+    fun replaceNewerOnHigherPriority() {
+        preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+                .pushFile(VERSION_TWO, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.PRODUCT)
+
+        preparer.pushFile(VERSION_THREE, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(3, Partition.PRODUCT)
+    }
+
+    @Test
+    fun fallbackToLowerPriority() {
+        preparer.pushFile(VERSION_TWO, Partition.VENDOR)
+                .pushFile(VERSION_ONE, Partition.PRODUCT)
+                .pushFile(VERSION_THREE, Partition.SYSTEM_EXT)
+                .rebootForTest()
+
+        assertVersionAndPartition(3, Partition.SYSTEM_EXT)
+
+        preparer.deleteFile(VERSION_THREE, Partition.SYSTEM_EXT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.VENDOR)
+    }
+
+    @Test
+    fun fallbackToHigherPriority() {
+        preparer.pushFile(VERSION_THREE, Partition.VENDOR)
+                .pushFile(VERSION_ONE, Partition.PRODUCT)
+                .pushFile(VERSION_TWO, Partition.SYSTEM_EXT)
+                .rebootForTest()
+
+        assertVersionAndPartition(3, Partition.VENDOR)
+
+        preparer.deleteFile(VERSION_THREE, Partition.VENDOR)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.SYSTEM_EXT)
+    }
+
+    @Test
+    fun removeBoth() {
+        preparer.pushFile(VERSION_ONE, Partition.VENDOR)
+                .pushFile(VERSION_TWO, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertVersionAndPartition(2, Partition.PRODUCT)
+
+        preparer.deleteFile(VERSION_ONE, Partition.VENDOR)
+                .deleteFile(VERSION_TWO, Partition.PRODUCT)
+                .rebootForTest()
+
+        assertThat(device.getAppPackageInfo(TEST_PKG_NAME)).isNull()
+    }
+
+    private fun assertVersionAndPartition(versionCode: Int, partition: Partition) {
+        assertThat(HostUtils.getVersionCode(device, TEST_PKG_NAME)).isEqualTo(versionCode)
+
+        val privateFlags = HostUtils.getPrivateFlags(device, TEST_PKG_NAME)
+
+        when (partition) {
+            Partition.SYSTEM,
+            Partition.SYSTEM_PRIVILEGED -> {
+                assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString())
+                assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString())
+                assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString())
+            }
+            Partition.VENDOR -> {
+                assertThat(privateFlags).contains(Partition.VENDOR.toString())
+                assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString())
+                assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString())
+            }
+            Partition.PRODUCT -> {
+                assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString())
+                assertThat(privateFlags).contains(Partition.PRODUCT.toString())
+                assertThat(privateFlags).doesNotContain(Partition.SYSTEM_EXT.toString())
+            }
+            Partition.SYSTEM_EXT -> {
+                assertThat(privateFlags).doesNotContain(Partition.VENDOR.toString())
+                assertThat(privateFlags).doesNotContain(Partition.PRODUCT.toString())
+                assertThat(privateFlags).contains(Partition.SYSTEM_EXT.toString())
+            }
+        }.run { /* exhaust */ }
+    }
+
+    // Following methods don't use HostUtils in order to test cache behavior when using the same
+    // name across partitions. Writes all files under the version 1 name.
+    private fun makeDevicePath(partition: Partition) =
+            partition.baseAppFolder
+                    .resolve(File(VERSION_ONE).nameWithoutExtension)
+                    .resolve(VERSION_ONE)
+                    .toString()
+
+    private fun SystemPreparer.pushFile(file: String, partition: Partition) =
+            pushResourceFile(file, makeDevicePath(partition))
+
+    private fun SystemPreparer.deleteFile(file: String, partition: Partition) =
+            deleteFile(makeDevicePath(partition))
+
+    /**
+     * Custom reboot used to write app data after the first reboot. This can then be verified
+     * after each subsequent reboot to ensure no data is lost.
+     */
+    private fun SystemPreparer.rebootForTest() {
+        if (firstReboot) {
+            firstReboot = false
+            preparer.reboot()
+
+            val file = tempFolder.newFile()
+            file.writeText("Test")
+            pushFile(file, "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
+        } else {
+            val versionBefore = HostUtils.getVersionCode(device, TEST_PKG_NAME)
+            preparer.reboot()
+            val versionAfter = HostUtils.getVersionCode(device, TEST_PKG_NAME)
+
+            if (versionBefore != null && versionAfter != null) {
+                val fileContents = device.pullFileContents(
+                        "${HostUtils.getDataDir(device, TEST_PKG_NAME)}/files/test.txt")
+                if (versionAfter < versionBefore) {
+                    // A downgrade will wipe app data
+                    assertThat(fileContents).isNull()
+                } else {
+                    // An upgrade or update will preserve app data
+                    assertThat(fileContents).isEqualTo("Test")
+                }
+            }
+        }
+    }
+}
diff --git a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
index 46120af..99dff08 100644
--- a/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
+++ b/services/tests/PackageManagerServiceTests/host/src/com/android/server/pm/test/SystemStubMultiUserDisableUninstallTest.kt
@@ -37,9 +37,6 @@
 class SystemStubMultiUserDisableUninstallTest : BaseHostJUnit4Test() {
 
     companion object {
-        private const val TEST_PKG_NAME = "com.android.server.pm.test.test_app"
-        private const val VERSION_STUB = "PackageManagerTestAppStub.apk"
-        private const val VERSION_ONE = "PackageManagerTestAppVersion1.apk"
 
         /**
          * How many total users on device to test, including primary. This will clean up any
@@ -90,6 +87,12 @@
                 savedDevice?.removeUser(it)
             }
 
+            savedDevice?.let { device ->
+                savedPreparer?.let { preparer ->
+                    HostUtils.deleteAllTestPackages(device, preparer)
+                }
+            }
+
             savedDevice?.uninstallPackage(TEST_PKG_NAME)
             savedDevice?.deleteFile(stubFile.parent.toString())
             savedDevice?.deleteFile(deviceCompressedFile.parent.toString())
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c982f49..425addc 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -12015,12 +12015,13 @@
      * @hide
      */
     @SystemApi
+    @TestApi
     @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
     public @NetworkTypeBitMask long getSupportedRadioAccessFamily() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                return (long) telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
+                return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
             } else {
                 // This can happen when the ITelephony interface is not up yet.
                 return NETWORK_TYPE_BITMASK_UNKNOWN;
diff --git a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
index b7d72db..c769a6f 100644
--- a/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
+++ b/tests/RollbackTest/NetworkStagedRollbackTest/src/com/android/tests/rollback/host/NetworkStagedRollbackTest.java
@@ -16,8 +16,6 @@
 
 package com.android.tests.rollback.host;
 
-import static com.android.tests.rollback.host.WatchdogEventLogger.watchdogEventOccurred;
-
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
@@ -31,7 +29,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -98,12 +95,11 @@
             // Verify rollback was executed after health check deadline
             runPhase("testNetworkFailedRollback_Phase3");
 
-            List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
-            assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+            assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
                     REASON_EXPLICIT_HEALTH_CHECK, null));
-            assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+            assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
                     null, null));
-            assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+            assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
         } finally {
             // Reconnect internet again so we won't break tests which assume internet available
             getDevice().executeShellCommand("svc wifi enable");
@@ -134,8 +130,7 @@
         // Verify rollback was not executed after health check deadline
         runPhase("testNetworkPassedDoesNotRollback_Phase3");
 
-        List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
-        assertEquals(watchdogEventOccurred(watchdogEvents, null, null,
+        assertEquals(mLogger.watchdogEventOccurred(null, null,
                 REASON_EXPLICIT_HEALTH_CHECK, null), false);
     }
 }
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index 725bfa9..2ebb9c1 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -16,8 +16,6 @@
 
 package com.android.tests.rollback.host;
 
-import static com.android.tests.rollback.host.WatchdogEventLogger.watchdogEventOccurred;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -158,12 +156,11 @@
 
         runPhase("testBadApkOnly_Phase4");
 
-        List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
                 REASON_APP_CRASH, TESTAPP_A));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
     }
 
     @Test
@@ -192,12 +189,11 @@
         // verify rollback committed
         runPhase("testNativeWatchdogTriggersRollback_Phase3");
 
-        List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
                         REASON_NATIVE_CRASH, null));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
     }
 
     @Test
@@ -233,12 +229,11 @@
         // verify all available rollbacks have been committed
         runPhase("testNativeWatchdogTriggersRollbackForAll_Phase4");
 
-        List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
                         REASON_NATIVE_CRASH, null));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
     }
 
     /**
@@ -316,12 +311,11 @@
         // Verify rollback occurred due to crash of apk-in-apex
         runPhase("testRollbackApexWithApkCrashing_Phase3");
 
-        List<String> watchdogEvents = mLogger.getWatchdogLoggingEvents();
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_INITIATE, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_INITIATE, null,
                 REASON_APP_CRASH, TESTAPP_A));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_BOOT_TRIGGERED, null,
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_BOOT_TRIGGERED, null,
                 null, null));
-        assertTrue(watchdogEventOccurred(watchdogEvents, ROLLBACK_SUCCESS, null, null, null));
+        assertTrue(mLogger.watchdogEventOccurred(ROLLBACK_SUCCESS, null, null, null));
     }
 
     /**
diff --git a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
index 8873150..6b0d1f8 100644
--- a/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
+++ b/tests/RollbackTest/lib/src/com/android/tests/rollback/host/WatchdogEventLogger.java
@@ -17,52 +17,50 @@
 package com.android.tests.rollback.host;
 
 import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.device.LogcatReceiver;
-import com.android.tradefed.result.InputStreamSource;
 
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.List;
+import static com.google.common.truth.Truth.assertThat;
 
 public class WatchdogEventLogger {
-    private LogcatReceiver mReceiver;
+    private static final String[] ROLLBACK_EVENT_TYPES = {
+            "ROLLBACK_INITIATE", "ROLLBACK_BOOT_TRIGGERED", "ROLLBACK_SUCCESS"};
+    private static final String[] ROLLBACK_EVENT_ATTRS = {
+            "logPackage", "rollbackReason", "failedPackageName"};
+    private static final String PROP_PREFIX = "persist.sys.rollbacktest.";
 
-    public void start(ITestDevice device) {
-        mReceiver =  new LogcatReceiver(device, "logcat -s WatchdogRollbackLogger",
-                device.getOptions().getMaxLogcatDataSize(), 0);
-        mReceiver.start();
-    }
+    private ITestDevice mDevice;
 
-    public void stop() {
-        if (mReceiver != null) {
-            mReceiver.stop();
-            mReceiver.clear();
-        }
-    }
-
-    /**
-     * Returns a list of all Watchdog logging events which have occurred.
-     */
-    public List<String> getWatchdogLoggingEvents() throws Exception {
-        try (InputStreamSource logcatStream = mReceiver.getLogcatData()) {
-            return getWatchdogLoggingEvents(logcatStream);
-        }
-    }
-
-    private static List<String> getWatchdogLoggingEvents(InputStreamSource inputStreamSource)
-            throws Exception {
-        List<String> watchdogEvents = new ArrayList<>();
-        InputStream inputStream = inputStreamSource.createInputStream();
-        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
-        String line;
-        while ((line = reader.readLine()) != null) {
-            if (line.contains("Watchdog event occurred")) {
-                watchdogEvents.add(line);
+    private void resetProperties(boolean enabled) throws Exception {
+        try {
+            mDevice.enableAdbRoot();
+            assertThat(mDevice.setProperty(
+                    PROP_PREFIX + "enabled", String.valueOf(enabled))).isTrue();
+            for (String type : ROLLBACK_EVENT_TYPES) {
+                String key = PROP_PREFIX + type;
+                assertThat(mDevice.setProperty(key, "")).isTrue();
+                for (String attr : ROLLBACK_EVENT_ATTRS) {
+                    assertThat(mDevice.setProperty(key + "." + attr, "")).isTrue();
+                }
             }
+        } finally {
+            mDevice.disableAdbRoot();
         }
-        return watchdogEvents;
+    }
+
+    public void start(ITestDevice device) throws Exception {
+        mDevice = device;
+        resetProperties(true);
+    }
+
+    public void stop() throws Exception {
+        if (mDevice != null) {
+            resetProperties(false);
+        }
+    }
+
+    private boolean matchProperty(String type, String attr, String expectedVal) throws Exception {
+        String key = PROP_PREFIX + type + "." + attr;
+        String val = mDevice.getProperty(key);
+        return expectedVal == null || expectedVal.equals(val);
     }
 
     /**
@@ -71,33 +69,11 @@
      * Check the value of all non-null parameters against the list of Watchdog events that have
      * occurred, and return {@code true} if an event exists which matches all criteria.
      */
-    public static boolean watchdogEventOccurred(List<String> loggingEvents,
-            String type, String logPackage,
+    public boolean watchdogEventOccurred(String type, String logPackage,
             String rollbackReason, String failedPackageName) throws Exception {
-        List<String> eventCriteria = new ArrayList<>();
-        if (type != null) {
-            eventCriteria.add("type: " + type);
-        }
-        if (logPackage != null) {
-            eventCriteria.add("logPackage: " + logPackage);
-        }
-        if (rollbackReason != null) {
-            eventCriteria.add("rollbackReason: " + rollbackReason);
-        }
-        if (failedPackageName != null) {
-            eventCriteria.add("failedPackageName: " + failedPackageName);
-        }
-        for (String loggingEvent: loggingEvents) {
-            boolean matchesCriteria = true;
-            for (String criterion: eventCriteria) {
-                if (!loggingEvent.contains(criterion)) {
-                    matchesCriteria = false;
-                }
-            }
-            if (matchesCriteria) {
-                return true;
-            }
-        }
-        return false;
+        return mDevice.getBooleanProperty(PROP_PREFIX + type, false)
+                && matchProperty(type, "logPackage", logPackage)
+                && matchProperty(type, "rollbackReason", rollbackReason)
+                && matchProperty(type, "failedPackageName", failedPackageName);
     }
 }