diff --git a/debian/rules b/debian/rules index 6833b6a24e..eb9df91de7 100755 --- a/debian/rules +++ b/debian/rules @@ -12,7 +12,7 @@ export QT_SELECT=qt5 DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) # Skip tests on the archs they are known to be flaky with current configuration -testskip_architectures := arm64 powerpc ppc64el s390x +testskip_architectures := powerpc ppc64el s390x %: dh $@ --parallel --fail-missing --with python3 diff --git a/qml/Greeter/Greeter.qml b/qml/Greeter/Greeter.qml index cdea9d618e..f154198f9c 100644 --- a/qml/Greeter/Greeter.qml +++ b/qml/Greeter/Greeter.qml @@ -63,7 +63,8 @@ Showable { property int failedLoginsDelayAttempts: 7 // number of failed logins property real failedLoginsDelayMinutes: 5 // minutes of forced waiting - property int failedFingerprintLoginsDisableAttempts: 3 // number of failed fingerprint logins + property int failedFingerprintLoginsDisableAttempts: 5 // number of failed fingerprint logins + property int failedFingerprintReaderRetryDelay: 250 // time to wait before retrying a failed fingerprint read [ms] readonly property bool animating: loader.item ? loader.item.animating : false @@ -559,6 +560,14 @@ Showable { onLanguageChanged: LightDMService.infographic.readyForDataChange() } + Timer { + id: fpRetryTimer + running: false + repeat: false + onTriggered: biometryd.startOperation() + interval: failedFingerprintReaderRetryDelay + } + Observer { id: biometryd objectName: "biometryd" @@ -570,6 +579,14 @@ Showable { Biometryd.available && AccountsService.enableFingerprintIdentification + function startOperation() { + if (idEnabled) { + var identifier = Biometryd.defaultDevice.identifier; + operation = identifier.identifyUser(); + operation.start(biometryd); + } + } + function cancelOperation() { if (operation) { operation.cancel(); @@ -579,27 +596,23 @@ Showable { function restartOperation() { cancelOperation(); - - if (idEnabled) { - var identifier = Biometryd.defaultDevice.identifier; - operation = identifier.identifyUser(); - operation.start(biometryd); + if (failedFingerprintReaderRetryDelay > 0) { + fpRetryTimer.running = true; + } else { + startOperation(); } } function failOperation(reason) { console.log("Failed to identify user by fingerprint:", reason); restartOperation(); - if (!d.secureFingerprint) { - d.startUnlock(false /* toTheRight */); // use normal login instead - } var msg = d.secureFingerprint ? i18n.tr("Try again") : d.alphanumeric ? i18n.tr("Enter passphrase to unlock") : i18n.tr("Enter passcode to unlock"); d.showFingerprintMessage(msg); } - Component.onCompleted: restartOperation() + Component.onCompleted: startOperation() Component.onDestruction: cancelOperation() onIdEnabledChanged: restartOperation() @@ -623,7 +636,7 @@ Showable { onFailed: { if (!d.secureFingerprint) { failOperation("fingerprint reader is locked"); - } else { + } else if (reason !== "ERROR_CANCELED") { AccountsService.failedFingerprintLogins++; failOperation(reason); } diff --git a/tests/qmltests/Greeter/tst_Greeter.qml b/tests/qmltests/Greeter/tst_Greeter.qml index 44bdb744be..6dd72396b0 100644 --- a/tests/qmltests/Greeter/tst_Greeter.qml +++ b/tests/qmltests/Greeter/tst_Greeter.qml @@ -49,7 +49,7 @@ Item { height: loader.height background: defaultBackground viewSource: Qt.resolvedUrl("TestView.qml") - + failedFingerprintReaderRetryDelay: 0 Component.onDestruction: { loader.itemDestroyed = true; } @@ -548,7 +548,8 @@ Item { verify(biometryd.operation.running); biometryd.operation.mockSuccess(LightDM.Users.data(index, LightDM.UserRoles.UidRole)); - viewTryToUnlockSpy.wait(); + viewShowErrorMessageSpy.wait(); + compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Enter passphrase to unlock")); verify(greeter.locked); } @@ -577,22 +578,29 @@ Item { compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Try again")); } + function repeatBiometrydMockFailure(operationHost, count) { + for (var i = 0; i < count; i++) { + operationHost.operation.mockFailure("error"); + } + } + function test_fingerprintTooManyFailures() { var index = selectUser("has-password"); unlockAndShowGreeter(); // turn on lockscreen mode var biometryd = findInvisibleChild(greeter, "biometryd"); - biometryd.operation.mockFailure("error"); - biometryd.operation.mockFailure("error"); + repeatBiometrydMockFailure(biometryd, 4); compare(viewTryToUnlockSpy.count, 0); - biometryd.operation.mockFailure("error"); - viewTryToUnlockSpy.wait(); - viewTryToUnlockSpy.clear(); + repeatBiometrydMockFailure(biometryd, 1); + viewShowErrorMessageSpy.wait(); + compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Try again")); + viewShowErrorMessageSpy.clear(); // Confirm that we are stuck in this mode until next login biometryd.operation.mockSuccess(LightDM.Users.data(index, LightDM.UserRoles.UidRole)); - viewTryToUnlockSpy.wait(); + viewShowErrorMessageSpy.wait(); + compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Enter passphrase to unlock")); unlockAndShowGreeter(); @@ -605,17 +613,16 @@ Item { unlockAndShowGreeter(); // turn on lockscreen mode var biometryd = findInvisibleChild(greeter, "biometryd"); - biometryd.operation.mockFailure("error"); - biometryd.operation.mockFailure("error"); + repeatBiometrydMockFailure(biometryd, 4); compare(viewTryToUnlockSpy.count, 0); unlockAndShowGreeter(); - biometryd.operation.mockFailure("error"); - biometryd.operation.mockFailure("error"); + repeatBiometrydMockFailure(biometryd, 4); compare(viewTryToUnlockSpy.count, 0); - biometryd.operation.mockFailure("error"); - viewTryToUnlockSpy.wait(); + repeatBiometrydMockFailure(biometryd, 1); + viewShowErrorMessageSpy.wait(); + compare(viewShowErrorMessageSpy.signalArguments[0][0], i18n.tr("Try again")); } function test_fingerprintWrongUid() {