A verify nobody answered is not a failure, and the trustlet is not ours to ship
Two things the packaging left behind. A verify that ran its 600-frame budget without the sensor being touched was reported to the client as verify-unknown-error. Nothing had gone wrong: nobody had pressed. It cost three verifications during packaging, each reading as a broken daemon. fprintd's contract is that a verify runs until the client stops it, so the frame cap bounds one trustlet scan session rather than the user's patience, and a window with no press simply runs again. Verified across the rollover: 600 frames untouched, "still waiting", then a press matching on its first contact frame in 44 ms. Presses that happen and never reach a verdict now report verify-retry-scan -- a bad scan, which fprintd has a word for, and not the matcher saying no. The cost is that an unanswered verify polls every ~200 ms for as long as the client holds it. The cure is measured and available -- gpio75 is silent at idle and bursts on contact -- but it would make the IRQ the only way a press is ever noticed, deleting the poll under every rate this daemon has been measured at. Noted where the loop waits, not done. And the trustlet: focal64.mbn is a proprietary OEM-signed blob, so the package ships a fp6-vendor-blobs manifest fragment instead, the same mechanism soc-fairphone-fp6-audio uses for the amp config. It needed a new directive there -- a QTEE image is an ELF header file plus one payload per program header, not one file -- and reassembly on the phone reproduces the image QTEE has accepted since August, byte for byte.
This commit is contained in:
parent
905e261d63
commit
b228287c5b
5 changed files with 67 additions and 5 deletions
|
|
@ -66,7 +66,7 @@ namespace {
|
|||
|
||||
// Bumping this is what publishes a package: the registry answers 409 for a
|
||||
// version it already has, which a build treats as a no-op.
|
||||
constexpr const char* Version = "0.1.1";
|
||||
constexpr const char* Version = "0.1.2";
|
||||
|
||||
bool g_verbose = false;
|
||||
// 500 ms was the research harness's pace, chosen so a human could read the
|
||||
|
|
@ -1623,6 +1623,16 @@ public:
|
|||
// returns immediately and the loop runs as fast as QTEE allows --
|
||||
// exactly what a press wants. The timeout is the idle fallback, so
|
||||
// a release is still noticed promptly.
|
||||
//
|
||||
// IDLE WAIT, NOT DONE: since a verify now runs until the client
|
||||
// stops it, an unanswered one polls the trustlet every ~200 ms for
|
||||
// as long as the lock screen is up. The measurement above says the
|
||||
// cure is available -- gpio75 is silent at idle and bursts on
|
||||
// contact -- so an idle frame could wait on the edge for seconds
|
||||
// instead of capturing. It is not done here because it would make
|
||||
// the IRQ the only way a press is ever noticed, deleting the poll
|
||||
// that is currently the safety net under every rate this daemon
|
||||
// has been measured at, and that trade needs a finger to settle.
|
||||
if (g_edgeWake) sensor_.WaitEdges(g_frameGapMs);
|
||||
else std::this_thread::sleep_for(std::chrono::milliseconds(g_frameGapMs));
|
||||
}
|
||||
|
|
@ -1991,7 +2001,27 @@ private:
|
|||
break;
|
||||
}
|
||||
case Job::Kind::Verify: {
|
||||
// fprintd's contract is that a verify runs until the client
|
||||
// stops it. The frame cap bounds one trustlet scan session,
|
||||
// not the user's patience, so a window in which the sensor was
|
||||
// never touched is not an outcome -- it is nothing having
|
||||
// happened yet, and the loop simply runs again. Reporting it
|
||||
// instead cost three verifications during packaging, each
|
||||
// ending in verify-unknown-error, which reads as a broken
|
||||
// daemon and meant only that nobody pressed.
|
||||
//
|
||||
// The cost of waiting is a poll: the loop captures every
|
||||
// ~200 ms whether or not a finger is there. It is bounded in
|
||||
// practice by the client -- pam_fprintd stops on its own
|
||||
// timeout, and a claimant that vanishes from the bus has its
|
||||
// claim dropped -- and by VerifyStop, which is what sets
|
||||
// cancel_. See the idle-wait note in Verify().
|
||||
auto o = session_.Verify(j.uid, cancel_, /*maxFrames*/ 600, j.acceptFids);
|
||||
while (!o.decided && !o.cancelled && o.presses == 0) {
|
||||
std::println("verify: {} frame(s), sensor never touched -- still waiting",
|
||||
o.frames);
|
||||
o = session_.Verify(j.uid, cancel_, /*maxFrames*/ 600, j.acceptFids);
|
||||
}
|
||||
std::println("verify: {} over {} press(es), {} frame(s)",
|
||||
o.cancelled ? "cancelled" : !o.decided ? "undecided"
|
||||
: o.matched ? "MATCH" : "NO MATCH", o.presses, o.frames);
|
||||
|
|
@ -1999,7 +2029,11 @@ private:
|
|||
ev->done = true;
|
||||
ev->fid = o.fid;
|
||||
if (o.cancelled) { ev->status = ""; ev->done = false; }
|
||||
else if (!o.decided) ev->status = "verify-unknown-error";
|
||||
// Presses happened and none of them reached a verdict: every
|
||||
// frame was a finger arriving but never settling. That is a
|
||||
// bad scan, which fprintd has a word for, and it is not the
|
||||
// same as the matcher saying no.
|
||||
else if (!o.decided) ev->status = "verify-retry-scan";
|
||||
else if (o.matched) ev->status = "verify-match";
|
||||
else ev->status = "verify-no-match";
|
||||
PostEvent(std::move(ev));
|
||||
|
|
|
|||
Loading…
Reference in a new issue