Merge "Update TEST_MAPPING for ravenwood" into main
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 32c40df..ea9bb39 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -46,7 +46,6 @@
 import android.os.WorkSource;
 import android.util.ArraySet;
 import android.util.Pair;
-import android.util.StatsEvent;
 
 import com.android.internal.os.TimeoutRecord;
 
@@ -1224,7 +1223,8 @@
      * @return The stats event for the cached apps high watermark since last pull.
      */
     @NonNull
-    public abstract StatsEvent getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull);
+    // TODO: restore to android.util.StatsEvent once Ravenwood includes Mainline stubs
+    public abstract Object getCachedAppsHighWatermarkStats(int atomTag, boolean resetAfterPull);
 
     /**
      * Internal method for clearing app data, with the extra param that is used to indicate restore.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 11660f9..7e07e1f 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -853,6 +853,7 @@
             args.argi2 = pid;
             args.argi3 = Long.hashCode(Thread.currentThread().getId());
             args.argi4 = THREAD_PRIORITY_DEFAULT;
+            args.arg1 = Boolean.TRUE; // backgroundOk
             return args;
         });
     }
@@ -1105,6 +1106,11 @@
         final SomeArgs args =
                 Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get();
         if (args.argi3 == tid) {
+            boolean backgroundOk = (args.arg1 == Boolean.TRUE);
+            if (priority >= THREAD_PRIORITY_BACKGROUND && !backgroundOk) {
+                throw new IllegalArgumentException(
+                        "Priority " + priority + " blocked by setCanSelfBackground()");
+            }
             args.argi4 = priority;
         } else {
             throw new UnsupportedOperationException(
@@ -1119,8 +1125,16 @@
      *
      * @hide
      */
+    @android.ravenwood.annotation.RavenwoodReplace
     public static final native void setCanSelfBackground(boolean backgroundOk);
 
+    /** @hide */
+    public static final void setCanSelfBackground$ravenwood(boolean backgroundOk) {
+        final SomeArgs args =
+                Preconditions.requireNonNullViaRavenwoodRule(sIdentity$ravenwood).get();
+        args.arg1 = Boolean.valueOf(backgroundOk);
+    }
+
     /**
      * Sets the scheduling group for a thread.
      * @hide
diff --git a/core/java/com/android/server/LocalServices.java b/core/java/com/android/server/LocalServices.java
index 9c632ea..2a10918 100644
--- a/core/java/com/android/server/LocalServices.java
+++ b/core/java/com/android/server/LocalServices.java
@@ -29,6 +29,7 @@
  *
  * {@hide}
  */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
 public final class LocalServices {
     private LocalServices() {}
 
@@ -69,4 +70,14 @@
             sLocalServiceObjects.remove(type);
         }
     }
+
+    /**
+     * Remove all known service instances, must be only used in tests.
+     */
+    @VisibleForTesting
+    public static void removeAllServicesForTest() {
+        synchronized (sLocalServiceObjects) {
+            sLocalServiceObjects.clear();
+        }
+    }
 }
diff --git a/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml b/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml
index 6979825..e4befc2 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-v31/style_preference.xml
@@ -70,6 +70,7 @@
 
     <style name="SettingsSwitchPreferenceCompat.SettingsLib" parent="@style/Preference.SwitchPreferenceCompat.Material">
         <item name="layout">@layout/settingslib_preference</item>
+        <item name="singleLineTitle">false</item>
         <item name="iconSpaceReserved">@bool/settingslib_config_icon_space_reserved</item>
     </style>
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
index ba80a8d..1a653c3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerContent.kt
@@ -34,9 +34,9 @@
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.height
-import androidx.compose.foundation.layout.heightIn
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.layout.width
@@ -66,6 +66,7 @@
 import androidx.compose.ui.platform.LocalContext
 import androidx.compose.ui.platform.LocalLayoutDirection
 import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpOffset
 import androidx.compose.ui.unit.LayoutDirection
 import androidx.compose.ui.unit.dp
@@ -97,7 +98,7 @@
 fun BouncerContent(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerDialogFactory,
-    modifier: Modifier
+    modifier: Modifier = Modifier,
 ) {
     val isFullScreenUserSwitcherEnabled = viewModel.isUserSwitcherVisible
     val isSideBySideSupported by viewModel.isSideBySideSupported.collectAsState()
@@ -142,6 +143,7 @@
     viewModel: BouncerViewModel,
     dialogFactory: BouncerDialogFactory,
     modifier: Modifier = Modifier,
+    layout: BouncerSceneLayout = BouncerSceneLayout.STANDARD,
     outputOnly: Boolean = false,
 ) {
     val foldPosture: FoldPosture by foldPosture()
@@ -161,6 +163,7 @@
             FoldSplittable(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
+                layout = layout,
                 outputOnly = outputOnly,
                 isSplit = false,
             )
@@ -170,6 +173,7 @@
             FoldSplittable(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
+                layout = layout,
                 outputOnly = outputOnly,
                 isSplit = true,
             )
@@ -193,6 +197,7 @@
 private fun SceneScope.FoldSplittable(
     viewModel: BouncerViewModel,
     dialogFactory: BouncerDialogFactory,
+    layout: BouncerSceneLayout,
     outputOnly: Boolean,
     isSplit: Boolean,
     modifier: Modifier = Modifier,
@@ -210,13 +215,21 @@
         // Content above the fold, when split on a foldable device in a "table top" posture:
         Box(
             modifier =
-                Modifier.element(SceneElements.AboveFold).fillMaxWidth().thenIf(isSplit) {
-                    Modifier.weight(splitRatio)
-                },
+                Modifier.element(SceneElements.AboveFold)
+                    .fillMaxWidth()
+                    .then(
+                        if (isSplit) {
+                            Modifier.weight(splitRatio)
+                        } else if (outputOnly) {
+                            Modifier.fillMaxHeight()
+                        } else {
+                            Modifier
+                        }
+                    ),
         ) {
             Column(
                 horizontalAlignment = Alignment.CenterHorizontally,
-                modifier = Modifier.fillMaxWidth().padding(top = 92.dp),
+                modifier = Modifier.fillMaxWidth().padding(top = layout.topPadding),
             ) {
                 Crossfade(
                     targetState = message,
@@ -230,11 +243,23 @@
                     )
                 }
 
-                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+                if (!outputOnly) {
+                    Spacer(Modifier.height(layout.spacingBetweenMessageAndEnteredInput))
 
+                    UserInputArea(
+                        viewModel = viewModel,
+                        visibility = UserInputAreaVisibility.OUTPUT_ONLY,
+                        layout = layout,
+                    )
+                }
+            }
+
+            if (outputOnly) {
                 UserInputArea(
                     viewModel = viewModel,
                     visibility = UserInputAreaVisibility.OUTPUT_ONLY,
+                    layout = layout,
+                    modifier = Modifier.align(Alignment.Center),
                 )
             }
         }
@@ -242,25 +267,32 @@
         // Content below the fold, when split on a foldable device in a "table top" posture:
         Box(
             modifier =
-                Modifier.element(SceneElements.BelowFold).fillMaxWidth().thenIf(isSplit) {
-                    Modifier.weight(1 - splitRatio)
-                },
+                Modifier.element(SceneElements.BelowFold)
+                    .fillMaxWidth()
+                    .weight(
+                        if (isSplit) {
+                            1 - splitRatio
+                        } else {
+                            1f
+                        }
+                    ),
         ) {
             Column(
                 horizontalAlignment = Alignment.CenterHorizontally,
-                modifier = Modifier.fillMaxWidth(),
+                modifier = Modifier.fillMaxSize()
             ) {
                 if (!outputOnly) {
                     Box(Modifier.weight(1f)) {
                         UserInputArea(
                             viewModel = viewModel,
                             visibility = UserInputAreaVisibility.INPUT_ONLY,
-                            modifier = Modifier.align(Alignment.Center),
+                            layout = layout,
+                            modifier = Modifier.align(Alignment.BottomCenter),
                         )
                     }
                 }
 
-                Spacer(Modifier.heightIn(min = 21.dp, max = 48.dp))
+                Spacer(Modifier.height(48.dp))
 
                 val actionButtonModifier = Modifier.height(56.dp)
 
@@ -275,7 +307,7 @@
                     }
                 }
 
-                Spacer(Modifier.height(48.dp))
+                Spacer(Modifier.height(layout.bottomPadding))
             }
         }
 
@@ -311,6 +343,7 @@
 private fun UserInputArea(
     viewModel: BouncerViewModel,
     visibility: UserInputAreaVisibility,
+    layout: BouncerSceneLayout,
     modifier: Modifier = Modifier,
 ) {
     val authMethodViewModel: AuthMethodBouncerViewModel? by
@@ -327,6 +360,7 @@
                 UserInputAreaVisibility.INPUT_ONLY ->
                     PinPad(
                         viewModel = nonNullViewModel,
+                        layout = layout,
                         modifier = modifier,
                     )
             }
@@ -341,7 +375,8 @@
             if (visibility == UserInputAreaVisibility.INPUT_ONLY) {
                 PatternBouncer(
                     viewModel = nonNullViewModel,
-                    modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false)
+                    layout = layout,
+                    modifier = modifier.aspectRatio(1f, matchHeightConstraintsFirst = false),
                 )
             }
         else -> Unit
@@ -449,7 +484,7 @@
 }
 
 /**
- * Renders the dropdown menu that displays the actual users and/or user actions that can be
+ * Renders the dropdowm menu that displays the actual users and/or user actions that can be
  * selected.
  */
 @Composable
@@ -519,6 +554,7 @@
             StandardLayout(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
+                layout = BouncerSceneLayout.SPLIT,
                 outputOnly = true,
                 modifier = startContentModifier,
             )
@@ -527,10 +563,12 @@
             UserInputArea(
                 viewModel = viewModel,
                 visibility = UserInputAreaVisibility.INPUT_ONLY,
+                layout = BouncerSceneLayout.SPLIT,
                 modifier = endContentModifier,
             )
         },
-        modifier = modifier
+        layout = BouncerSceneLayout.SPLIT,
+        modifier = modifier,
     )
 }
 
@@ -542,6 +580,7 @@
 private fun SwappableLayout(
     startContent: @Composable (Modifier) -> Unit,
     endContent: @Composable (Modifier) -> Unit,
+    layout: BouncerSceneLayout,
     modifier: Modifier = Modifier,
 ) {
     val layoutDirection = LocalLayoutDirection.current
@@ -597,7 +636,7 @@
                     alpha = animatedAlpha(animatedOffset)
                 }
         ) {
-            endContent(Modifier.widthIn(max = 400.dp).align(Alignment.BottomCenter))
+            endContent(Modifier.align(layout.swappableEndContentAlignment).widthIn(max = 400.dp))
         }
     }
 }
@@ -635,9 +674,11 @@
             StandardLayout(
                 viewModel = viewModel,
                 dialogFactory = dialogFactory,
+                layout = BouncerSceneLayout.SIDE_BY_SIDE,
                 modifier = endContentModifier,
             )
         },
+        layout = BouncerSceneLayout.SIDE_BY_SIDE,
         modifier = modifier,
     )
 }
@@ -663,6 +704,7 @@
         StandardLayout(
             viewModel = viewModel,
             dialogFactory = dialogFactory,
+            layout = BouncerSceneLayout.STACKED,
             modifier = Modifier.fillMaxWidth().weight(1f),
         )
     }
@@ -732,3 +774,48 @@
 private val SceneTransitions = transitions {
     from(SceneKeys.ContiguousSceneKey, to = SceneKeys.SplitSceneKey) { spec = tween() }
 }
+
+/** Whether a more compact size should be used for various spacing dimensions. */
+internal val BouncerSceneLayout.isUseCompactSize: Boolean
+    get() =
+        when (this) {
+            BouncerSceneLayout.SIDE_BY_SIDE -> true
+            BouncerSceneLayout.SPLIT -> true
+            else -> false
+        }
+
+/** Amount of space to place between the message and the entered input UI elements, in dips. */
+private val BouncerSceneLayout.spacingBetweenMessageAndEnteredInput: Dp
+    get() =
+        when {
+            this == BouncerSceneLayout.STACKED -> 24.dp
+            isUseCompactSize -> 96.dp
+            else -> 128.dp
+        }
+
+/** Amount of space to place above the topmost UI element, in dips. */
+private val BouncerSceneLayout.topPadding: Dp
+    get() =
+        if (this == BouncerSceneLayout.SPLIT) {
+            40.dp
+        } else {
+            92.dp
+        }
+
+/** Amount of space to place below the bottommost UI element, in dips. */
+private val BouncerSceneLayout.bottomPadding: Dp
+    get() =
+        if (this == BouncerSceneLayout.SPLIT) {
+            40.dp
+        } else {
+            48.dp
+        }
+
+/** The in-a-box alignment for the content on the "end" side of a swappable layout. */
+private val BouncerSceneLayout.swappableEndContentAlignment: Alignment
+    get() =
+        if (this == BouncerSceneLayout.SPLIT) {
+            Alignment.Center
+        } else {
+            Alignment.BottomCenter
+        }
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
index eb06889..2799959 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PasswordBouncer.kt
@@ -18,8 +18,6 @@
 
 import android.view.ViewTreeObserver
 import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Spacer
-import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.material3.LocalTextStyle
@@ -31,7 +29,6 @@
 import androidx.compose.runtime.State
 import androidx.compose.runtime.collectAsState
 import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.produceState
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
@@ -64,10 +61,6 @@
             focusRequester.requestFocus()
         }
     }
-    val (isTextFieldFocused, onTextFieldFocusChanged) = remember { mutableStateOf(false) }
-    LaunchedEffect(isTextFieldFocused) {
-        viewModel.onTextFieldFocusChanged(isFocused = isTextFieldFocused)
-    }
 
     val password: String by viewModel.password.collectAsState()
     val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
@@ -113,7 +106,7 @@
                 ),
             modifier =
                 Modifier.focusRequester(focusRequester)
-                    .onFocusChanged { onTextFieldFocusChanged(it.isFocused) }
+                    .onFocusChanged { viewModel.onTextFieldFocusChanged(it.isFocused) }
                     .drawBehind {
                         drawLine(
                             color = color,
@@ -123,8 +116,6 @@
                         )
                     },
         )
-
-        Spacer(Modifier.height(100.dp))
     }
 }
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
index ff1cbd6..a4b1955 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PatternBouncer.kt
@@ -48,6 +48,7 @@
 import com.android.compose.animation.Easings
 import com.android.compose.modifiers.thenIf
 import com.android.internal.R
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
 import com.android.systemui.bouncer.ui.viewmodel.PatternBouncerViewModel
 import com.android.systemui.bouncer.ui.viewmodel.PatternDotViewModel
 import kotlin.math.min
@@ -64,6 +65,7 @@
 @Composable
 internal fun PatternBouncer(
     viewModel: PatternBouncerViewModel,
+    layout: BouncerSceneLayout,
     modifier: Modifier = Modifier,
 ) {
     DisposableEffect(Unit) {
@@ -190,6 +192,8 @@
     // This is the position of the input pointer.
     var inputPosition: Offset? by remember { mutableStateOf(null) }
     var gridCoordinates: LayoutCoordinates? by remember { mutableStateOf(null) }
+    var offset: Offset by remember { mutableStateOf(Offset.Zero) }
+    var scale: Float by remember { mutableStateOf(1f) }
 
     Canvas(
         modifier
@@ -224,21 +228,42 @@
                             },
                         ) { change, _ ->
                             inputPosition = change.position
-                            viewModel.onDrag(
-                                xPx = change.position.x,
-                                yPx = change.position.y,
-                                containerSizePx = size.width,
-                            )
+                            change.position.minus(offset).div(scale).let {
+                                viewModel.onDrag(
+                                    xPx = it.x,
+                                    yPx = it.y,
+                                    containerSizePx = size.width,
+                                )
+                            }
                         }
                     }
             }
     ) {
         gridCoordinates?.let { nonNullCoordinates ->
             val containerSize = nonNullCoordinates.size
+            if (containerSize.width <= 0 || containerSize.height <= 0) {
+                return@let
+            }
+
             val horizontalSpacing = containerSize.width.toFloat() / colCount
             val verticalSpacing = containerSize.height.toFloat() / rowCount
             val spacing = min(horizontalSpacing, verticalSpacing)
-            val verticalOffset = containerSize.height - spacing * rowCount
+            val horizontalOffset =
+                offset(
+                    availableSize = containerSize.width,
+                    spacingPerDot = spacing,
+                    dotCount = colCount,
+                    isCentered = true,
+                )
+            val verticalOffset =
+                offset(
+                    availableSize = containerSize.height,
+                    spacingPerDot = spacing,
+                    dotCount = rowCount,
+                    isCentered = layout.isCenteredVertically,
+                )
+            offset = Offset(horizontalOffset, verticalOffset)
+            scale = (colCount * spacing) / containerSize.width
 
             if (isAnimationEnabled) {
                 // Draw lines between dots.
@@ -248,8 +273,9 @@
                         val lineFadeOutAnimationProgress =
                             lineFadeOutAnimatables[previousDot]!!.value
                         val startLerp = 1 - lineFadeOutAnimationProgress
-                        val from = pixelOffset(previousDot, spacing, verticalOffset)
-                        val to = pixelOffset(dot, spacing, verticalOffset)
+                        val from =
+                            pixelOffset(previousDot, spacing, horizontalOffset, verticalOffset)
+                        val to = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
                         val lerpedFrom =
                             Offset(
                                 x = from.x + (to.x - from.x) * startLerp,
@@ -270,7 +296,7 @@
                 // position.
                 inputPosition?.let { lineEnd ->
                     currentDot?.let { dot ->
-                        val from = pixelOffset(dot, spacing, verticalOffset)
+                        val from = pixelOffset(dot, spacing, horizontalOffset, verticalOffset)
                         val lineLength =
                             sqrt((from.y - lineEnd.y).pow(2) + (from.x - lineEnd.x).pow(2))
                         drawLine(
@@ -288,7 +314,7 @@
             // Draw each dot on the grid.
             dots.forEach { dot ->
                 drawCircle(
-                    center = pixelOffset(dot, spacing, verticalOffset),
+                    center = pixelOffset(dot, spacing, horizontalOffset, verticalOffset),
                     color = dotColor,
                     radius = dotRadius * (dotScalingAnimatables[dot]?.value ?: 1f),
                 )
@@ -301,10 +327,11 @@
 private fun pixelOffset(
     dot: PatternDotViewModel,
     spacing: Float,
+    horizontalOffset: Float,
     verticalOffset: Float,
 ): Offset {
     return Offset(
-        x = dot.x * spacing + spacing / 2,
+        x = dot.x * spacing + spacing / 2 + horizontalOffset,
         y = dot.y * spacing + spacing / 2 + verticalOffset,
     )
 }
@@ -371,6 +398,35 @@
     }
 }
 
+/**
+ * Returns the amount of offset along the axis, in pixels, that should be applied to all dots.
+ *
+ * @param availableSize The size of the container, along the axis of interest.
+ * @param spacingPerDot The amount of pixels that each dot should take (including the area around
+ *   that dot).
+ * @param dotCount The number of dots along the axis (e.g. if the axis of interest is the
+ *   horizontal/x axis, this is the number of columns in the dot grid).
+ * @param isCentered Whether the dots should be centered along the axis of interest; if `false`, the
+ *   dots will be pushed towards to end/bottom of the axis.
+ */
+private fun offset(
+    availableSize: Int,
+    spacingPerDot: Float,
+    dotCount: Int,
+    isCentered: Boolean = false,
+): Float {
+    val default = availableSize - spacingPerDot * dotCount
+    return if (isCentered) {
+        default / 2
+    } else {
+        default
+    }
+}
+
+/** Whether the UI should be centered vertically. */
+private val BouncerSceneLayout.isCenteredVertically: Boolean
+    get() = this == BouncerSceneLayout.SPLIT
+
 private const val DOT_DIAMETER_DP = 16
 private const val SELECTED_DOT_DIAMETER_DP = 24
 private const val SELECTED_DOT_REACTION_ANIMATION_DURATION_MS = 83
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
index 59617c9..8f5d9f4 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinBouncer.kt
@@ -52,6 +52,7 @@
 import com.android.compose.animation.Easings
 import com.android.compose.grid.VerticalGrid
 import com.android.compose.modifiers.thenIf
+import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
 import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
 import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
 import com.android.systemui.common.shared.model.ContentDescription
@@ -65,9 +66,11 @@
 import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 
+/** Renders the PIN button pad. */
 @Composable
 fun PinPad(
     viewModel: PinBouncerViewModel,
+    layout: BouncerSceneLayout,
     modifier: Modifier = Modifier,
 ) {
     DisposableEffect(Unit) {
@@ -92,9 +95,9 @@
     }
 
     VerticalGrid(
-        columns = 3,
-        verticalSpacing = 12.dp,
-        horizontalSpacing = 20.dp,
+        columns = columns,
+        verticalSpacing = layout.verticalSpacing,
+        horizontalSpacing = calculateHorizontalSpacingBetweenColumns(layout.gridWidth),
         modifier = modifier,
     ) {
         repeat(9) { index ->
@@ -254,7 +257,7 @@
 
     val cornerRadius: Dp by
         animateDpAsState(
-            if (isAnimationEnabled && isPressed) 24.dp else pinButtonSize / 2,
+            if (isAnimationEnabled && isPressed) 24.dp else pinButtonMaxSize / 2,
             label = "PinButton round corners",
             animationSpec = tween(animDurationMillis, easing = animEasing)
         )
@@ -284,7 +287,7 @@
         contentAlignment = Alignment.Center,
         modifier =
             modifier
-                .sizeIn(maxWidth = pinButtonSize, maxHeight = pinButtonSize)
+                .sizeIn(maxWidth = pinButtonMaxSize, maxHeight = pinButtonMaxSize)
                 .aspectRatio(1f)
                 .drawBehind {
                     drawRoundRect(
@@ -345,10 +348,32 @@
     }
 }
 
-private val pinButtonSize = 84.dp
-private val pinButtonErrorShrinkFactor = 67.dp / pinButtonSize
+/** Returns the amount of horizontal spacing between columns, in dips. */
+private fun calculateHorizontalSpacingBetweenColumns(
+    gridWidth: Dp,
+): Dp {
+    return (gridWidth - (pinButtonMaxSize * columns)) / (columns - 1)
+}
+
+/** The width of the grid of PIN pad buttons, in dips. */
+private val BouncerSceneLayout.gridWidth: Dp
+    get() = if (isUseCompactSize) 292.dp else 300.dp
+
+/** The spacing between rows of PIN pad buttons, in dips. */
+private val BouncerSceneLayout.verticalSpacing: Dp
+    get() = if (isUseCompactSize) 8.dp else 12.dp
+
+/** Number of columns in the PIN pad grid. */
+private const val columns = 3
+/** Maximum size (width and height) of each PIN pad button. */
+private val pinButtonMaxSize = 84.dp
+/** Scale factor to apply to buttons when animating the "error" animation on them. */
+private val pinButtonErrorShrinkFactor = 67.dp / pinButtonMaxSize
+/** Animation duration of the "shrink" phase of the error animation, on each PIN pad button. */
 private const val pinButtonErrorShrinkMs = 50
+/** Amount of time to wait between application of the "error" animation to each row of buttons. */
 private const val pinButtonErrorStaggerDelayMs = 33
+/** Animation duration of the "revert" phase of the error animation, on each PIN pad button. */
 private const val pinButtonErrorRevertMs = 617
 
 // Pin button motion spec: http://shortn/_9TTIG6SoEa
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 80d45bc..78b854e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -22,8 +22,11 @@
 import androidx.constraintlayout.widget.ConstraintSet
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.internal.logging.UiEventLogger
 import com.android.internal.util.LatencyTracker
 import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockscreenCredential
+import com.android.keyguard.KeyguardPinViewController.PinBouncerUiEvent
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.classifier.FalsingCollector
@@ -91,6 +94,7 @@
     @Mock lateinit var passwordTextView: PasswordTextView
     @Mock lateinit var deleteButton: NumPadButton
     @Mock lateinit var enterButton: View
+    @Mock lateinit var uiEventLogger: UiEventLogger
 
     @Captor lateinit var postureCallbackCaptor: ArgumentCaptor<DevicePostureController.Callback>
 
@@ -137,6 +141,7 @@
             postureController,
             featureFlags,
             mSelectedUserInteractor,
+            uiEventLogger
         )
     }
 
@@ -251,4 +256,21 @@
 
         verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
     }
+
+    @Test
+    fun onUserInput_autoConfirmation_attemptsUnlock() {
+        val pinViewController = constructPinViewController(mockKeyguardPinView)
+        whenever(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
+        whenever(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
+        whenever(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
+        whenever(passwordTextView.text).thenReturn("000000")
+        whenever(enterButton.visibility).thenReturn(View.INVISIBLE)
+        whenever(mockKeyguardPinView.enteredCredential)
+            .thenReturn(LockscreenCredential.createPin("000000"))
+
+        pinViewController.onUserInput()
+
+        verify(uiEventLogger).log(PinBouncerUiEvent.ATTEMPT_UNLOCK_WITH_AUTO_CONFIRM_FEATURE)
+        verify(keyguardUpdateMonitor).setCredentialAttempted()
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index b309483..714fe64 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 import android.view.inputmethod.InputMethodManager;
 
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -210,6 +211,7 @@
         private final KeyguardViewController mKeyguardViewController;
         private final FeatureFlags mFeatureFlags;
         private final SelectedUserInteractor mSelectedUserInteractor;
+        private final UiEventLogger mUiEventLogger;
 
         @Inject
         public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -222,7 +224,8 @@
                 EmergencyButtonController.Factory emergencyButtonControllerFactory,
                 DevicePostureController devicePostureController,
                 KeyguardViewController keyguardViewController,
-                FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
+                FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
+                UiEventLogger uiEventLogger) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
             mLatencyTracker = latencyTracker;
@@ -238,6 +241,7 @@
             mKeyguardViewController = keyguardViewController;
             mFeatureFlags = featureFlags;
             mSelectedUserInteractor = selectedUserInteractor;
+            mUiEventLogger = uiEventLogger;
         }
 
         /** Create a new {@link KeyguardInputViewController}. */
@@ -265,7 +269,8 @@
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
                         mLiftToActivateListener, emergencyButtonController, mFalsingCollector,
-                        mDevicePostureController, mFeatureFlags, mSelectedUserInteractor);
+                        mDevicePostureController, mFeatureFlags, mSelectedUserInteractor,
+                        mUiEventLogger);
             } else if (keyguardInputView instanceof KeyguardSimPinView) {
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 947d90f..2aab1f1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -20,6 +20,8 @@
 
 import android.view.View;
 
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
@@ -44,6 +46,7 @@
     private View mOkButton = mView.findViewById(R.id.key_enter);
 
     private long mPinLength;
+    private final UiEventLogger mUiEventLogger;
 
     private boolean mDisabledAutoConfirmation;
 
@@ -56,7 +59,8 @@
             EmergencyButtonController emergencyButtonController,
             FalsingCollector falsingCollector,
             DevicePostureController postureController,
-            FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor) {
+            FeatureFlags featureFlags, SelectedUserInteractor selectedUserInteractor,
+            UiEventLogger uiEventLogger) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker, liftToActivateListener,
                 emergencyButtonController, falsingCollector, featureFlags, selectedUserInteractor);
@@ -67,6 +71,7 @@
         view.setIsLockScreenLandscapeEnabled(mFeatureFlags.isEnabled(LOCKSCREEN_ENABLE_LANDSCAPE));
         mBackspaceKey = view.findViewById(R.id.delete_button);
         mPinLength = mLockPatternUtils.getPinLength(selectedUserInteractor.getSelectedUserId());
+        mUiEventLogger = uiEventLogger;
     }
 
     @Override
@@ -95,6 +100,7 @@
             updateAutoConfirmationState();
             if (mPasswordEntry.getText().length() == mPinLength
                     && mOkButton.getVisibility() == View.INVISIBLE) {
+                mUiEventLogger.log(PinBouncerUiEvent.ATTEMPT_UNLOCK_WITH_AUTO_CONFIRM_FEATURE);
                 verifyPasswordAndUnlock();
             }
         }
@@ -184,4 +190,21 @@
                 mSelectedUserInteractor.getSelectedUserId())
                 && mPinLength != LockPatternUtils.PIN_LENGTH_UNAVAILABLE;
     }
+
+    /** UI Events for the auto confirmation feature in*/
+    enum PinBouncerUiEvent implements UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "Attempting to unlock the device with the auto confirm feature.")
+        ATTEMPT_UNLOCK_WITH_AUTO_CONFIRM_FEATURE(1547);
+
+        private final int mId;
+
+        PinBouncerUiEvent(int id) {
+            mId = id;
+        }
+
+        @Override
+        public int getId() {
+            return mId;
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
index c1871e0..71bac06 100644
--- a/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
+++ b/packages/SystemUI/src/com/android/systemui/DisplayCutoutBaseView.kt
@@ -209,11 +209,11 @@
         return result
     }
 
-    open fun enableShowProtection(show: Boolean) {
-        if (showProtection == show) {
+    open fun enableShowProtection(isCameraActive: Boolean) {
+        if (showProtection == isCameraActive) {
             return
         }
-        showProtection = show
+        showProtection = isCameraActive
         updateProtectionBoundingPath()
         // Delay the relayout until the end of the animation when hiding the cutout,
         // otherwise we'd clip it.
diff --git a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
index ab35e06..3abcb13 100644
--- a/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/FaceScanningOverlay.kt
@@ -28,12 +28,10 @@
 import android.graphics.Paint
 import android.graphics.Path
 import android.graphics.RectF
-import android.hardware.biometrics.BiometricSourceType
 import android.view.View
 import androidx.core.graphics.ColorUtils
 import com.android.app.animation.Interpolators
 import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.keyguard.KeyguardUpdateMonitorCallback
 import com.android.settingslib.Utils
 import com.android.systemui.biometrics.AuthController
 import com.android.systemui.log.ScreenDecorationsLogger
@@ -66,26 +64,11 @@
         com.android.internal.R.attr.materialColorPrimaryFixed)
     private var cameraProtectionAnimator: ValueAnimator? = null
     var hideOverlayRunnable: Runnable? = null
-    var faceAuthSucceeded = false
 
     init {
         visibility = View.INVISIBLE // only show this view when face scanning is happening
     }
 
-    override fun onAttachedToWindow() {
-        super.onAttachedToWindow()
-        mainExecutor.execute {
-            keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
-        }
-    }
-
-    override fun onDetachedFromWindow() {
-        super.onDetachedFromWindow()
-        mainExecutor.execute {
-            keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
-        }
-    }
-
     override fun setColor(color: Int) {
         cameraProtectionColor = color
         invalidate()
@@ -103,18 +86,22 @@
         }
     }
 
-    override fun enableShowProtection(show: Boolean) {
-        val animationRequired =
+    override fun enableShowProtection(isCameraActive: Boolean) {
+        val scanningAnimationRequiredWhenCameraActive =
                 keyguardUpdateMonitor.isFaceDetectionRunning || authController.isShowing
-        val showScanningAnimNow = animationRequired && show
-        if (showScanningAnimNow == showScanningAnim) {
+        val faceAuthSucceeded = keyguardUpdateMonitor.isFaceAuthenticated
+        val showScanningAnimationNow = scanningAnimationRequiredWhenCameraActive && isCameraActive
+        if (showScanningAnimationNow == showScanningAnim) {
             return
         }
-        logger.cameraProtectionShownOrHidden(keyguardUpdateMonitor.isFaceDetectionRunning,
+        logger.cameraProtectionShownOrHidden(
+                showScanningAnimationNow,
+                keyguardUpdateMonitor.isFaceDetectionRunning,
                 authController.isShowing,
-                show,
+                faceAuthSucceeded,
+                isCameraActive,
                 showScanningAnim)
-        showScanningAnim = showScanningAnimNow
+        showScanningAnim = showScanningAnimationNow
         updateProtectionBoundingPath()
         // Delay the relayout until the end of the animation when hiding,
         // otherwise we'd clip it.
@@ -125,7 +112,7 @@
 
         cameraProtectionAnimator?.cancel()
         cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
-                if (showScanningAnimNow) SHOW_CAMERA_PROTECTION_SCALE
+                if (showScanningAnimationNow) SHOW_CAMERA_PROTECTION_SCALE
                 else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
             startDelay =
                     if (showScanningAnim) 0
@@ -335,70 +322,16 @@
         invalidate()
     }
 
-    private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
-        override fun onBiometricAuthenticated(
-            userId: Int,
-            biometricSourceType: BiometricSourceType?,
-            isStrongBiometric: Boolean
-        ) {
-            if (biometricSourceType == BiometricSourceType.FACE) {
-                post {
-                    faceAuthSucceeded = true
-                    logger.biometricEvent("biometricAuthenticated")
-                    enableShowProtection(false)
-                }
-            }
-        }
-
-        override fun onBiometricAcquired(
-            biometricSourceType: BiometricSourceType?,
-            acquireInfo: Int
-        ) {
-            if (biometricSourceType == BiometricSourceType.FACE) {
-                post {
-                    faceAuthSucceeded = false // reset
-                }
-            }
-        }
-
-        override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
-            if (biometricSourceType == BiometricSourceType.FACE) {
-                post {
-                    faceAuthSucceeded = false
-                    logger.biometricEvent("biometricFailed")
-                    enableShowProtection(false)
-                }
-            }
-        }
-
-        override fun onBiometricError(
-            msgId: Int,
-            errString: String?,
-            biometricSourceType: BiometricSourceType?
-        ) {
-            if (biometricSourceType == BiometricSourceType.FACE) {
-                post {
-                    faceAuthSucceeded = false
-                    logger.biometricEvent("biometricError")
-                    enableShowProtection(false)
-                }
-            }
-        }
-    }
-
     companion object {
         private const val HIDDEN_RIM_SCALE = HIDDEN_CAMERA_PROTECTION_SCALE
         private const val SHOW_CAMERA_PROTECTION_SCALE = 1f
 
-        private const val PULSE_RADIUS_IN = 1.1f
         private const val PULSE_RADIUS_OUT = 1.125f
         private const val PULSE_RADIUS_SUCCESS = 1.25f
 
         private const val CAMERA_PROTECTION_APPEAR_DURATION = 250L
         private const val PULSE_APPEAR_DURATION = 250L // without start delay
 
-        private const val HALF_PULSE_DURATION = 500L
-
         private const val PULSE_SUCCESS_DISAPPEAR_DURATION = 400L
         private const val CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION = 500L // without start delay
 
diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
index 702a23e..e1c6f41 100644
--- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
@@ -35,7 +35,7 @@
  *
  * To enable logcat echoing for an entire buffer:
  * ```
- *   adb shell settings put global systemui/buffer/ScreenDecorationsLog <logLevel>
+ *   adb shell cmd statusbar echo -b ScreenDecorationsLog:<logLevel>
  *
  * ```
  */
@@ -134,33 +134,35 @@
     }
 
     fun cameraProtectionShownOrHidden(
+        showAnimationNow: Boolean,
         faceDetectionRunning: Boolean,
         biometricPromptShown: Boolean,
-        requestedState: Boolean,
+        faceAuthenticated: Boolean,
+        isCameraActive: Boolean,
         currentlyShowing: Boolean
     ) {
         logBuffer.log(
             TAG,
             DEBUG,
             {
+                str1 = "$showAnimationNow"
                 bool1 = faceDetectionRunning
                 bool2 = biometricPromptShown
-                bool3 = requestedState
+                str2 = "$faceAuthenticated"
+                bool3 = isCameraActive
                 bool4 = currentlyShowing
             },
             {
-                "isFaceDetectionRunning: $bool1, " +
+                "cameraProtectionShownOrHidden showAnimationNow: $str1, " +
+                    "isFaceDetectionRunning: $bool1, " +
                     "isBiometricPromptShowing: $bool2, " +
-                    "requestedState: $bool3, " +
+                    "faceAuthenticated: $str2, " +
+                    "isCameraActive: $bool3, " +
                     "currentState: $bool4"
             }
         )
     }
 
-    fun biometricEvent(@CompileTimeConstant info: String) {
-        logBuffer.log(TAG, DEBUG, info)
-    }
-
     fun cameraProtectionEvent(@CompileTimeConstant cameraProtectionEvent: String) {
         logBuffer.log(TAG, DEBUG, cameraProtectionEvent)
     }
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index be0c09e..eacdc2f 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -32,6 +32,8 @@
         android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
         android.os.Binder.init$ravenwood();
 
+        com.android.server.LocalServices.removeAllServicesForTest();
+
         if (rule.mProvideMainThread) {
             final HandlerThread main = new HandlerThread(MAIN_THREAD_NAME);
             main.start();
@@ -45,6 +47,8 @@
             Looper.clearMainLooperForTest();
         }
 
+        com.android.server.LocalServices.removeAllServicesForTest();
+
         android.os.Process.reset$ravenwood();
         android.os.Binder.reset$ravenwood();
     }
diff --git a/ravenwood/ravenwood-annotation-allowed-classes.txt b/ravenwood/ravenwood-annotation-allowed-classes.txt
index eba6e0b..72e9ba3 100644
--- a/ravenwood/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/ravenwood-annotation-allowed-classes.txt
@@ -64,3 +64,5 @@
 android.graphics.PointF
 android.graphics.Rect
 android.graphics.RectF
+
+com.android.server.LocalServices
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
index e3797c9..f55ecb0 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandler.java
@@ -916,18 +916,27 @@
                             && event.getPointerCount() == 2;
                     mHandler.removeMessages(MESSAGE_TRANSITION_TO_DELEGATING_STATE);
 
-                    if (isActivated() && event.getPointerCount() == 2) {
-                        storePointerDownLocation(mSecondPointerDownLocation, event);
-                        mHandler.sendEmptyMessageDelayed(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
-                                ViewConfiguration.getTapTimeout());
-                    } else if (mIsTwoFingerCountReached) {
-                        // Placing two-finger triple-taps behind isActivated to avoid
-                        // blocking panning scaling state
+                    if (event.getPointerCount() == 2) {
                         if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)) {
                             // 3tap and hold
                             afterLongTapTimeoutTransitionToDraggingState(event);
                         } else {
-                            afterMultiTapTimeoutTransitionToDelegatingState();
+                            if (mDetectTwoFingerTripleTap) {
+                                // If mDetectTwoFingerTripleTap, delay transition to the delegating
+                                // state for mMultiTapMaxDelay to ensure reachability of
+                                // multi finger multi tap
+                                afterMultiTapTimeoutTransitionToDelegatingState();
+                            }
+
+                            if (isActivated()) {
+                                // If activated, delay transition to the panning scaling
+                                // state for tap timeout to ensure reachability of
+                                // multi finger multi tap
+                                storePointerDownLocation(mSecondPointerDownLocation, event);
+                                mHandler.sendEmptyMessageDelayed(
+                                        MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE,
+                                        ViewConfiguration.getTapTimeout());
+                            }
                         }
                     } else {
                         transitionToDelegatingStateAndClear();
@@ -953,6 +962,9 @@
                         // (which is a rare combo to be used aside from magnification)
                         if (isMultiTapTriggered(2 /* taps */) && event.getPointerCount() == 1) {
                             transitionToViewportDraggingStateAndClear(event);
+                        } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)
+                                && event.getPointerCount() == 2) {
+                            transitionToViewportDraggingStateAndClear(event);
                         } else if (isActivated() && event.getPointerCount() == 2) {
                             if (mIsSinglePanningEnabled
                                     && overscrollState(event, mFirstPointerDownLocation)
@@ -961,11 +973,6 @@
                             }
                             //Primary pointer is swiping, so transit to PanningScalingState
                             transitToPanningScalingStateAndClear();
-                        } else if (isMultiFingerMultiTapTriggered(/* targetTapCount= */ 2, event)
-                                && event.getPointerCount() == 2) {
-                            // Placing two-finger triple-taps behind isActivated to avoid
-                            // blocking panning scaling state
-                            transitionToViewportDraggingStateAndClear(event);
                         } else if (mIsSinglePanningEnabled
                                 && isActivated()
                                 && event.getPointerCount() == 1) {
@@ -979,8 +986,11 @@
                         }
                     } else if (isActivated() && pointerDownValid(mSecondPointerDownLocation)
                             && distanceClosestPointerToPoint(
-                            mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance) {
-                        //Second pointer is swiping, so transit to PanningScalingState
+                            mSecondPointerDownLocation, /* move */ event) > mSwipeMinDistance
+                            // If mCompleteTapCount is not zero, it means that it is a multi tap
+                            // gesture. So, we should not transit to the PanningScalingState.
+                            && mCompletedTapCount == 0) {
+                        // Second pointer is swiping, so transit to PanningScalingState
                         transitToPanningScalingStateAndClear();
                     }
                 }
@@ -988,6 +998,7 @@
                 case ACTION_UP: {
 
                     mHandler.removeMessages(MESSAGE_ON_TRIPLE_TAP_AND_HOLD);
+                    mHandler.removeMessages(MESSAGE_TRANSITION_TO_PANNINGSCALING_STATE);
 
                     if (!mFullScreenMagnificationController.magnificationRegionContains(
                             mDisplayId, event.getX(), event.getY())) {
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 898b693..31c9348 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -45,7 +45,6 @@
 
 import com.android.internal.pm.pkg.component.ParsedMainComponent;
 import com.android.internal.util.function.pooled.PooledLambda;
-import com.android.permission.persistence.RuntimePermissionsState;
 import com.android.server.pm.Installer.LegacyDexoptDisabledException;
 import com.android.server.pm.KnownPackages;
 import com.android.server.pm.PackageArchiver;
@@ -1104,7 +1103,9 @@
      * Read legacy permission states for permissions migration to new permission subsystem.
      * Note that this api is supposed to be used for permissions state migration only.
      */
-    public abstract RuntimePermissionsState getLegacyPermissionsState(@UserIdInt int userId);
+    // TODO: restore to com.android.permission.persistence.RuntimePermissionsState
+    // once Ravenwood includes Mainline stubs
+    public abstract Object getLegacyPermissionsState(@UserIdInt int userId);
 
     /**
      * @return permissions file version for the given user.
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 35c6120..d359280 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -467,8 +467,7 @@
             }
 
             public byte[] getUuid() throws RemoteException {
-                //TODO(b/247124878): return the UUID defined in this file when the API is put in use
-                throw new RemoteException("This API is not implemented yet.");
+                return UUID;
             }
 
             @Override
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index af6a002..b23ccb8 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -397,6 +397,9 @@
             @Nullable Certificate[] trustedInstallers,
             Map<Integer, ApkChecksum> checksums,
             @NonNull Injector injector) {
+        if (!file.exists()) {
+            return;
+        }
         final String filePath = file.getAbsolutePath();
 
         // Always available: FSI or IncFs.
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index ba2cf1d..1a65297 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -690,6 +690,9 @@
                     pkgSetting.setInstallReason(installReason, userId);
                     pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId);
                     pkgSetting.setFirstInstallTime(System.currentTimeMillis(), userId);
+                    // Clear any existing archive state.
+                    pkgSetting.setArchiveTimeMillis(0, userId);
+                    pkgSetting.setArchiveState(null, userId);
                     mPm.mSettings.writePackageRestrictionsLPr(userId);
                     mPm.mSettings.writeKernelMappingLPr(pkgSetting);
                     installed = true;
@@ -2263,6 +2266,9 @@
                         ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId,
                                 installerPackageName);
                     }
+                    // Clear any existing archive state.
+                    ps.setArchiveState(null, userId);
+                    ps.setArchiveTimeMillis(0, userId);
                 } else if (allUsers != null) {
                     // The caller explicitly specified INSTALL_ALL_USERS flag.
                     // Thus, updating the settings to install the app for all users.
@@ -2285,6 +2291,9 @@
                                 ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, currentUserId,
                                         installerPackageName);
                             }
+                            // Clear any existing archive state.
+                            ps.setArchiveState(null, currentUserId);
+                            ps.setArchiveTimeMillis(0, currentUserId);
                         } else {
                             ps.setInstalled(false, currentUserId);
                         }
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 293b873..26dc576 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -770,6 +770,11 @@
         onChanged();
     }
 
+    void setArchiveState(ArchiveState archiveState, int userId) {
+        modifyUserState(userId).setArchiveState(archiveState);
+        onChanged();
+    }
+
     void setArchiveTimeMillis(long value, int userId) {
         modifyUserState(userId).setArchiveTimeMillis(value);
         onChanged();
diff --git a/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
index dbf4047..28818f7 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionMigrationHelperImpl.java
@@ -93,7 +93,7 @@
         Map<Integer, Map<String, LegacyPermissionState>> appIdPermissionStates = new ArrayMap<>();
 
         RuntimePermissionsState legacyState =
-                mPackageManagerInternal.getLegacyPermissionsState(userId);
+                (RuntimePermissionsState) mPackageManagerInternal.getLegacyPermissionsState(userId);
         PackageManagerLocal packageManagerLocal =
                 LocalManagerRegistry.getManager(PackageManagerLocal.class);
 
diff --git a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 940feb5..e876241 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -4871,7 +4871,7 @@
     }
 
     private int pullCachedAppsHighWatermark(int atomTag, List<StatsEvent> pulledData) {
-        pulledData.add(LocalServices.getService(ActivityManagerInternal.class)
+        pulledData.add((StatsEvent) LocalServices.getService(ActivityManagerInternal.class)
                 .getCachedAppsHighWatermarkStats(atomTag, true));
         return StatsManager.PULL_SUCCESS;
     }
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
index 3b39160..9114027 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationGestureHandlerTest.java
@@ -588,6 +588,38 @@
     }
 
     @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+    public void testTwoFingerTap_StateIsActivated_shouldInDelegating() {
+        assumeTrue(mMgh.mIsSinglePanningEnabled);
+        mMgh.setSinglePanningEnabled(false);
+        goFromStateIdleTo(STATE_ACTIVATED);
+        allowEventDelegation();
+
+        send(downEvent());
+        send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+        send(upEvent());
+        fastForward(ViewConfiguration.getDoubleTapTimeout());
+
+        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(Flags.FLAG_ENABLE_MAGNIFICATION_MULTIPLE_FINGER_MULTIPLE_TAP_GESTURE)
+    public void testTwoFingerTap_StateIsIdle_shouldInDelegating() {
+        assumeTrue(mMgh.mIsSinglePanningEnabled);
+        mMgh.setSinglePanningEnabled(false);
+        goFromStateIdleTo(STATE_IDLE);
+        allowEventDelegation();
+
+        send(downEvent());
+        send(pointerEvent(ACTION_POINTER_DOWN, DEFAULT_X * 2, DEFAULT_Y));
+        send(upEvent());
+        fastForward(ViewConfiguration.getDoubleTapTimeout());
+
+        assertTrue(mMgh.mCurrentState == mMgh.mDelegatingState);
+    }
+
+    @Test
     public void testMultiTap_outOfDistanceSlop_shouldInIdle() {
         // All delay motion events should be sent, if multi-tap with out of distance slop.
         // STATE_IDLE will check if tapCount() < 2.
@@ -719,6 +751,20 @@
     }
 
     @Test
+    public void testTwoFingerDown_twoPointerDownAndActivatedState_panningState() {
+        goFromStateIdleTo(STATE_ACTIVATED);
+        PointF pointer1 = DEFAULT_POINT;
+        PointF pointer2 = new PointF(DEFAULT_X * 1.5f, DEFAULT_Y);
+
+        send(downEvent());
+        send(pointerEvent(ACTION_POINTER_DOWN, new PointF[] {pointer1, pointer2}, 1));
+        fastForward(ViewConfiguration.getTapTimeout());
+        assertIn(STATE_PANNING);
+
+        returnToNormalFrom(STATE_PANNING);
+    }
+
+    @Test
     public void testActivatedWithTripleTap_invokeShowWindowPromptAction() {
         goFromStateIdleTo(STATE_ACTIVATED);
 
diff --git a/tools/hoststubgen/hoststubgen/Android.bp b/tools/hoststubgen/hoststubgen/Android.bp
index d415dc5..0e2e158 100644
--- a/tools/hoststubgen/hoststubgen/Android.bp
+++ b/tools/hoststubgen/hoststubgen/Android.bp
@@ -284,6 +284,7 @@
 
 java_host_for_device {
     name: "hoststubgen-helper-libcore-runtime.ravenwood",
+    defaults: ["hoststubgen-for-prototype-only-java"],
     libs: [
         "hoststubgen-helper-libcore-runtime",
     ],