summaryrefslogtreecommitdiff
path: root/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
diff options
context:
space:
mode:
authorDave Mankoff <mankoff@google.com>2020-09-23 14:01:33 +0000
committerDave Mankoff <mankoff@google.com>2020-09-24 11:04:51 -0400
commitcf2591fa361b6a046ead5b2c17ae0e9296200bb0 (patch)
treeca73165652bfd133ba24d9d9f11958a653a849bd /packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
parent4873666fc075f59f65b72dc119e038ee209d2942 (diff)
Revert^2 "4/N Setup Controller fo KeyguardSecurityContainer."
0d6a9012a73a2d999d52aaefca0a6ad52322db31 Change-Id: I881bbcdc63fcbafa8966064cccb7ce1edc710062
Diffstat (limited to 'packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java')
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java460
1 files changed, 442 insertions, 18 deletions
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 17f25bd08ef4..11f951be9a15 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -16,33 +16,197 @@
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 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;
+ }
+ }
+
}