Merge "Deprecating flags and settings resets in RescueParty" into main
diff --git a/api/coverage/tools/ExtractFlaggedApis.kt b/api/coverage/tools/ExtractFlaggedApis.kt
index 5efda98..bf67187 100644
--- a/api/coverage/tools/ExtractFlaggedApis.kt
+++ b/api/coverage/tools/ExtractFlaggedApis.kt
@@ -75,10 +75,10 @@
 fun getClassFlag(classItem: ClassItem): String? {
     var classFlag = getFlagAnnotation(classItem)
     var cur = classItem
-    // If a class is not an inner class, use its @FlaggedApi annotation value.
+    // If a class is not a nested class, use its @FlaggedApi annotation value.
     // Otherwise, use the flag value of the closest outer class that is annotated by @FlaggedApi.
-    while (cur.isInnerClass() && classFlag == null) {
-        cur = cur.parent() as ClassItem
+    while (classFlag == null) {
+        cur = cur.containingClass() ?: break
         classFlag = getFlagAnnotation(cur)
     }
     return classFlag
diff --git a/api/gen_combined_removed_dex.sh b/api/gen_combined_removed_dex.sh
index e0153f7..2860e2e 100755
--- a/api/gen_combined_removed_dex.sh
+++ b/api/gen_combined_removed_dex.sh
@@ -6,6 +6,6 @@
 
 # Convert each removed.txt to the "dex format" equivalent, and print all output.
 for f in "$@"; do
-    "$metalava_path" signature-to-dex "$f" "${tmp_dir}/tmp"
+    "$metalava_path" signature-to-dex "$f" --out "${tmp_dir}/tmp"
     cat "${tmp_dir}/tmp"
 done
diff --git a/core/java/android/os/DeadObjectException.java b/core/java/android/os/DeadObjectException.java
index fc3870e..0a50778 100644
--- a/core/java/android/os/DeadObjectException.java
+++ b/core/java/android/os/DeadObjectException.java
@@ -26,7 +26,7 @@
  * receive this error from an app, at a minimum, you should
  * recover by resetting the connection. For instance, you should
  * drop the binder, clean up associated state, and reset your
- * connection to the service which through this error. In order
+ * connection to the service which threw this error. In order
  * to simplify your error recovery paths, you may also want to
  * "simply" restart your process. However, this may not be an
  * option if the service you are talking to is unreliable or
diff --git a/core/java/android/view/autofill/OWNERS b/core/java/android/view/autofill/OWNERS
index 898947a..7f3b4e5 100644
--- a/core/java/android/view/autofill/OWNERS
+++ b/core/java/android/view/autofill/OWNERS
@@ -1,10 +1,11 @@
 # Bug component: 351486
 
-simranjit@google.com
 haoranzhang@google.com
+jiewenlei@google.com
+simranjit@google.com
 skxu@google.com
+shuc@google.com
 yunicorn@google.com
-reemabajwa@google.com
 
 # Bug component: 543785 = per-file *Augmented*
 per-file *Augmented* = wangqi@google.com
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index 8236783..511c680 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -109,4 +109,5 @@
     boolean isWeakEscrowTokenActive(long handle, int userId);
     boolean isWeakEscrowTokenValid(long handle, in byte[] token, int userId);
     void unlockUserKeyIfUnsecured(int userId);
+    boolean writeRepairModeCredential(int userId);
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index e46b8d7..f4ad487 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -449,6 +449,21 @@
     }
 
     /**
+     * Save the current password data to the repair mode file.
+     *
+     * @return true if success or false otherwise.
+     */
+    public boolean writeRepairModeCredential(int userId) {
+        throwIfCalledOnMainThread();
+        try {
+            return getLockSettings().writeRepairModeCredential(userId);
+        } catch (RemoteException re) {
+            Log.e(TAG, "Failed to write repair mode credential", re);
+            return false;
+        }
+    }
+
+    /**
      * Check to see if a credential matches the saved one.
      * If credential matches, return an opaque attestation that the challenge was verified.
      *
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 0734e68..11c220b 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -261,6 +261,8 @@
         public float lineEndY = Float.MIN_VALUE;
         @Nullable
         Animator activationAnimator;
+        @Nullable
+        Animator deactivationAnimator;
      }
 
     /**
@@ -667,7 +669,7 @@
      */
     private void resetPattern() {
         if (mKeepDotActivated && !mPattern.isEmpty()) {
-            resetLastActivatedCellProgress();
+            resetPatternCellSize();
         }
         mPattern.clear();
         mPatternPath.reset();
@@ -676,14 +678,20 @@
         invalidate();
     }
 
-    private void resetLastActivatedCellProgress() {
-        final ArrayList<Cell> pattern = mPattern;
-        final Cell lastCell = pattern.get(pattern.size() - 1);
-        final CellState cellState = mCellStates[lastCell.row][lastCell.column];
-        if (cellState.activationAnimator != null) {
-            cellState.activationAnimator.cancel();
+    private void resetPatternCellSize() {
+        for (int i = 0; i < mCellStates.length; i++) {
+            for (int j = 0; j < mCellStates[i].length; j++) {
+                CellState cellState = mCellStates[i][j];
+                if (cellState.activationAnimator != null) {
+                    cellState.activationAnimator.cancel();
+                }
+                if (cellState.deactivationAnimator != null) {
+                    cellState.deactivationAnimator.cancel();
+                }
+                cellState.activationAnimationProgress = 0f;
+                cellState.radius = mDotSize / 2f;
+            }
         }
-        cellState.activationAnimationProgress = 0f;
     }
 
     /**
@@ -819,12 +827,16 @@
                     !mPatternDrawLookup[fillInGapCell.row][fillInGapCell.column]) {
                 addCellToPattern(fillInGapCell);
                 if (mKeepDotActivated) {
-                    startCellDeactivatedAnimation(fillInGapCell);
+                    if (mFadePattern) {
+                        startCellDeactivatedAnimation(fillInGapCell, /* fillInGap= */ true);
+                    } else {
+                        startCellActivatedAnimation(fillInGapCell);
+                    }
                 }
             }
 
             if (mKeepDotActivated && lastCell != null) {
-                startCellDeactivatedAnimation(lastCell);
+                startCellDeactivatedAnimation(lastCell, /* fillInGap= */ false);
             }
 
             addCellToPattern(cell);
@@ -872,17 +884,25 @@
     }
 
     private void startCellActivatedAnimation(Cell cell) {
-        startCellActivationAnimation(cell, CELL_ACTIVATE);
+        startCellActivationAnimation(cell, CELL_ACTIVATE, /* fillInGap= */ false);
     }
 
-    private void startCellDeactivatedAnimation(Cell cell) {
-        startCellActivationAnimation(cell, CELL_DEACTIVATE);
+    private void startCellDeactivatedAnimation(Cell cell, boolean fillInGap) {
+        startCellActivationAnimation(cell, CELL_DEACTIVATE, /* fillInGap= */ fillInGap);
     }
 
-    private void startCellActivationAnimation(Cell cell, int activate) {
+    /**
+     * Start cell animation.
+     * @param cell The cell to be animated.
+     * @param activate Whether the cell is being activated or deactivated.
+     * @param fillInGap Whether the cell is a gap cell, i.e. filled in based on current pattern.
+     */
+    private void startCellActivationAnimation(Cell cell, int activate, boolean fillInGap) {
         final CellState cellState = mCellStates[cell.row][cell.column];
 
-        if (cellState.activationAnimator != null) {
+        // When mKeepDotActivated is true, don't cancel the previous animator since it would leave
+        // a dot in an in-between size if the next dot is reached before the animation is finished.
+        if (cellState.activationAnimator != null && !mKeepDotActivated) {
             cellState.activationAnimator.cancel();
         }
         AnimatorSet animatorSet = new AnimatorSet();
@@ -898,24 +918,37 @@
                 .with(createLineEndAnimation(cellState, startX, startY,
                         getCenterXForColumn(cell.column), getCenterYForRow(cell.row)));
         if (mDotSize != mDotSizeActivated) {
-            animatorSetBuilder.with(createDotRadiusAnimation(cellState));
+            animatorSetBuilder.with(createDotRadiusAnimation(cellState, activate, fillInGap));
         }
         if (mDotColor != mDotActivatedColor) {
-            animatorSetBuilder.with(createDotActivationColorAnimation(cellState, activate));
+            animatorSetBuilder.with(
+                    createDotActivationColorAnimation(cellState, activate, fillInGap));
         }
 
-        animatorSet.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                cellState.activationAnimator = null;
-                invalidate();
-            }
-        });
-        cellState.activationAnimator = animatorSet;
+        if (activate == CELL_ACTIVATE) {
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    cellState.activationAnimator = null;
+                    invalidate();
+                }
+            });
+            cellState.activationAnimator = animatorSet;
+        } else {
+            animatorSet.addListener(new AnimatorListenerAdapter() {
+                @Override
+                public void onAnimationEnd(Animator animation) {
+                    cellState.deactivationAnimator = null;
+                    invalidate();
+                }
+            });
+            cellState.deactivationAnimator = animatorSet;
+        }
         animatorSet.start();
     }
 
-    private Animator createDotActivationColorAnimation(CellState cellState, int activate) {
+    private Animator createDotActivationColorAnimation(
+            CellState cellState, int activate, boolean fillInGap) {
         ValueAnimator.AnimatorUpdateListener updateListener =
                 valueAnimator -> {
                     cellState.activationAnimationProgress =
@@ -934,7 +967,7 @@
         deactivateAnimator.setDuration(DOT_ACTIVATION_DURATION_MILLIS);
         AnimatorSet set = new AnimatorSet();
 
-        if (mKeepDotActivated) {
+        if (mKeepDotActivated && !fillInGap) {
             set.play(activate == CELL_ACTIVATE ? activateAnimator : deactivateAnimator);
         } else {
             // 'activate' ignored in this case, do full deactivate -> activate cycle
@@ -977,7 +1010,7 @@
         return valueAnimator;
     }
 
-    private Animator createDotRadiusAnimation(CellState state) {
+    private Animator createDotRadiusAnimation(CellState state, int activate, boolean fillInGap) {
         float defaultRadius = mDotSize / 2f;
         float activatedRadius = mDotSizeActivated / 2f;
 
@@ -998,7 +1031,19 @@
         deactivationAnimator.setDuration(DOT_RADIUS_DECREASE_DURATION_MILLIS);
 
         AnimatorSet set = new AnimatorSet();
-        set.playSequentially(activationAnimator, deactivationAnimator);
+        if (mKeepDotActivated) {
+            if (mFadePattern) {
+                if (fillInGap) {
+                    set.playSequentially(activationAnimator, deactivationAnimator);
+                } else {
+                    set.play(activate == CELL_ACTIVATE ? activationAnimator : deactivationAnimator);
+                }
+            } else if (activate == CELL_ACTIVATE) {
+                set.play(activationAnimator);
+            }
+        } else {
+            set.playSequentially(activationAnimator, deactivationAnimator);
+        }
         return set;
     }
 
@@ -1176,9 +1221,15 @@
         // report pattern detected
         if (!mPattern.isEmpty()) {
             setPatternInProgress(false);
-            cancelLineAnimations();
             if (mKeepDotActivated) {
+                // When mKeepDotActivated is true, cancelling dot animations and resetting dot radii
+                // are handled in #resetPattern(), since we want to keep the dots activated until
+                // the pattern are reset.
                 deactivateLastCell();
+            } else {
+                // When mKeepDotActivated is false, cancelling animations and resetting dot radii
+                // are handled here.
+                cancelLineAnimations();
             }
             notifyPatternDetected();
             // Also clear pattern if fading is enabled
@@ -1198,7 +1249,7 @@
 
     private void deactivateLastCell() {
         Cell lastCell = mPattern.get(mPattern.size() - 1);
-        startCellDeactivatedAnimation(lastCell);
+        startCellDeactivatedAnimation(lastCell, /* fillInGap= */ false);
     }
 
     private void cancelLineAnimations() {
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 67cceb5..581dee5 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -185,6 +185,9 @@
          https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
     <shortcode country="it" pattern="\\d{5}" premium="44[0-4]\\d{2}|47[0-4]\\d{2}|48[0-4]\\d{2}|44[5-9]\\d{4}|47[5-9]\\d{4}|48[5-9]\\d{4}|455\\d{2}|499\\d{2}" free="116\\d{3}|4112503|40\\d{0,12}" standard="430\\d{2}|431\\d{2}|434\\d{4}|435\\d{4}|439\\d{7}" />
 
+    <!-- Jordan: 1-5 digits (standard system default, not country specific) -->
+    <shortcode country="jo" pattern="\\d{1,5}" free="99066" />
+
     <!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
     <shortcode country="jp" pattern="\\d{1,5}" free="8083" />
 
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
index b90480a..92a7d8e 100644
--- a/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/LockPatternUtilsTest.java
@@ -68,6 +68,7 @@
 
 import java.nio.charset.StandardCharsets;
 import java.util.List;
+import java.util.concurrent.CompletableFuture;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -316,6 +317,40 @@
         assertNotEquals(UserHandle.USER_CURRENT_OR_SELF, LockPatternUtils.USER_REPAIR_MODE);
     }
 
+    @Test
+    public void testWriteRepairModeCredential_mainThread() {
+        createTestLockSettings();
+        var context = InstrumentationRegistry.getTargetContext();
+
+        var future = new CompletableFuture<Exception>();
+        context.getMainThreadHandler().post(() -> {
+            try {
+                mLockPatternUtils.writeRepairModeCredential(USER_ID);
+                future.complete(null);
+            } catch (Exception e) {
+                future.complete(e);
+            }
+        });
+
+        var e = future.join();
+        assertThat(e).isNotNull();
+        assertThat(e.getMessage()).contains("should not be called from the main thread");
+    }
+
+    @Test
+    public void testWriteRepairModeCredential() throws Exception {
+        var ils = createTestLockSettings();
+
+        when(ils.writeRepairModeCredential(USER_ID)).thenReturn(false);
+        assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isFalse();
+
+        when(ils.writeRepairModeCredential(USER_ID)).thenReturn(true);
+        assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isTrue();
+
+        when(ils.writeRepairModeCredential(USER_ID)).thenThrow(new RemoteException());
+        assertThat(mLockPatternUtils.writeRepairModeCredential(USER_ID)).isFalse();
+    }
+
     private TestStrongAuthTracker createStrongAuthTracker() {
         final Context context = new ContextWrapper(InstrumentationRegistry.getTargetContext());
         return new TestStrongAuthTracker(context, Looper.getMainLooper());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS
new file mode 100644
index 0000000..482aaab
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/OWNERS
@@ -0,0 +1 @@
+per-file KtProtolog.kt = file:platform/development:/tools/winscope/OWNERS
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 2141516..1ed6476 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -79,7 +79,6 @@
         "libcamera_client",
         "libmtp",
         "libpiex",
-        "libprocessgroup",
         "libandroidfw",
         "libhidlallocatorutils",
         "libhidlbase",
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 64e3e8e..97678aa 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -5491,8 +5491,9 @@
     void noteAppKill(final ProcessRecord app, final @Reason int reason,
             final @SubReason int subReason, final String msg) {
         if (DEBUG_PROCESSES) {
-            Slog.i(TAG, "note: " + app + " is being killed, reason: " + reason
-                    + ", sub-reason: " + subReason + ", message: " + msg);
+            Slog.i(TAG, "note: " + app + " is being killed, reason: "
+                    + ApplicationExitInfo.reasonCodeToString(reason) + ", sub-reason: "
+                    + ApplicationExitInfo.subreasonToString(subReason) + ", message: " + msg);
         }
         if (app.getPid() > 0 && !app.isolated && app.getDeathRecipient() != null) {
             // We are killing it, put it into the dying process list.
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 7157646..7aae91b 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1773,6 +1773,20 @@
         }
     }
 
+    @Override
+    public boolean writeRepairModeCredential(int userId) {
+        checkWritePermission();
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            synchronized (mSpManager) {
+                long protectorId = getCurrentLskfBasedProtectorId(userId);
+                return mSpManager.writeRepairModeCredentialLocked(protectorId, userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
     /**
      * @param savedCredential if the user is a profile with
      * {@link UserManager#isCredentialSharableWithParent()} with unified challenge and
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ae5a5cb..8d6be09 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -3693,7 +3693,7 @@
             hideBootMessagesLocked();
             // If the screen still doesn't come up after 30 seconds, give
             // up and turn it on.
-            mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000);
+            mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30 * 1000 * Build.HW_TIMEOUT_MULTIPLIER);
         }
 
         mPolicy.systemBooted();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
index 70150c5..4396c67 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockscreenRepairModeTest.java
@@ -19,6 +19,8 @@
 import static com.android.internal.widget.LockPatternUtils.USER_REPAIR_MODE;
 import static com.android.internal.widget.LockPatternUtils.VERIFY_FLAG_WRITE_REPAIR_MODE_PW;
 
+import static com.google.common.truth.Truth.assertThat;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertSame;
 
@@ -199,6 +201,27 @@
                         .getResponseCode());
     }
 
+    @Test
+    public void writeRepairModeCredential_noLock() {
+        assertThat(mService.writeRepairModeCredential(PRIMARY_USER_ID)).isFalse();
+    }
+
+    @Test
+    public void writeRepairModeCredential_hasLock() {
+        mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+        assertThat(mService.writeRepairModeCredential(PRIMARY_USER_ID)).isTrue();
+    }
+
+    @Test
+    public void writeRepairModeCredential_verifyRepairModeUser() {
+        mService.setLockCredential(newPin("1234"), nonePassword(), PRIMARY_USER_ID);
+        mService.writeRepairModeCredential(PRIMARY_USER_ID);
+        setRepairModeActive(true);
+
+        var response = mService.verifyCredential(newPin("1234"), USER_REPAIR_MODE, 0);
+        assertThat(response.isMatched()).isTrue();
+    }
+
     private void setRepairModeActive(boolean active) {
         Settings.Global.putInt(mContext.getContentResolver(),
                 Settings.Global.REPAIR_MODE_ACTIVE, active ? 1 : 0);