1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.car.settings.security;
18 
19 import android.content.Context;
20 import android.os.CountDownTimer;
21 import android.os.SystemClock;
22 
23 import androidx.annotation.Nullable;
24 
25 import com.android.car.settings.R;
26 import com.android.internal.annotations.VisibleForTesting;
27 import com.android.internal.widget.LockPatternUtils;
28 
29 /** Common lockout handling code. */
30 public class ConfirmLockLockoutHelper {
31 
32     private static ConfirmLockLockoutHelper sInstance;
33 
34     private final Context mContext;
35     private final int mUserId;
36     private final LockPatternUtils mLockPatternUtils;
37     private ConfirmLockUIController mUiController;
38     private CountDownTimer mCountDownTimer;
39 
40     /** Return an instance of {@link ConfirmLockLockoutHelper}. */
getInstance(Context context, int userId)41     public static ConfirmLockLockoutHelper getInstance(Context context, int userId) {
42         if (sInstance == null) {
43             sInstance = new ConfirmLockLockoutHelper(context, userId,
44                     new LockPatternUtils(context));
45         }
46         return sInstance;
47     }
48 
49     @VisibleForTesting
ConfirmLockLockoutHelper(Context context, int userId, LockPatternUtils lockPatternUtils)50     ConfirmLockLockoutHelper(Context context, int userId,
51             LockPatternUtils lockPatternUtils) {
52         mContext = context;
53         mUserId = userId;
54         mLockPatternUtils = lockPatternUtils;
55     }
56 
57     /** Sets the UI controller. */
setConfirmLockUIController(ConfirmLockUIController uiController)58     public void setConfirmLockUIController(ConfirmLockUIController uiController) {
59         mUiController = uiController;
60     }
61 
62     /** Gets the lock pattern utils used by this helper. */
getLockPatternUtils()63     public LockPatternUtils getLockPatternUtils() {
64         return mLockPatternUtils;
65     }
66 
67     /** Handles when the lock check is completed but returns a timeout. */
onCheckCompletedWithTimeout(int timeoutMs)68     public void onCheckCompletedWithTimeout(int timeoutMs) {
69         if (timeoutMs <= 0) {
70             return;
71         }
72 
73         long deadline = mLockPatternUtils.setLockoutAttemptDeadline(mUserId, timeoutMs);
74         handleAttemptLockout(deadline);
75     }
76 
77     /** To be called when the UI is resumed to reset the timeout countdown if necessary. */
onResumeUI()78     public void onResumeUI() {
79         if (isLockedOut()) {
80             handleAttemptLockout(mLockPatternUtils.getLockoutAttemptDeadline(mUserId));
81         } else {
82             mUiController.refreshUI(isLockedOut());
83         }
84     }
85 
86     /** To be called when the UI is paused to cancel the ongoing countdown timer. */
onPauseUI()87     public void onPauseUI() {
88         if (mCountDownTimer != null) {
89             mCountDownTimer.cancel();
90         }
91     }
92 
93     @VisibleForTesting
94     @Nullable
getCountDownTimer()95     CountDownTimer getCountDownTimer() {
96         return mCountDownTimer;
97     }
98 
handleAttemptLockout(long deadline)99     private void handleAttemptLockout(long deadline) {
100         long elapsedRealtime = SystemClock.elapsedRealtime();
101         mUiController.refreshUI(isLockedOut());
102         mCountDownTimer = newCountDownTimer(deadline - elapsedRealtime).start();
103     }
104 
isLockedOut()105     private boolean isLockedOut() {
106         return mLockPatternUtils.getLockoutAttemptDeadline(mUserId) != 0;
107     }
108 
newCountDownTimer(long countDownMillis)109     private CountDownTimer newCountDownTimer(long countDownMillis) {
110         return new CountDownTimer(countDownMillis,
111                 LockPatternUtils.FAILED_ATTEMPT_COUNTDOWN_INTERVAL_MS) {
112             @Override
113             public void onTick(long millisUntilFinished) {
114                 int secondsCountdown = (int) (millisUntilFinished / 1000);
115                 mUiController.setErrorText(
116                         mContext.getString(
117                                 R.string.lockpattern_too_many_failed_confirmation_attempts,
118                                 secondsCountdown));
119             }
120 
121             @Override
122             public void onFinish() {
123                 mUiController.refreshUI(/* isLockedOut= */ false);
124                 mUiController.setErrorText("");
125             }
126         };
127     }
128 
129     /** Interface for controlling the associated lock UI. */
130     public interface ConfirmLockUIController {
131         /** Sets the error text with the given string. */
132         void setErrorText(String text);
133         /** Refreshes the UI based on the locked out state. */
134         void refreshUI(boolean isLockedOut);
135     }
136 }
137