Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59fb99b1 authored by Dave Mankoff's avatar Dave Mankoff Committed by Android (Google) Code Review
Browse files

Merge changes from topic "b169145796-kgd-pin-input"

* changes:
  10/N Ensure KeyguardSecurityContainer always has Callback
  Revert^2 "9/N Clean Up Keyguard Class Structure"
  Revert^2 "8/N Remove View Injection from KeyguardMessageArea"
  Revert^2 "7/N controllers for remaining Keyguard Password ..."
  Revert^2 "6/N Add Controller for KeyguardPatternView"
  Revert^2 "5/N Add KeyguardSecurityViewFlipperController."
  Revert^2 "4/N Setup Controller fo KeyguardSecurityContainer."
parents ce302adb 28822be5
Loading
Loading
Loading
Loading
+28 −2
Original line number Diff line number Diff line
@@ -33,9 +33,13 @@ import android.view.SurfaceView;
import android.view.ViewGroup;

import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.systemui.dagger.qualifiers.Main;

import java.util.NoSuchElementException;

import javax.inject.Inject;

/**
 * Encapsulates all logic for secondary lockscreen state management.
 */
@@ -142,9 +146,9 @@ public class AdminSecondaryLockScreenController {
        }
    };

    public AdminSecondaryLockScreenController(Context context, ViewGroup parent,
    private AdminSecondaryLockScreenController(Context context, KeyguardSecurityContainer parent,
            KeyguardUpdateMonitor updateMonitor, KeyguardSecurityCallback callback,
            Handler handler) {
            @Main Handler handler) {
        mContext = context;
        mHandler = handler;
        mParent = parent;
@@ -234,4 +238,26 @@ public class AdminSecondaryLockScreenController {
            getHolder().removeCallback(mSurfaceHolderCallback);
        }
    }

    @KeyguardBouncerScope
    public static class Factory {
        private final Context mContext;
        private final KeyguardSecurityContainer mParent;
        private final KeyguardUpdateMonitor mUpdateMonitor;
        private final Handler mHandler;

        @Inject
        public Factory(Context context, KeyguardSecurityContainer parent,
                KeyguardUpdateMonitor updateMonitor, @Main Handler handler) {
            mContext = context;
            mParent = parent;
            mUpdateMonitor = updateMonitor;
            mHandler = handler;
        }

        public AdminSecondaryLockScreenController create(KeyguardSecurityCallback callback) {
            return new AdminSecondaryLockScreenController(mContext, mParent, mUpdateMonitor,
                    callback, mHandler);
        }
    }
}
+11 −254
Original line number Diff line number Diff line
@@ -16,46 +16,26 @@

package com.android.keyguard;

import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;

import android.content.Context;
import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.View;
import android.widget.LinearLayout;

import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.systemui.Dependency;
import com.android.systemui.R;

/**
 * Base class for PIN and password unlock screens.
 */
public abstract class KeyguardAbsKeyInputView extends LinearLayout
        implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback {
    protected KeyguardSecurityCallback mCallback;
    protected LockPatternUtils mLockPatternUtils;
    protected AsyncTask<?, ?, ?> mPendingLockCheck;
    protected SecurityMessageDisplay mSecurityMessageDisplay;
public abstract class KeyguardAbsKeyInputView extends KeyguardInputView {
    protected View mEcaView;
    protected boolean mEnableHaptics;
    private boolean mDismissing;
    protected boolean mResumed;
    private CountDownTimer mCountdownTimer = null;
    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;

    // To avoid accidental lockout due to events while the device in in the pocket, ignore
    // any passwords with length less than or equal to this length.
    protected static final int MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT = 3;
    private KeyDownListener mKeyDownListener;

    public KeyguardAbsKeyInputView(Context context) {
        this(context, null);
@@ -63,38 +43,10 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout

    public KeyguardAbsKeyInputView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
    }

    @Override
    public void setKeyguardCallback(KeyguardSecurityCallback callback) {
        mCallback = callback;
    }

    @Override
    public void setLockPatternUtils(LockPatternUtils utils) {
        mLockPatternUtils = utils;
        mEnableHaptics = mLockPatternUtils.isTactileFeedbackEnabled();
    }

    @Override
    public void reset() {
        // start fresh
        mDismissing = false;
        resetPasswordText(false /* animate */, false /* announce */);
        // if the user is currently locked out, enforce it.
        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
                KeyguardUpdateMonitor.getCurrentUser());
        if (shouldLockout(deadline)) {
            handleAttemptLockout(deadline);
        } else {
            resetState();
        }
    }

    // Allow subclasses to override this behavior
    protected boolean shouldLockout(long deadline) {
        return deadline != 0;
    void setEnableHaptics(boolean enableHaptics) {
        mEnableHaptics = enableHaptics;
    }

    protected abstract int getPasswordTextViewId();
@@ -102,24 +54,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout

    @Override
    protected void onFinishInflate() {
        mLockPatternUtils = new LockPatternUtils(mContext);
        mEcaView = findViewById(R.id.keyguard_selector_fade_container);

        EmergencyButton button = findViewById(R.id.emergency_call_button);
        if (button != null) {
            button.setCallback(this);
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mSecurityMessageDisplay = KeyguardMessageArea.findSecurityMessageDisplay(this);
    }

    @Override
    public void onEmergencyButtonClickedWhenInCall() {
        mCallback.reset();
    }

    /*
@@ -131,195 +66,14 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
        return R.string.kg_wrong_password;
    }

    protected void verifyPasswordAndUnlock() {
        if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.

        final LockscreenCredential password = getEnteredCredential();
        setPasswordEntryInputEnabled(false);
        if (mPendingLockCheck != null) {
            mPendingLockCheck.cancel(false);
        }

        final int userId = KeyguardUpdateMonitor.getCurrentUser();
        if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
            // to avoid accidental lockout, only count attempts that are long enough to be a
            // real password. This may require some tweaking.
            setPasswordEntryInputEnabled(true);
            onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
            password.zeroize();
            return;
        }

        if (LatencyTracker.isEnabled(mContext)) {
            LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
            LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
        }

        mKeyguardUpdateMonitor.setCredentialAttempted();
        mPendingLockCheck = LockPatternChecker.checkCredential(
                mLockPatternUtils,
                password,
                userId,
                new LockPatternChecker.OnCheckCallback() {

                    @Override
                    public void onEarlyMatched() {
                        if (LatencyTracker.isEnabled(mContext)) {
                            LatencyTracker.getInstance(mContext).onActionEnd(
                                    ACTION_CHECK_CREDENTIAL);
                        }
                        onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
                                true /* isValidPassword */);
                        password.zeroize();
                    }

                    @Override
                    public void onChecked(boolean matched, int timeoutMs) {
                        if (LatencyTracker.isEnabled(mContext)) {
                            LatencyTracker.getInstance(mContext).onActionEnd(
                                    ACTION_CHECK_CREDENTIAL_UNLOCKED);
                        }
                        setPasswordEntryInputEnabled(true);
                        mPendingLockCheck = null;
                        if (!matched) {
                            onPasswordChecked(userId, false /* matched */, timeoutMs,
                                    true /* isValidPassword */);
                        }
                        password.zeroize();
                    }

                    @Override
                    public void onCancelled() {
                        // We already got dismissed with the early matched callback, so we cancelled
                        // the check. However, we still need to note down the latency.
                        if (LatencyTracker.isEnabled(mContext)) {
                            LatencyTracker.getInstance(mContext).onActionEnd(
                                    ACTION_CHECK_CREDENTIAL_UNLOCKED);
                        }
                        password.zeroize();
                    }
                });
    }

    private void onPasswordChecked(int userId, boolean matched, int timeoutMs,
            boolean isValidPassword) {
        boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
        if (matched) {
            mCallback.reportUnlockAttempt(userId, true, 0);
            if (dismissKeyguard) {
                mDismissing = true;
                mCallback.dismiss(true, userId);
            }
        } else {
            if (isValidPassword) {
                mCallback.reportUnlockAttempt(userId, false, timeoutMs);
                if (timeoutMs > 0) {
                    long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
                            userId, timeoutMs);
                    handleAttemptLockout(deadline);
                }
            }
            if (timeoutMs == 0) {
                mSecurityMessageDisplay.setMessage(getWrongPasswordStringId());
            }
        }
        resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
    }

    protected abstract void resetPasswordText(boolean animate, boolean announce);
    protected abstract LockscreenCredential getEnteredCredential();
    protected abstract void setPasswordEntryEnabled(boolean enabled);
    protected abstract void setPasswordEntryInputEnabled(boolean enabled);

    // Prevent user from using the PIN/Password entry until scheduled deadline.
    protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
        setPasswordEntryEnabled(false);
        long elapsedRealtime = SystemClock.elapsedRealtime();
        long secondsInFuture = (long) Math.ceil(
                (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
        mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {

            @Override
            public void onTick(long millisUntilFinished) {
                int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
                mSecurityMessageDisplay.setMessage(mContext.getResources().getQuantityString(
                        R.plurals.kg_too_many_failed_attempts_countdown,
                        secondsRemaining, secondsRemaining));
            }

            @Override
            public void onFinish() {
                mSecurityMessageDisplay.setMessage("");
                resetState();
            }
        }.start();
    }

    protected void onUserInput() {
        if (mCallback != null) {
            mCallback.userActivity();
            mCallback.onUserInput();
        }
        mSecurityMessageDisplay.setMessage("");
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
        // We don't want to consider it valid user input because the UI
        // will already respond to the event.
        if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
            onUserInput();
        }
        return false;
    }

    @Override
    public boolean needsInput() {
        return false;
    }

    @Override
    public void onPause() {
        mResumed = false;

        if (mCountdownTimer != null) {
            mCountdownTimer.cancel();
            mCountdownTimer = null;
        }
        if (mPendingLockCheck != null) {
            mPendingLockCheck.cancel(false);
            mPendingLockCheck = null;
        }
        reset();
    }

    @Override
    public void onResume(int reason) {
        mResumed = true;
    }

    @Override
    public KeyguardSecurityCallback getCallback() {
        return mCallback;
    }

    @Override
    public void showPromptReason(int reason) {
        if (reason != PROMPT_REASON_NONE) {
            int promtReasonStringRes = getPromptReasonStringRes(reason);
            if (promtReasonStringRes != 0) {
                mSecurityMessageDisplay.setMessage(promtReasonStringRes);
            }
        }
    }

    @Override
    public void showMessage(CharSequence message, ColorStateList colorState) {
        if (colorState != null) {
            mSecurityMessageDisplay.setNextMessageColor(colorState);
        }
        mSecurityMessageDisplay.setMessage(message);
        return mKeyDownListener != null && mKeyDownListener.onKeyDown(keyCode, event);
    }

    protected abstract int getPromptReasonStringRes(int reason);
@@ -333,9 +87,12 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout
        }
    }

    @Override
    public boolean startDisappearAnimation(Runnable finishRunnable) {
        return false;
    public void setKeyDownListener(KeyDownListener keyDownListener) {
        mKeyDownListener = keyDownListener;
    }

    public interface KeyDownListener {
        boolean onKeyDown(int keyCode, KeyEvent keyEvent);
    }
}
+275 −0
Original line number Diff line number Diff line
/*
 * 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.keyguard;

import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL;
import static com.android.internal.util.LatencyTracker.ACTION_CHECK_CREDENTIAL_UNLOCKED;
import static com.android.keyguard.KeyguardAbsKeyInputView.MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT;

import android.content.res.ColorStateList;
import android.os.AsyncTask;
import android.os.CountDownTimer;
import android.os.SystemClock;
import android.view.KeyEvent;

import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockscreenCredential;
import com.android.keyguard.EmergencyButton.EmergencyButtonCallback;
import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.R;

public abstract class KeyguardAbsKeyInputViewController<T extends KeyguardAbsKeyInputView>
        extends KeyguardInputViewController<T> {
    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    private final LockPatternUtils mLockPatternUtils;
    private final LatencyTracker mLatencyTracker;
    private CountDownTimer mCountdownTimer;
    protected KeyguardMessageAreaController mMessageAreaController;
    private boolean mDismissing;
    protected AsyncTask<?, ?, ?> mPendingLockCheck;
    protected boolean mResumed;

    private final KeyDownListener mKeyDownListener = (keyCode, keyEvent) -> {
        // Fingerprint sensor sends a KeyEvent.KEYCODE_UNKNOWN.
        // We don't want to consider it valid user input because the UI
        // will already respond to the event.
        if (keyCode != KeyEvent.KEYCODE_UNKNOWN) {
            onUserInput();
        }
        return false;
    };

    private final EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() {
        @Override
        public void onEmergencyButtonClickedWhenInCall() {
            getKeyguardSecurityCallback().reset();
        }
    };

    protected KeyguardAbsKeyInputViewController(T view,
            KeyguardUpdateMonitor keyguardUpdateMonitor,
            SecurityMode securityMode,
            LockPatternUtils lockPatternUtils,
            KeyguardSecurityCallback keyguardSecurityCallback,
            KeyguardMessageAreaController.Factory messageAreaControllerFactory,
            LatencyTracker latencyTracker) {
        super(view, securityMode, keyguardSecurityCallback);
        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
        mLockPatternUtils = lockPatternUtils;
        mLatencyTracker = latencyTracker;
        KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView);
        mMessageAreaController = messageAreaControllerFactory.create(kma);
    }

    abstract void resetState();

    @Override
    public void init() {
        super.init();
        mMessageAreaController.init();
    }

    @Override
    protected void onViewAttached() {
        mView.setKeyDownListener(mKeyDownListener);
        mView.setEnableHaptics(mLockPatternUtils.isTactileFeedbackEnabled());
        EmergencyButton button = mView.findViewById(R.id.emergency_call_button);
        if (button != null) {
            button.setCallback(mEmergencyButtonCallback);
        }
    }

    @Override
    public void reset() {
        // start fresh
        mDismissing = false;
        mView.resetPasswordText(false /* animate */, false /* announce */);
        // if the user is currently locked out, enforce it.
        long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
                KeyguardUpdateMonitor.getCurrentUser());
        if (shouldLockout(deadline)) {
            handleAttemptLockout(deadline);
        } else {
            resetState();
        }
    }

    @Override
    public boolean needsInput() {
        return false;
    }

    @Override
    public void showMessage(CharSequence message, ColorStateList colorState) {
        if (colorState != null) {
            mMessageAreaController.setNextMessageColor(colorState);
        }
        mMessageAreaController.setMessage(message);
    }

    // Allow subclasses to override this behavior
    protected boolean shouldLockout(long deadline) {
        return deadline != 0;
    }

    // Prevent user from using the PIN/Password entry until scheduled deadline.
    protected void handleAttemptLockout(long elapsedRealtimeDeadline) {
        mView.setPasswordEntryEnabled(false);
        long elapsedRealtime = SystemClock.elapsedRealtime();
        long secondsInFuture = (long) Math.ceil(
                (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0);
        mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) {

            @Override
            public void onTick(long millisUntilFinished) {
                int secondsRemaining = (int) Math.round(millisUntilFinished / 1000.0);
                mMessageAreaController.setMessage(mView.getResources().getQuantityString(
                        R.plurals.kg_too_many_failed_attempts_countdown,
                        secondsRemaining, secondsRemaining));
            }

            @Override
            public void onFinish() {
                mMessageAreaController.setMessage("");
                resetState();
            }
        }.start();
    }

    void onPasswordChecked(int userId, boolean matched, int timeoutMs, boolean isValidPassword) {
        boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
        if (matched) {
            getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0);
            if (dismissKeyguard) {
                mDismissing = true;
                getKeyguardSecurityCallback().dismiss(true, userId);
            }
        } else {
            if (isValidPassword) {
                getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs);
                if (timeoutMs > 0) {
                    long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
                            userId, timeoutMs);
                    handleAttemptLockout(deadline);
                }
            }
            if (timeoutMs == 0) {
                mMessageAreaController.setMessage(mView.getWrongPasswordStringId());
            }
        }
        mView.resetPasswordText(true /* animate */, !matched /* announce deletion if no match */);
    }

    protected void verifyPasswordAndUnlock() {
        if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.

        final LockscreenCredential password = mView.getEnteredCredential();
        mView.setPasswordEntryInputEnabled(false);
        if (mPendingLockCheck != null) {
            mPendingLockCheck.cancel(false);
        }

        final int userId = KeyguardUpdateMonitor.getCurrentUser();
        if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
            // to avoid accidental lockout, only count attempts that are long enough to be a
            // real password. This may require some tweaking.
            mView.setPasswordEntryInputEnabled(true);
            onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
            password.zeroize();
            return;
        }

        mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL);
        mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);

        mKeyguardUpdateMonitor.setCredentialAttempted();
        mPendingLockCheck = LockPatternChecker.checkCredential(
                mLockPatternUtils,
                password,
                userId,
                new LockPatternChecker.OnCheckCallback() {

                    @Override
                    public void onEarlyMatched() {
                        mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL);

                        onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
                                true /* isValidPassword */);
                        password.zeroize();
                    }

                    @Override
                    public void onChecked(boolean matched, int timeoutMs) {
                        mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
                        mView.setPasswordEntryInputEnabled(true);
                        mPendingLockCheck = null;
                        if (!matched) {
                            onPasswordChecked(userId, false /* matched */, timeoutMs,
                                    true /* isValidPassword */);
                        }
                        password.zeroize();
                    }

                    @Override
                    public void onCancelled() {
                        // We already got dismissed with the early matched callback, so we cancelled
                        // the check. However, we still need to note down the latency.
                        mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED);
                        password.zeroize();
                    }
                });
    }

    @Override
    public void showPromptReason(int reason) {
        if (reason != PROMPT_REASON_NONE) {
            int promtReasonStringRes = mView.getPromptReasonStringRes(reason);
            if (promtReasonStringRes != 0) {
                mMessageAreaController.setMessage(promtReasonStringRes);
            }
        }
    }

    protected void onUserInput() {
        getKeyguardSecurityCallback().userActivity();
        getKeyguardSecurityCallback().onUserInput();
        mMessageAreaController.setMessage("");
    }

    @Override
    public void onResume(int reason) {
        mResumed = true;
    }

    @Override
    public void onPause() {
        mResumed = false;

        if (mCountdownTimer != null) {
            mCountdownTimer.cancel();
            mCountdownTimer = null;
        }
        if (mPendingLockCheck != null) {
            mPendingLockCheck.cancel(false);
            mPendingLockCheck = null;
        }
        reset();
    }
}
+5 −10

File changed.

Preview size limit exceeded, changes collapsed.

+10 −9

File changed.

Preview size limit exceeded, changes collapsed.

Loading