Commit dfdb8997 authored by Sarah Hu's avatar Sarah Hu Committed by Commit Bot

cros: Hide fingerprint view in the lock screen when user is forced to

use password/PIN to unlock.

Bug: 879297
Change-Id: Ia4518584977f24af1b7f7f2a849d1dc598e5fc95
Reviewed-on: https://chromium-review.googlesource.com/1211804Reviewed-by: default avatarJacob Dufault <jdufault@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Commit-Queue: Xiaoyin Hu <xiaoyinh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#594490}
parent c38fa0f4
......@@ -1294,7 +1294,9 @@ void LockContentsView::LayoutAuth(LoginBigUserView* to_update,
if (state->enable_tap_auth)
to_update_auth |= LoginAuthUserView::AUTH_TAP;
if (state->fingerprint_state !=
mojom::FingerprintUnlockState::UNAVAILABLE) {
mojom::FingerprintUnlockState::UNAVAILABLE &&
state->fingerprint_state !=
mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT) {
to_update_auth |= LoginAuthUserView::AUTH_FINGERPRINT;
}
......
......@@ -335,6 +335,8 @@ class LockDebugView::DebugDataDispatcherTransformer
case mojom::FingerprintUnlockState::AUTH_FAILED:
return mojom::FingerprintUnlockState::AUTH_DISABLED;
case mojom::FingerprintUnlockState::AUTH_DISABLED:
return mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT;
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
return mojom::FingerprintUnlockState::UNAVAILABLE;
}
};
......
......@@ -96,8 +96,8 @@ constexpr int kDisabledAuthMessageRoundedCornerRadiusDp = 8;
constexpr int kNonEmptyWidthDp = 1;
// Returns an observer that will hide |view| when it fires. The observer will
// delete itself after firing. Make sure to call |observer->SetReady()| after
// attaching it.
// delete itself after firing (by returning true). Make sure to call
// |observer->SetActive()| after attaching it.
ui::CallbackLayerAnimationObserver* BuildObserverToHideView(views::View* view) {
return new ui::CallbackLayerAnimationObserver(base::Bind(
[](views::View* view,
......@@ -187,6 +187,7 @@ class LoginAuthUserView::FingerprintView : public views::View {
case mojom::FingerprintUnlockState::UNAVAILABLE:
case mojom::FingerprintUnlockState::AVAILABLE:
case mojom::FingerprintUnlockState::AUTH_SUCCESS:
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
icon_->SetImage(gfx::CreateVectorIcon(
kLockScreenFingerprintIcon, kFingerprintIconSizeDp, SK_ColorWHITE));
return;
......@@ -210,6 +211,7 @@ class LoginAuthUserView::FingerprintView : public views::View {
case mojom::FingerprintUnlockState::UNAVAILABLE:
case mojom::FingerprintUnlockState::AVAILABLE:
case mojom::FingerprintUnlockState::AUTH_SUCCESS:
case mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_MESSAGE;
case mojom::FingerprintUnlockState::AUTH_FAILED:
return IDS_ASH_LOGIN_FINGERPRINT_UNLOCK_FAILED_MESSAGE;
......@@ -226,7 +228,9 @@ class LoginAuthUserView::FingerprintView : public views::View {
return;
state_ = state;
SetVisible(state != mojom::FingerprintUnlockState::UNAVAILABLE);
SetVisible(state != mojom::FingerprintUnlockState::UNAVAILABLE &&
state !=
mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT);
SetIcon(state);
SetText(state);
......@@ -590,6 +594,17 @@ void LoginAuthUserView::SetEasyUnlockIcon(
}
void LoginAuthUserView::CaptureStateForAnimationPreLayout() {
auto stop_animation = [](views::View* view) {
if (view->layer()->GetAnimator()->is_animating())
view->layer()->GetAnimator()->StopAnimating();
};
// Stop any running animation scheduled in ApplyAnimationPostLayout.
stop_animation(this);
stop_animation(password_view_);
stop_animation(pin_view_);
stop_animation(fingerprint_view_);
DCHECK(!cached_animation_state_);
cached_animation_state_ = std::make_unique<AnimationState>(this);
}
......@@ -597,11 +612,6 @@ void LoginAuthUserView::CaptureStateForAnimationPreLayout() {
void LoginAuthUserView::ApplyAnimationPostLayout() {
DCHECK(cached_animation_state_);
// Cancel any running animations.
pin_view_->layer()->GetAnimator()->AbortAllAnimations();
password_view_->layer()->GetAnimator()->AbortAllAnimations();
layer()->GetAnimator()->AbortAllAnimations();
bool has_password = (auth_methods() & AUTH_PASSWORD) != 0;
bool has_pin = (auth_methods() & AUTH_PIN) != 0;
bool has_fingerprint = (auth_methods() & AUTH_FINGERPRINT) != 0;
......@@ -700,7 +710,7 @@ void LoginAuthUserView::ApplyAnimationPostLayout() {
{
ui::ScopedLayerAnimationSettings settings(
password_view_->layer()->GetAnimator());
fingerprint_view_->layer()->GetAnimator());
settings.SetTransitionDuration(base::TimeDelta::FromMilliseconds(
login_constants::kChangeUserAnimationDurationMs));
settings.SetTweenType(gfx::Tween::Type::FAST_OUT_SLOW_IN);
......
......@@ -54,6 +54,10 @@ enum FingerprintUnlockState {
// recognized. There have been too many unlock attempts and fingerprint
// is now disabled.
AUTH_DISABLED,
// Fingerprint unlock is disabled because user is forced to use an
// authentication method that authenticates via cryptohome.
// I.e., password, cryptohome-based PIN, easy unlock.
AUTH_DISABLED_FROM_TIMEOUT,
};
// Information about the custom icon in the user pod.
......
......@@ -395,6 +395,15 @@ void ScreenLocker::Authenticate(const UserContext& user_context,
void ScreenLocker::OnPinAttemptDone(const UserContext& user_context,
bool success) {
if (success) {
// Mark strong auth if this is cryptohome based pin.
if (quick_unlock::PinBackend::GetInstance()->ShouldUseCryptohome(
user_context.GetAccountId())) {
quick_unlock::QuickUnlockStorage* quick_unlock_storage =
quick_unlock::QuickUnlockFactory::GetForAccountId(
user_context.GetAccountId());
if (quick_unlock_storage)
quick_unlock_storage->MarkStrongAuth();
}
OnAuthSuccess(user_context);
} else {
// PIN authentication has failed; try submitting as a normal password.
......@@ -672,6 +681,10 @@ void ScreenLocker::ScreenLockReady() {
} else {
VLOG(1) << "Fingerprint is not available on lock screen";
}
UpdateFingerprintState(
"ScreenLockReady",
user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId());
}
bool ScreenLocker::IsUserLoggedIn(const AccountId& account_id) const {
......@@ -756,4 +769,46 @@ void ScreenLocker::OnFingerprintAuthFailure(const user_manager::User& user) {
}
}
void ScreenLocker::UpdateFingerprintState(const std::string& source,
const AccountId& account_id) {
VLOG(1) << "Updating fingerprint state (source=" << source << ")";
update_fingerprint_state_timer_.Stop();
quick_unlock::QuickUnlockStorage* quick_unlock_storage =
quick_unlock::QuickUnlockFactory::GetForAccountId(account_id);
// If strong auth is required, disable fingerprint.
if (quick_unlock_storage && !quick_unlock_storage->HasStrongAuth() &&
quick_unlock_storage->fingerprint_storage()->IsFingerprintAvailable()) {
VLOG(1) << "Require strong auth to make fingerprint unlock available.";
delegate_->SetFingerprintState(account_id, FingerprintState::kTimeout);
// Prefs based pin will be unavailable when strong auth is required.
quick_unlock::PinBackend::GetInstance()->CanAuthenticate(
account_id, base::BindOnce(&ScreenLocker::OnPinCanAuthenticate,
weak_factory_.GetWeakPtr(), account_id));
return;
}
// If fingerprint is available, call this function again when strong auth
// will expire.
if (quick_unlock_storage &&
quick_unlock_storage->IsFingerprintAuthenticationAvailable()) {
const base::TimeDelta next_strong_auth =
quick_unlock_storage->TimeUntilNextStrongAuth();
VLOG(1) << "Scheduling next fingerprint state update in "
<< next_strong_auth;
update_fingerprint_state_timer_.Start(
FROM_HERE, next_strong_auth,
base::BindOnce(&ScreenLocker::UpdateFingerprintState,
base::Unretained(this),
"update_fingerprint_state_timer_", account_id));
}
}
void ScreenLocker::OnPinCanAuthenticate(const AccountId& account_id,
bool can_authenticate) {
LoginScreenClient::Get()->login_screen()->SetPinEnabledForUser(
account_id, can_authenticate);
}
} // namespace chromeos
......@@ -55,6 +55,7 @@ class ScreenLocker : public AuthStatusConsumer,
kSignin,
kFailed,
kRemoved,
kTimeout,
};
// Delegate used to send internal state changes back to the UI.
......@@ -242,6 +243,11 @@ class ScreenLocker : public AuthStatusConsumer,
// check has completed.
void ContinueAuthenticate(const UserContext& user_context);
void UpdateFingerprintState(const std::string& source,
const AccountId& account_id);
void OnPinCanAuthenticate(const AccountId& account_id, bool can_authenticate);
// WebUIScreenLocker instance in use.
std::unique_ptr<WebUIScreenLocker> web_ui_;
......@@ -300,6 +306,10 @@ class ScreenLocker : public AuthStatusConsumer,
// ViewsScreenLocker instance in use.
std::unique_ptr<ViewsScreenLocker> views_screen_locker_;
// Password is required every 24 hours in order to use fingerprint unlock.
// This is used to update fingerprint state when password is required.
base::OneShotTimer update_fingerprint_state_timer_;
base::WeakPtrFactory<ScreenLocker> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ScreenLocker);
......
......@@ -52,6 +52,8 @@ ash::mojom::FingerprintUnlockState ConvertFromFingerprintState(
return ash::mojom::FingerprintUnlockState::AUTH_FAILED;
case ScreenLocker::FingerprintState::kRemoved:
return ash::mojom::FingerprintUnlockState::AUTH_DISABLED;
case ScreenLocker::FingerprintState::kTimeout:
return ash::mojom::FingerprintUnlockState::AUTH_DISABLED_FROM_TIMEOUT;
}
}
......
......@@ -22,7 +22,7 @@ FingerprintStorage::FingerprintStorage(PrefService* pref_service)
FingerprintStorage::~FingerprintStorage() {}
bool FingerprintStorage::IsFingerprintAuthenticationAvailable() const {
bool FingerprintStorage::IsFingerprintAvailable() const {
return !ExceededUnlockAttempts() && IsFingerprintEnabled() && HasRecord();
}
......
......@@ -28,6 +28,10 @@ class FingerprintStorage {
explicit FingerprintStorage(PrefService* pref_service);
~FingerprintStorage();
// Returns true if fingerprint unlock is currently available.
// This does not check if strong auth is available.
bool IsFingerprintAvailable() const;
// Returns true if the user has fingerprint record registered.
bool HasRecord() const;
......@@ -46,9 +50,6 @@ class FingerprintStorage {
friend class chromeos::FingerprintStorageTestApi;
friend class QuickUnlockStorage;
// Returns true if fingerprint unlock is currently available.
bool IsFingerprintAuthenticationAvailable() const;
PrefService* pref_service_;
// Number of fingerprint unlock attempt.
int unlock_attempt_count_ = 0;
......
......@@ -46,8 +46,8 @@ class FingerprintStorageTestApi {
quick_unlock::FingerprintStorage* fingerprint_storage)
: fingerprint_storage_(fingerprint_storage) {}
bool IsFingerprintAuthenticationAvailable() const {
return fingerprint_storage_->IsFingerprintAuthenticationAvailable();
bool IsFingerprintAvailable() const {
return fingerprint_storage_->IsFingerprintAvailable();
}
private:
......@@ -90,14 +90,14 @@ TEST_F(FingerprintStorageUnitTest, AuthenticationUnAvailable) {
EXPECT_TRUE(fingerprint_storage->HasRecord());
EXPECT_EQ(0, fingerprint_storage->unlock_attempt_count());
EXPECT_TRUE(test_api.IsFingerprintAuthenticationAvailable());
EXPECT_TRUE(test_api.IsFingerprintAvailable());
// No fingerprint records registered makes fingerprint authentication
// unavailable.
SetRecords(0);
EXPECT_FALSE(test_api.IsFingerprintAuthenticationAvailable());
EXPECT_FALSE(test_api.IsFingerprintAvailable());
SetRecords(1);
EXPECT_TRUE(test_api.IsFingerprintAuthenticationAvailable());
EXPECT_TRUE(test_api.IsFingerprintAvailable());
// Too many authentication attempts make fingerprint authentication
// unavailable.
......@@ -105,9 +105,9 @@ TEST_F(FingerprintStorageUnitTest, AuthenticationUnAvailable) {
++i) {
fingerprint_storage->AddUnlockAttempt();
}
EXPECT_FALSE(test_api.IsFingerprintAuthenticationAvailable());
EXPECT_FALSE(test_api.IsFingerprintAvailable());
fingerprint_storage->ResetUnlockAttemptCount();
EXPECT_TRUE(test_api.IsFingerprintAuthenticationAvailable());
EXPECT_TRUE(test_api.IsFingerprintAvailable());
}
} // namespace chromeos
......@@ -258,6 +258,17 @@ void PinBackend::TryAuthenticate(const AccountId& account_id,
}
}
bool PinBackend::ShouldUseCryptohome(const AccountId& account_id) {
if (!cryptohome_backend_)
return false;
// Even if cryptohome is supported, the user may have registered a PIN with
// the prefs backend from a previous version. If that's the case, we should
// talk to the prefs backend instead of the cryptohome backend.
QuickUnlockStorage* storage = GetPrefsBackend(account_id);
return !storage || !storage->pin_storage_prefs()->IsPinSet();
}
void PinBackend::OnIsCryptohomeBackendSupported(bool is_supported) {
if (is_supported)
cryptohome_backend_ = std::make_unique<PinStorageCryptohome>();
......@@ -276,16 +287,5 @@ void PinBackend::OnPinMigrationAttemptComplete(Profile* profile, bool success) {
scoped_keep_alive_.reset();
}
bool PinBackend::ShouldUseCryptohome(const AccountId& account_id) {
if (!cryptohome_backend_)
return false;
// Even if cryptohome is supported, the user may have registered a PIN with
// the prefs backend from a previous version. If that's the case, we should
// talk to the prefs backend instead of the cryptohome backend.
QuickUnlockStorage* storage = GetPrefsBackend(account_id);
return !storage || !storage->pin_storage_prefs()->IsPinSet();
}
} // namespace quick_unlock
} // namespace chromeos
......@@ -72,6 +72,11 @@ class PinBackend {
const Key& key,
BoolCallback result);
// Returns true if the cryptohome backend should be used. Sometimes the prefs
// backend should be used even when cryptohome is available, ie, when there is
// an non-migrated PIN key.
bool ShouldUseCryptohome(const AccountId& account_id);
// Resets any cached state for testing purposes.
static void ResetForTesting();
......@@ -83,11 +88,6 @@ class PinBackend {
// should be cleared from prefs.
void OnPinMigrationAttemptComplete(Profile* profile, bool success);
// Returns true if the cryptohome backend should be used. Sometimes the prefs
// backend should be used even when cryptohome is available, ie, when there is
// an non-migrated PIN key.
bool ShouldUseCryptohome(const AccountId& account_id);
// True if still trying to determine which backend should be used.
bool resolving_backend_ = true;
// Determining if the device supports cryptohome-based keys requires an async
......
......@@ -14,6 +14,17 @@
namespace chromeos {
namespace quick_unlock {
namespace {
base::TimeDelta GetStrongAuthTimeout(PrefService* pref_service) {
PasswordConfirmationFrequency strong_auth_interval =
static_cast<PasswordConfirmationFrequency>(
pref_service->GetInteger(prefs::kQuickUnlockTimeout));
return PasswordConfirmationFrequencyToTimeDelta(strong_auth_interval);
}
} // namespace
QuickUnlockStorage::QuickUnlockStorage(PrefService* pref_service)
: pref_service_(pref_service) {
fingerprint_storage_ = std::make_unique<FingerprintStorage>(pref_service);
......@@ -31,15 +42,7 @@ void QuickUnlockStorage::MarkStrongAuth() {
bool QuickUnlockStorage::HasStrongAuth() const {
if (last_strong_auth_.is_null())
return false;
// PIN and fingerprint share the same timeout policy.
PasswordConfirmationFrequency strong_auth_interval =
static_cast<PasswordConfirmationFrequency>(
pref_service_->GetInteger(prefs::kQuickUnlockTimeout));
base::TimeDelta strong_auth_timeout =
PasswordConfirmationFrequencyToTimeDelta(strong_auth_interval);
return TimeSinceLastStrongAuth() < strong_auth_timeout;
return TimeSinceLastStrongAuth() < GetStrongAuthTimeout(pref_service_);
}
base::TimeDelta QuickUnlockStorage::TimeSinceLastStrongAuth() const {
......@@ -47,9 +50,13 @@ base::TimeDelta QuickUnlockStorage::TimeSinceLastStrongAuth() const {
return base::TimeTicks::Now() - last_strong_auth_;
}
base::TimeDelta QuickUnlockStorage::TimeUntilNextStrongAuth() const {
DCHECK(!last_strong_auth_.is_null());
return GetStrongAuthTimeout(pref_service_) - TimeSinceLastStrongAuth();
}
bool QuickUnlockStorage::IsFingerprintAuthenticationAvailable() const {
return HasStrongAuth() &&
fingerprint_storage_->IsFingerprintAuthenticationAvailable();
return HasStrongAuth() && fingerprint_storage_->IsFingerprintAvailable();
}
bool QuickUnlockStorage::IsPinAuthenticationAvailable() const {
......
......@@ -44,7 +44,12 @@ class QuickUnlockStorage : public KeyedService {
// called if HasStrongAuth returns false.
base::TimeDelta TimeSinceLastStrongAuth() const;
// Returns the time until next strong authentication required. This should
// not be called if HasStrongAuth returns false.
base::TimeDelta TimeUntilNextStrongAuth() const;
// Returns true if fingerprint unlock is currently available.
// This checks whether there's fingerprint setup, as well as HasStrongAuth.
bool IsFingerprintAuthenticationAvailable() const;
// Returns true if PIN unlock is currently available.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment