/* * 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.car.settings.security; import android.content.Context; import android.os.CountDownTimer; import android.os.SystemClock; import androidx.annotation.Nullable; import com.android.car.settings.R; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.widget.LockPatternUtils; /** Common lockout handling code. */ public class ConfirmLockLockoutHelper { private static ConfirmLockLockoutHelper sInstance; private final Context mContext; private final int mUserId; private final LockPatternUtils mLockPatternUtils; private ConfirmLockUIController mUiController; private CountDownTimer mCountDownTimer; /** Return an instance of {@link ConfirmLockLockoutHelper}. */ public static ConfirmLockLockoutHelper getInstance(Context context, int userId) { if (sInstance == null) { sInstance = new ConfirmLockLockoutHelper(context, userId, new LockPatternUtils(context)); } return sInstance; } @VisibleForTesting ConfirmLockLockoutHelper(Context context, int userId, LockPatternUtils lockPatternUtils) { mContext = context; mUserId = userId; mLockPatternUtils = lockPatternUtils; } /** Sets the UI controller. */ public void setConfirmLockUIController(ConfirmLockUIController uiController) { mUiController = uiController; } /** Gets the lock pattern utils used by this helper. */ public LockPatternUtils getLockPatternUtils() { return mLockPatternUtils; } /** Handles when the lock check is completed but returns a timeout. */ public void onCheckCompletedWithTimeout(int timeoutMs) { if (timeoutMs <= 0) { return; } long deadline = mLockPatternUtils.setLockoutAttemptDeadline(mUserId, timeoutMs); handleAttemptLockout(deadline); } /** To be called when the UI is resumed to reset the timeout countdown if necessary. */ public void onResumeUI() { if (isLockedOut()) { handleAttemptLockout(mLockPatternUtils.getLockoutAttemptDeadline(mUserId)); } else { mUiController.refreshUI(isLockedOut()); } } /** To be called when the UI is paused to cancel the ongoing countdown timer. */ public void onPauseUI() { if (mCountDownTimer != null) { mCountDownTimer.cancel(); mUiController.setErrorText(""); } } @VisibleForTesting @Nullable CountDownTimer getCountDownTimer() { return mCountDownTimer; } private void handleAttemptLockout(long deadline) { long elapsedRealtime = SystemClock.elapsedRealtime(); mUiController.refreshUI(isLockedOut()); mCountDownTimer = newCountDownTimer(deadline - elapsedRealtime).start(); } private boolean isLockedOut() { return mLockPatternUtils.getLockoutAttemptDeadline(mUserId) != 0; } private CountDownTimer newCountDownTimer(long countDownMillis) { return new CountDownTimer(countDownMillis, LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) { @Override public void onTick(long millisUntilFinished) { int secondsCountdown = (int) (millisUntilFinished / 1000); mUiController.setErrorText( mContext.getString( R.string.lockpattern_too_many_failed_confirmation_attempts, secondsCountdown)); } @Override public void onFinish() { mUiController.refreshUI(/* isLockedOut= */ false); mUiController.setErrorText(""); } }; } /** Interface for controlling the associated lock UI. */ public interface ConfirmLockUIController { /** Sets the error text with the given string. */ void setErrorText(String text); /** Refreshes the UI based on the locked out state. */ void refreshUI(boolean isLockedOut); } }