diff options
| author | 2020-09-25 14:25:18 +0000 | |
|---|---|---|
| committer | 2020-09-25 14:25:18 +0000 | |
| commit | 59fb99b1dc1eef652c4ae9e0ccd080ec4b56dfdd (patch) | |
| tree | f1285ee08e27cca05ac8d3bda604eebd84a66f2c | |
| parent | ce302adbc6dbe9b20d109b297ae4785f3bfd00b5 (diff) | |
| parent | 28822be53301c76b08ccc8b815c1a56c44e085b6 (diff) | |
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."
50 files changed, 3607 insertions, 2797 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java index e99245fa438f..23195af8bdea 100644 --- a/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java +++ b/packages/SystemUI/src/com/android/keyguard/AdminSecondaryLockScreenController.java @@ -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); + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 88f4176f5eac..cc6df45c598f 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -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); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java new file mode 100644 index 000000000000..53f847434dcc --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java @@ -0,0 +1,275 @@ +/* + * 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(); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java index be21d203411e..36d5543f1c01 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardDisplayManager.java @@ -39,7 +39,6 @@ import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; -import com.android.systemui.util.InjectionInflationController; import javax.inject.Inject; @@ -49,7 +48,6 @@ public class KeyguardDisplayManager { private final MediaRouter mMediaRouter; private final DisplayManager mDisplayService; - private final InjectionInflationController mInjectableInflater; private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; private final Context mContext; @@ -92,10 +90,8 @@ public class KeyguardDisplayManager { @Inject public KeyguardDisplayManager(Context context, - InjectionInflationController injectableInflater, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) { mContext = context; - mInjectableInflater = injectableInflater; mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; mMediaRouter = mContext.getSystemService(MediaRouter.class); mDisplayService = mContext.getSystemService(DisplayManager.class); @@ -131,8 +127,7 @@ public class KeyguardDisplayManager { Presentation presentation = mPresentations.get(displayId); if (presentation == null) { final Presentation newPresentation = new KeyguardPresentation(mContext, display, - mKeyguardStatusViewComponentFactory, - mInjectableInflater.injectable(LayoutInflater.from(mContext))); + mKeyguardStatusViewComponentFactory, LayoutInflater.from(mContext)); newPresentation.setOnDismissListener(dialog -> { if (newPresentation.equals(mPresentations.get(displayId))) { mPresentations.remove(displayId); @@ -250,7 +245,7 @@ public class KeyguardDisplayManager { private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory; - private final LayoutInflater mInjectableLayoutInflater; + private final LayoutInflater mLayoutInflater; private KeyguardClockSwitchController mKeyguardClockSwitchController; private View mClock; private int mUsableWidth; @@ -270,10 +265,10 @@ public class KeyguardDisplayManager { KeyguardPresentation(Context context, Display display, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, - LayoutInflater injectionLayoutInflater) { + LayoutInflater layoutInflater) { super(context, display, R.style.Theme_SystemUI_KeyguardPresentation); mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory; - mInjectableLayoutInflater = injectionLayoutInflater; + mLayoutInflater = layoutInflater; getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); setCancelable(false); } @@ -299,7 +294,7 @@ public class KeyguardDisplayManager { mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200; mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200; - setContentView(mInjectableLayoutInflater.inflate(R.layout.keyguard_presentation, null)); + setContentView(mLayoutInflater.inflate(R.layout.keyguard_presentation, null)); // Logic to make the lock screen fullscreen getWindow().getDecorView().setSystemUiVisibility( diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java index 7aabb17de90c..351369c51364 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java @@ -163,33 +163,34 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> @Inject public KeyguardHostViewController(KeyguardHostView view, KeyguardUpdateMonitor keyguardUpdateMonitor, - KeyguardSecurityContainerController keyguardSecurityContainerController, AudioManager audioManager, TelephonyManager telephonyManager, - ViewMediatorCallback viewMediatorCallback) { + ViewMediatorCallback viewMediatorCallback, + KeyguardSecurityContainerController.Factory + keyguardSecurityContainerControllerFactory) { super(view); mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mKeyguardSecurityContainerController = keyguardSecurityContainerController; mAudioManager = audioManager; mTelephonyManager = telephonyManager; mViewMediatorCallback = viewMediatorCallback; + mKeyguardSecurityContainerController = keyguardSecurityContainerControllerFactory.create( + mSecurityCallback); } /** Initialize the Controller. */ public void init() { super.init(); - mView.setViewMediatorCallback(mViewMediatorCallback); - // Update ViewMediator with the current input method requirements - mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput()); mKeyguardSecurityContainerController.init(); - mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback); - mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } @Override protected void onViewAttached() { + mView.setViewMediatorCallback(mViewMediatorCallback); + // Update ViewMediator with the current input method requirements + mViewMediatorCallback.setNeedsInput(mKeyguardSecurityContainerController.needsInput()); mKeyguardUpdateMonitor.registerCallback(mUpdateCallback); mView.setOnKeyListener(mOnKeyListener); + mKeyguardSecurityContainerController.showPrimarySecurityScreen(false); } @Override @@ -350,7 +351,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView> } public boolean handleBackKey() { - if (mKeyguardSecurityContainerController.getCurrentSecuritySelection() + if (mKeyguardSecurityContainerController.getCurrentSecurityMode() != SecurityMode.None) { mKeyguardSecurityContainerController.dismiss( false, KeyguardUpdateMonitor.getCurrentUser()); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java new file mode 100644 index 000000000000..d42a53cc875e --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java @@ -0,0 +1,55 @@ +/* + * 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 android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.LinearLayout; + +import androidx.annotation.Nullable; + +/** + * A Base class for all Keyguard password/pattern/pin related inputs. + */ +public abstract class KeyguardInputView extends LinearLayout { + + public KeyguardInputView(Context context) { + super(context); + } + + public KeyguardInputView(Context context, + @Nullable AttributeSet attrs) { + super(context, attrs); + } + + public KeyguardInputView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + abstract CharSequence getTitle(); + + boolean disallowInterceptTouch(MotionEvent event) { + return false; + } + + void startAppearAnimation() {} + + boolean startDisappearAnimation(Runnable finishRunnable) { + return false; + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java new file mode 100644 index 000000000000..fbda818740e8 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -0,0 +1,196 @@ +/* + * 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 android.content.res.ColorStateList; +import android.content.res.Resources; +import android.telephony.TelephonyManager; +import android.view.inputmethod.InputMethodManager; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.util.ViewController; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import javax.inject.Inject; + + +/** Controller for a {@link KeyguardSecurityView}. */ +public abstract class KeyguardInputViewController<T extends KeyguardInputView> + extends ViewController<T> implements KeyguardSecurityView { + + private final SecurityMode mSecurityMode; + private final KeyguardSecurityCallback mKeyguardSecurityCallback; + private boolean mPaused; + + + // The following is used to ignore callbacks from SecurityViews that are no longer current + // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the + // state for the current security method. + private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { + @Override + public void userActivity() { } + @Override + public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } + @Override + public boolean isVerifyUnlockOnly() { + return false; + } + @Override + public void dismiss(boolean securityVerified, int targetUserId) { } + @Override + public void dismiss(boolean authenticated, int targetId, + boolean bypassSecondaryLockScreen) { } + @Override + public void onUserInput() { } + @Override + public void reset() {} + }; + + protected KeyguardInputViewController(T view, SecurityMode securityMode, + KeyguardSecurityCallback keyguardSecurityCallback) { + super(view); + mSecurityMode = securityMode; + mKeyguardSecurityCallback = keyguardSecurityCallback; + } + + @Override + protected void onViewAttached() { + } + + @Override + protected void onViewDetached() { + } + + SecurityMode getSecurityMode() { + return mSecurityMode; + } + + protected KeyguardSecurityCallback getKeyguardSecurityCallback() { + if (mPaused) { + return mNullCallback; + } + + return mKeyguardSecurityCallback; + } + + @Override + public void reset() { + } + + @Override + public void onPause() { + mPaused = true; + } + + @Override + public void onResume(int reason) { + mPaused = false; + } + + @Override + public void showPromptReason(int reason) { + } + + @Override + public void showMessage(CharSequence message, ColorStateList colorState) { + } + + public void startAppearAnimation() { + mView.startAppearAnimation(); + } + + public boolean startDisappearAnimation(Runnable finishRunnable) { + return mView.startDisappearAnimation(finishRunnable); + } + + @Override + public CharSequence getTitle() { + return mView.getTitle(); + } + + /** Finds the index of this view in the suppplied parent view. */ + public int getIndexIn(KeyguardSecurityViewFlipper view) { + return view.indexOfChild(mView); + } + + /** Factory for a {@link KeyguardInputViewController}. */ + public static class Factory { + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final LockPatternUtils mLockPatternUtils; + private final LatencyTracker mLatencyTracker; + private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory; + private final InputMethodManager mInputMethodManager; + private final DelayableExecutor mMainExecutor; + private final Resources mResources; + private LiftToActivateListener mLiftToActivateListener; + private TelephonyManager mTelephonyManager; + + @Inject + public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor, + LockPatternUtils lockPatternUtils, + LatencyTracker latencyTracker, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, + InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor, + @Main Resources resources, LiftToActivateListener liftToActivateListener, + TelephonyManager telephonyManager) { + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mLockPatternUtils = lockPatternUtils; + mLatencyTracker = latencyTracker; + mMessageAreaControllerFactory = messageAreaControllerFactory; + mInputMethodManager = inputMethodManager; + mMainExecutor = mainExecutor; + mResources = resources; + mLiftToActivateListener = liftToActivateListener; + mTelephonyManager = telephonyManager; + } + + /** Create a new {@link KeyguardInputViewController}. */ + public KeyguardInputViewController create(KeyguardInputView keyguardInputView, + SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) { + if (keyguardInputView instanceof KeyguardPatternView) { + return new KeyguardPatternViewController((KeyguardPatternView) keyguardInputView, + mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, + keyguardSecurityCallback, mLatencyTracker, mMessageAreaControllerFactory); + } else if (keyguardInputView instanceof KeyguardPasswordView) { + return new KeyguardPasswordViewController((KeyguardPasswordView) keyguardInputView, + mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, + keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, + mInputMethodManager, mMainExecutor, mResources); + } else if (keyguardInputView instanceof KeyguardPINView) { + return new KeyguardPinViewController((KeyguardPINView) keyguardInputView, + mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, + keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, + mLiftToActivateListener); + } else if (keyguardInputView instanceof KeyguardSimPinView) { + return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView, + mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, + keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, + mLiftToActivateListener, mTelephonyManager); + } else if (keyguardInputView instanceof KeyguardSimPukView) { + return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView, + mKeyguardUpdateMonitor, securityMode, mLockPatternUtils, + keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker, + mLiftToActivateListener, mTelephonyManager); + } + + throw new RuntimeException("Unable to find controller for " + keyguardInputView); + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java index a8b1451d92c7..1a0a4370fca4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageArea.java @@ -16,8 +16,6 @@ package com.android.keyguard; -import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT; - import android.content.Context; import android.content.res.ColorStateList; import android.content.res.TypedArray; @@ -31,20 +29,14 @@ import android.util.TypedValue; import android.view.View; import android.widget.TextView; -import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.statusbar.policy.ConfigurationController; import java.lang.ref.WeakReference; -import javax.inject.Inject; -import javax.inject.Named; - /*** * Manages a number of views inside of the given layout. See below for a list of widgets. */ -public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay, - ConfigurationController.ConfigurationListener { +public class KeyguardMessageArea extends TextView implements SecurityMessageDisplay { /** Handler token posted with accessibility announcement runnables. */ private static final Object ANNOUNCE_TOKEN = new Object(); @@ -56,71 +48,26 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp private static final int DEFAULT_COLOR = -1; private final Handler mHandler; - private final ConfigurationController mConfigurationController; private ColorStateList mDefaultColorState; private CharSequence mMessage; private ColorStateList mNextMessageColorState = ColorStateList.valueOf(DEFAULT_COLOR); private boolean mBouncerVisible; - private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { - public void onFinishedGoingToSleep(int why) { - setSelected(false); - } - - public void onStartedWakingUp() { - setSelected(true); - } - - @Override - public void onKeyguardBouncerChanged(boolean bouncer) { - mBouncerVisible = bouncer; - update(); - } - }; - - public KeyguardMessageArea(Context context) { - super(context, null); - throw new IllegalStateException("This constructor should never be invoked"); - } - - @Inject - public KeyguardMessageArea(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - ConfigurationController configurationController) { - this(context, attrs, Dependency.get(KeyguardUpdateMonitor.class), configurationController); - } - - public KeyguardMessageArea(Context context, AttributeSet attrs, KeyguardUpdateMonitor monitor, - ConfigurationController configurationController) { + public KeyguardMessageArea(Context context, AttributeSet attrs) { super(context, attrs); setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug - monitor.registerCallback(mInfoCallback); mHandler = new Handler(Looper.myLooper()); - mConfigurationController = configurationController; onThemeChanged(); } @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mConfigurationController.addCallback(this); - onThemeChanged(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mConfigurationController.removeCallback(this); - } - - @Override public void setNextMessageColor(ColorStateList colorState) { mNextMessageColorState = colorState; } - @Override - public void onThemeChanged() { + void onThemeChanged() { TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor }); @@ -130,8 +77,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp update(); } - @Override - public void onDensityOrFontScaleChanged() { + void onDensityOrFontScaleChanged() { TypedArray array = mContext.obtainStyledAttributes(R.style.Keyguard_TextView, new int[] { android.R.attr.textSize }); @@ -177,12 +123,6 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp return messageArea; } - @Override - protected void onFinishInflate() { - boolean shouldMarquee = Dependency.get(KeyguardUpdateMonitor.class).isDeviceInteractive(); - setSelected(shouldMarquee); // This is required to ensure marquee works - } - private void securityMessageChanged(CharSequence message) { mMessage = message; update(); @@ -196,7 +136,7 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp update(); } - private void update() { + void update() { CharSequence status = mMessage; setVisibility(TextUtils.isEmpty(status) || !mBouncerVisible ? INVISIBLE : VISIBLE); setText(status); @@ -208,6 +148,9 @@ public class KeyguardMessageArea extends TextView implements SecurityMessageDisp setTextColor(colorState); } + public void setBouncerVisible(boolean bouncerVisible) { + mBouncerVisible = bouncerVisible; + } /** * Runnable used to delay accessibility announcements. diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java index f056bdbb9706..1618e8e58055 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardMessageAreaController.java @@ -16,7 +16,10 @@ package com.android.keyguard; +import android.content.res.ColorStateList; + import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.util.ViewController; import javax.inject.Inject; @@ -26,6 +29,35 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ConfigurationController mConfigurationController; + + private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() { + public void onFinishedGoingToSleep(int why) { + mView.setSelected(false); + } + + public void onStartedWakingUp() { + mView.setSelected(true); + } + + @Override + public void onKeyguardBouncerChanged(boolean bouncer) { + mView.setBouncerVisible(bouncer); + mView.update(); + } + }; + + private ConfigurationListener mConfigurationListener = new ConfigurationListener() { + @Override + public void onThemeChanged() { + mView.onThemeChanged(); + } + + @Override + public void onDensityOrFontScaleChanged() { + mView.onDensityOrFontScaleChanged(); + } + }; + private KeyguardMessageAreaController(KeyguardMessageArea view, KeyguardUpdateMonitor keyguardUpdateMonitor, ConfigurationController configurationController) { @@ -37,17 +69,31 @@ public class KeyguardMessageAreaController extends ViewController<KeyguardMessag @Override protected void onViewAttached() { - //mConfigurationController.addCallback(); - //mKeyguardUpdateMonitor.registerCallback(); + mConfigurationController.addCallback(mConfigurationListener); + mKeyguardUpdateMonitor.registerCallback(mInfoCallback); + mView.setSelected(mKeyguardUpdateMonitor.isDeviceInteractive()); + mView.onThemeChanged(); } @Override protected void onViewDetached() { - //mConfigurationController.removeCallback(); - //mKeyguardUpdateMonitor.removeCallback(); + mConfigurationController.removeCallback(mConfigurationListener); + mKeyguardUpdateMonitor.removeCallback(mInfoCallback); + } + + public void setMessage(CharSequence s) { + mView.setMessage(s); + } + + public void setMessage(int resId) { + mView.setMessage(resId); + } + + public void setNextMessageColor(ColorStateList colorState) { + mView.setNextMessageColor(colorState); } - /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */ + /** Factory for creating {@link com.android.keyguard.KeyguardMessageAreaController}. */ public static class Factory { private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final ConfigurationController mConfigurationController; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java index 12ea1d586e10..580d7043a220 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPINView.java @@ -24,7 +24,6 @@ import android.view.animation.AnimationUtils; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; -import com.android.systemui.Dependency; import com.android.systemui.R; /** @@ -40,10 +39,8 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private ViewGroup mRow1; private ViewGroup mRow2; private ViewGroup mRow3; - private View mDivider; private int mDisappearYTranslation; private View[][] mViews; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; public KeyguardPINView(Context context) { this(context, null); @@ -63,15 +60,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { mContext, android.R.interpolator.fast_out_linear_in)); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); } @Override protected void resetState() { - super.resetState(); - if (mSecurityMessageDisplay != null) { - mSecurityMessageDisplay.setMessage(""); - } } @Override @@ -88,7 +80,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { mRow1 = findViewById(R.id.row1); mRow2 = findViewById(R.id.row2); mRow3 = findViewById(R.id.row3); - mDivider = findViewById(R.id.divider); mViews = new View[][]{ new View[]{ mRow0, null, null @@ -112,18 +103,6 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { new View[]{ null, mEcaView, null }}; - - View cancelBtn = findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(view -> { - mCallback.reset(); - mCallback.onCancelClicked(); - }); - } - } - - @Override - public void showUsabilityHint() { } @Override @@ -147,24 +126,21 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { }); } - @Override - public boolean startDisappearAnimation(final Runnable finishRunnable) { + public boolean startDisappearAnimation(boolean needsSlowUnlockTransition, + final Runnable finishRunnable) { + enableClipping(false); setTranslationY(0); AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */, mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator()); - DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor - .needsSlowUnlockTransition() + DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils; disappearAnimationUtils.startAnimation2d(mViews, - new Runnable() { - @Override - public void run() { - enableClipping(true); - if (finishRunnable != null) { - finishRunnable.run(); - } + () -> { + enableClipping(true); + if (finishRunnable != null) { + finishRunnable.run(); } }); return true; diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index 97317cf5580f..aaa5efec807e 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -16,50 +16,37 @@ package com.android.keyguard; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; + import android.content.Context; import android.graphics.Rect; -import android.os.UserHandle; -import android.text.Editable; -import android.text.InputType; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.text.method.TextKeyListener; import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.View; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodInfo; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; import android.widget.TextView; -import android.widget.TextView.OnEditorActionListener; import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.TextViewInputDisabler; import com.android.systemui.R; - -import java.util.List; /** * Displays an alphanumeric (latin-1) key entry for the user to enter * an unlock password */ -public class KeyguardPasswordView extends KeyguardAbsKeyInputView - implements KeyguardSecurityView, OnEditorActionListener, TextWatcher { +public class KeyguardPasswordView extends KeyguardAbsKeyInputView { - private final boolean mShowImeAtScreenOn; private final int mDisappearYTranslation; // A delay constant to be used in a workaround for the situation where InputMethodManagerService // is not switched to the new user yet. // TODO: Remove this by ensuring such a race condition never happens. - private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms - InputMethodManager mImm; private TextView mPasswordEntry; private TextViewInputDisabler mPasswordEntryDisabler; - private View mSwitchImeButton; private Interpolator mLinearOutSlowInInterpolator; private Interpolator mFastOutLinearInInterpolator; @@ -70,8 +57,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView public KeyguardPasswordView(Context context, AttributeSet attrs) { super(context, attrs); - mShowImeAtScreenOn = context.getResources(). - getBoolean(R.bool.kg_show_ime_at_screen_on); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); mLinearOutSlowInInterpolator = AnimationUtils.loadInterpolator( @@ -82,20 +67,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView @Override protected void resetState() { - mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); - if (mSecurityMessageDisplay != null) { - mSecurityMessageDisplay.setMessage(""); - } - final boolean wasDisabled = mPasswordEntry.isEnabled(); - setPasswordEntryEnabled(true); - setPasswordEntryInputEnabled(true); - // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage. - if (!mResumed || !mPasswordEntry.isVisibleToUser()) { - return; - } - if (wasDisabled) { - mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); - } } @Override @@ -104,29 +75,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override - public boolean needsInput() { - return true; - } - - @Override - public void onResume(final int reason) { - super.onResume(reason); - - // Wait a bit to focus the field so the focusable flag on the window is already set then. - post(new Runnable() { - @Override - public void run() { - if (isShown() && mPasswordEntry.isEnabled()) { - mPasswordEntry.requestFocus(); - if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) { - mImm.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); - } - } - } - }); - } - - @Override protected int getPromptReasonStringRes(int reason) { switch (reason) { case PROMPT_REASON_RESTART: @@ -146,97 +94,13 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } } - @Override - public void onPause() { - super.onPause(); - mImm.hideSoftInputFromWindow(getWindowToken(), 0); - } - - @Override - public void onStartingToHide() { - mImm.hideSoftInputFromWindow(getWindowToken(), 0); - } - - private void updateSwitchImeButton() { - // If there's more than one IME, enable the IME switcher button - final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; - final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes(mImm, false); - if (wasVisible != shouldBeVisible) { - mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); - } - - // TODO: Check if we still need this hack. - // If no icon is visible, reset the start margin on the password field so the text is - // still centered. - if (mSwitchImeButton.getVisibility() != View.VISIBLE) { - android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); - if (params instanceof MarginLayoutParams) { - final MarginLayoutParams mlp = (MarginLayoutParams) params; - mlp.setMarginStart(0); - mPasswordEntry.setLayoutParams(params); - } - } - } @Override protected void onFinishInflate() { super.onFinishInflate(); - mImm = (InputMethodManager) getContext().getSystemService( - Context.INPUT_METHOD_SERVICE); - mPasswordEntry = findViewById(getPasswordTextViewId()); - mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); mPasswordEntryDisabler = new TextViewInputDisabler(mPasswordEntry); - mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); - mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT - | InputType.TYPE_TEXT_VARIATION_PASSWORD); - mPasswordEntry.setOnEditorActionListener(this); - mPasswordEntry.addTextChangedListener(this); - - // Poke the wakelock any time the text is selected or modified - mPasswordEntry.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mCallback.userActivity(); - } - }); - - // Set selected property on so the view can send accessibility events. - mPasswordEntry.setSelected(true); - - mSwitchImeButton = findViewById(R.id.switch_ime_button); - mSwitchImeButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - mCallback.userActivity(); // Leave the screen on a bit longer - // Do not show auxiliary subtypes in password lock screen. - mImm.showInputMethodPickerFromSystem(false /* showAuxiliarySubtypes */, - getContext().getDisplayId()); - } - }); - - View cancelBtn = findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(view -> { - mCallback.reset(); - mCallback.onCancelClicked(); - }); - } - - // If there's more than one IME, enable the IME switcher button - updateSwitchImeButton(); - - // When we the current user is switching, InputMethodManagerService sometimes has not - // switched internal state yet here. As a quick workaround, we check the keyboard state - // again. - // TODO: Remove this workaround by ensuring such a race condition never happens. - postDelayed(new Runnable() { - @Override - public void run() { - updateSwitchImeButton(); - } - }, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); } @Override @@ -265,59 +129,6 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView mPasswordEntryDisabler.setInputEnabled(enabled); } - /** - * Method adapted from com.android.inputmethod.latin.Utils - * - * @param imm The input method manager - * @param shouldIncludeAuxiliarySubtypes - * @return true if we have multiple IMEs to choose from - */ - private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, - final boolean shouldIncludeAuxiliarySubtypes) { - final List<InputMethodInfo> enabledImis = - imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser()); - - // Number of the filtered IMEs - int filteredImisCount = 0; - - for (InputMethodInfo imi : enabledImis) { - // We can return true immediately after we find two or more filtered IMEs. - if (filteredImisCount > 1) return true; - final List<InputMethodSubtype> subtypes = - imm.getEnabledInputMethodSubtypeList(imi, true); - // IMEs that have no subtypes should be counted. - if (subtypes.isEmpty()) { - ++filteredImisCount; - continue; - } - - int auxCount = 0; - for (InputMethodSubtype subtype : subtypes) { - if (subtype.isAuxiliary()) { - ++auxCount; - } - } - final int nonAuxCount = subtypes.size() - auxCount; - - // IMEs that have one or more non-auxiliary subtypes should be counted. - // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary - // subtypes should be counted as well. - if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { - ++filteredImisCount; - continue; - } - } - - return filteredImisCount > 1 - // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's enabled - // input method subtype (The current IME should be LatinIME.) - || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; - } - - @Override - public void showUsabilityHint() { - } - @Override public int getWrongPasswordStringId() { return R.string.kg_wrong_password; @@ -346,45 +157,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (mCallback != null) { - mCallback.userActivity(); - } - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - // Poor man's user edit detection, assuming empty text is programmatic and everything else - // is from the user. - if (!TextUtils.isEmpty(s)) { - onUserInput(); - } - } - - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - // Check if this was the result of hitting the enter key - final boolean isSoftImeEvent = event == null - && (actionId == EditorInfo.IME_NULL - || actionId == EditorInfo.IME_ACTION_DONE - || actionId == EditorInfo.IME_ACTION_NEXT); - final boolean isKeyboardEnterKey = event != null - && KeyEvent.isConfirmKey(event.getKeyCode()) - && event.getAction() == KeyEvent.ACTION_DOWN; - if (isSoftImeEvent || isKeyboardEnterKey) { - verifyPasswordAndUnlock(); - return true; - } - return false; - } - - @Override public CharSequence getTitle() { - return getContext().getString( + return getResources().getString( com.android.internal.R.string.keyguard_accessibility_password_unlock); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java new file mode 100644 index 000000000000..d34ea8c5e018 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java @@ -0,0 +1,275 @@ +/* + * 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 android.content.res.Resources; +import android.os.UserHandle; +import android.text.Editable; +import android.text.InputType; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.text.method.TextKeyListener; +import android.view.KeyEvent; +import android.view.View; +import android.view.ViewGroup.MarginLayoutParams; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodInfo; +import android.view.inputmethod.InputMethodManager; +import android.view.inputmethod.InputMethodSubtype; +import android.widget.TextView; +import android.widget.TextView.OnEditorActionListener; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.util.concurrency.DelayableExecutor; + +import java.util.List; + +public class KeyguardPasswordViewController + extends KeyguardAbsKeyInputViewController<KeyguardPasswordView> { + + private static final int DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON = 500; // 500ms + + private final KeyguardSecurityCallback mKeyguardSecurityCallback; + private final InputMethodManager mInputMethodManager; + private final DelayableExecutor mMainExecutor; + private final boolean mShowImeAtScreenOn; + private TextView mPasswordEntry; + private View mSwitchImeButton; + + private final OnEditorActionListener mOnEditorActionListener = (v, actionId, event) -> { + // Check if this was the result of hitting the enter key + final boolean isSoftImeEvent = event == null + && (actionId == EditorInfo.IME_NULL + || actionId == EditorInfo.IME_ACTION_DONE + || actionId == EditorInfo.IME_ACTION_NEXT); + final boolean isKeyboardEnterKey = event != null + && KeyEvent.isConfirmKey(event.getKeyCode()) + && event.getAction() == KeyEvent.ACTION_DOWN; + if (isSoftImeEvent || isKeyboardEnterKey) { + verifyPasswordAndUnlock(); + return true; + } + return false; + }; + + private final TextWatcher mTextWatcher = new TextWatcher() { + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + mKeyguardSecurityCallback.userActivity(); + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + if (!TextUtils.isEmpty(s)) { + onUserInput(); + } + } + }; + + protected KeyguardPasswordViewController(KeyguardPasswordView view, + KeyguardUpdateMonitor keyguardUpdateMonitor, + SecurityMode securityMode, + LockPatternUtils lockPatternUtils, + KeyguardSecurityCallback keyguardSecurityCallback, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, + LatencyTracker latencyTracker, + InputMethodManager inputMethodManager, + @Main DelayableExecutor mainExecutor, + @Main Resources resources) { + super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, + messageAreaControllerFactory, latencyTracker); + mKeyguardSecurityCallback = keyguardSecurityCallback; + mInputMethodManager = inputMethodManager; + mMainExecutor = mainExecutor; + mShowImeAtScreenOn = resources.getBoolean(R.bool.kg_show_ime_at_screen_on); + mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); + mSwitchImeButton = mView.findViewById(R.id.switch_ime_button); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); + mPasswordEntry.setKeyListener(TextKeyListener.getInstance()); + mPasswordEntry.setInputType(InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_PASSWORD); + + // Set selected property on so the view can send accessibility events. + mPasswordEntry.setSelected(true); + mPasswordEntry.setOnEditorActionListener(mOnEditorActionListener); + mPasswordEntry.addTextChangedListener(mTextWatcher); + // Poke the wakelock any time the text is selected or modified + mPasswordEntry.setOnClickListener(v -> mKeyguardSecurityCallback.userActivity()); + + mSwitchImeButton.setOnClickListener(v -> { + mKeyguardSecurityCallback.userActivity(); // Leave the screen on a bit longer + // Do not show auxiliary subtypes in password lock screen. + mInputMethodManager.showInputMethodPickerFromSystem(false, + mView.getContext().getDisplayId()); + }); + + View cancelBtn = mView.findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + mKeyguardSecurityCallback.reset(); + mKeyguardSecurityCallback.onCancelClicked(); + }); + } + + // If there's more than one IME, enable the IME switcher button + updateSwitchImeButton(); + + // When we the current user is switching, InputMethodManagerService sometimes has not + // switched internal state yet here. As a quick workaround, we check the keyboard state + // again. + // TODO: Remove this workaround by ensuring such a race condition never happens. + mMainExecutor.executeDelayed( + this::updateSwitchImeButton, DELAY_MILLIS_TO_REEVALUATE_IME_SWITCH_ICON); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mPasswordEntry.setOnEditorActionListener(null); + } + + @Override + public boolean needsInput() { + return true; + } + + @Override + void resetState() { + mPasswordEntry.setTextOperationUser(UserHandle.of(KeyguardUpdateMonitor.getCurrentUser())); + mMessageAreaController.setMessage(""); + final boolean wasDisabled = mPasswordEntry.isEnabled(); + mView.setPasswordEntryEnabled(true); + mView.setPasswordEntryInputEnabled(true); + // Don't call showSoftInput when PasswordEntry is invisible or in pausing stage. + if (!mResumed || !mPasswordEntry.isVisibleToUser()) { + return; + } + if (wasDisabled) { + mInputMethodManager.showSoftInput(mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + } + } + + @Override + public void onResume(int reason) { + super.onResume(reason); + // Wait a bit to focus the field so the focusable flag on the window is already set then. + mMainExecutor.execute(() -> { + if (mView.isShown() && mPasswordEntry.isEnabled()) { + mPasswordEntry.requestFocus(); + if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) { + mInputMethodManager.showSoftInput( + mPasswordEntry, InputMethodManager.SHOW_IMPLICIT); + } + } + }); + } + + @Override + public void onPause() { + super.onPause(); + mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); + } + + @Override + public void onStartingToHide() { + mInputMethodManager.hideSoftInputFromWindow(mView.getWindowToken(), 0); + } + + private void updateSwitchImeButton() { + // If there's more than one IME, enable the IME switcher button + final boolean wasVisible = mSwitchImeButton.getVisibility() == View.VISIBLE; + final boolean shouldBeVisible = hasMultipleEnabledIMEsOrSubtypes( + mInputMethodManager, false); + if (wasVisible != shouldBeVisible) { + mSwitchImeButton.setVisibility(shouldBeVisible ? View.VISIBLE : View.GONE); + } + + // TODO: Check if we still need this hack. + // If no icon is visible, reset the start margin on the password field so the text is + // still centered. + if (mSwitchImeButton.getVisibility() != View.VISIBLE) { + android.view.ViewGroup.LayoutParams params = mPasswordEntry.getLayoutParams(); + if (params instanceof MarginLayoutParams) { + final MarginLayoutParams mlp = (MarginLayoutParams) params; + mlp.setMarginStart(0); + mPasswordEntry.setLayoutParams(params); + } + } + } + + /** + * Method adapted from com.android.inputmethod.latin.Utils + * + * @param imm The input method manager + * @param shouldIncludeAuxiliarySubtypes + * @return true if we have multiple IMEs to choose from + */ + private boolean hasMultipleEnabledIMEsOrSubtypes(InputMethodManager imm, + final boolean shouldIncludeAuxiliarySubtypes) { + final List<InputMethodInfo> enabledImis = + imm.getEnabledInputMethodListAsUser(KeyguardUpdateMonitor.getCurrentUser()); + + // Number of the filtered IMEs + int filteredImisCount = 0; + + for (InputMethodInfo imi : enabledImis) { + // We can return true immediately after we find two or more filtered IMEs. + if (filteredImisCount > 1) return true; + final List<InputMethodSubtype> subtypes = + imm.getEnabledInputMethodSubtypeList(imi, true); + // IMEs that have no subtypes should be counted. + if (subtypes.isEmpty()) { + ++filteredImisCount; + continue; + } + + int auxCount = 0; + for (InputMethodSubtype subtype : subtypes) { + if (subtype.isAuxiliary()) { + ++auxCount; + } + } + final int nonAuxCount = subtypes.size() - auxCount; + + // IMEs that have one or more non-auxiliary subtypes should be counted. + // If shouldIncludeAuxiliarySubtypes is true, IMEs that have two or more auxiliary + // subtypes should be counted as well. + if (nonAuxCount > 0 || (shouldIncludeAuxiliarySubtypes && auxCount > 1)) { + ++filteredImisCount; + continue; + } + } + + return filteredImisCount > 1 + // imm.getEnabledInputMethodSubtypeList(null, false) will return the current IME's + //enabled input method subtype (The current IME should be LatinIME.) + || imm.getEnabledInputMethodSubtypeList(null, false).size() > 1; + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index c4a9fcb45284..bdcf467c2456 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -15,62 +15,39 @@ */ 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.graphics.Rect; -import android.os.AsyncTask; -import android.os.CountDownTimer; import android.os.SystemClock; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; -import android.widget.LinearLayout; -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.LatencyTracker; -import com.android.internal.widget.LockPatternChecker; -import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; -import com.android.internal.widget.LockscreenCredential; import com.android.settingslib.animation.AppearAnimationCreator; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; -import com.android.systemui.Dependency; import com.android.systemui.R; -import java.util.List; - -public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView, - AppearAnimationCreator<LockPatternView.CellState>, - EmergencyButton.EmergencyButtonCallback { +public class KeyguardPatternView extends KeyguardInputView + implements AppearAnimationCreator<LockPatternView.CellState> { private static final String TAG = "SecurityPatternView"; private static final boolean DEBUG = KeyguardConstants.DEBUG; - // how long before we clear the wrong pattern - private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000; // how long we stay awake after each key beyond MIN_PATTERN_BEFORE_POKE_WAKELOCK private static final int UNLOCK_PATTERN_WAKE_INTERVAL_MS = 7000; - // how many cells the user has to cross before we poke the wakelock - private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2; - // How much we scale up the duration of the disappear animation when the current user is locked public static final float DISAPPEAR_MULTIPLIER_LOCKED = 1.5f; // Extra padding, in pixels, that should eat touch events. private static final int PATTERNS_TOUCH_AREA_EXTENSION = 40; - private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final AppearAnimationUtils mAppearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtilsLocked; @@ -78,11 +55,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit private final Rect mTempRect = new Rect(); private final Rect mLockPatternScreenBounds = new Rect(); - private CountDownTimer mCountdownTimer = null; - private LockPatternUtils mLockPatternUtils; - private AsyncTask<?, ?, ?> mPendingLockCheck; private LockPatternView mLockPatternView; - private KeyguardSecurityCallback mCallback; /** * Keeps track of the last time we poked the wake lock during dispatching of the touch event. @@ -92,26 +65,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit */ private long mLastPokeTime = -UNLOCK_PATTERN_WAKE_INTERVAL_MS; - /** - * Useful for clearing out the wrong pattern after a delay - */ - private Runnable mCancelPatternRunnable = new Runnable() { - @Override - public void run() { - mLockPatternView.clearPattern(); - } - }; - @VisibleForTesting KeyguardMessageArea mSecurityMessageDisplay; private View mEcaView; private ViewGroup mContainer; - private int mDisappearYTranslation; - - enum FooterMode { - Normal, - ForgotLockPattern, - VerifyUnlocked - } public KeyguardPatternView(Context context) { this(context, null); @@ -119,7 +75,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit public KeyguardPatternView(Context context, AttributeSet attrs) { super(context, attrs); - mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mAppearAnimationUtils = new AppearAnimationUtils(context, AppearAnimationUtils.DEFAULT_APPEAR_DURATION, 1.5f /* translationScale */, 2.0f /* delayScale */, AnimationUtils.loadInterpolator( @@ -132,50 +87,16 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit (long) (125 * DISAPPEAR_MULTIPLIER_LOCKED), 1.2f /* translationScale */, 0.6f /* delayScale */, AnimationUtils.loadInterpolator( mContext, android.R.interpolator.fast_out_linear_in)); - mDisappearYTranslation = getResources().getDimensionPixelSize( - R.dimen.disappear_y_translation); - } - - @Override - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - mCallback = callback; - } - - @Override - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; } @Override protected void onFinishInflate() { super.onFinishInflate(); - mLockPatternUtils = mLockPatternUtils == null - ? new LockPatternUtils(mContext) : mLockPatternUtils; mLockPatternView = findViewById(R.id.lockPatternView); - mLockPatternView.setSaveEnabled(false); - mLockPatternView.setOnPatternListener(new UnlockPatternListener()); - mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( - KeyguardUpdateMonitor.getCurrentUser())); - - // vibrate mode will be the same for the life of this screen - mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); mEcaView = findViewById(R.id.keyguard_selector_fade_container); mContainer = findViewById(R.id.container); - - EmergencyButton button = findViewById(R.id.emergency_call_button); - if (button != null) { - button.setCallback(this); - } - - View cancelBtn = findViewById(R.id.cancel_button); - if (cancelBtn != null) { - cancelBtn.setOnClickListener(view -> { - mCallback.reset(); - mCallback.onCancelClicked(); - }); - } } @Override @@ -185,11 +106,6 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } @Override - public void onEmergencyButtonClickedWhenInCall() { - mCallback.reset(); - } - - @Override public boolean onTouchEvent(MotionEvent ev) { boolean result = super.onTouchEvent(ev); // as long as the user is entering a pattern (i.e sending a touch event that was handled @@ -217,248 +133,11 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } @Override - public void reset() { - // reset lock pattern - mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( - KeyguardUpdateMonitor.getCurrentUser())); - mLockPatternView.enableInput(); - mLockPatternView.setEnabled(true); - mLockPatternView.clearPattern(); - - if (mSecurityMessageDisplay == null) { - return; - } - - // if the user is currently locked out, enforce it. - long deadline = mLockPatternUtils.getLockoutAttemptDeadline( - KeyguardUpdateMonitor.getCurrentUser()); - if (deadline != 0) { - handleAttemptLockout(deadline); - } else { - displayDefaultSecurityMessage(); - } - } - - private void displayDefaultSecurityMessage() { - if (mSecurityMessageDisplay != null) { - mSecurityMessageDisplay.setMessage(""); - } - } - - @Override - public void showUsabilityHint() { - } - - @Override - public boolean disallowInterceptTouch(MotionEvent event) { + boolean disallowInterceptTouch(MotionEvent event) { return !mLockPatternView.isEmpty() || mLockPatternScreenBounds.contains((int) event.getRawX(), (int) event.getRawY()); } - /** TODO: hook this up */ - public void cleanUp() { - if (DEBUG) Log.v(TAG, "Cleanup() called on " + this); - mLockPatternUtils = null; - mLockPatternView.setOnPatternListener(null); - } - - private class UnlockPatternListener implements LockPatternView.OnPatternListener { - - @Override - public void onPatternStart() { - mLockPatternView.removeCallbacks(mCancelPatternRunnable); - mSecurityMessageDisplay.setMessage(""); - } - - @Override - public void onPatternCleared() { - } - - @Override - public void onPatternCellAdded(List<LockPatternView.Cell> pattern) { - mCallback.userActivity(); - mCallback.onUserInput(); - } - - @Override - public void onPatternDetected(final List<LockPatternView.Cell> pattern) { - mKeyguardUpdateMonitor.setCredentialAttempted(); - mLockPatternView.disableInput(); - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - } - - final int userId = KeyguardUpdateMonitor.getCurrentUser(); - if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { - mLockPatternView.enableInput(); - onPatternChecked(userId, false, 0, false /* not valid - too short */); - return; - } - - if (LatencyTracker.isEnabled(mContext)) { - LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); - LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); - } - mPendingLockCheck = LockPatternChecker.checkCredential( - mLockPatternUtils, - LockscreenCredential.createPattern(pattern), - userId, - new LockPatternChecker.OnCheckCallback() { - - @Override - public void onEarlyMatched() { - if (LatencyTracker.isEnabled(mContext)) { - LatencyTracker.getInstance(mContext).onActionEnd( - ACTION_CHECK_CREDENTIAL); - } - onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */, - true /* isValidPattern */); - } - - @Override - public void onChecked(boolean matched, int timeoutMs) { - if (LatencyTracker.isEnabled(mContext)) { - LatencyTracker.getInstance(mContext).onActionEnd( - ACTION_CHECK_CREDENTIAL_UNLOCKED); - } - mLockPatternView.enableInput(); - mPendingLockCheck = null; - if (!matched) { - onPatternChecked(userId, false /* matched */, timeoutMs, - true /* isValidPattern */); - } - } - - @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); - } - } - }); - if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { - mCallback.userActivity(); - mCallback.onUserInput(); - } - } - - private void onPatternChecked(int userId, boolean matched, int timeoutMs, - boolean isValidPattern) { - boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; - if (matched) { - mCallback.reportUnlockAttempt(userId, true, 0); - if (dismissKeyguard) { - mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); - mCallback.dismiss(true, userId); - } - } else { - mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); - if (isValidPattern) { - mCallback.reportUnlockAttempt(userId, false, timeoutMs); - if (timeoutMs > 0) { - long deadline = mLockPatternUtils.setLockoutAttemptDeadline( - userId, timeoutMs); - handleAttemptLockout(deadline); - } - } - if (timeoutMs == 0) { - mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern); - mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); - } - } - } - } - - private void handleAttemptLockout(long elapsedRealtimeDeadline) { - mLockPatternView.clearPattern(); - mLockPatternView.setEnabled(false); - final long elapsedRealtime = SystemClock.elapsedRealtime(); - final long secondsInFuture = (long) Math.ceil( - (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); - mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { - - @Override - public void onTick(long millisUntilFinished) { - final 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() { - mLockPatternView.setEnabled(true); - displayDefaultSecurityMessage(); - } - - }.start(); - } - - @Override - public boolean needsInput() { - return false; - } - - @Override - public void onPause() { - if (mCountdownTimer != null) { - mCountdownTimer.cancel(); - mCountdownTimer = null; - } - if (mPendingLockCheck != null) { - mPendingLockCheck.cancel(false); - mPendingLockCheck = null; - } - displayDefaultSecurityMessage(); - } - - @Override - public void onResume(int reason) { - } - - @Override - public KeyguardSecurityCallback getCallback() { - return mCallback; - } - - @Override - public void showPromptReason(int reason) { - switch (reason) { - case PROMPT_REASON_RESTART: - mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_restart_pattern); - break; - case PROMPT_REASON_TIMEOUT: - mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern); - break; - case PROMPT_REASON_DEVICE_ADMIN: - mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_device_admin); - break; - case PROMPT_REASON_USER_REQUEST: - mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_user_request); - break; - case PROMPT_REASON_PREPARE_FOR_UPDATE: - mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern); - break; - case PROMPT_REASON_NONE: - break; - default: - mSecurityMessageDisplay.setMessage(R.string.kg_prompt_reason_timeout_pattern); - break; - } - } - - @Override - public void showMessage(CharSequence message, ColorStateList colorState) { - if (colorState != null) { - mSecurityMessageDisplay.setNextMessageColor(colorState); - } - mSecurityMessageDisplay.setMessage(message); - } - - @Override public void startAppearAnimation() { enableClipping(false); setAlpha(1f); @@ -467,12 +146,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit 0, mAppearAnimationUtils.getInterpolator()); mAppearAnimationUtils.startAnimation2d( mLockPatternView.getCellStates(), - new Runnable() { - @Override - public void run() { - enableClipping(true); - } - }, + () -> enableClipping(true), this); if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) { mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0, @@ -484,11 +158,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit } } - @Override - public boolean startDisappearAnimation(final Runnable finishRunnable) { - float durationMultiplier = mKeyguardUpdateMonitor.needsSlowUnlockTransition() - ? DISAPPEAR_MULTIPLIER_LOCKED - : 1f; + public boolean startDisappearAnimation(boolean needsSlowUnlockTransition, + final Runnable finishRunnable) { + float durationMultiplier = needsSlowUnlockTransition ? DISAPPEAR_MULTIPLIER_LOCKED : 1f; mLockPatternView.clearPattern(); enableClipping(false); setTranslationY(0); @@ -497,10 +169,8 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit -mDisappearAnimationUtils.getStartTranslation(), mDisappearAnimationUtils.getInterpolator()); - DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor - .needsSlowUnlockTransition() - ? mDisappearAnimationUtilsLocked - : mDisappearAnimationUtils; + DisappearAnimationUtils disappearAnimationUtils = needsSlowUnlockTransition + ? mDisappearAnimationUtilsLocked : mDisappearAnimationUtils; disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(), () -> { enableClipping(true); @@ -549,7 +219,7 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit @Override public CharSequence getTitle() { - return getContext().getString( + return getResources().getString( com.android.internal.R.string.keyguard_accessibility_pattern_unlock); } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java new file mode 100644 index 000000000000..3db9db7be00c --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java @@ -0,0 +1,349 @@ +/* + * 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 android.content.res.ColorStateList; +import android.os.AsyncTask; +import android.os.CountDownTimer; +import android.os.SystemClock; +import android.view.View; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternChecker; +import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockPatternView.Cell; +import com.android.internal.widget.LockscreenCredential; +import com.android.keyguard.EmergencyButton.EmergencyButtonCallback; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; + +import java.util.List; + +public class KeyguardPatternViewController + extends KeyguardInputViewController<KeyguardPatternView> { + + // how many cells the user has to cross before we poke the wakelock + private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2; + + // how long before we clear the wrong pattern + private static final int PATTERN_CLEAR_TIMEOUT_MS = 2000; + + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final LockPatternUtils mLockPatternUtils; + private final LatencyTracker mLatencyTracker; + private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory; + + private KeyguardMessageAreaController mMessageAreaController; + private LockPatternView mLockPatternView; + private CountDownTimer mCountdownTimer; + private AsyncTask<?, ?, ?> mPendingLockCheck; + + private EmergencyButtonCallback mEmergencyButtonCallback = new EmergencyButtonCallback() { + @Override + public void onEmergencyButtonClickedWhenInCall() { + getKeyguardSecurityCallback().reset(); + } + }; + + /** + * Useful for clearing out the wrong pattern after a delay + */ + private Runnable mCancelPatternRunnable = new Runnable() { + @Override + public void run() { + mLockPatternView.clearPattern(); + } + }; + + private class UnlockPatternListener implements LockPatternView.OnPatternListener { + + @Override + public void onPatternStart() { + mLockPatternView.removeCallbacks(mCancelPatternRunnable); + mMessageAreaController.setMessage(""); + } + + @Override + public void onPatternCleared() { + } + + @Override + public void onPatternCellAdded(List<Cell> pattern) { + getKeyguardSecurityCallback().userActivity(); + getKeyguardSecurityCallback().onUserInput(); + } + + @Override + public void onPatternDetected(final List<LockPatternView.Cell> pattern) { + mKeyguardUpdateMonitor.setCredentialAttempted(); + mLockPatternView.disableInput(); + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + } + + final int userId = KeyguardUpdateMonitor.getCurrentUser(); + if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) { + mLockPatternView.enableInput(); + onPatternChecked(userId, false, 0, false /* not valid - too short */); + return; + } + + mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL); + mLatencyTracker.onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); + mPendingLockCheck = LockPatternChecker.checkCredential( + mLockPatternUtils, + LockscreenCredential.createPattern(pattern), + userId, + new LockPatternChecker.OnCheckCallback() { + + @Override + public void onEarlyMatched() { + mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL); + onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */, + true /* isValidPattern */); + } + + @Override + public void onChecked(boolean matched, int timeoutMs) { + mLatencyTracker.onActionEnd(ACTION_CHECK_CREDENTIAL_UNLOCKED); + mLockPatternView.enableInput(); + mPendingLockCheck = null; + if (!matched) { + onPatternChecked(userId, false /* matched */, timeoutMs, + true /* isValidPattern */); + } + } + + @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); + } + }); + if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { + getKeyguardSecurityCallback().userActivity(); + getKeyguardSecurityCallback().onUserInput(); + } + } + + private void onPatternChecked(int userId, boolean matched, int timeoutMs, + boolean isValidPattern) { + boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId; + if (matched) { + getKeyguardSecurityCallback().reportUnlockAttempt(userId, true, 0); + if (dismissKeyguard) { + mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct); + getKeyguardSecurityCallback().dismiss(true, userId); + } + } else { + mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong); + if (isValidPattern) { + getKeyguardSecurityCallback().reportUnlockAttempt(userId, false, timeoutMs); + if (timeoutMs > 0) { + long deadline = mLockPatternUtils.setLockoutAttemptDeadline( + userId, timeoutMs); + handleAttemptLockout(deadline); + } + } + if (timeoutMs == 0) { + mMessageAreaController.setMessage(R.string.kg_wrong_pattern); + mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS); + } + } + } + } + + protected KeyguardPatternViewController(KeyguardPatternView view, + KeyguardUpdateMonitor keyguardUpdateMonitor, + SecurityMode securityMode, + LockPatternUtils lockPatternUtils, + KeyguardSecurityCallback keyguardSecurityCallback, + LatencyTracker latencyTracker, + KeyguardMessageAreaController.Factory messageAreaControllerFactory) { + super(view, securityMode, keyguardSecurityCallback); + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mLockPatternUtils = lockPatternUtils; + mLatencyTracker = latencyTracker; + mMessageAreaControllerFactory = messageAreaControllerFactory; + KeyguardMessageArea kma = KeyguardMessageArea.findSecurityMessageDisplay(mView); + mMessageAreaController = mMessageAreaControllerFactory.create(kma); + mLockPatternView = mView.findViewById(R.id.lockPatternView); + } + + @Override + public void init() { + super.init(); + mMessageAreaController.init(); + } + + @Override + protected void onViewAttached() { + mLockPatternView.setOnPatternListener(new UnlockPatternListener()); + mLockPatternView.setSaveEnabled(false); + mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( + KeyguardUpdateMonitor.getCurrentUser())); + // vibrate mode will be the same for the life of this screen + mLockPatternView.setTactileFeedbackEnabled(mLockPatternUtils.isTactileFeedbackEnabled()); + + EmergencyButton button = mView.findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(mEmergencyButtonCallback); + } + + View cancelBtn = mView.findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + getKeyguardSecurityCallback().reset(); + getKeyguardSecurityCallback().onCancelClicked(); + }); + } + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mLockPatternView.setOnPatternListener(null); + EmergencyButton button = mView.findViewById(R.id.emergency_call_button); + if (button != null) { + button.setCallback(null); + } + View cancelBtn = mView.findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(null); + } + } + + @Override + public void reset() { + // reset lock pattern + mLockPatternView.setInStealthMode(!mLockPatternUtils.isVisiblePatternEnabled( + KeyguardUpdateMonitor.getCurrentUser())); + mLockPatternView.enableInput(); + mLockPatternView.setEnabled(true); + mLockPatternView.clearPattern(); + + // if the user is currently locked out, enforce it. + long deadline = mLockPatternUtils.getLockoutAttemptDeadline( + KeyguardUpdateMonitor.getCurrentUser()); + if (deadline != 0) { + handleAttemptLockout(deadline); + } else { + displayDefaultSecurityMessage(); + } + } + + @Override + public void onPause() { + super.onPause(); + + if (mCountdownTimer != null) { + mCountdownTimer.cancel(); + mCountdownTimer = null; + } + + if (mPendingLockCheck != null) { + mPendingLockCheck.cancel(false); + mPendingLockCheck = null; + } + displayDefaultSecurityMessage(); + } + + @Override + public boolean needsInput() { + return false; + } + + @Override + public void showPromptReason(int reason) { + /// TODO: move all this logic into the MessageAreaController? + switch (reason) { + case PROMPT_REASON_RESTART: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern); + break; + case PROMPT_REASON_TIMEOUT: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; + case PROMPT_REASON_DEVICE_ADMIN: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin); + break; + case PROMPT_REASON_USER_REQUEST: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request); + break; + case PROMPT_REASON_PREPARE_FOR_UPDATE: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; + case PROMPT_REASON_NONE: + break; + default: + mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern); + break; + } + } + + @Override + public void showMessage(CharSequence message, ColorStateList colorState) { + if (colorState != null) { + mMessageAreaController.setNextMessageColor(colorState); + } + mMessageAreaController.setMessage(message); + } + + @Override + public void startAppearAnimation() { + super.startAppearAnimation(); + } + + @Override + public boolean startDisappearAnimation(Runnable finishRunnable) { + return mView.startDisappearAnimation( + mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable); + } + + private void displayDefaultSecurityMessage() { + mMessageAreaController.setMessage(""); + } + + private void handleAttemptLockout(long elapsedRealtimeDeadline) { + mLockPatternView.clearPattern(); + mLockPatternView.setEnabled(false); + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long secondsInFuture = (long) Math.ceil( + (elapsedRealtimeDeadline - elapsedRealtime) / 1000.0); + mCountdownTimer = new CountDownTimer(secondsInFuture * 1000, 1000) { + + @Override + public void onTick(long millisUntilFinished) { + final 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() { + mLockPatternView.setEnabled(true); + displayDefaultSecurityMessage(); + } + + }.start(); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index c7f27cf8a71a..7fa43116a7b1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -16,11 +16,17 @@ package com.android.keyguard; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_DEVICE_ADMIN; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_NONE; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_RESTART; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_TIMEOUT; +import static com.android.keyguard.KeyguardSecurityView.PROMPT_REASON_USER_REQUEST; + import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.KeyEvent; -import android.view.MotionEvent; import android.view.View; import com.android.internal.widget.LockscreenCredential; @@ -29,22 +35,12 @@ import com.android.systemui.R; /** * A Pin based Keyguard input view */ -public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView - implements View.OnKeyListener, View.OnTouchListener { +public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView { protected PasswordTextView mPasswordEntry; private View mOkButton; private View mDeleteButton; - private View mButton0; - private View mButton1; - private View mButton2; - private View mButton3; - private View mButton4; - private View mButton5; - private View mButton6; - private View mButton7; - private View mButton8; - private View mButton9; + private View[] mButtons = new View[10]; public KeyguardPinBasedInputView(Context context) { this(context, null); @@ -62,7 +58,6 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override protected void resetState() { - setPasswordEntryEnabled(true); } @Override @@ -86,10 +81,10 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (KeyEvent.isConfirmKey(keyCode)) { - performClick(mOkButton); + mOkButton.performClick(); return true; } else if (keyCode == KeyEvent.KEYCODE_DEL) { - performClick(mDeleteButton); + mDeleteButton.performClick(); return true; } if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { @@ -125,42 +120,9 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } } - private void performClick(View view) { - view.performClick(); - } - private void performNumberClick(int number) { - switch (number) { - case 0: - performClick(mButton0); - break; - case 1: - performClick(mButton1); - break; - case 2: - performClick(mButton2); - break; - case 3: - performClick(mButton3); - break; - case 4: - performClick(mButton4); - break; - case 5: - performClick(mButton5); - break; - case 6: - performClick(mButton6); - break; - case 7: - performClick(mButton7); - break; - case 8: - performClick(mButton8); - break; - case 9: - performClick(mButton9); - break; + if (number >= 0 && number <= 9) { + mButtons[number].performClick(); } } @@ -177,94 +139,31 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView @Override protected void onFinishInflate() { mPasswordEntry = findViewById(getPasswordTextViewId()); - mPasswordEntry.setOnKeyListener(this); // Set selected property on so the view can send accessibility events. mPasswordEntry.setSelected(true); - mPasswordEntry.setUserActivityListener(new PasswordTextView.UserActivityListener() { - @Override - public void onUserActivity() { - onUserInput(); - } - }); - mOkButton = findViewById(R.id.key_enter); - if (mOkButton != null) { - mOkButton.setOnTouchListener(this); - mOkButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mPasswordEntry.isEnabled()) { - verifyPasswordAndUnlock(); - } - } - }); - mOkButton.setOnHoverListener(new LiftToActivateListener(getContext())); - } mDeleteButton = findViewById(R.id.delete_button); mDeleteButton.setVisibility(View.VISIBLE); - mDeleteButton.setOnTouchListener(this); - mDeleteButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - // check for time-based lockouts - if (mPasswordEntry.isEnabled()) { - mPasswordEntry.deleteLastChar(); - } - } - }); - mDeleteButton.setOnLongClickListener(new View.OnLongClickListener() { - @Override - public boolean onLongClick(View v) { - // check for time-based lockouts - if (mPasswordEntry.isEnabled()) { - resetPasswordText(true /* animate */, true /* announce */); - } - doHapticKeyClick(); - return true; - } - }); - mButton0 = findViewById(R.id.key0); - mButton1 = findViewById(R.id.key1); - mButton2 = findViewById(R.id.key2); - mButton3 = findViewById(R.id.key3); - mButton4 = findViewById(R.id.key4); - mButton5 = findViewById(R.id.key5); - mButton6 = findViewById(R.id.key6); - mButton7 = findViewById(R.id.key7); - mButton8 = findViewById(R.id.key8); - mButton9 = findViewById(R.id.key9); + mButtons[0] = findViewById(R.id.key0); + mButtons[1] = findViewById(R.id.key1); + mButtons[2] = findViewById(R.id.key2); + mButtons[3] = findViewById(R.id.key3); + mButtons[4] = findViewById(R.id.key4); + mButtons[5] = findViewById(R.id.key5); + mButtons[6] = findViewById(R.id.key6); + mButtons[7] = findViewById(R.id.key7); + mButtons[8] = findViewById(R.id.key8); + mButtons[9] = findViewById(R.id.key9); mPasswordEntry.requestFocus(); super.onFinishInflate(); } @Override - public void onResume(int reason) { - super.onResume(reason); - mPasswordEntry.requestFocus(); - } - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { - doHapticKeyClick(); - } - return false; - } - - @Override - public boolean onKey(View v, int keyCode, KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN) { - return onKeyDown(keyCode, event); - } - return false; - } - - @Override public CharSequence getTitle() { return getContext().getString( com.android.internal.R.string.keyguard_accessibility_pin_unlock); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java new file mode 100644 index 000000000000..4d0ebfffbe04 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java @@ -0,0 +1,113 @@ +/* + * 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 android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnKeyListener; +import android.view.View.OnTouchListener; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; + +public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView> + extends KeyguardAbsKeyInputViewController<T> { + + private final LiftToActivateListener mLiftToActivateListener; + protected PasswordTextView mPasswordEntry; + + private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> { + if (event.getAction() == KeyEvent.ACTION_DOWN) { + return mView.onKeyDown(keyCode, event); + } + return false; + }; + + private final OnTouchListener mOnTouchListener = (v, event) -> { + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + mView.doHapticKeyClick(); + } + return false; + }; + + protected KeyguardPinBasedInputViewController(T view, + KeyguardUpdateMonitor keyguardUpdateMonitor, + SecurityMode securityMode, + LockPatternUtils lockPatternUtils, + KeyguardSecurityCallback keyguardSecurityCallback, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, + LatencyTracker latencyTracker, + LiftToActivateListener liftToActivateListener) { + super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, + messageAreaControllerFactory, latencyTracker); + mLiftToActivateListener = liftToActivateListener; + mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId()); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + + mPasswordEntry.setOnKeyListener(mOnKeyListener); + mPasswordEntry.setUserActivityListener(this::onUserInput); + + View deleteButton = mView.findViewById(R.id.delete_button); + deleteButton.setOnTouchListener(mOnTouchListener); + deleteButton.setOnClickListener(v -> { + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + mPasswordEntry.deleteLastChar(); + } + }); + deleteButton.setOnLongClickListener(v -> { + // check for time-based lockouts + if (mPasswordEntry.isEnabled()) { + mView.resetPasswordText(true /* animate */, true /* announce */); + } + mView.doHapticKeyClick(); + return true; + }); + + View okButton = mView.findViewById(R.id.key_enter); + if (okButton != null) { + okButton.setOnTouchListener(mOnTouchListener); + okButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (mPasswordEntry.isEnabled()) { + verifyPasswordAndUnlock(); + } + } + }); + okButton.setOnHoverListener(mLiftToActivateListener); + } + } + + @Override + public void onResume(int reason) { + super.onResume(reason); + mPasswordEntry.requestFocus(); + } + + @Override + void resetState() { + mView.setPasswordEntryEnabled(true); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java new file mode 100644 index 000000000000..6769436be8ef --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java @@ -0,0 +1,66 @@ +/* + * 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 android.view.View; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; + +public class KeyguardPinViewController + extends KeyguardPinBasedInputViewController<KeyguardPINView> { + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + + protected KeyguardPinViewController(KeyguardPINView view, + KeyguardUpdateMonitor keyguardUpdateMonitor, + SecurityMode securityMode, LockPatternUtils lockPatternUtils, + KeyguardSecurityCallback keyguardSecurityCallback, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, + LatencyTracker latencyTracker, + LiftToActivateListener liftToActivateListener) { + super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, + messageAreaControllerFactory, latencyTracker, liftToActivateListener); + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + + View cancelBtn = mView.findViewById(R.id.cancel_button); + if (cancelBtn != null) { + cancelBtn.setOnClickListener(view -> { + getKeyguardSecurityCallback().reset(); + getKeyguardSecurityCallback().onCancelClicked(); + }); + } + } + + @Override + void resetState() { + super.resetState(); + mMessageAreaController.setMessage(""); + } + + @Override + public boolean startDisappearAnimation(Runnable finishRunnable) { + return mView.startDisappearAnimation( + mKeyguardUpdateMonitor.needsSlowUnlockTransition(), finishRunnable); + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index 81d37a830f8f..b62ea6bc2ff6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -19,8 +19,6 @@ import static android.view.WindowInsets.Type.ime; import static android.view.WindowInsets.Type.systemBars; import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; -import static com.android.systemui.DejankUtils.whitelistIpcs; - import static java.lang.Integer.max; import android.animation.Animator; @@ -28,25 +26,14 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.app.Activity; import android.app.AlertDialog; -import android.app.admin.DevicePolicyManager; import android.content.Context; -import android.content.Intent; -import android.content.res.ColorStateList; import android.graphics.Insets; import android.graphics.Rect; -import android.metrics.LogMaker; -import android.os.Handler; -import android.os.Looper; -import android.os.UserHandle; import android.util.AttributeSet; -import android.util.Log; import android.util.MathUtils; -import android.util.Slog; import android.util.TypedValue; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.VelocityTracker; -import android.view.View; import android.view.ViewConfiguration; import android.view.WindowInsets; import android.view.WindowInsetsAnimation; @@ -61,42 +48,30 @@ import androidx.annotation.VisibleForTesting; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; -import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.UiEvent; import com.android.internal.logging.UiEventLogger; -import com.android.internal.logging.UiEventLoggerImpl; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; -import com.android.settingslib.utils.ThreadUtils; -import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; -import com.android.systemui.SystemUIFactory; -import com.android.systemui.shared.system.SysUiStatsLog; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.InjectionInflationController; import java.util.List; -public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView { - private static final boolean DEBUG = KeyguardConstants.DEBUG; - private static final String TAG = "KeyguardSecurityView"; - - private static final int USER_TYPE_PRIMARY = 1; - private static final int USER_TYPE_WORK_PROFILE = 2; - private static final int USER_TYPE_SECONDARY_USER = 3; +public class KeyguardSecurityContainer extends FrameLayout { + static final int USER_TYPE_PRIMARY = 1; + static final int USER_TYPE_WORK_PROFILE = 2; + static final int USER_TYPE_SECONDARY_USER = 3; // Bouncer is dismissed due to no security. - private static final int BOUNCER_DISMISS_NONE_SECURITY = 0; + static final int BOUNCER_DISMISS_NONE_SECURITY = 0; // Bouncer is dismissed due to pin, password or pattern entered. - private static final int BOUNCER_DISMISS_PASSWORD = 1; + static final int BOUNCER_DISMISS_PASSWORD = 1; // Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated. - private static final int BOUNCER_DISMISS_BIOMETRIC = 2; + static final int BOUNCER_DISMISS_BIOMETRIC = 2; // Bouncer is dismissed due to extended access granted. - private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3; + static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3; // Bouncer is dismissed due to sim card unlock code entered. - private static final int BOUNCER_DISMISS_SIM = 4; + static final int BOUNCER_DISMISS_SIM = 4; // Make the view move slower than the finger, as if the spring were applying force. private static final float TOUCH_Y_MULTIPLIER = 0.25f; @@ -105,36 +80,23 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // How much to scale the default slop by, to avoid accidental drags. private static final float SLOP_SCALE = 4f; - private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl(); - private static final long IME_DISAPPEAR_DURATION_MS = 125; - private KeyguardSecurityModel mSecurityModel; - private LockPatternUtils mLockPatternUtils; - @VisibleForTesting KeyguardSecurityViewFlipper mSecurityViewFlipper; - private boolean mIsVerifyUnlockOnly; - private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid; - private KeyguardSecurityView mCurrentSecurityView; - private SecurityCallback mSecurityCallback; private AlertDialog mAlertDialog; - private InjectionInflationController mInjectionInflationController; private boolean mSwipeUpToRetry; - private AdminSecondaryLockScreenController mSecondaryLockScreenController; private final ViewConfiguration mViewConfiguration; private final SpringAnimation mSpringAnimation; private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); - private final KeyguardUpdateMonitor mUpdateMonitor; - private final KeyguardStateController mKeyguardStateController; - private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); private float mLastTouchY = -1; private int mActivePointerId = -1; private boolean mIsDragging; private float mStartTouchY = -1; private boolean mDisappearAnimRunning; + private SwipeListener mSwipeListener; private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { @@ -186,19 +148,22 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe // Used to notify the container when something interesting happens. public interface SecurityCallback { - public boolean dismiss(boolean authenticated, int targetUserId, - boolean bypassSecondaryLockScreen); - public void userActivity(); - public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); + boolean dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen); + void userActivity(); + void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); /** * @param strongAuth wheher the user has authenticated with strong authentication like * pattern, password or PIN but not by trust agents or fingerprint * @param targetUserId a user that needs to be the foreground user at the finish completion. */ - public void finish(boolean strongAuth, int targetUserId); - public void reset(); - public void onCancelClicked(); + void finish(boolean strongAuth, int targetUserId); + void reset(); + void onCancelClicked(); + } + + public interface SwipeListener { + void onSwipeUp(); } @VisibleForTesting @@ -249,52 +214,24 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - mSecurityModel = Dependency.get(KeyguardSecurityModel.class); - mLockPatternUtils = new LockPatternUtils(context); - mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); - mInjectionInflationController = new InjectionInflationController( - SystemUIFactory.getInstance().getSysUIComponent().createViewInstanceCreatorFactory()); mViewConfiguration = ViewConfiguration.get(context); - mKeyguardStateController = Dependency.get(KeyguardStateController.class); - mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this, - mUpdateMonitor, mCallback, new Handler(Looper.myLooper())); - } - - public void setSecurityCallback(SecurityCallback callback) { - mSecurityCallback = callback; } - @Override - public void onResume(int reason) { - if (mCurrentSecuritySelection != SecurityMode.None) { - getSecurityView(mCurrentSecuritySelection).onResume(reason); - } + void onResume(SecurityMode securityMode, boolean faceAuthEnabled) { mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback); - updateBiometricRetry(); + updateBiometricRetry(securityMode, faceAuthEnabled); } - @Override public void onPause() { if (mAlertDialog != null) { mAlertDialog.dismiss(); mAlertDialog = null; } - mSecondaryLockScreenController.hide(); - if (mCurrentSecuritySelection != SecurityMode.None) { - getSecurityView(mCurrentSecuritySelection).onPause(); - } mSecurityViewFlipper.setWindowInsetsAnimationCallback(null); } @Override - public void onStartingToHide() { - if (mCurrentSecuritySelection != SecurityMode.None) { - getSecurityView(mCurrentSecuritySelection).onStartingToHide(); - } - } - - @Override public boolean shouldDelayChildPressedState() { return true; } @@ -316,13 +253,12 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe return false; } // Avoid dragging the pattern view - if (mCurrentSecurityView.disallowInterceptTouch(event)) { + if (mSecurityViewFlipper.getSecurityView().disallowInterceptTouch(event)) { return false; } int index = event.findPointerIndex(mActivePointerId); float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE; - if (mCurrentSecurityView != null && index != -1 - && mStartTouchY - event.getY(index) > touchSlop) { + if (index != -1 && mStartTouchY - event.getY(index) > touchSlop) { mIsDragging = true; return true; } @@ -370,31 +306,28 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } if (action == MotionEvent.ACTION_UP) { if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, - MIN_DRAG_SIZE, getResources().getDisplayMetrics()) - && !mUpdateMonitor.isFaceDetectionRunning()) { - mUpdateMonitor.requestFaceAuth(); - mCallback.userActivity(); - showMessage(null, null); + MIN_DRAG_SIZE, getResources().getDisplayMetrics())) { + if (mSwipeListener != null) { + mSwipeListener.onSwipeUp(); + } } } return true; } + void setSwipeListener(SwipeListener swipeListener) { + mSwipeListener = swipeListener; + } + private void startSpringAnimation(float startVelocity) { mSpringAnimation .setStartVelocity(startVelocity) .animateToFinalPosition(0); } - public void startAppearAnimation() { - if (mCurrentSecuritySelection != SecurityMode.None) { - getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); - } - } - - public boolean startDisappearAnimation(Runnable onFinishRunnable) { + public void startDisappearAnimation(SecurityMode securitySelection) { mDisappearAnimRunning = true; - if (mCurrentSecuritySelection == SecurityMode.Password) { + if (securitySelection == SecurityMode.Password) { mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(), IME_DISAPPEAR_DURATION_MS, Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() { @@ -439,19 +372,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe } }); } - if (mCurrentSecuritySelection != SecurityMode.None) { - return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation( - onFinishRunnable); - } - return false; } /** * Enables/disables swipe up to retry on the bouncer. */ - private void updateBiometricRetry() { - SecurityMode securityMode = getSecurityMode(); - mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled() + private void updateBiometricRetry(SecurityMode securityMode, boolean faceAuthEnabled) { + mSwipeUpToRetry = faceAuthEnabled && securityMode != SecurityMode.SimPin && securityMode != SecurityMode.SimPuk && securityMode != SecurityMode.None; @@ -461,53 +388,11 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe return mSecurityViewFlipper.getTitle(); } - @VisibleForTesting - protected KeyguardSecurityView getSecurityView(SecurityMode securityMode) { - final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); - KeyguardSecurityView view = null; - final int children = mSecurityViewFlipper.getChildCount(); - for (int child = 0; child < children; child++) { - if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) { - view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child)); - break; - } - } - int layoutId = getLayoutIdFor(securityMode); - if (view == null && layoutId != 0) { - final LayoutInflater inflater = LayoutInflater.from(mContext); - if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); - View v = mInjectionInflationController.injectable(inflater) - .inflate(layoutId, mSecurityViewFlipper, false); - mSecurityViewFlipper.addView(v); - updateSecurityView(v); - view = (KeyguardSecurityView)v; - view.reset(); - } - - return view; - } - - private void updateSecurityView(View view) { - if (view instanceof KeyguardSecurityView) { - KeyguardSecurityView ksv = (KeyguardSecurityView) view; - ksv.setKeyguardCallback(mCallback); - ksv.setLockPatternUtils(mLockPatternUtils); - } else { - Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); - } - } @Override public void onFinishInflate() { super.onFinishInflate(); mSecurityViewFlipper = findViewById(R.id.view_flipper); - mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); - } - - public void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; - mSecurityModel.setLockPatternUtils(utils); - mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); } @Override @@ -539,11 +424,12 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe mAlertDialog.show(); } - private void showTimeoutDialog(int userId, int timeoutMs) { - int timeoutInSeconds = (int) timeoutMs / 1000; + void showTimeoutDialog(int userId, int timeoutMs, LockPatternUtils lockPatternUtils, + SecurityMode securityMode) { + int timeoutInSeconds = timeoutMs / 1000; int messageId = 0; - switch (mSecurityModel.getSecurityMode(userId)) { + switch (securityMode) { case Pattern: messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; break; @@ -563,13 +449,13 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe if (messageId != 0) { final String message = mContext.getString(messageId, - mLockPatternUtils.getCurrentFailedPasswordAttempts(userId), + lockPatternUtils.getCurrentFailedPasswordAttempts(userId), timeoutInSeconds); showDialog(null, message); } } - private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { + void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { String message = null; switch (userType) { case USER_TYPE_PRIMARY: @@ -588,7 +474,7 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe showDialog(null, message); } - private void showWipeDialog(int attempts, int userType) { + void showWipeDialog(int attempts, int userType) { String message = null; switch (userType) { case USER_TYPE_PRIMARY: @@ -607,358 +493,8 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe showDialog(null, message); } - private void reportFailedUnlockAttempt(int userId, int timeoutMs) { - // +1 for this time - final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1; - - if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); - - final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); - final int failedAttemptsBeforeWipe = - dpm.getMaximumFailedPasswordsForWipe(null, userId); - - final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? - (failedAttemptsBeforeWipe - failedAttempts) - : Integer.MAX_VALUE; // because DPM returns 0 if no restriction - if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { - // The user has installed a DevicePolicyManager that requests a user/profile to be wiped - // N attempts. Once we get below the grace period, we post this dialog every time as a - // clear warning until the deletion fires. - // Check which profile has the strictest policy for failed password attempts - final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); - int userType = USER_TYPE_PRIMARY; - if (expiringUser == userId) { - // TODO: http://b/23522538 - if (expiringUser != UserHandle.USER_SYSTEM) { - userType = USER_TYPE_SECONDARY_USER; - } - } else if (expiringUser != UserHandle.USER_NULL) { - userType = USER_TYPE_WORK_PROFILE; - } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY - if (remainingBeforeWipe > 0) { - showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); - } else { - // Too many attempts. The device will be wiped shortly. - Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); - showWipeDialog(failedAttempts, userType); - } - } - mLockPatternUtils.reportFailedPasswordAttempt(userId); - if (timeoutMs > 0) { - mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); - showTimeoutDialog(userId, timeoutMs); - } - } - - /** - * Shows the primary security screen for the user. This will be either the multi-selector - * or the user's security method. - * @param turningOff true if the device is being turned off - */ - void showPrimarySecurityScreen(boolean turningOff) { - SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode( - KeyguardUpdateMonitor.getCurrentUser())); - if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); - showSecurityScreen(securityMode); - } - - /** - * Shows the next security screen if there is one. - * @param authenticated true if the user entered the correct authentication - * @param targetUserId a user that needs to be the foreground user at the finish (if called) - * completion. - * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary - * secondary lock screen requirement, if any. - * @return true if keyguard is done - */ - boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, - boolean bypassSecondaryLockScreen) { - if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); - boolean finish = false; - boolean strongAuth = false; - int eventSubtype = -1; - BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN; - if (mUpdateMonitor.getUserHasTrust(targetUserId)) { - finish = true; - eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS; - } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) { - finish = true; - eventSubtype = BOUNCER_DISMISS_BIOMETRIC; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC; - } else if (SecurityMode.None == mCurrentSecuritySelection) { - SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (SecurityMode.None == securityMode) { - finish = true; // no security required - eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY; - } else { - showSecurityScreen(securityMode); // switch to the alternate security view - } - } else if (authenticated) { - switch (mCurrentSecuritySelection) { - case Pattern: - case Password: - case PIN: - strongAuth = true; - finish = true; - eventSubtype = BOUNCER_DISMISS_PASSWORD; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD; - break; - - case SimPin: - case SimPuk: - // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home - SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); - if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( - KeyguardUpdateMonitor.getCurrentUser())) { - finish = true; - eventSubtype = BOUNCER_DISMISS_SIM; - uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; - } else { - showSecurityScreen(securityMode); - } - break; - - default: - Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe"); - showPrimarySecurityScreen(false); - break; - } - } - // Check for device admin specified additional security measures. - if (finish && !bypassSecondaryLockScreen) { - Intent secondaryLockscreenIntent = - mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId); - if (secondaryLockscreenIntent != null) { - mSecondaryLockScreenController.show(secondaryLockscreenIntent); - return false; - } - } - if (eventSubtype != -1) { - mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) - .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); - } - if (uiEvent != BouncerUiEvent.UNKNOWN) { - sUiEventLogger.log(uiEvent); - } - if (finish) { - mSecurityCallback.finish(strongAuth, targetUserId); - } - return finish; - } - - /** - * Switches to the given security view unless it's already being shown, in which case - * this is a no-op. - * - * @param securityMode - */ - private void showSecurityScreen(SecurityMode securityMode) { - if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); - - if (securityMode == mCurrentSecuritySelection) return; - - KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); - KeyguardSecurityView newView = getSecurityView(securityMode); - - // Emulate Activity life cycle - if (oldView != null) { - oldView.onPause(); - oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view - } - if (securityMode != SecurityMode.None) { - newView.onResume(KeyguardSecurityView.VIEW_REVEALED); - newView.setKeyguardCallback(mCallback); - } - - // Find and show this child. - final int childCount = mSecurityViewFlipper.getChildCount(); - - final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); - for (int i = 0; i < childCount; i++) { - if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) { - mSecurityViewFlipper.setDisplayedChild(i); - break; - } - } - - mCurrentSecuritySelection = securityMode; - mCurrentSecurityView = newView; - mSecurityCallback.onSecurityModeChanged(securityMode, - securityMode != SecurityMode.None && newView.needsInput()); - } - - private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { - public void userActivity() { - if (mSecurityCallback != null) { - mSecurityCallback.userActivity(); - } - } - - @Override - public void onUserInput() { - mUpdateMonitor.cancelFaceAuth(); - } - - @Override - public void dismiss(boolean authenticated, int targetId) { - dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); - } - - @Override - public void dismiss(boolean authenticated, int targetId, - boolean bypassSecondaryLockScreen) { - mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); - } - - public boolean isVerifyUnlockOnly() { - return mIsVerifyUnlockOnly; - } - - public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { - if (success) { - SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, - SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); - mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); - // Force a garbage collection in an attempt to erase any lockscreen password left in - // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard - // dismiss animation janky. - ThreadUtils.postOnBackgroundThread(() -> { - try { - Thread.sleep(5000); - } catch (InterruptedException ignored) { } - Runtime.getRuntime().gc(); - }); - } else { - SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, - SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); - KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs); - } - mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) - .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE)); - sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS - : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE); - } - - public void reset() { - mSecurityCallback.reset(); - } - - public void onCancelClicked() { - mSecurityCallback.onCancelClicked(); - } - }; - - // The following is used to ignore callbacks from SecurityViews that are no longer current - // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the - // state for the current security method. - private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { - @Override - public void userActivity() { } - @Override - public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } - @Override - public boolean isVerifyUnlockOnly() { return false; } - @Override - public void dismiss(boolean securityVerified, int targetUserId) { } - @Override - public void dismiss(boolean authenticated, int targetId, - boolean bypassSecondaryLockScreen) { } - @Override - public void onUserInput() { } - @Override - public void reset() {} - }; - - private int getSecurityViewIdForMode(SecurityMode securityMode) { - switch (securityMode) { - case Pattern: return R.id.keyguard_pattern_view; - case PIN: return R.id.keyguard_pin_view; - case Password: return R.id.keyguard_password_view; - case SimPin: return R.id.keyguard_sim_pin_view; - case SimPuk: return R.id.keyguard_sim_puk_view; - } - return 0; - } - - @VisibleForTesting - public int getLayoutIdFor(SecurityMode securityMode) { - switch (securityMode) { - case Pattern: return R.layout.keyguard_pattern_view; - case PIN: return R.layout.keyguard_pin_view; - case Password: return R.layout.keyguard_password_view; - case SimPin: return R.layout.keyguard_sim_pin_view; - case SimPuk: return R.layout.keyguard_sim_puk_view; - default: - return 0; - } - } - - public SecurityMode getSecurityMode() { - return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()); - } - - public SecurityMode getCurrentSecurityMode() { - return mCurrentSecuritySelection; - } - - public KeyguardSecurityView getCurrentSecurityView() { - return mCurrentSecurityView; - } - - public void verifyUnlock() { - mIsVerifyUnlockOnly = true; - showSecurityScreen(getSecurityMode()); - } - - public SecurityMode getCurrentSecuritySelection() { - return mCurrentSecuritySelection; - } - - public void dismiss(boolean authenticated, int targetUserId) { - mCallback.dismiss(authenticated, targetUserId); - } - - public boolean needsInput() { - return mSecurityViewFlipper.needsInput(); - } - - @Override - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - mSecurityViewFlipper.setKeyguardCallback(callback); - } - - @Override public void reset() { - mSecurityViewFlipper.reset(); mDisappearAnimRunning = false; } - - @Override - public KeyguardSecurityCallback getCallback() { - return mSecurityViewFlipper.getCallback(); - } - - @Override - public void showPromptReason(int reason) { - if (mCurrentSecuritySelection != SecurityMode.None) { - if (reason != PROMPT_REASON_NONE) { - Log.i(TAG, "Strong auth required, reason: " + reason); - } - getSecurityView(mCurrentSecuritySelection).showPromptReason(reason); - } - } - - public void showMessage(CharSequence message, ColorStateList colorState) { - if (mCurrentSecuritySelection != SecurityMode.None) { - getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState); - } - } - - @Override - public void showUsabilityHint() { - mSecurityViewFlipper.showUsabilityHint(); - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java index 17f25bd08ef4..1c23605a8516 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java @@ -16,33 +16,167 @@ package com.android.keyguard; +import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_BIOMETRIC; +import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_EXTENDED_ACCESS; +import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_NONE_SECURITY; +import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_PASSWORD; +import static com.android.keyguard.KeyguardSecurityContainer.BOUNCER_DISMISS_SIM; +import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_PRIMARY; +import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_SECONDARY_USER; +import static com.android.keyguard.KeyguardSecurityContainer.USER_TYPE_WORK_PROFILE; +import static com.android.systemui.DejankUtils.whitelistIpcs; + +import android.app.admin.DevicePolicyManager; +import android.content.Intent; import android.content.res.ColorStateList; +import android.metrics.LogMaker; +import android.os.UserHandle; +import android.util.Log; +import android.util.Slog; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityContainer.BouncerUiEvent; import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback; +import com.android.keyguard.KeyguardSecurityContainer.SwipeListener; import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.keyguard.dagger.KeyguardBouncerScope; +import com.android.settingslib.utils.ThreadUtils; +import com.android.systemui.shared.system.SysUiStatsLog; +import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.util.ViewController; import javax.inject.Inject; /** Controller for {@link KeyguardSecurityContainer} */ -public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> { +@KeyguardBouncerScope +public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> + implements KeyguardSecurityView { + + private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final String TAG = "KeyguardSecurityView"; + private final AdminSecondaryLockScreenController mAdminSecondaryLockScreenController; private final LockPatternUtils mLockPatternUtils; - private final KeyguardSecurityViewController.Factory mKeyguardSecurityViewControllerFactory; + private final KeyguardUpdateMonitor mUpdateMonitor; + private final KeyguardSecurityModel mSecurityModel; + private final MetricsLogger mMetricsLogger; + private final UiEventLogger mUiEventLogger; + private final KeyguardStateController mKeyguardStateController; + private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; + private final SecurityCallback mSecurityCallback; + + private SecurityMode mCurrentSecurityMode = SecurityMode.Invalid; + + private KeyguardSecurityCallback mKeyguardSecurityCallback = new KeyguardSecurityCallback() { + public void userActivity() { + if (mSecurityCallback != null) { + mSecurityCallback.userActivity(); + } + } + + @Override + public void onUserInput() { + mUpdateMonitor.cancelFaceAuth(); + } + + @Override + public void dismiss(boolean authenticated, int targetId) { + dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); + } + + @Override + public void dismiss(boolean authenticated, int targetId, + boolean bypassSecondaryLockScreen) { + mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); + } + + public boolean isVerifyUnlockOnly() { + return false; + } + + public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { + if (success) { + SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, + SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); + mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); + // Force a garbage collection in an attempt to erase any lockscreen password left in + // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard + // dismiss animation janky. + ThreadUtils.postOnBackgroundThread(() -> { + try { + Thread.sleep(5000); + } catch (InterruptedException ignored) { } + Runtime.getRuntime().gc(); + }); + } else { + SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, + SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); + reportFailedUnlockAttempt(userId, timeoutMs); + } + mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) + .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE)); + mUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS + : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE); + } + + public void reset() { + mSecurityCallback.reset(); + } + + public void onCancelClicked() { + mSecurityCallback.onCancelClicked(); + } + }; + - @Inject - KeyguardSecurityContainerController(KeyguardSecurityContainer view, + private SwipeListener mSwipeListener = new SwipeListener() { + @Override + public void onSwipeUp() { + if (!mUpdateMonitor.isFaceDetectionRunning()) { + mUpdateMonitor.requestFaceAuth(); + mKeyguardSecurityCallback.userActivity(); + showMessage(null, null); + } + } + }; + + private KeyguardSecurityContainerController(KeyguardSecurityContainer view, + AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory, LockPatternUtils lockPatternUtils, - KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) { + KeyguardUpdateMonitor keyguardUpdateMonitor, + KeyguardSecurityModel keyguardSecurityModel, + MetricsLogger metricsLogger, + UiEventLogger uiEventLogger, + KeyguardStateController keyguardStateController, + SecurityCallback securityCallback, + KeyguardSecurityViewFlipperController securityViewFlipperController) { super(view); mLockPatternUtils = lockPatternUtils; - view.setLockPatternUtils(mLockPatternUtils); - mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory; + mUpdateMonitor = keyguardUpdateMonitor; + mSecurityModel = keyguardSecurityModel; + mMetricsLogger = metricsLogger; + mUiEventLogger = uiEventLogger; + mKeyguardStateController = keyguardStateController; + mSecurityCallback = securityCallback; + mSecurityViewFlipperController = securityViewFlipperController; + mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create( + mKeyguardSecurityCallback); + } + + @Override + public void init() { + super.init(); + mSecurityViewFlipperController.init(); } @Override protected void onViewAttached() { + mView.setSwipeListener(mSwipeListener); } @Override @@ -51,68 +185,311 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard /** */ public void onPause() { + mAdminSecondaryLockScreenController.hide(); + if (mCurrentSecurityMode != SecurityMode.None) { + getCurrentSecurityController().onPause(); + } mView.onPause(); } + + /** + * Shows the primary security screen for the user. This will be either the multi-selector + * or the user's security method. + * @param turningOff true if the device is being turned off + */ public void showPrimarySecurityScreen(boolean turningOff) { - mView.showPrimarySecurityScreen(turningOff); + SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser())); + if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); + showSecurityScreen(securityMode); } + @Override public void showPromptReason(int reason) { - mView.showPromptReason(reason); + if (mCurrentSecurityMode != SecurityMode.None) { + if (reason != PROMPT_REASON_NONE) { + Log.i(TAG, "Strong auth required, reason: " + reason); + } + getCurrentSecurityController().showPromptReason(reason); + } } public void showMessage(CharSequence message, ColorStateList colorState) { - mView.showMessage(message, colorState); + if (mCurrentSecurityMode != SecurityMode.None) { + getCurrentSecurityController().showMessage(message, colorState); + } } - public SecurityMode getCurrentSecuritySelection() { - return mView.getCurrentSecuritySelection(); + public SecurityMode getCurrentSecurityMode() { + return mCurrentSecurityMode; } public void dismiss(boolean authenticated, int targetUserId) { - mView.dismiss(authenticated, targetUserId); + mKeyguardSecurityCallback.dismiss(authenticated, targetUserId); } public void reset() { mView.reset(); + mSecurityViewFlipperController.reset(); } public CharSequence getTitle() { return mView.getTitle(); } - public void onResume(int screenOn) { - mView.onResume(screenOn); + @Override + public void onResume(int reason) { + if (mCurrentSecurityMode != SecurityMode.None) { + getCurrentSecurityController().onResume(reason); + } + mView.onResume( + mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()), + mKeyguardStateController.isFaceAuthEnabled()); } public void startAppearAnimation() { - mView.startAppearAnimation(); + if (mCurrentSecurityMode != SecurityMode.None) { + getCurrentSecurityController().startAppearAnimation(); + } } public boolean startDisappearAnimation(Runnable onFinishRunnable) { - return mView.startDisappearAnimation(onFinishRunnable); - } + mView.startDisappearAnimation(getCurrentSecurityMode()); - public void onStartingToHide() { - mView.onStartingToHide(); + if (mCurrentSecurityMode != SecurityMode.None) { + return getCurrentSecurityController().startDisappearAnimation(onFinishRunnable); + } + + return false; } - public void setSecurityCallback(SecurityCallback securityCallback) { - mView.setSecurityCallback(securityCallback); + public void onStartingToHide() { + if (mCurrentSecurityMode != SecurityMode.None) { + getCurrentSecurityController().onStartingToHide(); + } } + /** + * Shows the next security screen if there is one. + * @param authenticated true if the user entered the correct authentication + * @param targetUserId a user that needs to be the foreground user at the finish (if called) + * completion. + * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary + * secondary lock screen requirement, if any. + * @return true if keyguard is done + */ public boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen) { - return mView.showNextSecurityScreenOrFinish( - authenticated, targetUserId, bypassSecondaryLockScreen); + + if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); + boolean finish = false; + boolean strongAuth = false; + int eventSubtype = -1; + BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN; + if (mUpdateMonitor.getUserHasTrust(targetUserId)) { + finish = true; + eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS; + } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) { + finish = true; + eventSubtype = BOUNCER_DISMISS_BIOMETRIC; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC; + } else if (SecurityMode.None == getCurrentSecurityMode()) { + SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); + if (SecurityMode.None == securityMode) { + finish = true; // no security required + eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY; + } else { + showSecurityScreen(securityMode); // switch to the alternate security view + } + } else if (authenticated) { + switch (getCurrentSecurityMode()) { + case Pattern: + case Password: + case PIN: + strongAuth = true; + finish = true; + eventSubtype = BOUNCER_DISMISS_PASSWORD; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD; + break; + + case SimPin: + case SimPuk: + // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home + SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); + if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( + KeyguardUpdateMonitor.getCurrentUser())) { + finish = true; + eventSubtype = BOUNCER_DISMISS_SIM; + uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; + } else { + showSecurityScreen(securityMode); + } + break; + + default: + Log.v(TAG, "Bad security screen " + getCurrentSecurityMode() + + ", fail safe"); + showPrimarySecurityScreen(false); + break; + } + } + // Check for device admin specified additional security measures. + if (finish && !bypassSecondaryLockScreen) { + Intent secondaryLockscreenIntent = + mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId); + if (secondaryLockscreenIntent != null) { + mAdminSecondaryLockScreenController.show(secondaryLockscreenIntent); + return false; + } + } + if (eventSubtype != -1) { + mMetricsLogger.write(new LogMaker(MetricsProto.MetricsEvent.BOUNCER) + .setType(MetricsProto.MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); + } + if (uiEvent != BouncerUiEvent.UNKNOWN) { + mUiEventLogger.log(uiEvent); + } + if (finish) { + mSecurityCallback.finish(strongAuth, targetUserId); + } + return finish; } public boolean needsInput() { - return mView.needsInput(); + return getCurrentSecurityController().needsInput(); } - public SecurityMode getCurrentSecurityMode() { - return mView.getCurrentSecurityMode(); + /** + * Switches to the given security view unless it's already being shown, in which case + * this is a no-op. + * + * @param securityMode + */ + @VisibleForTesting + void showSecurityScreen(SecurityMode securityMode) { + if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); + + if (securityMode == SecurityMode.Invalid || securityMode == mCurrentSecurityMode) { + return; + } + + KeyguardInputViewController<KeyguardInputView> oldView = getCurrentSecurityController(); + + // Emulate Activity life cycle + if (oldView != null) { + oldView.onPause(); + } + + KeyguardInputViewController<KeyguardInputView> newView = changeSecurityMode(securityMode); + if (newView != null) { + newView.onResume(KeyguardSecurityView.VIEW_REVEALED); + mSecurityViewFlipperController.show(newView); + } + + mSecurityCallback.onSecurityModeChanged( + securityMode, newView != null && newView.needsInput()); + } + + public void reportFailedUnlockAttempt(int userId, int timeoutMs) { + // +1 for this time + final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1; + + if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); + + final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); + final int failedAttemptsBeforeWipe = + dpm.getMaximumFailedPasswordsForWipe(null, userId); + + final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 + ? (failedAttemptsBeforeWipe - failedAttempts) + : Integer.MAX_VALUE; // because DPM returns 0 if no restriction + if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { + // The user has installed a DevicePolicyManager that requests a user/profile to be wiped + // N attempts. Once we get below the grace period, we post this dialog every time as a + // clear warning until the deletion fires. + // Check which profile has the strictest policy for failed password attempts + final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); + int userType = USER_TYPE_PRIMARY; + if (expiringUser == userId) { + // TODO: http://b/23522538 + if (expiringUser != UserHandle.USER_SYSTEM) { + userType = USER_TYPE_SECONDARY_USER; + } + } else if (expiringUser != UserHandle.USER_NULL) { + userType = USER_TYPE_WORK_PROFILE; + } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY + if (remainingBeforeWipe > 0) { + mView.showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); + } else { + // Too many attempts. The device will be wiped shortly. + Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); + mView.showWipeDialog(failedAttempts, userType); + } + } + mLockPatternUtils.reportFailedPasswordAttempt(userId); + if (timeoutMs > 0) { + mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); + mView.showTimeoutDialog(userId, timeoutMs, mLockPatternUtils, + mSecurityModel.getSecurityMode(userId)); + } + } + + private KeyguardInputViewController<KeyguardInputView> getCurrentSecurityController() { + return mSecurityViewFlipperController + .getSecurityView(mCurrentSecurityMode, mKeyguardSecurityCallback); + } + + private KeyguardInputViewController<KeyguardInputView> changeSecurityMode( + SecurityMode securityMode) { + mCurrentSecurityMode = securityMode; + return getCurrentSecurityController(); + } + + static class Factory { + + private final KeyguardSecurityContainer mView; + private final AdminSecondaryLockScreenController.Factory + mAdminSecondaryLockScreenControllerFactory; + private final LockPatternUtils mLockPatternUtils; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final KeyguardSecurityModel mKeyguardSecurityModel; + private final MetricsLogger mMetricsLogger; + private final UiEventLogger mUiEventLogger; + private final KeyguardStateController mKeyguardStateController; + private final KeyguardSecurityViewFlipperController mSecurityViewFlipperController; + + @Inject + Factory(KeyguardSecurityContainer view, + AdminSecondaryLockScreenController.Factory + adminSecondaryLockScreenControllerFactory, + LockPatternUtils lockPatternUtils, + KeyguardUpdateMonitor keyguardUpdateMonitor, + KeyguardSecurityModel keyguardSecurityModel, + MetricsLogger metricsLogger, + UiEventLogger uiEventLogger, + KeyguardStateController keyguardStateController, + KeyguardSecurityViewFlipperController securityViewFlipperController) { + mView = view; + mAdminSecondaryLockScreenControllerFactory = adminSecondaryLockScreenControllerFactory; + mLockPatternUtils = lockPatternUtils; + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mKeyguardSecurityModel = keyguardSecurityModel; + mMetricsLogger = metricsLogger; + mUiEventLogger = uiEventLogger; + mKeyguardStateController = keyguardStateController; + mSecurityViewFlipperController = securityViewFlipperController; + } + + public KeyguardSecurityContainerController create( + SecurityCallback securityCallback) { + return new KeyguardSecurityContainerController(mView, + mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, + mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, + mKeyguardStateController, securityCallback, mSecurityViewFlipperController); + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java index ac2160ecb4ae..c77c86711abf 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityModel.java @@ -18,13 +18,14 @@ package com.android.keyguard; import static com.android.systemui.DejankUtils.whitelistIpcs; import android.app.admin.DevicePolicyManager; -import android.content.Context; +import android.content.res.Resources; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import com.android.internal.widget.LockPatternUtils; import com.android.systemui.Dependency; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; import javax.inject.Inject; @@ -33,7 +34,7 @@ public class KeyguardSecurityModel { /** * The different types of security available. - * @see KeyguardSecurityContainer#showSecurityScreen + * @see KeyguardSecurityContainerController#showSecurityScreen */ public enum SecurityMode { Invalid, // NULL state @@ -45,21 +46,15 @@ public class KeyguardSecurityModel { SimPuk // Unlock by entering a sim puk } - private final Context mContext; private final boolean mIsPukScreenAvailable; - private LockPatternUtils mLockPatternUtils; + private final LockPatternUtils mLockPatternUtils; @Inject - KeyguardSecurityModel(Context context) { - mContext = context; - mLockPatternUtils = new LockPatternUtils(context); - mIsPukScreenAvailable = mContext.getResources().getBoolean( + KeyguardSecurityModel(@Main Resources resources, LockPatternUtils lockPatternUtils) { + mIsPukScreenAvailable = resources.getBoolean( com.android.internal.R.bool.config_enable_puk_unlock_screen); - } - - void setLockPatternUtils(LockPatternUtils utils) { - mLockPatternUtils = utils; + mLockPatternUtils = lockPatternUtils; } public SecurityMode getSecurityMode(int userId) { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java index 43cef3acf147..ac00e9453c97 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java @@ -18,11 +18,9 @@ package com.android.keyguard; import android.content.res.ColorStateList; import android.view.MotionEvent; -import com.android.internal.widget.LockPatternUtils; - public interface KeyguardSecurityView { - static public final int SCREEN_ON = 1; - static public final int VIEW_REVEALED = 2; + int SCREEN_ON = 1; + int VIEW_REVEALED = 2; int PROMPT_REASON_NONE = 0; @@ -63,18 +61,6 @@ public interface KeyguardSecurityView { int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7; /** - * Interface back to keyguard to tell it when security - * @param callback - */ - void setKeyguardCallback(KeyguardSecurityCallback callback); - - /** - * Set {@link LockPatternUtils} object. Useful for providing a mock interface. - * @param utils - */ - void setLockPatternUtils(LockPatternUtils utils); - - /** * Reset the view and prepare to take input. This should do things like clearing the * password or pattern and clear error messages. */ @@ -101,12 +87,6 @@ public interface KeyguardSecurityView { boolean needsInput(); /** - * Get {@link KeyguardSecurityCallback} for the given object - * @return KeyguardSecurityCallback - */ - KeyguardSecurityCallback getCallback(); - - /** * Show a string explaining why the security view needs to be solved. * * @param reason a flag indicating which string should be shown, see {@link #PROMPT_REASON_NONE} @@ -123,12 +103,6 @@ public interface KeyguardSecurityView { void showMessage(CharSequence message, ColorStateList colorState); /** - * Instruct the view to show usability hints, if any. - * - */ - void showUsabilityHint(); - - /** * Starts the animation which should run when the security view appears. */ void startAppearAnimation(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java deleted file mode 100644 index ef9ba19fbb43..000000000000 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewController.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 android.view.View; - -import com.android.systemui.util.ViewController; - -import javax.inject.Inject; - - -/** Controller for a {@link KeyguardSecurityView}. */ -public class KeyguardSecurityViewController extends ViewController<View> { - - private final KeyguardSecurityView mView; - - private KeyguardSecurityViewController(KeyguardSecurityView view) { - super((View) view); - // KeyguardSecurityView isn't actually a View, so we need to track it ourselves. - mView = view; - } - - @Override - protected void onViewAttached() { - - } - - @Override - protected void onViewDetached() { - - } - - /** Factory for a {@link KeyguardSecurityViewController}. */ - public static class Factory { - @Inject - public Factory() { - } - - /** Create a new {@link KeyguardSecurityViewController}. */ - public KeyguardSecurityViewController create(KeyguardSecurityView view) { - return new KeyguardSecurityViewController(view); - } - } -} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java index 24da3ad46f23..b8439af6daaa 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java @@ -18,7 +18,6 @@ package com.android.keyguard; import android.annotation.NonNull; import android.content.Context; -import android.content.res.ColorStateList; import android.content.res.TypedArray; import android.graphics.Rect; import android.util.AttributeSet; @@ -31,7 +30,6 @@ import android.view.ViewHierarchyEncoder; import android.widget.FrameLayout; import android.widget.ViewFlipper; -import com.android.internal.widget.LockPatternUtils; import com.android.systemui.R; /** @@ -39,7 +37,7 @@ import com.android.systemui.R; * we can emulate {@link android.view.WindowManager.LayoutParams#FLAG_SLIPPERY} within a view * hierarchy. */ -public class KeyguardSecurityViewFlipper extends ViewFlipper implements KeyguardSecurityView { +public class KeyguardSecurityViewFlipper extends ViewFlipper { private static final String TAG = "KeyguardSecurityViewFlipper"; private static final boolean DEBUG = KeyguardConstants.DEBUG; @@ -69,111 +67,16 @@ public class KeyguardSecurityViewFlipper extends ViewFlipper implements Keyguard return result; } - KeyguardSecurityView getSecurityView() { + KeyguardInputView getSecurityView() { View child = getChildAt(getDisplayedChild()); - if (child instanceof KeyguardSecurityView) { - return (KeyguardSecurityView) child; + if (child instanceof KeyguardInputView) { + return (KeyguardInputView) child; } return null; } - @Override - public void setKeyguardCallback(KeyguardSecurityCallback callback) { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.setKeyguardCallback(callback); - } - } - - @Override - public void setLockPatternUtils(LockPatternUtils utils) { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.setLockPatternUtils(utils); - } - } - - @Override - public void reset() { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.reset(); - } - } - - @Override - public void onPause() { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.onPause(); - } - } - - @Override - public void onResume(int reason) { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.onResume(reason); - } - } - - @Override - public boolean needsInput() { - KeyguardSecurityView ksv = getSecurityView(); - return (ksv != null) ? ksv.needsInput() : false; - } - - @Override - public KeyguardSecurityCallback getCallback() { - KeyguardSecurityView ksv = getSecurityView(); - return (ksv != null) ? ksv.getCallback() : null; - } - - @Override - public void showPromptReason(int reason) { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.showPromptReason(reason); - } - } - - @Override - public void showMessage(CharSequence message, ColorStateList colorState) { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.showMessage(message, colorState); - } - } - - @Override - public void showUsabilityHint() { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.showUsabilityHint(); - } - } - - @Override - public void startAppearAnimation() { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - ksv.startAppearAnimation(); - } - } - - @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - KeyguardSecurityView ksv = getSecurityView(); - if (ksv != null) { - return ksv.startDisappearAnimation(finishRunnable); - } else { - return false; - } - } - - @Override public CharSequence getTitle() { - KeyguardSecurityView ksv = getSecurityView(); + KeyguardInputView ksv = getSecurityView(); if (ksv != null) { return ksv.getTitle(); } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java new file mode 100644 index 000000000000..49530355a6fb --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipperController.java @@ -0,0 +1,148 @@ +/* + * 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 android.util.Log; +import android.view.LayoutInflater; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.KeyguardInputViewController.Factory; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.keyguard.dagger.KeyguardBouncerScope; +import com.android.systemui.R; +import com.android.systemui.util.ViewController; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +/** + * Controller for a {@link KeyguardSecurityViewFlipper}. + */ +@KeyguardBouncerScope +public class KeyguardSecurityViewFlipperController + extends ViewController<KeyguardSecurityViewFlipper> { + + private static final boolean DEBUG = KeyguardConstants.DEBUG; + private static final String TAG = "KeyguardSecurityView"; + + private final List<KeyguardInputViewController<KeyguardInputView>> mChildren = + new ArrayList<>(); + private final LayoutInflater mLayoutInflater; + private final Factory mKeyguardSecurityViewControllerFactory; + + @Inject + protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view, + LayoutInflater layoutInflater, + KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) { + super(view); + mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory; + mLayoutInflater = layoutInflater; + } + + @Override + protected void onViewAttached() { + + } + + @Override + protected void onViewDetached() { + + } + + public void reset() { + for (KeyguardInputViewController<KeyguardInputView> child : mChildren) { + child.reset(); + } + } + + @VisibleForTesting + KeyguardInputViewController<KeyguardInputView> getSecurityView(SecurityMode securityMode, + KeyguardSecurityCallback keyguardSecurityCallback) { + KeyguardInputViewController<KeyguardInputView> childController = null; + for (KeyguardInputViewController<KeyguardInputView> child : mChildren) { + if (child.getSecurityMode() == securityMode) { + childController = child; + break; + } + } + + if (childController == null + && securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) { + + int layoutId = getLayoutIdFor(securityMode); + KeyguardInputView view = null; + if (layoutId != 0) { + if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); + view = (KeyguardInputView) mLayoutInflater.inflate( + layoutId, mView, false); + mView.addView(view); + childController = mKeyguardSecurityViewControllerFactory.create( + view, securityMode, keyguardSecurityCallback); + childController.init(); + + mChildren.add(childController); + } + } + + if (childController == null) { + childController = new NullKeyguardInputViewController( + securityMode, keyguardSecurityCallback); + } + + return childController; + } + + private int getLayoutIdFor(SecurityMode securityMode) { + switch (securityMode) { + case Pattern: return com.android.systemui.R.layout.keyguard_pattern_view; + case PIN: return com.android.systemui.R.layout.keyguard_pin_view; + case Password: return com.android.systemui.R.layout.keyguard_password_view; + case SimPin: return com.android.systemui.R.layout.keyguard_sim_pin_view; + case SimPuk: return R.layout.keyguard_sim_puk_view; + default: + return 0; + } + } + + /** Makes the supplied child visible if it is contained win this view, */ + public void show(KeyguardInputViewController<KeyguardInputView> childController) { + int index = childController.getIndexIn(mView); + if (index != -1) { + mView.setDisplayedChild(index); + } + } + + private static class NullKeyguardInputViewController + extends KeyguardInputViewController<KeyguardInputView> { + protected NullKeyguardInputViewController(SecurityMode securityMode, + KeyguardSecurityCallback keyguardSecurityCallback) { + super(null, securityMode, keyguardSecurityCallback); + } + + @Override + public boolean needsInput() { + return false; + } + + @Override + public void onStartingToHide() { + + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java index 1c47aa0151f0..c0f9ce794628 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java @@ -16,66 +16,19 @@ package com.android.keyguard; -import android.annotation.NonNull; -import android.app.AlertDialog; -import android.app.AlertDialog.Builder; -import android.app.Dialog; -import android.app.ProgressDialog; import android.content.Context; -import android.content.res.ColorStateList; import android.content.res.Configuration; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.telephony.PinResult; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; import android.util.AttributeSet; -import android.util.Log; import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; -import com.android.systemui.Dependency; import com.android.systemui.R; /** * Displays a PIN pad for unlocking. */ public class KeyguardSimPinView extends KeyguardPinBasedInputView { - private static final String LOG_TAG = "KeyguardSimPinView"; - private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES; public static final String TAG = "KeyguardSimPinView"; - private ProgressDialog mSimUnlockProgressDialog = null; - private CheckSimPin mCheckSimPinThread; - - // Below flag is set to true during power-up or when a new SIM card inserted on device. - // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would - // be displayed to inform user about the number of remaining PIN attempts left. - private boolean mShowDefaultMessage = true; - private int mRemainingAttempts = -1; - private AlertDialog mRemainingAttemptsDialog; - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private ImageView mSimImageView; - - KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - switch(simState) { - case TelephonyManager.SIM_STATE_READY: { - mRemainingAttempts = -1; - resetState(); - break; - } - default: - resetState(); - } - } - }; - public KeyguardSimPinView(Context context) { this(context, null); } @@ -84,81 +37,9 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { super(context, attrs); } - @Override - public void resetState() { - super.resetState(); - if (DEBUG) Log.v(TAG, "Resetting state"); - handleSubInfoChangeIfNeeded(); - if (mShowDefaultMessage) { - showDefaultMessage(); - } - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); - + public void setEsimLocked(boolean locked) { KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); - esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); - } - - private void setLockedSimMessage() { - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); - int count = 1; - TelephonyManager telephonyManager = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager != null) { - count = telephonyManager.getActiveModemCount(); - } - Resources rez = getResources(); - String msg; - TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor }); - int color = array.getColor(0, Color.WHITE); - array.recycle(); - if (count < 2) { - msg = rez.getString(R.string.kg_sim_pin_instructions); - } else { - SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) - .getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash - msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } - } - if (isEsimLocked) { - msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); - } - - if (mSecurityMessageDisplay != null && getVisibility() == VISIBLE) { - mSecurityMessageDisplay.setMessage(msg); - } - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); - } - - private void showDefaultMessage() { - setLockedSimMessage(); - if (mRemainingAttempts >= 0) { - return; - } - - // Sending empty PIN here to query the number of remaining PIN attempts - new CheckSimPin("", mSubId) { - void onSimCheckResponse(final PinResult result) { - Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " - + result.toString()); - if (result.getAttemptsRemaining() >= 0) { - mRemainingAttempts = result.getAttemptsRemaining(); - setLockedSimMessage(); - } - } - }.start(); - } - - private void handleSubInfoChangeIfNeeded() { - KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); - int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED); - if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { - mSubId = subId; - mShowDefaultMessage = true; - mRemainingAttempts = -1; - } + esimButton.setVisibility(locked ? View.VISIBLE : View.GONE); } @Override @@ -173,35 +54,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { return 0; } - private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { - String displayMessage; - int msgId; - if (attemptsRemaining == 0) { - displayMessage = getContext().getString(R.string.kg_password_wrong_pin_code_pukked); - } else if (attemptsRemaining > 0) { - msgId = isDefault ? R.plurals.kg_password_default_pin_message : - R.plurals.kg_password_wrong_pin_code; - displayMessage = getContext().getResources() - .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); - } else { - msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; - displayMessage = getContext().getString(msgId); - } - if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) { - displayMessage = getResources() - .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); - } - if (DEBUG) Log.d(LOG_TAG, "getPinPasswordErrorMessage:" - + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); - return displayMessage; - } - - @Override - protected boolean shouldLockout(long deadline) { - // SIM PIN doesn't have a timed lockout - return false; - } - @Override protected int getPasswordTextViewId() { return R.id.simPinEntry; @@ -214,173 +66,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { if (mEcaView instanceof EmergencyCarrierArea) { ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); } - mSimImageView = findViewById(R.id.keyguard_sim); - } - - @Override - public void showUsabilityHint() { - - } - - @Override - public void onResume(int reason) { - super.onResume(reason); - Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); - resetState(); - } - - @Override - public void onPause() { - // dismiss the dialog. - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.dismiss(); - mSimUnlockProgressDialog = null; - } - Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); - } - - /** - * Since the IPC can block, we want to run the request in a separate thread - * with a callback. - */ - private abstract class CheckSimPin extends Thread { - private final String mPin; - private int mSubId; - - protected CheckSimPin(String pin, int subId) { - mPin = pin; - mSubId = subId; - } - - abstract void onSimCheckResponse(@NonNull PinResult result); - - @Override - public void run() { - if (DEBUG) { - Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); - } - TelephonyManager telephonyManager = - ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) - .createForSubscriptionId(mSubId); - final PinResult result = telephonyManager.supplyPinReportPinResult(mPin); - if (result == null) { - Log.e(TAG, "Error result for supplyPinReportResult."); - post(new Runnable() { - @Override - public void run() { - onSimCheckResponse(PinResult.getDefaultFailedResult()); - } - }); - } else { - if (DEBUG) { - Log.v(TAG, "supplyPinReportResult returned: " + result.toString()); - } - post(new Runnable() { - @Override - public void run() { - onSimCheckResponse(result); - } - }); - } - } - } - - private Dialog getSimUnlockProgressDialog() { - if (mSimUnlockProgressDialog == null) { - mSimUnlockProgressDialog = new ProgressDialog(mContext); - mSimUnlockProgressDialog.setMessage( - mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); - mSimUnlockProgressDialog.setIndeterminate(true); - mSimUnlockProgressDialog.setCancelable(false); - mSimUnlockProgressDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } - return mSimUnlockProgressDialog; - } - - private Dialog getSimRemainingAttemptsDialog(int remaining) { - String msg = getPinPasswordErrorMessage(remaining, false); - if (mRemainingAttemptsDialog == null) { - Builder builder = new AlertDialog.Builder(mContext); - builder.setMessage(msg); - builder.setCancelable(false); - builder.setNeutralButton(R.string.ok, null); - mRemainingAttemptsDialog = builder.create(); - mRemainingAttemptsDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } else { - mRemainingAttemptsDialog.setMessage(msg); - } - return mRemainingAttemptsDialog; - } - - @Override - protected void verifyPasswordAndUnlock() { - String entry = mPasswordEntry.getText(); - - if (entry.length() < 4) { - // otherwise, display a message to the user, and don't submit. - mSecurityMessageDisplay.setMessage(R.string.kg_invalid_sim_pin_hint); - resetPasswordText(true /* animate */, true /* announce */); - mCallback.userActivity(); - return; - } - - getSimUnlockProgressDialog().show(); - - if (mCheckSimPinThread == null) { - mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { - @Override - void onSimCheckResponse(final PinResult result) { - post(new Runnable() { - @Override - public void run() { - mRemainingAttempts = result.getAttemptsRemaining(); - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - resetPasswordText(true /* animate */, - /* announce */ - result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); - if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { - Dependency.get(KeyguardUpdateMonitor.class) - .reportSimUnlocked(mSubId); - mRemainingAttempts = -1; - mShowDefaultMessage = true; - if (mCallback != null) { - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); - } - } else { - mShowDefaultMessage = false; - if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { - if (result.getAttemptsRemaining() <= 2) { - // this is getting critical - show dialog - getSimRemainingAttemptsDialog( - result.getAttemptsRemaining()).show(); - } else { - // show message - mSecurityMessageDisplay.setMessage( - getPinPasswordErrorMessage( - result.getAttemptsRemaining(), false)); - } - } else { - // "PIN operation failed!" - no idea what this was and no way to - // find out. :/ - mSecurityMessageDisplay.setMessage(getContext().getString( - R.string.kg_password_pin_failed)); - } - if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " - + " CheckSimPin.onSimCheckResponse: " + result - + " attemptsRemaining=" + result.getAttemptsRemaining()); - } - mCallback.userActivity(); - mCheckSimPinThread = null; - } - }); - } - }; - mCheckSimPinThread.start(); - } } @Override @@ -389,11 +74,6 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView { } @Override - public boolean startDisappearAnimation(Runnable finishRunnable) { - return false; - } - - @Override public CharSequence getTitle() { return getContext().getString( com.android.internal.R.string.keyguard_accessibility_sim_pin_unlock); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java new file mode 100644 index 000000000000..cc8bf4f2d028 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -0,0 +1,350 @@ +/* + * 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 android.annotation.NonNull; +import android.app.AlertDialog; +import android.app.AlertDialog.Builder; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.telephony.PinResult; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; + +public class KeyguardSimPinViewController + extends KeyguardPinBasedInputViewController<KeyguardSimPinView> { + public static final String TAG = "KeyguardSimPinView"; + private static final String LOG_TAG = "KeyguardSimPinView"; + private static final boolean DEBUG = KeyguardConstants.DEBUG_SIM_STATES; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final TelephonyManager mTelephonyManager; + + private ProgressDialog mSimUnlockProgressDialog; + private CheckSimPin mCheckSimPinThread; + private int mRemainingAttempts; + // Below flag is set to true during power-up or when a new SIM card inserted on device. + // When this is true and when SIM card is PIN locked state, on PIN lock screen, message would + // be displayed to inform user about the number of remaining PIN attempts left. + private boolean mShowDefaultMessage; + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private AlertDialog mRemainingAttemptsDialog; + private ImageView mSimImageView; + + KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + if (simState == TelephonyManager.SIM_STATE_READY) { + mRemainingAttempts = -1; + resetState(); + } else { + resetState(); + } + } + }; + + protected KeyguardSimPinViewController(KeyguardSimPinView view, + KeyguardUpdateMonitor keyguardUpdateMonitor, + SecurityMode securityMode, LockPatternUtils lockPatternUtils, + KeyguardSecurityCallback keyguardSecurityCallback, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, + LatencyTracker latencyTracker, + LiftToActivateListener liftToActivateListener, + TelephonyManager telephonyManager) { + super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, + messageAreaControllerFactory, latencyTracker, liftToActivateListener); + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mTelephonyManager = telephonyManager; + mSimImageView = mView.findViewById(R.id.keyguard_sim); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + } + + @Override + void resetState() { + super.resetState(); + if (DEBUG) Log.v(TAG, "Resetting state"); + handleSubInfoChangeIfNeeded(); + mMessageAreaController.setMessage(""); + if (mShowDefaultMessage) { + showDefaultMessage(); + } + + mView.setEsimLocked(KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)); + } + + @Override + public boolean startDisappearAnimation(Runnable finishRunnable) { + return false; + } + + @Override + public void onResume(int reason) { + super.onResume(reason); + mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + mView.resetState(); + } + + @Override + public void onPause() { + super.onPause(); + mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); + + // dismiss the dialog. + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.dismiss(); + mSimUnlockProgressDialog = null; + } + } + + @Override + protected void verifyPasswordAndUnlock() { + String entry = mPasswordEntry.getText(); + + if (entry.length() < 4) { + // otherwise, display a message to the user, and don't submit. + mMessageAreaController.setMessage( + com.android.systemui.R.string.kg_invalid_sim_pin_hint); + mView.resetPasswordText(true /* animate */, true /* announce */); + getKeyguardSecurityCallback().userActivity(); + return; + } + + getSimUnlockProgressDialog().show(); + + if (mCheckSimPinThread == null) { + mCheckSimPinThread = new CheckSimPin(mPasswordEntry.getText(), mSubId) { + @Override + void onSimCheckResponse(final PinResult result) { + mView.post(() -> { + mRemainingAttempts = result.getAttemptsRemaining(); + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + mView.resetPasswordText(true /* animate */, + /* announce */ + result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); + if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { + mKeyguardUpdateMonitor.reportSimUnlocked(mSubId); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + getKeyguardSecurityCallback().dismiss( + true, KeyguardUpdateMonitor.getCurrentUser()); + } else { + mShowDefaultMessage = false; + if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { + if (result.getAttemptsRemaining() <= 2) { + // this is getting critical - show dialog + getSimRemainingAttemptsDialog( + result.getAttemptsRemaining()).show(); + } else { + // show message + mMessageAreaController.setMessage( + getPinPasswordErrorMessage( + result.getAttemptsRemaining(), false)); + } + } else { + // "PIN operation failed!" - no idea what this was and no way to + // find out. :/ + mMessageAreaController.setMessage(mView.getResources().getString( + R.string.kg_password_pin_failed)); + } + if (DEBUG) { + Log.d(LOG_TAG, "verifyPasswordAndUnlock " + + " CheckSimPin.onSimCheckResponse: " + result + + " attemptsRemaining=" + result.getAttemptsRemaining()); + } + } + getKeyguardSecurityCallback().userActivity(); + mCheckSimPinThread = null; + }); + } + }; + mCheckSimPinThread.start(); + } + } + + private Dialog getSimUnlockProgressDialog() { + if (mSimUnlockProgressDialog == null) { + mSimUnlockProgressDialog = new ProgressDialog(mView.getContext()); + mSimUnlockProgressDialog.setMessage( + mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message)); + mSimUnlockProgressDialog.setIndeterminate(true); + mSimUnlockProgressDialog.setCancelable(false); + mSimUnlockProgressDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + return mSimUnlockProgressDialog; + } + + + private Dialog getSimRemainingAttemptsDialog(int remaining) { + String msg = getPinPasswordErrorMessage(remaining, false); + if (mRemainingAttemptsDialog == null) { + Builder builder = new AlertDialog.Builder(mView.getContext()); + builder.setMessage(msg); + builder.setCancelable(false); + builder.setNeutralButton(R.string.ok, null); + mRemainingAttemptsDialog = builder.create(); + mRemainingAttemptsDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } else { + mRemainingAttemptsDialog.setMessage(msg); + } + return mRemainingAttemptsDialog; + } + + + private String getPinPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { + String displayMessage; + int msgId; + if (attemptsRemaining == 0) { + displayMessage = mView.getResources().getString( + R.string.kg_password_wrong_pin_code_pukked); + } else if (attemptsRemaining > 0) { + msgId = isDefault ? R.plurals.kg_password_default_pin_message : + R.plurals.kg_password_wrong_pin_code; + displayMessage = mView.getResources() + .getQuantityString(msgId, attemptsRemaining, attemptsRemaining); + } else { + msgId = isDefault ? R.string.kg_sim_pin_instructions : R.string.kg_password_pin_failed; + displayMessage = mView.getResources().getString(msgId); + } + if (KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)) { + displayMessage = mView.getResources() + .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); + } + if (DEBUG) { + Log.d(LOG_TAG, "getPinPasswordErrorMessage: attemptsRemaining=" + + attemptsRemaining + " displayMessage=" + displayMessage); + } + return displayMessage; + } + + private void showDefaultMessage() { + setLockedSimMessage(); + if (mRemainingAttempts >= 0) { + return; + } + + // Sending empty PIN here to query the number of remaining PIN attempts + new CheckSimPin("", mSubId) { + void onSimCheckResponse(final PinResult result) { + Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " + + result.toString()); + if (result.getAttemptsRemaining() >= 0) { + mRemainingAttempts = result.getAttemptsRemaining(); + setLockedSimMessage(); + } + } + }.start(); + } + + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class CheckSimPin extends Thread { + private final String mPin; + private int mSubId; + + protected CheckSimPin(String pin, int subId) { + mPin = pin; + mSubId = subId; + } + + abstract void onSimCheckResponse(@NonNull PinResult result); + + @Override + public void run() { + if (DEBUG) { + Log.v(TAG, "call supplyPinReportResultForSubscriber(subid=" + mSubId + ")"); + } + TelephonyManager telephonyManager = + mTelephonyManager.createForSubscriptionId(mSubId); + final PinResult result = telephonyManager.supplyPinReportPinResult(mPin); + if (result == null) { + Log.e(TAG, "Error result for supplyPinReportResult."); + mView.post(() -> onSimCheckResponse(PinResult.getDefaultFailedResult())); + } else { + if (DEBUG) { + Log.v(TAG, "supplyPinReportResult returned: " + result.toString()); + } + mView.post(() -> onSimCheckResponse(result)); + } + } + } + + private void setLockedSimMessage() { + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); + int count = 1; + if (mTelephonyManager != null) { + count = mTelephonyManager.getActiveModemCount(); + } + Resources rez = mView.getResources(); + String msg; + TypedArray array = mView.getContext().obtainStyledAttributes( + new int[] { R.attr.wallpaperTextColor }); + int color = array.getColor(0, Color.WHITE); + array.recycle(); + if (count < 2) { + msg = rez.getString(R.string.kg_sim_pin_instructions); + } else { + SubscriptionInfo info = mKeyguardUpdateMonitor.getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; // don't crash + msg = rez.getString(R.string.kg_sim_pin_instructions_multi, displayName); + if (info != null) { + color = info.getIconTint(); + } + } + if (isEsimLocked) { + msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); + } + + if (mView.getVisibility() == View.VISIBLE) { + mMessageAreaController.setMessage(msg); + } + mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + } + + private void handleSubInfoChangeIfNeeded() { + int subId = mKeyguardUpdateMonitor + .getNextSubIdForState(TelephonyManager.SIM_STATE_PIN_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; + } + } +} diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java index 5148dd709026..0d72c93e9041 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java @@ -16,27 +16,10 @@ package com.android.keyguard; -import android.annotation.NonNull; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.app.ProgressDialog; import android.content.Context; -import android.content.res.ColorStateList; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Color; -import android.telephony.PinResult; -import android.telephony.SubscriptionInfo; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; import android.util.AttributeSet; import android.util.Log; -import android.view.View; -import android.view.WindowManager; -import android.widget.ImageView; -import com.android.systemui.Dependency; import com.android.systemui.R; @@ -44,48 +27,9 @@ import com.android.systemui.R; * Displays a PIN pad for entering a PUK (Pin Unlock Kode) provided by a carrier. */ public class KeyguardSimPukView extends KeyguardPinBasedInputView { - private static final String LOG_TAG = "KeyguardSimPukView"; private static final boolean DEBUG = KeyguardConstants.DEBUG; public static final String TAG = "KeyguardSimPukView"; - private ProgressDialog mSimUnlockProgressDialog = null; - private CheckSimPuk mCheckSimPukThread; - - // Below flag is set to true during power-up or when a new SIM card inserted on device. - // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would - // be displayed to inform user about the number of remaining PUK attempts left. - private boolean mShowDefaultMessage = true; - private int mRemainingAttempts = -1; - private String mPukText; - private String mPinText; - private StateMachine mStateMachine = new StateMachine(); - private AlertDialog mRemainingAttemptsDialog; - private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - private ImageView mSimImageView; - - KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { - @Override - public void onSimStateChanged(int subId, int slotId, int simState) { - if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); - switch(simState) { - // If the SIM is unlocked via a key sequence through the emergency dialer, it will - // move into the READY state and the PUK lock keyguard should be removed. - case TelephonyManager.SIM_STATE_READY: { - mRemainingAttempts = -1; - mShowDefaultMessage = true; - // mCallback can be null if onSimStateChanged callback is called when keyguard - // isn't active. - if (mCallback != null) { - mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); - } - break; - } - default: - resetState(); - } - } - }; - public KeyguardSimPukView(Context context) { this(context, null); } @@ -94,136 +38,14 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { super(context, attrs); } - private class StateMachine { - final int ENTER_PUK = 0; - final int ENTER_PIN = 1; - final int CONFIRM_PIN = 2; - final int DONE = 3; - private int state = ENTER_PUK; - - public void next() { - int msg = 0; - if (state == ENTER_PUK) { - if (checkPuk()) { - state = ENTER_PIN; - msg = R.string.kg_puk_enter_pin_hint; - } else { - msg = R.string.kg_invalid_sim_puk_hint; - } - } else if (state == ENTER_PIN) { - if (checkPin()) { - state = CONFIRM_PIN; - msg = R.string.kg_enter_confirm_pin_hint; - } else { - msg = R.string.kg_invalid_sim_pin_hint; - } - } else if (state == CONFIRM_PIN) { - if (confirmPin()) { - state = DONE; - msg = R.string.keyguard_sim_unlock_progress_dialog_message; - updateSim(); - } else { - state = ENTER_PIN; // try again? - msg = R.string.kg_invalid_confirm_pin_hint; - } - } - resetPasswordText(true /* animate */, true /* announce */); - if (msg != 0) { - mSecurityMessageDisplay.setMessage(msg); - } - } - - - void reset() { - mPinText=""; - mPukText=""; - state = ENTER_PUK; - handleSubInfoChangeIfNeeded(); - if (mShowDefaultMessage) { - showDefaultMessage(); - } - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); - - KeyguardEsimArea esimButton = findViewById(R.id.keyguard_esim_area); - esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); - mPasswordEntry.requestFocus(); - } - - - } - - private void showDefaultMessage() { - if (mRemainingAttempts >= 0) { - mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( - mRemainingAttempts, true)); - return; - } - - boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mContext, mSubId); - int count = 1; - TelephonyManager telephonyManager = - (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager != null) { - count = telephonyManager.getActiveModemCount(); - } - Resources rez = getResources(); - String msg; - TypedArray array = mContext.obtainStyledAttributes(new int[] { R.attr.wallpaperTextColor }); - int color = array.getColor(0, Color.WHITE); - array.recycle(); - if (count < 2) { - msg = rez.getString(R.string.kg_puk_enter_puk_hint); - } else { - SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) - .getSubscriptionInfoForSubId(mSubId); - CharSequence displayName = info != null ? info.getDisplayName() : ""; - msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); - if (info != null) { - color = info.getIconTint(); - } - } - if (isEsimLocked) { - msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); - } - if (mSecurityMessageDisplay != null) { - mSecurityMessageDisplay.setMessage(msg); - } - mSimImageView.setImageTintList(ColorStateList.valueOf(color)); - - // Sending empty PUK here to query the number of remaining PIN attempts - new CheckSimPuk("", "", mSubId) { - void onSimLockChangedResponse(final PinResult result) { - if (result == null) Log.e(LOG_TAG, "onSimCheckResponse, pin result is NULL"); - else { - Log.d(LOG_TAG, "onSimCheckResponse " + " empty One result " - + result.toString()); - if (result.getAttemptsRemaining() >= 0) { - mRemainingAttempts = result.getAttemptsRemaining(); - mSecurityMessageDisplay.setMessage( - getPukPasswordErrorMessage(result.getAttemptsRemaining(), true)); - } - } - } - }.start(); - } - - private void handleSubInfoChangeIfNeeded() { - KeyguardUpdateMonitor monitor = Dependency.get(KeyguardUpdateMonitor.class); - int subId = monitor.getNextSubIdForState(TelephonyManager.SIM_STATE_PUK_REQUIRED); - if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { - mSubId = subId; - mShowDefaultMessage = true; - mRemainingAttempts = -1; - } - } - @Override protected int getPromptReasonStringRes(int reason) { // No message on SIM Puk return 0; } - private String getPukPasswordErrorMessage(int attemptsRemaining, boolean isDefault) { + String getPukPasswordErrorMessage( + int attemptsRemaining, boolean isDefault, boolean isEsimLocked) { String displayMessage; if (attemptsRemaining == 0) { @@ -238,28 +60,19 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { R.string.kg_password_puk_failed; displayMessage = getContext().getString(msgId); } - if (KeyguardEsimArea.isEsimLocked(mContext, mSubId)) { + if (isEsimLocked) { displayMessage = getResources() .getString(R.string.kg_sim_lock_esim_instructions, displayMessage); } - if (DEBUG) Log.d(LOG_TAG, "getPukPasswordErrorMessage:" - + " attemptsRemaining=" + attemptsRemaining + " displayMessage=" + displayMessage); + if (DEBUG) { + Log.d(TAG, "getPukPasswordErrorMessage:" + + " attemptsRemaining=" + attemptsRemaining + + " displayMessage=" + displayMessage); + } return displayMessage; } @Override - public void resetState() { - super.resetState(); - mStateMachine.reset(); - } - - @Override - protected boolean shouldLockout(long deadline) { - // SIM PUK doesn't have a timed lockout - return false; - } - - @Override protected int getPasswordTextViewId() { return R.id.pukEntry; } @@ -271,197 +84,6 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView { if (mEcaView instanceof EmergencyCarrierArea) { ((EmergencyCarrierArea) mEcaView).setCarrierTextVisible(true); } - mSimImageView = findViewById(R.id.keyguard_sim); - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateMonitorCallback); - resetState(); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - Dependency.get(KeyguardUpdateMonitor.class).removeCallback(mUpdateMonitorCallback); - } - - @Override - public void showUsabilityHint() { - } - - @Override - public void onPause() { - // dismiss the dialog. - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.dismiss(); - mSimUnlockProgressDialog = null; - } - } - - /** - * Since the IPC can block, we want to run the request in a separate thread - * with a callback. - */ - private abstract class CheckSimPuk extends Thread { - - private final String mPin, mPuk; - private final int mSubId; - - protected CheckSimPuk(String puk, String pin, int subId) { - mPuk = puk; - mPin = pin; - mSubId = subId; - } - - abstract void onSimLockChangedResponse(@NonNull PinResult result); - - @Override - public void run() { - if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); - TelephonyManager telephonyManager = - ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE)) - .createForSubscriptionId(mSubId); - final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin); - if (result == null) { - Log.e(TAG, "Error result for supplyPukReportResult."); - post(new Runnable() { - @Override - public void run() { - onSimLockChangedResponse(PinResult.getDefaultFailedResult()); - } - }); - } else { - if (DEBUG) { - Log.v(TAG, "supplyPukReportResult returned: " + result.toString()); - } - post(new Runnable() { - @Override - public void run() { - onSimLockChangedResponse(result); - } - }); - } - } - } - - private Dialog getSimUnlockProgressDialog() { - if (mSimUnlockProgressDialog == null) { - mSimUnlockProgressDialog = new ProgressDialog(mContext); - mSimUnlockProgressDialog.setMessage( - mContext.getString(R.string.kg_sim_unlock_progress_dialog_message)); - mSimUnlockProgressDialog.setIndeterminate(true); - mSimUnlockProgressDialog.setCancelable(false); - if (!(mContext instanceof Activity)) { - mSimUnlockProgressDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } - } - return mSimUnlockProgressDialog; - } - - private Dialog getPukRemainingAttemptsDialog(int remaining) { - String msg = getPukPasswordErrorMessage(remaining, false); - if (mRemainingAttemptsDialog == null) { - AlertDialog.Builder builder = new AlertDialog.Builder(mContext); - builder.setMessage(msg); - builder.setCancelable(false); - builder.setNeutralButton(R.string.ok, null); - mRemainingAttemptsDialog = builder.create(); - mRemainingAttemptsDialog.getWindow().setType( - WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); - } else { - mRemainingAttemptsDialog.setMessage(msg); - } - return mRemainingAttemptsDialog; - } - - private boolean checkPuk() { - // make sure the puk is at least 8 digits long. - if (mPasswordEntry.getText().length() == 8) { - mPukText = mPasswordEntry.getText(); - return true; - } - return false; - } - - private boolean checkPin() { - // make sure the PIN is between 4 and 8 digits - int length = mPasswordEntry.getText().length(); - if (length >= 4 && length <= 8) { - mPinText = mPasswordEntry.getText(); - return true; - } - return false; - } - - public boolean confirmPin() { - return mPinText.equals(mPasswordEntry.getText()); - } - - private void updateSim() { - getSimUnlockProgressDialog().show(); - - if (mCheckSimPukThread == null) { - mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { - @Override - void onSimLockChangedResponse(final PinResult result) { - post(new Runnable() { - @Override - public void run() { - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - resetPasswordText(true /* animate */, - /* announce */ - result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); - if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { - Dependency.get(KeyguardUpdateMonitor.class) - .reportSimUnlocked(mSubId); - mRemainingAttempts = -1; - mShowDefaultMessage = true; - if (mCallback != null) { - mCallback.dismiss(true, - KeyguardUpdateMonitor.getCurrentUser()); - } - } else { - mShowDefaultMessage = false; - if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { - // show message - mSecurityMessageDisplay.setMessage(getPukPasswordErrorMessage( - result.getAttemptsRemaining(), false)); - if (result.getAttemptsRemaining() <= 2) { - // this is getting critical - show dialog - getPukRemainingAttemptsDialog( - result.getAttemptsRemaining()).show(); - } else { - // show message - mSecurityMessageDisplay.setMessage( - getPukPasswordErrorMessage( - result.getAttemptsRemaining(), false)); - } - } else { - mSecurityMessageDisplay.setMessage(getContext().getString( - R.string.kg_password_puk_failed)); - } - if (DEBUG) Log.d(LOG_TAG, "verifyPasswordAndUnlock " - + " UpdateSim.onSimCheckResponse: " - + " attemptsRemaining=" + result.getAttemptsRemaining()); - } - mStateMachine.reset(); - mCheckSimPukThread = null; - } - }); - } - }; - mCheckSimPukThread.start(); - } - } - - @Override - protected void verifyPasswordAndUnlock() { - mStateMachine.next(); } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java new file mode 100644 index 000000000000..a87374939ba6 --- /dev/null +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -0,0 +1,413 @@ +/* + * 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 android.annotation.NonNull; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.ProgressDialog; +import android.content.res.ColorStateList; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.graphics.Color; +import android.telephony.PinResult; +import android.telephony.SubscriptionInfo; +import android.telephony.SubscriptionManager; +import android.telephony.TelephonyManager; +import android.util.Log; +import android.view.View; +import android.view.WindowManager; +import android.widget.ImageView; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.Dependency; +import com.android.systemui.R; + +public class KeyguardSimPukViewController + extends KeyguardPinBasedInputViewController<KeyguardSimPukView> { + private static final boolean DEBUG = KeyguardConstants.DEBUG; + public static final String TAG = "KeyguardSimPukView"; + + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; + private final TelephonyManager mTelephonyManager; + + private String mPukText; + private String mPinText; + private int mRemainingAttempts; + // Below flag is set to true during power-up or when a new SIM card inserted on device. + // When this is true and when SIM card is PUK locked state, on PIN lock screen, message would + // be displayed to inform user about the number of remaining PUK attempts left. + private boolean mShowDefaultMessage; + private StateMachine mStateMachine = new StateMachine(); + private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private CheckSimPuk mCheckSimPukThread; + private ProgressDialog mSimUnlockProgressDialog; + + KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { + @Override + public void onSimStateChanged(int subId, int slotId, int simState) { + if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")"); + // If the SIM is unlocked via a key sequence through the emergency dialer, it will + // move into the READY state and the PUK lock keyguard should be removed. + if (simState == TelephonyManager.SIM_STATE_READY) { + mRemainingAttempts = -1; + mShowDefaultMessage = true; + getKeyguardSecurityCallback().dismiss(true, KeyguardUpdateMonitor.getCurrentUser()); + } else { + resetState(); + } + } + }; + private ImageView mSimImageView; + private AlertDialog mRemainingAttemptsDialog; + + protected KeyguardSimPukViewController(KeyguardSimPukView view, + KeyguardUpdateMonitor keyguardUpdateMonitor, + SecurityMode securityMode, LockPatternUtils lockPatternUtils, + KeyguardSecurityCallback keyguardSecurityCallback, + KeyguardMessageAreaController.Factory messageAreaControllerFactory, + LatencyTracker latencyTracker, + LiftToActivateListener liftToActivateListener, + TelephonyManager telephonyManager) { + super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback, + messageAreaControllerFactory, latencyTracker, liftToActivateListener); + mKeyguardUpdateMonitor = keyguardUpdateMonitor; + mTelephonyManager = telephonyManager; + mSimImageView = mView.findViewById(R.id.keyguard_sim); + } + + @Override + protected void onViewAttached() { + super.onViewAttached(); + mKeyguardUpdateMonitor.registerCallback(mUpdateMonitorCallback); + } + + @Override + protected void onViewDetached() { + super.onViewDetached(); + mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); + } + + @Override + void resetState() { + super.resetState(); + mStateMachine.reset(); + } + + @Override + protected void verifyPasswordAndUnlock() { + mStateMachine.next(); + } + + private class StateMachine { + static final int ENTER_PUK = 0; + static final int ENTER_PIN = 1; + static final int CONFIRM_PIN = 2; + static final int DONE = 3; + + private int mState = ENTER_PUK; + + public void next() { + int msg = 0; + if (mState == ENTER_PUK) { + if (checkPuk()) { + mState = ENTER_PIN; + msg = com.android.systemui.R.string.kg_puk_enter_pin_hint; + } else { + msg = com.android.systemui.R.string.kg_invalid_sim_puk_hint; + } + } else if (mState == ENTER_PIN) { + if (checkPin()) { + mState = CONFIRM_PIN; + msg = com.android.systemui.R.string.kg_enter_confirm_pin_hint; + } else { + msg = com.android.systemui.R.string.kg_invalid_sim_pin_hint; + } + } else if (mState == CONFIRM_PIN) { + if (confirmPin()) { + mState = DONE; + msg = com.android.systemui.R.string.keyguard_sim_unlock_progress_dialog_message; + updateSim(); + } else { + mState = ENTER_PIN; // try again? + msg = com.android.systemui.R.string.kg_invalid_confirm_pin_hint; + } + } + mView.resetPasswordText(true /* animate */, true /* announce */); + if (msg != 0) { + mMessageAreaController.setMessage(msg); + } + } + + + void reset() { + mPinText = ""; + mPukText = ""; + mState = ENTER_PUK; + handleSubInfoChangeIfNeeded(); + if (mShowDefaultMessage) { + showDefaultMessage(); + } + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); + + KeyguardEsimArea esimButton = mView.findViewById(R.id.keyguard_esim_area); + esimButton.setVisibility(isEsimLocked ? View.VISIBLE : View.GONE); + mPasswordEntry.requestFocus(); + } + } + + private void showDefaultMessage() { + if (mRemainingAttempts >= 0) { + mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage( + mRemainingAttempts, true, + KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); + return; + } + + boolean isEsimLocked = KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId); + int count = 1; + if (mTelephonyManager != null) { + count = mTelephonyManager.getActiveModemCount(); + } + Resources rez = mView.getResources(); + String msg; + TypedArray array = mView.getContext().obtainStyledAttributes( + new int[] { R.attr.wallpaperTextColor }); + int color = array.getColor(0, Color.WHITE); + array.recycle(); + if (count < 2) { + msg = rez.getString(R.string.kg_puk_enter_puk_hint); + } else { + SubscriptionInfo info = Dependency.get(KeyguardUpdateMonitor.class) + .getSubscriptionInfoForSubId(mSubId); + CharSequence displayName = info != null ? info.getDisplayName() : ""; + msg = rez.getString(R.string.kg_puk_enter_puk_hint_multi, displayName); + if (info != null) { + color = info.getIconTint(); + } + } + if (isEsimLocked) { + msg = rez.getString(R.string.kg_sim_lock_esim_instructions, msg); + } + mMessageAreaController.setMessage(msg); + mSimImageView.setImageTintList(ColorStateList.valueOf(color)); + + // Sending empty PUK here to query the number of remaining PIN attempts + new CheckSimPuk("", "", mSubId) { + void onSimLockChangedResponse(final PinResult result) { + if (result == null) Log.e(TAG, "onSimCheckResponse, pin result is NULL"); + else { + Log.d(TAG, "onSimCheckResponse " + " empty One result " + + result.toString()); + if (result.getAttemptsRemaining() >= 0) { + mRemainingAttempts = result.getAttemptsRemaining(); + mMessageAreaController.setMessage( + mView.getPukPasswordErrorMessage( + result.getAttemptsRemaining(), true, + KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); + } + } + } + }.start(); + } + + private boolean checkPuk() { + // make sure the puk is at least 8 digits long. + if (mPasswordEntry.getText().length() == 8) { + mPukText = mPasswordEntry.getText(); + return true; + } + return false; + } + + private boolean checkPin() { + // make sure the PIN is between 4 and 8 digits + int length = mPasswordEntry.getText().length(); + if (length >= 4 && length <= 8) { + mPinText = mPasswordEntry.getText(); + return true; + } + return false; + } + + public boolean confirmPin() { + return mPinText.equals(mPasswordEntry.getText()); + } + + + + + private void updateSim() { + getSimUnlockProgressDialog().show(); + + if (mCheckSimPukThread == null) { + mCheckSimPukThread = new CheckSimPuk(mPukText, mPinText, mSubId) { + @Override + void onSimLockChangedResponse(final PinResult result) { + mView.post(() -> { + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + mView.resetPasswordText(true /* animate */, + /* announce */ + result.getType() != PinResult.PIN_RESULT_TYPE_SUCCESS); + if (result.getType() == PinResult.PIN_RESULT_TYPE_SUCCESS) { + mKeyguardUpdateMonitor.reportSimUnlocked(mSubId); + mRemainingAttempts = -1; + mShowDefaultMessage = true; + + getKeyguardSecurityCallback().dismiss( + true, KeyguardUpdateMonitor.getCurrentUser()); + } else { + mShowDefaultMessage = false; + if (result.getType() == PinResult.PIN_RESULT_TYPE_INCORRECT) { + // show message + mMessageAreaController.setMessage(mView.getPukPasswordErrorMessage( + result.getAttemptsRemaining(), false, + KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId))); + if (result.getAttemptsRemaining() <= 2) { + // this is getting critical - show dialog + getPukRemainingAttemptsDialog( + result.getAttemptsRemaining()).show(); + } else { + // show message + mMessageAreaController.setMessage( + mView.getPukPasswordErrorMessage( + result.getAttemptsRemaining(), false, + KeyguardEsimArea.isEsimLocked( + mView.getContext(), mSubId))); + } + } else { + mMessageAreaController.setMessage(mView.getResources().getString( + R.string.kg_password_puk_failed)); + } + if (DEBUG) { + Log.d(TAG, "verifyPasswordAndUnlock " + + " UpdateSim.onSimCheckResponse: " + + " attemptsRemaining=" + result.getAttemptsRemaining()); + } + } + mStateMachine.reset(); + mCheckSimPukThread = null; + }); + } + }; + mCheckSimPukThread.start(); + } + } + + @Override + protected boolean shouldLockout(long deadline) { + // SIM PUK doesn't have a timed lockout + return false; + } + + private Dialog getSimUnlockProgressDialog() { + if (mSimUnlockProgressDialog == null) { + mSimUnlockProgressDialog = new ProgressDialog(mView.getContext()); + mSimUnlockProgressDialog.setMessage( + mView.getResources().getString(R.string.kg_sim_unlock_progress_dialog_message)); + mSimUnlockProgressDialog.setIndeterminate(true); + mSimUnlockProgressDialog.setCancelable(false); + if (!(mView.getContext() instanceof Activity)) { + mSimUnlockProgressDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } + } + return mSimUnlockProgressDialog; + } + + private void handleSubInfoChangeIfNeeded() { + int subId = mKeyguardUpdateMonitor.getNextSubIdForState( + TelephonyManager.SIM_STATE_PUK_REQUIRED); + if (subId != mSubId && SubscriptionManager.isValidSubscriptionId(subId)) { + mSubId = subId; + mShowDefaultMessage = true; + mRemainingAttempts = -1; + } + } + + + private Dialog getPukRemainingAttemptsDialog(int remaining) { + String msg = mView.getPukPasswordErrorMessage(remaining, false, + KeyguardEsimArea.isEsimLocked(mView.getContext(), mSubId)); + if (mRemainingAttemptsDialog == null) { + AlertDialog.Builder builder = new AlertDialog.Builder(mView.getContext()); + builder.setMessage(msg); + builder.setCancelable(false); + builder.setNeutralButton(R.string.ok, null); + mRemainingAttemptsDialog = builder.create(); + mRemainingAttemptsDialog.getWindow().setType( + WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + } else { + mRemainingAttemptsDialog.setMessage(msg); + } + return mRemainingAttemptsDialog; + } + + @Override + public void onPause() { + // dismiss the dialog. + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.dismiss(); + mSimUnlockProgressDialog = null; + } + } + + /** + * Since the IPC can block, we want to run the request in a separate thread + * with a callback. + */ + private abstract class CheckSimPuk extends Thread { + + private final String mPin, mPuk; + private final int mSubId; + + protected CheckSimPuk(String puk, String pin, int subId) { + mPuk = puk; + mPin = pin; + mSubId = subId; + } + + abstract void onSimLockChangedResponse(@NonNull PinResult result); + + @Override + public void run() { + if (DEBUG) Log.v(TAG, "call supplyPukReportResult()"); + TelephonyManager telephonyManager = mTelephonyManager.createForSubscriptionId(mSubId); + final PinResult result = telephonyManager.supplyPukReportPinResult(mPuk, mPin); + if (result == null) { + Log.e(TAG, "Error result for supplyPukReportResult."); + mView.post(() -> onSimLockChangedResponse(PinResult.getDefaultFailedResult())); + } else { + if (DEBUG) { + Log.v(TAG, "supplyPukReportResult returned: " + result.toString()); + } + mView.post(new Runnable() { + @Override + public void run() { + onSimLockChangedResponse(result); + } + }); + } + } + } + +} diff --git a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java index e59602b1cfff..425e50ed6397 100644 --- a/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java +++ b/packages/SystemUI/src/com/android/keyguard/LiftToActivateListener.java @@ -16,11 +16,12 @@ package com.android.keyguard; -import android.content.Context; import android.view.MotionEvent; import android.view.View; import android.view.accessibility.AccessibilityManager; +import javax.inject.Inject; + /** * Hover listener that implements lift-to-activate interaction for * accessibility. May be added to multiple views. @@ -31,9 +32,9 @@ class LiftToActivateListener implements View.OnHoverListener { private boolean mCachedClickableState; - public LiftToActivateListener(Context context) { - mAccessibilityManager = (AccessibilityManager) context.getSystemService( - Context.ACCESSIBILITY_SERVICE); + @Inject + LiftToActivateListener(AccessibilityManager accessibilityManager) { + mAccessibilityManager = accessibilityManager; } @Override diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java index b0457fce6a1a..2205fdd4267d 100644 --- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java +++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java @@ -26,6 +26,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityManager; import android.widget.TextView; import com.android.internal.widget.LockPatternUtils; @@ -90,7 +91,8 @@ public class NumPadKey extends ViewGroup { } setOnClickListener(mListener); - setOnHoverListener(new LiftToActivateListener(context)); + setOnHoverListener(new LiftToActivateListener( + (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE))); mLockPatternUtils = new LockPatternUtils(context); mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); diff --git a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java index b6010c8915e7..881108858b51 100644 --- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java +++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardBouncerModule.java @@ -22,6 +22,7 @@ import android.view.ViewGroup; import com.android.keyguard.KeyguardHostView; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardSecurityContainer; +import com.android.keyguard.KeyguardSecurityViewFlipper; import com.android.systemui.R; import com.android.systemui.statusbar.phone.KeyguardBouncer; @@ -58,7 +59,15 @@ public interface KeyguardBouncerModule { /** */ @Provides @KeyguardBouncerScope - static KeyguardSecurityContainer preovidesKeyguardSecurityContainer(KeyguardHostView hostView) { + static KeyguardSecurityContainer providesKeyguardSecurityContainer(KeyguardHostView hostView) { return hostView.findViewById(R.id.keyguard_security_container); } + + /** */ + @Provides + @KeyguardBouncerScope + static KeyguardSecurityViewFlipper providesKeyguardSecurityViewFlipper( + KeyguardSecurityContainer containerView) { + return containerView.findViewById(R.id.view_flipper); + } } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java index c90e6b1360df..e3037543f2e3 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/DependencyProvider.java @@ -289,6 +289,7 @@ public class DependencyProvider { /** */ @Provides + @SysUISingleton public LockPatternUtils provideLockPatternUtils(Context context) { return new LockPatternUtils(context); } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java index b35579d3624b..79925bad3cc7 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java @@ -62,6 +62,7 @@ import android.view.ViewConfiguration; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityManager; +import android.view.inputmethod.InputMethodManager; import com.android.internal.app.IBatteryStats; import com.android.internal.statusbar.IStatusBarService; @@ -183,6 +184,12 @@ public class FrameworkServicesModule { @Provides @Singleton + static InputMethodManager provideInputMethodManager(Context context) { + return context.getSystemService(InputMethodManager.class); + } + + @Provides + @Singleton static IPackageManager provideIPackageManager() { return IPackageManager.Stub.asInterface(ServiceManager.getService("package")); } diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java index eb8f065149c8..a6cd350b33ce 100644 --- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java +++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java @@ -23,7 +23,6 @@ import android.view.InflateException; import android.view.LayoutInflater; import android.view.View; -import com.android.keyguard.KeyguardMessageArea; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.qs.QSFooterImpl; import com.android.systemui.qs.QSPanel; @@ -108,11 +107,6 @@ public class InjectionInflationController { NotificationStackScrollLayout createNotificationStackScrollLayout(); /** - * Creates the KeyguardMessageArea. - */ - KeyguardMessageArea createKeyguardMessageArea(); - - /** * Creates the QSPanel. */ QSPanel createQSPanel(); diff --git a/packages/SystemUI/src/com/android/systemui/util/ViewController.java b/packages/SystemUI/src/com/android/systemui/util/ViewController.java index 64f8dbbb9e34..c7aa780fcacb 100644 --- a/packages/SystemUI/src/com/android/systemui/util/ViewController.java +++ b/packages/SystemUI/src/com/android/systemui/util/ViewController.java @@ -23,7 +23,20 @@ import android.view.View.OnAttachStateChangeListener; * Utility class that handles view lifecycle events for View Controllers. * * Implementations should handle setup and teardown related activities inside of - * {@link #onViewAttached()} and {@link #onViewDetached()}. + * {@link #onViewAttached()} and {@link #onViewDetached()}. Be sure to call {@link #init()} on + * any child controllers that this uses. This can be done in {@link init()} if the controllers + * are injected, or right after creation time of the child controller. + * + * Tip: View "attachment" happens top down - parents are notified that they are attached before + * any children. That means that if you call a method on a child controller in + * {@link #onViewAttached()}, the child controller may not have had its onViewAttach method + * called, so it may not be fully set up. + * + * As such, make sure that methods on your controller are safe to call _before_ its {@link #init()} + * and {@link #onViewAttached()} methods are called. Specifically, if your controller must call + * {@link View#findViewById(int)} on its root view to setup member variables, do so in its + * constructor. Save {@link #onViewAttached()} for things that can happen post-construction - adding + * listeners, dynamically changing content, or other runtime decisions. * * @param <T> View class that this ViewController is for. */ @@ -54,10 +67,12 @@ public abstract class ViewController<T extends View> { } mInited = true; - if (mView.isAttachedToWindow()) { - mOnAttachStateListener.onViewAttachedToWindow(mView); + if (mView != null) { + if (mView.isAttachedToWindow()) { + mOnAttachStateListener.onViewAttachedToWindow(mView); + } + mView.addOnAttachStateChangeListener(mOnAttachStateListener); } - mView.addOnAttachStateChangeListener(mOnAttachStateListener); } /** diff --git a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java index 9be2d124026c..dffad6ccbea5 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/AdminSecondaryLockScreenControllerTest.java @@ -41,8 +41,6 @@ import android.testing.TestableLooper.RunWithLooper; import android.testing.ViewUtils; import android.view.SurfaceControlViewHost; import android.view.SurfaceView; -import android.view.ViewGroup; -import android.widget.FrameLayout; import androidx.test.filters.SmallTest; @@ -67,7 +65,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private ComponentName mComponentName; private Intent mServiceIntent; private TestableLooper mTestableLooper; - private ViewGroup mParent; + private KeyguardSecurityContainer mKeyguardSecurityContainer; @Mock private Handler mHandler; @@ -84,8 +82,8 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { public void setUp() { MockitoAnnotations.initMocks(this); - mParent = spy(new FrameLayout(mContext)); - ViewUtils.attachView(mParent); + mKeyguardSecurityContainer = spy(new KeyguardSecurityContainer(mContext)); + ViewUtils.attachView(mKeyguardSecurityContainer); mTestableLooper = TestableLooper.get(this); mComponentName = new ComponentName(mContext, "FakeKeyguardClient.class"); @@ -96,13 +94,14 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { when(mKeyguardClient.queryLocalInterface(anyString())).thenReturn(mKeyguardClient); when(mKeyguardClient.asBinder()).thenReturn(mKeyguardClient); - mTestController = new AdminSecondaryLockScreenController( - mContext, mParent, mUpdateMonitor, mKeyguardCallback, mHandler); + mTestController = new AdminSecondaryLockScreenController.Factory( + mContext, mKeyguardSecurityContainer, mUpdateMonitor, mHandler) + .create(mKeyguardCallback); } @After public void tearDown() { - ViewUtils.detachView(mParent); + ViewUtils.detachView(mKeyguardSecurityContainer); } @Test @@ -146,7 +145,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { SurfaceView v = verifySurfaceReady(); mTestController.hide(); - verify(mParent).removeView(v); + verify(mKeyguardSecurityContainer).removeView(v); assertThat(mContext.isBound(mComponentName)).isFalse(); } @@ -154,7 +153,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { public void testHide_notShown() throws Exception { mTestController.hide(); // Nothing should happen if trying to hide when the view isn't attached yet. - verify(mParent, never()).removeView(any(SurfaceView.class)); + verify(mKeyguardSecurityContainer, never()).removeView(any(SurfaceView.class)); } @Test @@ -182,7 +181,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { private SurfaceView verifySurfaceReady() throws Exception { mTestableLooper.processAllMessages(); ArgumentCaptor<SurfaceView> captor = ArgumentCaptor.forClass(SurfaceView.class); - verify(mParent).addView(captor.capture()); + verify(mKeyguardSecurityContainer).addView(captor.capture()); mTestableLooper.processAllMessages(); verify(mKeyguardClient).onCreateKeyguardSurface(any(), any(IKeyguardCallback.class)); @@ -190,7 +189,7 @@ public class AdminSecondaryLockScreenControllerTest extends SysuiTestCase { } private void verifyViewDismissed(SurfaceView v) throws Exception { - verify(mParent).removeView(v); + verify(mKeyguardSecurityContainer).removeView(v); verify(mKeyguardCallback).dismiss(true, TARGET_USER_ID, true); assertThat(mContext.isBound(mComponentName)).isFalse(); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java new file mode 100644 index 000000000000..c2ade81a9877 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java @@ -0,0 +1,122 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.KeyEvent; + +import androidx.test.filters.SmallTest; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardAbsKeyInputView.KeyDownListener; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class KeyguardAbsKeyInputViewControllerTest extends SysuiTestCase { + + @Mock + private KeyguardAbsKeyInputView mAbsKeyInputView; + @Mock + private PasswordTextView mPasswordEntry; + @Mock + private KeyguardMessageArea mKeyguardMessageArea; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private SecurityMode mSecurityMode; + @Mock + private LockPatternUtils mLockPatternUtils; + @Mock + private KeyguardSecurityCallback mKeyguardSecurityCallback; + @Mock + private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; + @Mock + private KeyguardMessageAreaController mKeyguardMessageAreaController; + @Mock + private LatencyTracker mLatencyTracker; + + private KeyguardAbsKeyInputViewController mKeyguardAbsKeyInputViewController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) + .thenReturn(mKeyguardMessageAreaController); + when(mAbsKeyInputView.getPasswordTextViewId()).thenReturn(1); + when(mAbsKeyInputView.findViewById(1)).thenReturn(mPasswordEntry); + when(mAbsKeyInputView.isAttachedToWindow()).thenReturn(true); + when(mAbsKeyInputView.findViewById(R.id.keyguard_message_area)) + .thenReturn(mKeyguardMessageArea); + mKeyguardAbsKeyInputViewController = new KeyguardAbsKeyInputViewController(mAbsKeyInputView, + mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, + mKeyguardMessageAreaControllerFactory, mLatencyTracker) { + @Override + void resetState() { + } + + @Override + public void onResume(int reason) { + super.onResume(reason); + } + }; + mKeyguardAbsKeyInputViewController.init(); + reset(mKeyguardMessageAreaController); // Clear out implicit call to init. + } + + @Test + public void onKeyDown_clearsSecurityMessage() { + ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor = + ArgumentCaptor.forClass(KeyDownListener.class); + verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture()); + onKeyDownListenerArgumentCaptor.getValue().onKeyDown( + KeyEvent.KEYCODE_0, mock(KeyEvent.class)); + verify(mKeyguardSecurityCallback).userActivity(); + verify(mKeyguardMessageAreaController).setMessage(eq("")); + } + + @Test + public void onKeyDown_noSecurityMessageInteraction() { + ArgumentCaptor<KeyDownListener> onKeyDownListenerArgumentCaptor = + ArgumentCaptor.forClass(KeyDownListener.class); + verify(mAbsKeyInputView).setKeyDownListener(onKeyDownListenerArgumentCaptor.capture()); + onKeyDownListenerArgumentCaptor.getValue().onKeyDown( + KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class)); + verifyZeroInteractions(mKeyguardSecurityCallback); + verifyZeroInteractions(mKeyguardMessageAreaController); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index 5999e2cdec78..e7930795c7f8 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -41,11 +41,9 @@ import android.widget.FrameLayout; import android.widget.TextClock; import com.android.systemui.R; -import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.util.InjectionInflationController; import org.junit.Before; import org.junit.Test; @@ -78,12 +76,7 @@ public class KeyguardClockSwitchTest extends SysuiTestCase { when(mMockKeyguardSliceView.findViewById(R.id.keyguard_status_area)) .thenReturn(mMockKeyguardSliceView); - InjectionInflationController inflationController = new InjectionInflationController( - SystemUIFactory.getInstance() - .getSysUIComponent() - .createViewInstanceCreatorFactory()); - LayoutInflater layoutInflater = inflationController - .injectable(LayoutInflater.from(getContext())); + LayoutInflater layoutInflater = LayoutInflater.from(getContext()); layoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java index 54e879e2ff38..64632afe9bfa 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardHostViewControllerTest.java @@ -16,8 +16,10 @@ package com.android.keyguard; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.media.AudioManager; import android.telephony.TelephonyManager; @@ -47,13 +49,15 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase { @Mock private KeyguardHostView mKeyguardHostView; @Mock - private KeyguardSecurityContainerController mKeyguardSecurityContainerController; - @Mock private AudioManager mAudioManager; @Mock private TelephonyManager mTelephonyManager; @Mock private ViewMediatorCallback mViewMediatorCallback; + @Mock + KeyguardSecurityContainerController.Factory mKeyguardSecurityContainerControllerFactory; + @Mock + private KeyguardSecurityContainerController mKeyguardSecurityContainerController; @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule(); @@ -62,9 +66,12 @@ public class KeyguardHostViewControllerTest extends SysuiTestCase { @Before public void setup() { + when(mKeyguardSecurityContainerControllerFactory.create(any( + KeyguardSecurityContainer.SecurityCallback.class))) + .thenReturn(mKeyguardSecurityContainerController); mKeyguardHostViewController = new KeyguardHostViewController( - mKeyguardHostView, mKeyguardUpdateMonitor, mKeyguardSecurityContainerController, - mAudioManager, mTelephonyManager, mViewMediatorCallback); + mKeyguardHostView, mKeyguardUpdateMonitor, mAudioManager, mTelephonyManager, + mViewMediatorCallback, mKeyguardSecurityContainerControllerFactory); } @Test diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java new file mode 100644 index 000000000000..a7197cca530c --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaControllerTest.java @@ -0,0 +1,87 @@ +/* + * 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 org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; + +import android.test.suitebuilder.annotation.SmallTest; +import android.testing.AndroidTestingRunner; + +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class KeyguardMessageAreaControllerTest extends SysuiTestCase { + @Mock + private ConfigurationController mConfigurationController; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private KeyguardMessageArea mKeyguardMessageArea; + + private KeyguardMessageAreaController mMessageAreaController; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + mMessageAreaController = new KeyguardMessageAreaController.Factory( + mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea); + } + + @Test + public void onAttachedToWindow_registersConfigurationCallback() { + ArgumentCaptor<ConfigurationListener> configurationListenerArgumentCaptor = + ArgumentCaptor.forClass(ConfigurationListener.class); + + mMessageAreaController.onViewAttached(); + verify(mConfigurationController).addCallback(configurationListenerArgumentCaptor.capture()); + + mMessageAreaController.onViewDetached(); + verify(mConfigurationController).removeCallback( + eq(configurationListenerArgumentCaptor.getValue())); + } + + @Test + public void onAttachedToWindow_registersKeyguardUpdateMontiorCallback() { + ArgumentCaptor<KeyguardUpdateMonitorCallback> keyguardUpdateMonitorCallbackArgumentCaptor = + ArgumentCaptor.forClass(KeyguardUpdateMonitorCallback.class); + + mMessageAreaController.onViewAttached(); + verify(mKeyguardUpdateMonitor).registerCallback( + keyguardUpdateMonitorCallbackArgumentCaptor.capture()); + + mMessageAreaController.onViewDetached(); + verify(mKeyguardUpdateMonitor).removeCallback( + eq(keyguardUpdateMonitorCallbackArgumentCaptor.getValue())); + } + + @Test + public void testClearsTextField() { + mMessageAreaController.setMessage(""); + verify(mKeyguardMessageArea).setMessage(""); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java index fc7b9a4b47d1..31fb25a7a89c 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardMessageAreaTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016 The Android Open Source Project + * 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. @@ -11,65 +11,60 @@ * 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 + * limitations under the License. */ package com.android.keyguard; -import static junit.framework.Assert.assertEquals; - -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.verify; +import static com.google.common.truth.Truth.assertThat; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; +import android.view.View; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.ConfigurationController; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.mockito.Mock; import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class KeyguardMessageAreaTest extends SysuiTestCase { - @Mock - private ConfigurationController mConfigurationController; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private KeyguardMessageArea mMessageArea; + private KeyguardMessageArea mKeyguardMessageArea; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); - mMessageArea = new KeyguardMessageArea(mContext, null, mKeyguardUpdateMonitor, - mConfigurationController); - waitForIdleSync(); + mKeyguardMessageArea = new KeyguardMessageArea(mContext, null); + mKeyguardMessageArea.setBouncerVisible(true); } @Test - public void onAttachedToWindow_registersConfigurationCallback() { - mMessageArea.onAttachedToWindow(); - verify(mConfigurationController).addCallback(eq(mMessageArea)); - - mMessageArea.onDetachedFromWindow(); - verify(mConfigurationController).removeCallback(eq(mMessageArea)); + public void testShowsTextField() { + mKeyguardMessageArea.setVisibility(View.INVISIBLE); + mKeyguardMessageArea.setMessage("oobleck"); + assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.VISIBLE); + assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck"); } @Test - public void clearFollowedByMessage_keepsMessage() { - mMessageArea.setMessage(""); - mMessageArea.setMessage("test"); - - CharSequence[] messageText = new CharSequence[1]; - messageText[0] = mMessageArea.getText(); - - assertEquals("test", messageText[0]); + public void testHiddenWhenBouncerHidden() { + mKeyguardMessageArea.setBouncerVisible(false); + mKeyguardMessageArea.setVisibility(View.INVISIBLE); + mKeyguardMessageArea.setMessage("oobleck"); + assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE); + assertThat(mKeyguardMessageArea.getText()).isEqualTo("oobleck"); } + @Test + public void testClearsTextField() { + mKeyguardMessageArea.setVisibility(View.VISIBLE); + mKeyguardMessageArea.setMessage(""); + assertThat(mKeyguardMessageArea.getVisibility()).isEqualTo(View.INVISIBLE); + assertThat(mKeyguardMessageArea.getText()).isEqualTo(""); + } } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt new file mode 100644 index 000000000000..c69ec1a254c3 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt @@ -0,0 +1,84 @@ +/* + * 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 android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.internal.util.LatencyTracker +import com.android.internal.widget.LockPatternUtils +import com.android.internal.widget.LockPatternView +import com.android.systemui.R +import com.android.systemui.SysuiTestCase +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito.`when` +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper +class KeyguardPatternViewControllerTest : SysuiTestCase() { + @Mock + private lateinit var mKeyguardPatternView: KeyguardPatternView + @Mock + private lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock + private lateinit var mSecurityMode: KeyguardSecurityModel.SecurityMode + @Mock + private lateinit var mLockPatternUtils: LockPatternUtils + @Mock + private lateinit var mKeyguardSecurityCallback: KeyguardSecurityCallback + @Mock + private lateinit var mLatencyTracker: LatencyTracker + @Mock + private lateinit + var mKeyguardMessageAreaControllerFactory: KeyguardMessageAreaController.Factory + @Mock + private lateinit var mKeyguardMessageArea: KeyguardMessageArea + @Mock + private lateinit var mKeyguardMessageAreaController: KeyguardMessageAreaController + @Mock + private lateinit var mLockPatternView: LockPatternView + + private lateinit var mKeyguardPatternViewController: KeyguardPatternViewController + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + `when`(mKeyguardPatternView.isAttachedToWindow).thenReturn(true) + `when`(mKeyguardPatternView.findViewById<KeyguardMessageArea>(R.id.keyguard_message_area)) + .thenReturn(mKeyguardMessageArea) + `when`(mKeyguardPatternView.findViewById<LockPatternView>(R.id.lockPatternView)) + .thenReturn(mLockPatternView) + `when`(mKeyguardMessageAreaControllerFactory.create(mKeyguardMessageArea)) + .thenReturn(mKeyguardMessageAreaController) + mKeyguardPatternViewController = KeyguardPatternViewController(mKeyguardPatternView, + mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, + mLatencyTracker, mKeyguardMessageAreaControllerFactory) + } + + @Test + fun onPause_clearsTextField() { + mKeyguardPatternViewController.init() + mKeyguardPatternViewController.onPause() + verify(mKeyguardMessageAreaController).setMessage("") + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt deleted file mode 100644 index b4363cf215f1..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewTest.kt +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (C) 2018 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 androidx.test.filters.SmallTest -import android.testing.AndroidTestingRunner -import android.testing.TestableLooper -import android.view.LayoutInflater - -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.statusbar.policy.ConfigurationController -import com.google.common.truth.Truth.assertThat - -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mockito.mock - -@SmallTest -@RunWith(AndroidTestingRunner::class) -@TestableLooper.RunWithLooper -class KeyguardPatternViewTest : SysuiTestCase() { - - private lateinit var mKeyguardPatternView: KeyguardPatternView - private lateinit var mSecurityMessage: KeyguardMessageArea - - @Before - fun setup() { - val inflater = LayoutInflater.from(context) - mDependency.injectMockDependency(KeyguardUpdateMonitor::class.java) - mKeyguardPatternView = inflater.inflate(R.layout.keyguard_pattern_view, null) - as KeyguardPatternView - mSecurityMessage = KeyguardMessageArea(mContext, null, - mock(KeyguardUpdateMonitor::class.java), mock(ConfigurationController::class.java)) - mKeyguardPatternView.mSecurityMessageDisplay = mSecurityMessage - } - - @Test - fun onPause_clearsTextField() { - mSecurityMessage.setMessage("an old message") - mKeyguardPatternView.onPause() - assertThat(mSecurityMessage.text).isEqualTo("") - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java new file mode 100644 index 000000000000..4944284698a0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java @@ -0,0 +1,106 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper.RunWithLooper; +import android.view.View; + +import androidx.test.filters.SmallTest; + +import com.android.internal.util.LatencyTracker; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase { + + @Mock + private KeyguardPinBasedInputView mPinBasedInputView; + @Mock + private PasswordTextView mPasswordEntry; + @Mock + private KeyguardMessageArea mKeyguardMessageArea; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private SecurityMode mSecurityMode; + @Mock + private LockPatternUtils mLockPatternUtils; + @Mock + private KeyguardSecurityCallback mKeyguardSecurityCallback; + @Mock + private KeyguardMessageAreaController.Factory mKeyguardMessageAreaControllerFactory; + @Mock + private KeyguardMessageAreaController mKeyguardMessageAreaController; + @Mock + private LatencyTracker mLatencyTracker; + @Mock + private LiftToActivateListener mLiftToactivateListener; + @Mock + private View mDeleteButton; + @Mock + private View mOkButton; + + private KeyguardPinBasedInputViewController mKeyguardPinViewController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + when(mKeyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea.class))) + .thenReturn(mKeyguardMessageAreaController); + when(mPinBasedInputView.getPasswordTextViewId()).thenReturn(1); + when(mPinBasedInputView.findViewById(1)).thenReturn(mPasswordEntry); + when(mPinBasedInputView.isAttachedToWindow()).thenReturn(true); + when(mPinBasedInputView.findViewById(R.id.keyguard_message_area)) + .thenReturn(mKeyguardMessageArea); + when(mPinBasedInputView.findViewById(R.id.delete_button)) + .thenReturn(mDeleteButton); + when(mPinBasedInputView.findViewById(R.id.key_enter)) + .thenReturn(mOkButton); + mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView, + mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback, + mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) { + @Override + public void onResume(int reason) { + super.onResume(reason); + } + }; + mKeyguardPinViewController.init(); + } + + @Test + public void onResume_requestsFocus() { + mKeyguardPinViewController.onResume(KeyguardSecurityView.SCREEN_ON); + verify(mPasswordEntry).requestFocus(); + } +} + diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java deleted file mode 100644 index 6666a926c68b..000000000000 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2018 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 org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; - -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper.RunWithLooper; -import android.view.KeyEvent; -import android.view.LayoutInflater; - -import androidx.test.filters.SmallTest; - -import com.android.systemui.R; -import com.android.systemui.SysuiTestCase; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidTestingRunner.class) -@RunWithLooper -public class KeyguardPinBasedInputViewTest extends SysuiTestCase { - - @Mock - private PasswordTextView mPasswordEntry; - @Mock - private SecurityMessageDisplay mSecurityMessageDisplay; - @InjectMocks - private KeyguardPinBasedInputView mKeyguardPinView; - - @Before - public void setup() { - LayoutInflater inflater = LayoutInflater.from(getContext()); - mDependency.injectMockDependency(KeyguardUpdateMonitor.class); - mKeyguardPinView = - (KeyguardPinBasedInputView) inflater.inflate(R.layout.keyguard_pin_view, null); - MockitoAnnotations.initMocks(this); - } - - @Test - public void onResume_requestsFocus() { - mKeyguardPinView.onResume(KeyguardSecurityView.SCREEN_ON); - verify(mPasswordEntry).requestFocus(); - } - - @Test - public void onKeyDown_clearsSecurityMessage() { - mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_0, mock(KeyEvent.class)); - verify(mSecurityMessageDisplay).setMessage(eq("")); - } - - @Test - public void onKeyDown_noSecurityMessageInteraction() { - mKeyguardPinView.onKeyDown(KeyEvent.KEYCODE_UNKNOWN, mock(KeyEvent.class)); - verifyZeroInteractions(mSecurityMessageDisplay); - } -} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java index 559284ac0672..ae159c73b99f 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPresentationTest.java @@ -31,9 +31,7 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardDisplayManager.KeyguardPresentation; import com.android.keyguard.dagger.KeyguardStatusViewComponent; import com.android.systemui.R; -import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; -import com.android.systemui.util.InjectionInflationController; import org.junit.After; import org.junit.Before; @@ -65,7 +63,6 @@ public class KeyguardPresentationTest extends SysuiTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mDependency.injectMockDependency(KeyguardUpdateMonitor.class); when(mMockKeyguardClockSwitch.getContext()).thenReturn(mContext); when(mMockKeyguardSliceView.getContext()).thenReturn(mContext); when(mMockKeyguardStatusView.getContext()).thenReturn(mContext); @@ -77,11 +74,7 @@ public class KeyguardPresentationTest extends SysuiTestCase { allowTestableLooperAsMainThread(); - InjectionInflationController inflationController = new InjectionInflationController( - SystemUIFactory.getInstance() - .getSysUIComponent() - .createViewInstanceCreatorFactory()); - mLayoutInflater = inflationController.injectable(LayoutInflater.from(mContext)); + mLayoutInflater = LayoutInflater.from(mContext); mLayoutInflater.setPrivateFactory(new LayoutInflater.Factory2() { @Override diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java new file mode 100644 index 000000000000..eef38d316775 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java @@ -0,0 +1,129 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.WindowInsetsController; + +import androidx.test.filters.SmallTest; + +import com.android.internal.logging.MetricsLogger; +import com.android.internal.logging.UiEventLogger; +import com.android.internal.widget.LockPatternUtils; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.policy.KeyguardStateController; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper() +public class KeyguardSecurityContainerControllerTest extends SysuiTestCase { + + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + + @Mock + private KeyguardSecurityContainer mView; + @Mock + private AdminSecondaryLockScreenController.Factory mAdminSecondaryLockScreenControllerFactory; + @Mock + private AdminSecondaryLockScreenController mAdminSecondaryLockScreenController; + @Mock + private LockPatternUtils mLockPatternUtils; + @Mock + private KeyguardUpdateMonitor mKeyguardUpdateMonitor; + @Mock + private KeyguardSecurityModel mKeyguardSecurityModel; + @Mock + private MetricsLogger mMetricsLogger; + @Mock + private UiEventLogger mUiEventLogger; + @Mock + private KeyguardStateController mKeyguardStateController; + @Mock + private KeyguardInputViewController mInputViewController; + @Mock + private KeyguardSecurityContainer.SecurityCallback mSecurityCallback; + @Mock + private WindowInsetsController mWindowInsetsController; + @Mock + private KeyguardSecurityViewFlipper mSecurityViewFlipper; + @Mock + private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; + + private KeyguardSecurityContainerController mKeyguardSecurityContainerController; + + @Before + public void setup() { + when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class))) + .thenReturn(mAdminSecondaryLockScreenController); + when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); + + mKeyguardSecurityContainerController = new KeyguardSecurityContainerController.Factory( + mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils, + mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger, + mKeyguardStateController, mKeyguardSecurityViewFlipperController) + .create(mSecurityCallback); + } + + @Test + public void showSecurityScreen_canInflateAllModes() { + SecurityMode[] modes = SecurityMode.values(); + for (SecurityMode mode : modes) { + when(mInputViewController.getSecurityMode()).thenReturn(mode); + mKeyguardSecurityContainerController.showSecurityScreen(mode); + if (mode == SecurityMode.Invalid) { + verify(mKeyguardSecurityViewFlipperController, never()).getSecurityView( + any(SecurityMode.class), any(KeyguardSecurityCallback.class)); + } else { + verify(mKeyguardSecurityViewFlipperController).getSecurityView( + eq(mode), any(KeyguardSecurityCallback.class)); + } + } + } + + @Test + public void startDisappearAnimation_animatesKeyboard() { + when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( + SecurityMode.Password); + when(mInputViewController.getSecurityMode()).thenReturn( + SecurityMode.Password); + when(mKeyguardSecurityViewFlipperController.getSecurityView( + eq(SecurityMode.Password), any(KeyguardSecurityCallback.class))) + .thenReturn(mInputViewController); + mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */); + + mKeyguardSecurityContainerController.startDisappearAnimation(null); + verify(mInputViewController).startDisappearAnimation(eq(null)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index a867825e223d..854be1f76722 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -19,23 +19,19 @@ package com.android.keyguard; import static android.view.WindowInsets.Type.ime; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; -import android.view.LayoutInflater; import android.view.WindowInsetsController; import androidx.test.filters.SmallTest; -import com.android.systemui.R; +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; import com.android.systemui.SysuiTestCase; -import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.Before; import org.junit.Rule; @@ -50,68 +46,26 @@ import org.mockito.junit.MockitoRule; @TestableLooper.RunWithLooper() public class KeyguardSecurityContainerTest extends SysuiTestCase { - @Mock - private KeyguardSecurityModel mKeyguardSecurityModel; - @Mock - private KeyguardStateController mKeyguardStateController; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private KeyguardSecurityContainer.SecurityCallback mSecurityCallback; - @Mock - private KeyguardSecurityView mSecurityView; + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + @Mock private WindowInsetsController mWindowInsetsController; @Mock private KeyguardSecurityViewFlipper mSecurityViewFlipper; - @Rule - public MockitoRule mRule = MockitoJUnit.rule(); + private KeyguardSecurityContainer mKeyguardSecurityContainer; @Before public void setup() { - mDependency.injectTestDependency(KeyguardStateController.class, mKeyguardStateController); - mDependency.injectTestDependency(KeyguardSecurityModel.class, mKeyguardSecurityModel); - mDependency.injectTestDependency(KeyguardUpdateMonitor.class, mKeyguardUpdateMonitor); - mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()) { - @Override - protected KeyguardSecurityView getSecurityView( - KeyguardSecurityModel.SecurityMode securityMode) { - return mSecurityView; - } - }; - mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper; when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController); - mKeyguardSecurityContainer.setSecurityCallback(mSecurityCallback); - } - - @Test - public void showSecurityScreen_canInflateAllModes() { - Context context = getContext(); - - for (int theme : new int[] {R.style.Theme_SystemUI, R.style.Theme_SystemUI_Light}) { - context.setTheme(theme); - final LayoutInflater inflater = LayoutInflater.from(context); - KeyguardSecurityModel.SecurityMode[] modes = - KeyguardSecurityModel.SecurityMode.values(); - for (KeyguardSecurityModel.SecurityMode mode : modes) { - final int resId = mKeyguardSecurityContainer.getLayoutIdFor(mode); - if (resId == 0) { - continue; - } - inflater.inflate(resId, null /* root */, false /* attach */); - } - } + mKeyguardSecurityContainer = new KeyguardSecurityContainer(getContext()); + mKeyguardSecurityContainer.mSecurityViewFlipper = mSecurityViewFlipper; } @Test public void startDisappearAnimation_animatesKeyboard() { - when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( - KeyguardSecurityModel.SecurityMode.Password); - mKeyguardSecurityContainer.showPrimarySecurityScreen(false /* turningOff */); - - mKeyguardSecurityContainer.startDisappearAnimation(null); - verify(mSecurityView).startDisappearAnimation(eq(null)); + mKeyguardSecurityContainer.startDisappearAnimation(SecurityMode.Password); verify(mWindowInsetsController).controlWindowInsetsAnimation(eq(ime()), anyLong(), any(), any(), any()); } diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java new file mode 100644 index 000000000000..3b7f4b839853 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityViewFlipperControllerTest.java @@ -0,0 +1,102 @@ +/* + * 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 org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.view.LayoutInflater; +import android.view.ViewGroup; +import android.view.WindowInsetsController; + +import androidx.test.filters.SmallTest; + +import com.android.keyguard.KeyguardSecurityModel.SecurityMode; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@TestableLooper.RunWithLooper() +public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase { + + @Rule + public MockitoRule mRule = MockitoJUnit.rule(); + + @Mock + private KeyguardSecurityViewFlipper mView; + @Mock + private LayoutInflater mLayoutInflater; + @Mock + private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory; + @Mock + private KeyguardInputViewController mKeyguardInputViewController; + @Mock + private KeyguardInputView mInputView; + @Mock + private WindowInsetsController mWindowInsetsController; + @Mock + private KeyguardSecurityCallback mKeyguardSecurityCallback; + + private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController; + + @Before + public void setup() { + when(mKeyguardSecurityViewControllerFactory.create( + any(KeyguardInputView.class), any(SecurityMode.class), + any(KeyguardSecurityCallback.class))) + .thenReturn(mKeyguardInputViewController); + when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController); + + mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView, + mLayoutInflater, mKeyguardSecurityViewControllerFactory); + } + + @Test + public void showSecurityScreen_canInflateAllModes() { + SecurityMode[] modes = SecurityMode.values(); + // Always return an invalid controller so that we're always making a new one. + when(mKeyguardInputViewController.getSecurityMode()).thenReturn(SecurityMode.Invalid); + for (SecurityMode mode : modes) { + reset(mLayoutInflater); + when(mLayoutInflater.inflate(anyInt(), eq(mView), eq(false))) + .thenReturn(mInputView); + mKeyguardSecurityViewFlipperController.getSecurityView(mode, mKeyguardSecurityCallback); + if (mode == SecurityMode.Invalid || mode == SecurityMode.None) { + verify(mLayoutInflater, never()).inflate( + anyInt(), any(ViewGroup.class), anyBoolean()); + } else { + verify(mLayoutInflater).inflate(anyInt(), eq(mView), eq(false)); + } + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java index 0431704778c3..79ec4f2c553a 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewTest.java @@ -24,9 +24,7 @@ import android.testing.TestableLooper.RunWithLooper; import android.view.LayoutInflater; import com.android.systemui.R; -import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; -import com.android.systemui.util.InjectionInflationController; import org.junit.Before; import org.junit.Test; @@ -50,13 +48,7 @@ public class KeyguardStatusViewTest extends SysuiTestCase { @Before public void setUp() { allowTestableLooperAsMainThread(); - mDependency.injectMockDependency(KeyguardUpdateMonitor.class); - InjectionInflationController inflationController = new InjectionInflationController( - SystemUIFactory.getInstance() - .getSysUIComponent() - .createViewInstanceCreatorFactory()); - LayoutInflater layoutInflater = inflationController - .injectable(LayoutInflater.from(getContext())); + LayoutInflater layoutInflater = LayoutInflater.from(getContext()); mKeyguardStatusView = (KeyguardStatusView) layoutInflater.inflate(R.layout.keyguard_status_view, null); org.mockito.MockitoAnnotations.initMocks(this); |