diff --git a/implementations/main.cpp b/implementations/main.cpp index fbe640e..b260a6f 100644 --- a/implementations/main.cpp +++ b/implementations/main.cpp @@ -1402,6 +1402,7 @@ public: if (g_edgeWake) sensor_.DrainEdges(); en::TouchTracker tracker; + harvested_ = 0; // no fold has happened in this session yet bool inPress = false, pressMatched = false, pressRejected = false; int pressFrames = 0, rescans = 0; std::uint32_t pressFid = 0; @@ -1457,7 +1458,22 @@ public: v = ta::Verdict::Rejected; } switch (v) { - case ta::Verdict::Match: pressMatched = true; pressFid = r.fid; note += " MATCH"; break; + case ta::Verdict::Match: + pressMatched = true; pressFid = r.fid; note += " MATCH"; + // FOLD THE FRAME THAT JUST MATCHED, HERE, WITHOUT + // CAPTURING A NEW ONE. This is the only fold a quick tap + // will ever get, and stock does exactly this: in the + // reference trace UPDATE_TEMPLATE follows do_authenticate + // directly -- `auth success score` -> `authenticated + // result is updated` -> CANCEL -> `checking the + // template...` -> UPDATE_TEMPLATE -- with NO CAPTURE_IMAGE + // between them. The trustlet still holds the image it just + // matched against. + if (g_learn && FoldFrame(0, ev == ta::Event::FingerTouched)) { + harvested_ = 1; + note += " folded"; + } + break; case ta::Verdict::Rejected: pressRejected = true; note += " rej"; break; case ta::Verdict::NotIdentifiedYet: rescans++; note += " -11"; break; case ta::Verdict::MatcherNeverRan: break; @@ -1512,7 +1528,8 @@ public: SendCommand(app_, ta::Cmd::Cancel, {}); out.cancelled = true; } - // A press that matched is the only material template learning gets. + // The matching frame was already folded, at the moment it matched. + // This picks up any further frames the finger stayed down for. if (out.matched && g_learn) HarvestTemplate(g_learnMaxFrames); return out; } @@ -1532,10 +1549,27 @@ public: // Which means the frames learned from are exactly the frames a real // unlock produces -- the "enrolment must resemble verification" problem, // solved by the algorithm instead of by coaching the user. + // Fold one frame the trustlet already holds. No capture: the caller has + // just had a verdict out of it, so the image is the one that produced it. + bool FoldFrame(int slot, bool touchFrame) { + namespace ta = fingerprintd::ta; + std::vector up(ta::UpdateTemplatePayloadSize); + ta::BuildUpdateTemplate(up, static_cast(slot), touchFrame); + auto u = SendCommand(app_, ta::Cmd::UpdateTemplate, up); + if (!u.Ok()) { + std::println(" learn: UPDATE_TEMPLATE rc={} ({}) result={}", u.rc, + ta::StrError(u.rc), static_cast(u.result)); + return false; + } + templateDirty_ = true; + return true; + } + int HarvestTemplate(int maxFrames) { namespace ta = fingerprintd::ta; - int folded = 0; - for (int i = 0; i < maxFrames; i++) { + int folded = harvested_; + harvested_ = 0; + for (int i = folded; i < maxFrames; i++) { std::vector cap(ta::CaptureDeclaredLen); ta::BuildCapturePayload(cap); auto c = SendCommand(app_, ta::Cmd::CaptureImage, cap); @@ -1546,22 +1580,15 @@ public: c.metric, folded); break; } - std::vector up(ta::UpdateTemplatePayloadSize); - // Bit 6 on the first frame, mirroring stock: it marks the frame - // whose reported event was FingerTouched, and it selects which of - // the algorithm's two update entries runs. - ta::BuildUpdateTemplate(up, static_cast(folded), folded == 0); - auto u = SendCommand(app_, ta::Cmd::UpdateTemplate, up); - if (!u.Ok()) { - std::println(" learn: UPDATE_TEMPLATE rc={} ({}) result={} -- stopping", - u.rc, ta::StrError(u.rc), static_cast(u.result)); - break; - } + // Bit 6 only on the very first fold of the press, mirroring + // stock: it marks the frame whose reported event was + // FingerTouched, and it selects which of the algorithm's two + // update entries runs. + if (!FoldFrame(folded, folded == 0)) break; folded++; if (g_verbose) std::println(" learn: frame {} folded in (metric={})", folded, c.metric); } - if (folded > 0) templateDirty_ = true; std::println(" learn: {} frame(s) folded into the template{}", folded, templateDirty_ ? ", save pending" : ""); return folded; @@ -1701,8 +1728,11 @@ private: Sensor sensor_; fingerprintd::engine::Baseline baseline_; std::uint32_t gid_ = 0xFFFFFFFF; - // Set when a harvest folded at least one frame in; cleared by the save. + // Set when a fold succeeded; cleared by the save. bool templateDirty_ = false; + // Frames folded during the verify loop itself, carried into the harvest so + // the slot index keeps counting up across the two. + int harvested_ = 0; std::atomic fingerPresent_{false}; std::thread irqThread_; std::atomic irqQuit_{false};