From e8fde5d9666eea10307cbc27f4b1a94d3cbb4ec9 Mon Sep 17 00:00:00 2001 From: Jorim Jaggi Date: Thu, 30 Jun 2016 23:41:37 -0700 Subject: [PATCH] Improve initial unlock delay (1/2) When checking for the credentials, we add a new callback onEarlyVerified which gets called as soon as we know that the credential was correct. In KeyguardUpdateMonitor, we track the unlocked state of the user, and if it's still locked, we slow down all the transitions to allow for a more gradual unlock experience. Bug: 29007436 Change-Id: I406d228f9f3e41e07fe3292a61df175a7f579e4d --- Android.mk | 1 + .../ICheckCredentialProgressCallback.aidl | 22 +++++++ .../internal/widget/ILockSettings.aidl | 7 +- .../internal/widget/LockPatternChecker.java | 11 +++- .../internal/widget/LockPatternUtils.java | 59 ++++++++++++++++- .../keyguard/KeyguardAbsKeyInputView.java | 13 +++- .../com/android/keyguard/KeyguardPINView.java | 13 +++- .../android/keyguard/KeyguardPatternView.java | 45 +++++++++---- .../keyguard/KeyguardUpdateMonitor.java | 31 +++++---- .../statusbar/phone/KeyguardBouncer.java | 1 - .../statusbar/phone/ScrimController.java | 39 +++++++++-- .../phone/StatusBarKeyguardViewManager.java | 10 ++- .../android/server/LockSettingsService.java | 66 ++++++++++++------- 13 files changed, 252 insertions(+), 66 deletions(-) create mode 100644 core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl diff --git a/Android.mk b/Android.mk index ba4e173ed0c77..8c39d19a322f0 100644 --- a/Android.mk +++ b/Android.mk @@ -343,6 +343,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/view/IInputMethodManager.aidl \ core/java/com/android/internal/view/IInputMethodSession.aidl \ core/java/com/android/internal/view/IInputSessionCallback.aidl \ + core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl \ core/java/com/android/internal/widget/ILockSettings.aidl \ core/java/com/android/internal/widget/IRemoteViewsFactory.aidl \ core/java/com/android/internal/widget/IRemoteViewsAdapterConnection.aidl \ diff --git a/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl b/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl new file mode 100644 index 0000000000000..79717cf2b0c9f --- /dev/null +++ b/core/java/com/android/internal/widget/ICheckCredentialProgressCallback.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2016 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.internal.widget; + +/** {@hide} */ +oneway interface ICheckCredentialProgressCallback { + void onCredentialVerified(); +} diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 05b839df9fa47..9fa558e89ae79 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -17,6 +17,7 @@ package com.android.internal.widget; import android.app.trust.IStrongAuthTracker; +import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.VerifyCredentialResponse; /** {@hide} */ @@ -29,10 +30,12 @@ interface ILockSettings { String getString(in String key, in String defaultValue, in int userId); void setLockPattern(in String pattern, in String savedPattern, int userId); void resetKeyStore(int userId); - VerifyCredentialResponse checkPattern(in String pattern, int userId); + VerifyCredentialResponse checkPattern(in String pattern, int userId, + in ICheckCredentialProgressCallback progressCallback); VerifyCredentialResponse verifyPattern(in String pattern, long challenge, int userId); void setLockPassword(in String password, in String savedPassword, int userId); - VerifyCredentialResponse checkPassword(in String password, int userId); + VerifyCredentialResponse checkPassword(in String password, int userId, + in ICheckCredentialProgressCallback progressCallback); VerifyCredentialResponse verifyPassword(in String password, long challenge, int userId); VerifyCredentialResponse verifyTiedProfileChallenge(String password, boolean isPattern, long challenge, int userId); boolean checkVoldPassword(int userId); diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java index 713f56f46266c..df9b0ddc18047 100644 --- a/core/java/com/android/internal/widget/LockPatternChecker.java +++ b/core/java/com/android/internal/widget/LockPatternChecker.java @@ -14,6 +14,13 @@ public final class LockPatternChecker { * Interface for a callback to be invoked after security check. */ public interface OnCheckCallback { + + /** + * Invoked as soon as possible we know that the credentials match. This will be called + * earlier than {@link #onChecked} but only if the credentials match. + */ + default void onEarlyMatched() {} + /** * Invoked when a security check is finished. * @@ -92,7 +99,7 @@ public final class LockPatternChecker { @Override protected Boolean doInBackground(Void... args) { try { - return utils.checkPattern(pattern, userId); + return utils.checkPattern(pattern, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; @@ -199,7 +206,7 @@ public final class LockPatternChecker { @Override protected Boolean doInBackground(Void... args) { try { - return utils.checkPassword(password, userId); + return utils.checkPassword(password, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 2e0dfa5fa7312..eff116b7570ce 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -17,6 +17,7 @@ package com.android.internal.widget; import android.annotation.IntDef; +import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; import android.app.trust.IStrongAuthTracker; import android.app.trust.TrustManager; @@ -32,7 +33,6 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; -import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.IMountService; @@ -149,6 +149,7 @@ public class LockPatternUtils { private DevicePolicyManager mDevicePolicyManager; private ILockSettings mLockSettingsService; private UserManager mUserManager; + private final Handler mHandler = new Handler(); /** * Use {@link TrustManager#isTrustUsuallyManaged(int)}. @@ -341,10 +342,23 @@ public class LockPatternUtils { */ public boolean checkPattern(List pattern, int userId) throws RequestThrottledException { + return checkPattern(pattern, userId, null /* progressCallback */); + } + + /** + * Check to see if a pattern matches the saved pattern. If no pattern exists, + * always returns true. + * @param pattern The pattern to check. + * @return Whether the pattern matches the stored one. + */ + public boolean checkPattern(List pattern, int userId, + @Nullable CheckCredentialProgressCallback progressCallback) + throws RequestThrottledException { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = - getLockSettings().checkPattern(patternToString(pattern), userId); + getLockSettings().checkPattern(patternToString(pattern), userId, + wrapCallback(progressCallback)); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return true; @@ -423,10 +437,22 @@ public class LockPatternUtils { * @return Whether the password matches the stored one. */ public boolean checkPassword(String password, int userId) throws RequestThrottledException { + return checkPassword(password, userId, null /* progressCallback */); + } + + /** + * Check to see if a password matches the saved password. If no password exists, + * always returns true. + * @param password The password to check. + * @return Whether the password matches the stored one. + */ + public boolean checkPassword(String password, int userId, + @Nullable CheckCredentialProgressCallback progressCallback) + throws RequestThrottledException { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = - getLockSettings().checkPassword(password, userId); + getLockSettings().checkPassword(password, userId, wrapCallback(progressCallback)); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return true; } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { @@ -1475,6 +1501,33 @@ public class LockPatternUtils { return (getStrongAuthForUser(userId) & ~StrongAuthTracker.ALLOWING_FINGERPRINT) == 0; } + private ICheckCredentialProgressCallback wrapCallback( + final CheckCredentialProgressCallback callback) { + if (callback == null) { + return null; + } else { + return new ICheckCredentialProgressCallback.Stub() { + + @Override + public void onCredentialVerified() throws RemoteException { + mHandler.post(callback::onEarlyMatched); + } + }; + } + } + + /** + * Callback to be notified about progress when checking credentials. + */ + public interface CheckCredentialProgressCallback { + + /** + * Called as soon as possible when we know that the credentials match but the user hasn't + * been fully unlocked. + */ + void onEarlyMatched(); + } + /** * Tracks the global strong authentication state. */ diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java index 60eaad2699eea..038e08d758eb0 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -137,12 +137,21 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout entry, userId, new LockPatternChecker.OnCheckCallback() { + + @Override + public void onEarlyMatched() { + onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, + true /* isValidPassword */); + } + @Override public void onChecked(boolean matched, int timeoutMs) { setPasswordEntryInputEnabled(true); mPendingLockCheck = null; - onPasswordChecked(userId, matched, timeoutMs, - true /* isValidPassword */); + if (!matched) { + onPasswordChecked(userId, false /* matched */, timeoutMs, + true /* isValidPassword */); + } } }); } diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java index 7ea767c004fb5..4f5152aca6f38 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPINView.java @@ -36,6 +36,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private final AppearAnimationUtils mAppearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtils; + private final DisappearAnimationUtils mDisappearAnimationUtilsLocked; private ViewGroup mContainer; private ViewGroup mRow0; private ViewGroup mRow1; @@ -44,6 +45,7 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { private View mDivider; private int mDisappearYTranslation; private View[][] mViews; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; public KeyguardPINView(Context context) { this(context, null); @@ -56,8 +58,14 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { 125, 0.6f /* translationScale */, 0.45f /* delayScale */, AnimationUtils.loadInterpolator( mContext, android.R.interpolator.fast_out_linear_in)); + mDisappearAnimationUtilsLocked = new DisappearAnimationUtils(context, + (long) (125 * KeyguardPatternView.DISAPPEAR_MULTIPLIER_LOCKED), + 0.6f /* translationScale */, + 0.45f /* delayScale */, AnimationUtils.loadInterpolator( + mContext, android.R.interpolator.fast_out_linear_in)); mDisappearYTranslation = getResources().getDimensionPixelSize( R.dimen.disappear_y_translation); + mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context); } @Override @@ -136,7 +144,10 @@ public class KeyguardPINView extends KeyguardPinBasedInputView { setTranslationY(0); AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 280 /* duration */, mDisappearYTranslation, mDisappearAnimationUtils.getInterpolator()); - mDisappearAnimationUtils.startAnimation2d(mViews, + DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor.isUserUnlocked() + ? mDisappearAnimationUtils + : mDisappearAnimationUtilsLocked; + disappearAnimationUtils.startAnimation2d(mViews, new Runnable() { @Override public void run() { diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java index e07049262c6ec..84b90c44c1a69 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java @@ -55,9 +55,13 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit // 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; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private final AppearAnimationUtils mAppearAnimationUtils; private final DisappearAnimationUtils mDisappearAnimationUtils; + private final DisappearAnimationUtils mDisappearAnimationUtilsLocked; private CountDownTimer mCountdownTimer = null; private LockPatternUtils mLockPatternUtils; @@ -109,6 +113,10 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit 125, 1.2f /* translationScale */, 0.6f /* delayScale */, AnimationUtils.loadInterpolator( mContext, android.R.interpolator.fast_out_linear_in)); + mDisappearAnimationUtilsLocked = new DisappearAnimationUtils(context, + (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); } @@ -239,11 +247,21 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit pattern, userId, new LockPatternChecker.OnCheckCallback() { + + @Override + public void onEarlyMatched() { + onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */, + true /* isValidPattern */); + } + @Override public void onChecked(boolean matched, int timeoutMs) { mLockPatternView.enableInput(); mPendingLockCheck = null; - onPatternChecked(userId, matched, timeoutMs, true); + if (!matched) { + onPatternChecked(userId, false /* matched */, timeoutMs, + true /* isValidPattern */); + } } }); if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) { @@ -390,25 +408,30 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit @Override public boolean startDisappearAnimation(final Runnable finishRunnable) { + float durationMultiplier = mKeyguardUpdateMonitor.isUserUnlocked() + ? 1f + : DISAPPEAR_MULTIPLIER_LOCKED; mLockPatternView.clearPattern(); enableClipping(false); setTranslationY(0); - AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 300 /* duration */, + AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, + (long) (300 * durationMultiplier), -mDisappearAnimationUtils.getStartTranslation(), mDisappearAnimationUtils.getInterpolator()); - mDisappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(), - new Runnable() { - @Override - public void run() { - enableClipping(true); - if (finishRunnable != null) { - finishRunnable.run(); - } + + DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor.isUserUnlocked() + ? mDisappearAnimationUtils + : mDisappearAnimationUtilsLocked; + disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(), + () -> { + enableClipping(true); + if (finishRunnable != null) { + finishRunnable.run(); } }, KeyguardPatternView.this); if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) { mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0, - 200, + (long) (200 * durationMultiplier), - mDisappearAnimationUtils.getStartTranslation() * 3, false /* appearing */, mDisappearAnimationUtils.getInterpolator(), diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java index 94d9550f77e41..dec1fd229e9f0 100644 --- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -16,6 +16,16 @@ package com.android.keyguard; +import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; +import static android.os.BatteryManager.BATTERY_STATUS_FULL; +import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; +import static android.os.BatteryManager.EXTRA_HEALTH; +import static android.os.BatteryManager.EXTRA_LEVEL; +import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; +import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; +import static android.os.BatteryManager.EXTRA_PLUGGED; +import static android.os.BatteryManager.EXTRA_STATUS; + import android.app.ActivityManager; import android.app.ActivityManagerNative; import android.app.AlarmManager; @@ -29,7 +39,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.graphics.Bitmap; -import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationCallback; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; @@ -42,6 +51,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; +import android.os.UserManager; import android.provider.Settings; import android.telephony.ServiceState; import android.telephony.SubscriptionInfo; @@ -69,16 +79,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map.Entry; -import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; -import static android.os.BatteryManager.BATTERY_STATUS_FULL; -import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; -import static android.os.BatteryManager.EXTRA_HEALTH; -import static android.os.BatteryManager.EXTRA_LEVEL; -import static android.os.BatteryManager.EXTRA_MAX_CHARGING_CURRENT; -import static android.os.BatteryManager.EXTRA_MAX_CHARGING_VOLTAGE; -import static android.os.BatteryManager.EXTRA_PLUGGED; -import static android.os.BatteryManager.EXTRA_STATUS; - /** * Watches for updates that may be interesting to the keyguard, and provides * the up to date information as well as a registration for callbacks that care @@ -176,6 +176,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private boolean mGoingToSleep; private boolean mBouncer; private boolean mBootCompleted; + private boolean mUserUnlocked; // Device provisioning state private boolean mDeviceProvisioned; @@ -202,6 +203,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private AlarmManager mAlarmManager; private List mSubscriptionInfo; private TrustManager mTrustManager; + private UserManager mUserManager; private int mFingerprintRunningState = FINGERPRINT_STATE_STOPPED; private final Handler mHandler = new Handler() { @@ -554,6 +556,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { && !hasFingerprintUnlockTimedOut(sCurrentUser); } + public boolean isUserUnlocked() { + return mUserUnlocked; + } + public StrongAuthTracker getStrongAuthTracker() { return mStrongAuthTracker; } @@ -1058,6 +1064,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { if (mFpm != null) { mFpm.addLockoutResetCallback(mLockoutResetCallback); } + + mUserManager = context.getSystemService(UserManager.class); } private void updateFingerprintListeningState() { @@ -1390,6 +1398,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private void handleKeyguardReset() { if (DEBUG) Log.d(TAG, "handleKeyguardReset"); updateFingerprintListeningState(); + mUserUnlocked = mUserManager.isUserUnlocked(getCurrentUser()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index ee88b0041b7d1..b3e86b5dcdaa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -234,7 +234,6 @@ public class KeyguardBouncer { mKeyguardView.setViewMediatorCallback(mCallback); mContainer.addView(mRoot, mContainer.getChildCount()); mRoot.setVisibility(View.INVISIBLE); - mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME); } protected void removeView() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 135c294987cf6..e7b57c5e88ac8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -30,6 +30,7 @@ import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; import android.view.animation.PathInterpolator; +import com.android.keyguard.KeyguardUpdateMonitor; import com.android.systemui.R; import com.android.systemui.statusbar.ExpandableNotificationRow; import com.android.systemui.statusbar.NotificationData; @@ -46,10 +47,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, public static final long ANIMATION_DURATION = 220; public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR = new PathInterpolator(0f, 0, 0.7f, 1f); + public static final Interpolator KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED + = new PathInterpolator(0.3f, 0f, 0.8f, 1f); private static final float SCRIM_BEHIND_ALPHA = 0.62f; private static final float SCRIM_BEHIND_ALPHA_KEYGUARD = 0.45f; private static final float SCRIM_BEHIND_ALPHA_UNLOCKING = 0.2f; private static final float SCRIM_IN_FRONT_ALPHA = 0.75f; + private static final float SCRIM_IN_FRONT_ALPHA_LOCKED = 0.85f; private static final int TAG_KEY_ANIM = R.id.scrim; private static final int TAG_KEY_ANIM_TARGET = R.id.scrim_target; private static final int TAG_START_ALPHA = R.id.scrim_alpha_start; @@ -59,6 +63,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, private final ScrimView mScrimInFront; private final UnlockMethodCache mUnlockMethodCache; private final View mHeadsUpScrim; + private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; private float mScrimBehindAlpha = SCRIM_BEHIND_ALPHA; private float mScrimBehindAlphaKeyguard = SCRIM_BEHIND_ALPHA_KEYGUARD; @@ -99,6 +104,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mHeadsUpScrim = headsUpScrim; final Context context = scrimBehind.getContext(); mUnlockMethodCache = UnlockMethodCache.getInstance(context); + mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(context); updateHeadsUpScrim(false); } @@ -162,11 +168,19 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, mAnimateChange = true; mSkipFirstFrame = skipFirstFrame; mOnAnimationFinished = onAnimationFinished; - scheduleUpdate(); - // No need to wait for the next frame to be drawn for this case - onPreDraw will execute - // the changes we just scheduled. - onPreDraw(); + if (mKeyguardUpdateMonitor.isUserUnlocked()) { + scheduleUpdate(); + + // No need to wait for the next frame to be drawn for this case - onPreDraw will execute + // the changes we just scheduled. + onPreDraw(); + } else { + + // In case the user isn't unlocked, make sure to delay a bit because the system is hosed + // with too many things in this case, in order to not skip the initial frames. + mScrimInFront.postOnAnimationDelayed(this::scheduleUpdate, 16); + } } public void abortKeyguardFadingOut() { @@ -211,6 +225,11 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, return mDozeInFrontAlpha; } + private float getScrimInFrontAlpha() { + return mKeyguardUpdateMonitor.isUserUnlocked() + ? SCRIM_IN_FRONT_ALPHA + : SCRIM_IN_FRONT_ALPHA_LOCKED; + } private void scheduleUpdate() { if (mUpdatePending) return; @@ -250,10 +269,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, float fraction = 1 - behindFraction; fraction = (float) Math.pow(fraction, 0.8f); behindFraction = (float) Math.pow(behindFraction, 0.8f); - setScrimInFrontColor(fraction * SCRIM_IN_FRONT_ALPHA); + setScrimInFrontColor(fraction * getScrimInFrontAlpha()); setScrimBehindColor(behindFraction * mScrimBehindAlphaKeyguard); } else if (mBouncerShowing) { - setScrimInFrontColor(SCRIM_IN_FRONT_ALPHA); + setScrimInFrontColor(getScrimInFrontAlpha()); setScrimBehindColor(0f); } else { float fraction = Math.max(0, Math.min(mFraction, 1)); @@ -371,7 +390,13 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, } private Interpolator getInterpolator() { - return mAnimateKeyguardFadingOut ? KEYGUARD_FADE_OUT_INTERPOLATOR : mInterpolator; + if (mAnimateKeyguardFadingOut && !mKeyguardUpdateMonitor.isUserUnlocked()) { + return KEYGUARD_FADE_OUT_INTERPOLATOR_LOCKED; + } else if (mAnimateKeyguardFadingOut) { + return KEYGUARD_FADE_OUT_INTERPOLATOR; + } else { + return mInterpolator; + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 8d0d9cb27cea2..3691a42447ac1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -53,6 +53,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private static final long WAKE_AND_UNLOCK_SCRIM_FADEOUT_DURATION_MS = 200; + // Duration of the Keyguard dismissal animation in case the user is currently locked. This is to + // make everything a bit slower to bridge a gap until the user is unlocked and home screen has + // dranw its first frame. + private static final long KEYGUARD_DISMISS_DURATION_LOCKED = 2000; + private static String TAG = "StatusBarKeyguardViewManager"; protected final Context mContext; @@ -274,9 +279,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb /** * Hides the keyguard view */ - public void hide(long startTime, final long fadeoutDuration) { + public void hide(long startTime, long fadeoutDuration) { mShowing = false; + if (!KeyguardUpdateMonitor.getInstance(mContext).isUserUnlocked()) { + fadeoutDuration = KEYGUARD_DISMISS_DURATION_LOCKED; + } long uptimeMillis = SystemClock.uptimeMillis(); long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis); diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 8e0114a0815cf..ee2c09b510cef 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -69,6 +69,7 @@ import android.util.Log; import android.util.Slog; import com.android.internal.util.ArrayUtils; +import com.android.internal.widget.ICheckCredentialProgressCallback; import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.VerifyCredentialResponse; @@ -760,7 +761,7 @@ public class LockSettingsService extends ILockSettings.Stub { private void unlockChildProfile(int profileHandle) throws RemoteException { try { doVerifyPassword(getDecryptedPasswordForTiedProfile(profileHandle), false, - 0 /* no challenge */, profileHandle); + 0 /* no challenge */, profileHandle, null /* progressCallback */); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException @@ -947,7 +948,7 @@ public class LockSettingsService extends ILockSettings.Stub { CredentialHash willStore = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER); setUserKeyProtection(userId, pattern, - doVerifyPattern(pattern, willStore, true, 0, userId)); + doVerifyPattern(pattern, willStore, true, 0, userId, null /* progressCallback */)); mStorage.writePatternHash(enrolledHandle, userId); fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); @@ -1007,7 +1008,8 @@ public class LockSettingsService extends ILockSettings.Stub { CredentialHash willStore = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER); setUserKeyProtection(userId, password, - doVerifyPassword(password, willStore, true, 0, userId)); + doVerifyPassword(password, willStore, true, 0, userId, + null /* progressCallback */)); mStorage.writePasswordHash(enrolledHandle, userId); fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); @@ -1205,25 +1207,29 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException { - return doVerifyPattern(pattern, false, 0, userId); + public VerifyCredentialResponse checkPattern(String pattern, int userId, + ICheckCredentialProgressCallback progressCallback) throws RemoteException { + return doVerifyPattern(pattern, false, 0, userId, progressCallback); } @Override public VerifyCredentialResponse verifyPattern(String pattern, long challenge, int userId) throws RemoteException { - return doVerifyPattern(pattern, true, challenge, userId); + return doVerifyPattern(pattern, true, challenge, userId, null /* progressCallback */); } private VerifyCredentialResponse doVerifyPattern(String pattern, boolean hasChallenge, - long challenge, int userId) throws RemoteException { + long challenge, int userId, ICheckCredentialProgressCallback progressCallback) + throws RemoteException { checkPasswordReadPermission(userId); CredentialHash storedHash = mStorage.readPatternHash(userId); - return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId); + return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId, + progressCallback); } private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash, - boolean hasChallenge, long challenge, int userId) throws RemoteException { + boolean hasChallenge, long challenge, int userId, + ICheckCredentialProgressCallback progressCallback) throws RemoteException { boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern; String patternToVerify; @@ -1252,7 +1258,8 @@ public class LockSettingsService extends ILockSettings.Stub { public String adjustForKeystore(String pattern) { return LockPatternUtils.patternStringToBaseZero(pattern); } - } + }, + progressCallback ); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK @@ -1264,15 +1271,15 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public VerifyCredentialResponse checkPassword(String password, int userId) - throws RemoteException { - return doVerifyPassword(password, false, 0, userId); + public VerifyCredentialResponse checkPassword(String password, int userId, + ICheckCredentialProgressCallback progressCallback) throws RemoteException { + return doVerifyPassword(password, false, 0, userId, progressCallback); } @Override public VerifyCredentialResponse verifyPassword(String password, long challenge, int userId) throws RemoteException { - return doVerifyPassword(password, true, challenge, userId); + return doVerifyPassword(password, true, challenge, userId, null /* progressCallback */); } @Override @@ -1285,8 +1292,10 @@ public class LockSettingsService extends ILockSettings.Stub { final int parentProfileId = mUserManager.getProfileParent(userId).id; // Unlock parent by using parent's challenge final VerifyCredentialResponse parentResponse = isPattern - ? doVerifyPattern(password, true, challenge, parentProfileId) - : doVerifyPassword(password, true, challenge, parentProfileId); + ? doVerifyPattern(password, true, challenge, parentProfileId, + null /* progressCallback */) + : doVerifyPassword(password, true, challenge, parentProfileId, + null /* progressCallback */); if (parentResponse.getResponseCode() != VerifyCredentialResponse.RESPONSE_OK) { // Failed, just return parent's response return parentResponse; @@ -1296,7 +1305,7 @@ public class LockSettingsService extends ILockSettings.Stub { // Unlock work profile, and work profile with unified lock must use password only return doVerifyPassword(getDecryptedPasswordForTiedProfile(userId), true, challenge, - userId); + userId, null /* progressCallback */); } catch (UnrecoverableKeyException | InvalidKeyException | KeyStoreException | NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException @@ -1307,14 +1316,17 @@ public class LockSettingsService extends ILockSettings.Stub { } private VerifyCredentialResponse doVerifyPassword(String password, boolean hasChallenge, - long challenge, int userId) throws RemoteException { + long challenge, int userId, ICheckCredentialProgressCallback progressCallback) + throws RemoteException { checkPasswordReadPermission(userId); CredentialHash storedHash = mStorage.readPasswordHash(userId); - return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId); + return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId, + progressCallback); } private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash, - boolean hasChallenge, long challenge, int userId) throws RemoteException { + boolean hasChallenge, long challenge, int userId, + ICheckCredentialProgressCallback progressCallback) throws RemoteException { return verifyCredential(userId, storedHash, password, hasChallenge, challenge, new CredentialUtil() { @Override @@ -1332,12 +1344,12 @@ public class LockSettingsService extends ILockSettings.Stub { public String adjustForKeystore(String password) { return password; } - } - ); + }, progressCallback); } private VerifyCredentialResponse verifyCredential(int userId, CredentialHash storedHash, - String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil) + String credential, boolean hasChallenge, long challenge, CredentialUtil credentialUtil, + ICheckCredentialProgressCallback progressCallback) throws RemoteException { if ((storedHash == null || storedHash.hash.length == 0) && TextUtils.isEmpty(credential)) { // don't need to pass empty credentials to GateKeeper @@ -1395,6 +1407,10 @@ public class LockSettingsService extends ILockSettings.Stub { if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { // credential has matched + + if (progressCallback != null) { + progressCallback.onCredentialVerified(); + } unlockKeystore(credential, userId); Slog.i(TAG, "Unlocking user " + userId + @@ -1450,7 +1466,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { if (mLockPatternUtils.isLockPatternEnabled(userId)) { - if (checkPattern(password, userId).getResponseCode() + if (checkPattern(password, userId, null /* progressCallback */).getResponseCode() == GateKeeperResponse.RESPONSE_OK) { return true; } @@ -1460,7 +1476,7 @@ public class LockSettingsService extends ILockSettings.Stub { try { if (mLockPatternUtils.isLockPasswordEnabled(userId)) { - if (checkPassword(password, userId).getResponseCode() + if (checkPassword(password, userId, null /* progressCallback */).getResponseCode() == GateKeeperResponse.RESPONSE_OK) { return true; }