4/N Setup Controller fo KeyguardSecurityContainer.

Move the guts of KeyguardSecurityContainer into
KeyguardSecurityContainerController. This removes a lot of
Dependency#get calls, and is another step towards simplifying our
view inflation process.

CustomViews that previously implemented KeyguardSecurityView now
all extend KeyguardInputView, allowing them to be simultaneously
treated as one while also being recognized as a View (the interface
required a lot of casting back and forth to a View).

LockscreenUtil is made a Singleton in this CL.

Bug: 166448040
Test: atest SystemUITests && manual
Change-Id: I6fa05012c55f5e003ab551d2f8360891a62fa2a7
This commit is contained in:
Dave Mankoff
2020-09-02 15:26:42 -04:00
parent e1e67c57a6
commit 8f75cf9944
14 changed files with 904 additions and 655 deletions

View File

@@ -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);
}
}
}

View File

@@ -28,7 +28,6 @@ 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;
@@ -40,8 +39,8 @@ import com.android.systemui.R;
/**
* Base class for PIN and password unlock screens.
*/
public abstract class KeyguardAbsKeyInputView extends LinearLayout
implements KeyguardSecurityView, EmergencyButton.EmergencyButtonCallback {
public abstract class KeyguardAbsKeyInputView extends KeyguardInputView
implements EmergencyButton.EmergencyButtonCallback {
protected KeyguardSecurityCallback mCallback;
protected LockPatternUtils mLockPatternUtils;
protected AsyncTask<?, ?, ?> mPendingLockCheck;

View File

@@ -336,7 +336,7 @@ public class KeyguardHostViewController extends ViewController<KeyguardHostView>
}
public SecurityMode getCurrentSecurityMode() {
return mKeyguardSecurityContainerController.getCurrentSecurityMode();
return mKeyguardSecurityContainerController.getCurrentSecuritySelection();
}
public int getTop() {

View File

@@ -0,0 +1,42 @@
/*
* 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.widget.LinearLayout;
import androidx.annotation.Nullable;
/**
* A Base class for all Keyguard password/pattern/pin related inputs.
*/
public abstract class KeyguardInputView extends LinearLayout implements KeyguardSecurityView {
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);
}
}

View File

@@ -0,0 +1,160 @@
/*
* 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.view.MotionEvent;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
/** Controller for a {@link KeyguardSecurityView}. */
public class KeyguardInputViewController extends ViewController<KeyguardInputView>
implements KeyguardSecurityView {
private final SecurityMode mSecurityMode;
private final LockPatternUtils mLockPatternUtils;
private KeyguardInputViewController(KeyguardInputView view, SecurityMode securityMode,
LockPatternUtils lockPatternUtils,
KeyguardSecurityCallback keyguardSecurityCallback) {
super(view);
mSecurityMode = securityMode;
mLockPatternUtils = lockPatternUtils;
mView.setKeyguardCallback(keyguardSecurityCallback);
}
@Override
public void init() {
super.init();
mView.reset();
}
@Override
protected void onViewAttached() {
}
@Override
protected void onViewDetached() {
}
SecurityMode getSecurityMode() {
return mSecurityMode;
}
@Override
public void setKeyguardCallback(KeyguardSecurityCallback callback) {
mView.setKeyguardCallback(callback);
}
@Override
public void setLockPatternUtils(LockPatternUtils utils) {
mView.setLockPatternUtils(utils);
}
@Override
public void reset() {
mView.reset();
}
@Override
public void onPause() {
mView.onPause();
}
@Override
public void onResume(int reason) {
mView.onResume(reason);
}
@Override
public boolean needsInput() {
return mView.needsInput();
}
@Override
public KeyguardSecurityCallback getCallback() {
return mView.getCallback();
}
@Override
public void showPromptReason(int reason) {
mView.showPromptReason(reason);
}
@Override
public void showMessage(CharSequence message, ColorStateList colorState) {
mView.showMessage(message, colorState);
}
@Override
public void showUsabilityHint() {
mView.showUsabilityHint();
}
@Override
public void startAppearAnimation() {
mView.startAppearAnimation();
}
@Override
public boolean startDisappearAnimation(Runnable finishRunnable) {
return mView.startDisappearAnimation(finishRunnable);
}
@Override
public CharSequence getTitle() {
return mView.getTitle();
}
@Override
public boolean disallowInterceptTouch(MotionEvent event) {
return mView.disallowInterceptTouch(event);
}
@Override
public void onStartingToHide() {
mView.onStartingToHide();
}
public void showSelf() {
KeyguardSecurityViewFlipper flipper = (KeyguardSecurityViewFlipper) mView.getParent();
flipper.setDisplayedChild(flipper.indexOfChild(mView));
}
/** Factory for a {@link KeyguardInputViewController}. */
public static class Factory {
private final LockPatternUtils mLockPatternUtils;
@Inject
public Factory(LockPatternUtils lockPatternUtils) {
mLockPatternUtils = lockPatternUtils;
}
/** Create a new {@link KeyguardInputViewController}. */
public KeyguardInputViewController create(KeyguardInputView keyguardInputView,
SecurityMode securityMode, KeyguardSecurityCallback keyguardSecurityCallback) {
return new KeyguardInputViewController(keyguardInputView, securityMode,
mLockPatternUtils, keyguardSecurityCallback);
}
}
}

View File

@@ -32,7 +32,6 @@ 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;
@@ -48,8 +47,8 @@ import com.android.systemui.R;
import java.util.List;
public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView,
AppearAnimationCreator<LockPatternView.CellState>,
public class KeyguardPatternView extends KeyguardInputView
implements AppearAnimationCreator<LockPatternView.CellState>,
EmergencyButton.EmergencyButtonCallback {
private static final String TAG = "SecurityPatternView";

View File

@@ -21,8 +21,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;
@@ -30,25 +28,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;
@@ -63,42 +50,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;
@@ -107,36 +82,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,21 +148,28 @@ public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSe
}
};
KeyguardSecurityViewFlipper getSecurityViewFlipper() {
return mSecurityViewFlipper;
}
// 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
@@ -251,51 +220,23 @@ 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;
@@ -318,13 +259,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;
}
@@ -372,31 +312,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() {
@@ -441,19 +378,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;
@@ -463,53 +394,15 @@ 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);
mSecurityViewFlipper.setLockPatternUtils(utils);
}
@Override
@@ -546,11 +439,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;
@@ -570,13 +464,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:
@@ -595,7 +489,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:
@@ -614,356 +508,19 @@ 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();
}

View File

@@ -16,33 +16,197 @@
package com.android.keyguard;
import android.content.res.ColorStateList;
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 android.view.LayoutInflater;
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.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.InjectionInflationController;
import com.android.systemui.util.ViewController;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
/** Controller for {@link KeyguardSecurityContainer} */
public class KeyguardSecurityContainerController extends ViewController<KeyguardSecurityContainer> {
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 LayoutInflater mLayoutInflater;
private final KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
private final List<KeyguardInputViewController> mChildren = new ArrayList<>();
private SecurityCallback mSecurityCallback;
private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
private KeyguardSecurityView mCurrentSecurityView;
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();
}
};
// 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 SwipeListener mSwipeListener = new SwipeListener() {
@Override
public void onSwipeUp() {
if (!mUpdateMonitor.isFaceDetectionRunning()) {
mUpdateMonitor.requestFaceAuth();
mKeyguardSecurityCallback.userActivity();
showMessage(null, null);
}
}
};
@Inject
KeyguardSecurityContainerController(KeyguardSecurityContainer view,
AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
LockPatternUtils lockPatternUtils,
KeyguardSecurityViewController.Factory keyguardSecurityViewControllerFactory) {
KeyguardUpdateMonitor keyguardUpdateMonitor,
KeyguardSecurityModel keyguardSecurityModel,
MetricsLogger metricsLogger,
UiEventLogger uiEventLogger,
KeyguardStateController keyguardStateController,
LayoutInflater layoutInflater,
InjectionInflationController injectionInflationController,
KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory) {
super(view);
mLockPatternUtils = lockPatternUtils;
mUpdateMonitor = keyguardUpdateMonitor;
mSecurityModel = keyguardSecurityModel;
mMetricsLogger = metricsLogger;
mUiEventLogger = uiEventLogger;
mKeyguardStateController = keyguardStateController;
mLayoutInflater = injectionInflationController.injectable(layoutInflater);
view.setLockPatternUtils(mLockPatternUtils);
mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
mAdminSecondaryLockScreenController = adminSecondaryLockScreenControllerFactory.create(
mKeyguardSecurityCallback);
}
@Override
public void init() {
super.init();
}
@Override
protected void onViewAttached() {
mView.setSwipeListener(mSwipeListener);
}
@Override
@@ -51,27 +215,48 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
/** */
public void onPause() {
mAdminSecondaryLockScreenController.hide();
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).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 (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) {
mView.showMessage(message, colorState);
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState);
}
}
public SecurityMode getCurrentSecuritySelection() {
return mView.getCurrentSecuritySelection();
return mCurrentSecuritySelection;
}
public void dismiss(boolean authenticated, int targetUserId) {
mView.dismiss(authenticated, targetUserId);
mKeyguardSecurityCallback.dismiss(authenticated, targetUserId);
}
public void reset() {
@@ -82,37 +267,276 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
return mView.getTitle();
}
public void onResume(int screenOn) {
mView.onResume(screenOn);
@Override
public void onResume(int reason) {
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).onResume(reason);
}
mView.onResume(
mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()),
mKeyguardStateController.isFaceAuthEnabled());
}
public void startAppearAnimation() {
mView.startAppearAnimation();
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
}
}
public boolean startDisappearAnimation(Runnable onFinishRunnable) {
return mView.startDisappearAnimation(onFinishRunnable);
mView.startDisappearAnimation(getCurrentSecuritySelection());
if (mCurrentSecuritySelection != SecurityMode.None) {
return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
onFinishRunnable);
}
return false;
}
public void onStartingToHide() {
mView.onStartingToHide();
if (mCurrentSecuritySelection != SecurityMode.None) {
getSecurityView(mCurrentSecuritySelection).onStartingToHide();
}
}
public void setSecurityCallback(SecurityCallback securityCallback) {
mView.setSecurityCallback(securityCallback);
mSecurityCallback = securityCallback;
}
/**
* 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 == getCurrentSecuritySelection()) {
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 (getCurrentSecuritySelection()) {
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 " + getCurrentSecuritySelection()
+ ", 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();
}
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
*/
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 (newView != null) {
newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
newView.setKeyguardCallback(mKeyguardSecurityCallback);
}
// Find and show this child.
for (KeyguardInputViewController child : mChildren) {
if (child.getSecurityMode().equals(securityMode)) {
child.showSelf();
}
}
mCurrentSecuritySelection = securityMode;
mCurrentSecurityView = 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));
}
}
@Override
public void setKeyguardCallback(KeyguardSecurityCallback callback) {
// no-op. This should never be reset.
}
@Override
public void setLockPatternUtils(LockPatternUtils utils) {
// We already have one of these.
}
@Override
public KeyguardSecurityCallback getCallback() {
return mView.getCallback();
}
@Override
public void showUsabilityHint() {
mView.showUsabilityHint();
}
private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
KeyguardInputViewController childController = null;
for (KeyguardInputViewController mChild : mChildren) {
if (mChild.getSecurityMode() == securityMode) {
childController = mChild;
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.getSecurityViewFlipper(), false);
mView.getSecurityViewFlipper().addView(view);
childController = mKeyguardSecurityViewControllerFactory.create(
view, securityMode, mKeyguardSecurityCallback);
mChildren.add(childController);
}
}
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;
}
}
}

View File

@@ -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) {

View File

@@ -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);
}
}
}

View File

@@ -288,6 +288,7 @@ public class DependencyProvider {
/** */
@Provides
@SysUISingleton
public LockPatternUtils provideLockPatternUtils(Context context) {
return new LockPatternUtils(context);
}

View File

@@ -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();
}

View File

@@ -0,0 +1,151 @@
/*
* 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.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 com.android.systemui.util.InjectionInflationController;
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 LayoutInflater mLayoutInflater;
@Mock
private InjectionInflationController mInjectionInflationController;
@Mock
private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
@Mock
private KeyguardInputViewController mKeyguardInputViewController;
@Mock
private KeyguardInputView mInputView;
@Mock
private KeyguardSecurityContainer.SecurityCallback mSecurityCallback;
@Mock
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityViewFlipper mSecurityViewFlipper;
private KeyguardSecurityContainerController mKeyguardSecurityContainerController;
@Before
public void setup() {
when(mAdminSecondaryLockScreenControllerFactory.create(any(KeyguardSecurityCallback.class)))
.thenReturn(mAdminSecondaryLockScreenController);
when(mInjectionInflationController.injectable(mLayoutInflater)).thenReturn(mLayoutInflater);
when(mKeyguardSecurityViewControllerFactory.create(
any(KeyguardInputView.class), any(SecurityMode.class),
any(KeyguardSecurityCallback.class)))
.thenReturn(mKeyguardInputViewController);
when(mView.getSecurityViewFlipper()).thenReturn(mSecurityViewFlipper);
when(mSecurityViewFlipper.getWindowInsetsController()).thenReturn(mWindowInsetsController);
mKeyguardSecurityContainerController = new KeyguardSecurityContainerController(
mView, mAdminSecondaryLockScreenControllerFactory, mLockPatternUtils,
mKeyguardUpdateMonitor, mKeyguardSecurityModel, mMetricsLogger, mUiEventLogger,
mKeyguardStateController, mLayoutInflater, mInjectionInflationController,
mKeyguardSecurityViewControllerFactory
);
mKeyguardSecurityContainerController.setSecurityCallback(mSecurityCallback);
}
@Test
public void showSecurityScreen_canInflateAllModes() {
KeyguardSecurityModel.SecurityMode[] modes =
KeyguardSecurityModel.SecurityMode.values();
for (KeyguardSecurityModel.SecurityMode mode : modes) {
reset(mLayoutInflater);
when(mLayoutInflater.inflate(anyInt(), eq(mSecurityViewFlipper), eq(false)))
.thenReturn(mInputView);
when(mKeyguardInputViewController.getSecurityMode()).thenReturn(mode);
mKeyguardSecurityContainerController.showSecurityScreen(mode);
if (mode == SecurityMode.Invalid || mode == SecurityMode.None) {
verify(mLayoutInflater, never()).inflate(
anyInt(), any(ViewGroup.class), anyBoolean());
} else {
verify(mLayoutInflater).inflate(anyInt(), eq(mSecurityViewFlipper), eq(false));
}
}
}
@Test
public void startDisappearAnimation_animatesKeyboard() {
when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(
KeyguardSecurityModel.SecurityMode.Password);
when(mKeyguardInputViewController.getSecurityMode()).thenReturn(
KeyguardSecurityModel.SecurityMode.Password);
when(mLayoutInflater.inflate(anyInt(), eq(mSecurityViewFlipper), eq(false)))
.thenReturn(mInputView);
mKeyguardSecurityContainerController.showPrimarySecurityScreen(false /* turningOff */);
mKeyguardSecurityContainerController.startDisappearAnimation(null);
verify(mKeyguardInputViewController).startDisappearAnimation(eq(null));
}
}

View File

@@ -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());
}