Fingerprintd:Ta is the second core module: request payloads, response fields,
the error table, and the rule that decides what a frame meant. Payload building
and response reading only -- no TEE, no transport.
Very little of this is guessable, so each constant carries where it came from.
Three were found only because QTEE recorded a fault naming the instruction that
read them:
* the event context's scan-slot count at +712, which do_enroll branches on to
skip the entire slot loop -- an all-zero payload logged "groups->,
results->" and read exactly like a gate failing deep in the trustlet, when
it was zero iterations;
* CAPTURE_IMAGE's flags at payload+0x18, without which preprocessing, the
classifier and the enrol grouper never run at all, whatever is on the
sensor;
* SYNC_STATISTICS, whose absence leaves g_statistics NULL so the first enrol
frame that gets far enough takes a data abort and every later command
answers -90.
The verdict rule gets the most attention because it was mislabelled three times
before the comparison producing it was read. A frame is one of three things and
only the third is a verdict: the poison intact means the matcher never ran,
rc=-11 means not identified yet with attempts remaining, and only rc=0 carries
a match or a rejection. The poison exists because a zero-initialised buffer
cannot tell a released finger from a rejected one.
The tests are in two halves that cannot prop each other up. Explicit wire
conditions pin the classifier; three recorded runs pin the counting policy,
which is what actually went wrong. In the stock-budget run 31 of 48 frames
answered "not identified yet" and every frame that carried an image matched --
counting those 31 as attempts turns 8-for-8 into 8-of-39 and reads as a flaky
sensor. The wrong-finger control pins zero false accepts.
Fixtures are verdict-line excerpts, not the 40 KB transcripts, which are thick
with the device's SFS container names the test has no use for.
Verified by mutation: classifying -11 as a rejection, dropping SYNC_STATISTICS
from the init chain, and forgetting the +0x10 response payload offset each fail
the suite.
1.6 KiB
Recorded authentication verdicts
Verdict lines excerpted from three real authentication runs on the dev phone,
2026-09-02. Only the AUTH lines are carried: the full transcripts are ~40 KB
each and are thick with the device's SFS container names, none of which this
test needs. Originals are in the fp6 journal under
journal/fingerprint/captures/.
| file | run | recorded outcome |
|---|---|---|
auth-enrolled-finger.txt |
the enrolled finger, max_authentication_rescan_times: 0 |
15 match, 5 rejected, 5 frames the matcher never saw |
auth-wrong-finger.txt |
a different finger, same config | 0 match, 19 rejected, 2 never ran |
auth-stock-budget.txt |
the enrolled finger at the stock rescan budget | 8 match, 31 "no match", 9 never ran |
The third file is the important one and uses an older label: its 31 "no
match" lines are rc=-11 frames, which mean not identified yet, attempts
remain — not rejections. Counting them as rejections turns an 8-for-8 run into
an apparent 8-of-39 and reads as a flaky sensor. That mislabelling happened
three separate times before the comparison producing -11 was actually read,
which is why the counting rule has a test at all.
The two forced-terminal runs set the rescan budget to 0 so every frame yields a verdict. Their 15/20 is therefore a per-frame figure measured with the retry mechanism disabled, not a shipping reject rate; per press it was 5 of 5.
fid=1296911490 is the enrolled template's identifier on one dev phone. It is
an opaque id, not biometric data — a template is a separate 258850-byte
QTEE-encrypted container that never leaves the TEE.