fingerprintd/packaging/fingerprintd.service
Jorijn van der Graaf b228287c5b 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.
2026-09-05 04:01:06 +02:00

49 lines
2.4 KiB
Desktop File

# SPDX-License-Identifier: GPL-3.0-only
# SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
[Unit]
Description=Fingerprint daemon (FocalTech FT9391 behind QTEE)
Documentation=https://forgejo.catcrafts.net/Catcrafts/fingerprintd
# The trustlet does the matching and it is a proprietary OEM-signed blob
# extracted from the stock vendor partition, so it is not in this package.
# Without it there is no sensor, and the unit stays out of the way instead of
# restart-looping.
ConditionPathExists=/usr/lib/firmware/focal64.mbn
# /dev/tee0 is the qcomtee driver, which the pmOS kernel does not build yet
# (CONFIG_QCOMTEE). modules-load.d asks for the module; this condition is what
# makes the package harmless on a kernel that has none.
ConditionPathExists=/dev/tee0
# The templates are QTEE containers on the Android persist partition, reached
# through the SFS root's persist-data/root3 symlinks.
RequiresMountsFor=/mnt/persist
Requires=dbus.service
# fp6-vendor-blobs reassembles the trustlet from the stock modem
# partition; it runs in sysinit so this ordering already holds, and
# saying so keeps the ConditionPathExists above from looking arbitrary.
After=dbus.service fp6-vendor-blobs.service
[Service]
Type=simple
# --edge-wake wait on the sensor IRQ instead of polling for a finger
# --sfs-writable QTEE must be able to WRITE the template store, or an
# enrolment cannot be saved. It can also unlink a container it
# rejects, which is why it is opt-in rather than the default.
# --rpmb-write the anti-rollback counter lives in RPMB; a save that cannot
# write it does not commit.
# No --verbose: it prints the frame-by-frame state machine, which on a phone
# is both journal noise and a record of when its owner unlocked it.
ExecStart=/usr/bin/fingerprintd --daemon --edge-wake \
--sfs-root=/var/lib/fingerprintd/sfs --sfs-writable --rpmb-write
# Root is required and not reducible: the daemon drives the sensor rails over
# gpiochip, holds /dev/tee0, and serves QTEE's RPMB transactions against the
# raw UFS RPMB device. No sandboxing is declared here rather than declaring
# some that was never tested against those three.
Restart=on-failure
RestartSec=5
# SIGTERM: the worker finishes the invoke it is inside before the session goes
# down; QTEE's listener table is global to the boot and a half-torn session
# leaves it holding ours.
KillMode=mixed
TimeoutStopSec=15
[Install]
WantedBy=multi-user.target