Repository navigation
Releases: smileidentity/kamera-spm
Release list
kamera v1.2.1-SNAPSHOT.33
Snapshot of main at da15e119b1372fd0d69cb0d07081169b0f2590c8, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.33" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.33 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.33, dist-tag snapshot |
Changes since 1.2.0
Fixed
-
iOS: releasing a session while its preview moves off it no longer aborts the app: a
KameraPreviewViewchanged its layer's session on the main thread, as it went away and as it was
attached to a new session (the Flutter and React Native wrappers'renew()does so right after
release()). That reconfigures the session whilerelease()stops it on the session queue, and
AVFoundation raisedNSGenericException("stopRunning may not be called between calls to
beginConfiguration and commitConfiguration"), seen on a document's back capture. The view now makes
each change on the session's own queue, one hand-over at a time: the old session lets go first,
then the new one attaches, and a session attached meanwhile waits, the newest winning. So
attach(session:)now lands after it returns: readpreviewLayer.sessionand itsconnectiononly
once it has, and never assignpreviewLayer.sessionyourself.attachno longer resets the view's
gravity to aspect-fill, which is set once when the view is made. -
The exposure swap probes run on phones whose auto-exposure never says it has settled (Android and
iOS). A swap (ExposureLong,ExposureShort) planned only where auto-exposure's own state said
settled. The iPhone 16's front camera reported itself adjusting at every probe with a face in view,
so both phones answered every swapunsupported(23 October 2026), which told the server the
camera can't swap when it can. A swap now also plans where the sensor's exposure time, its ISO and
their product each held within 0.05 stops from the frame before (one whole ISO step allowed).
A swap's undo waits for auto-exposure to settle again, within the restore's wait, so the swap's
other half plans from a settled exposure. -
The Custos adapters pair the gyroscope with the accelerometer (
kamera-custos,
KameraCustos): beside each motion batch they chain Custos'simuevents, each gyroscope
reading paired by Custos'spairImuwith the accelerometer reading nearest it, so the server can
check that the attitude the accelerometer shows moves as the gyroscope says it turned. A gyro
reading waits until an accelerometer reading at or after its time has come, andclose()pairs
what is left, so the SDK calls it before chaining the seal. -
The Custos adapters hand every gyro reading to Custos to convert (
kamera-custos,
KameraCustos): each reading's floats go to Custos'smotionReading, the one conversion every
shim uses (Custos SPEC §6.2), instead of the adapter rounding to micro-radians itself. A reading
Custos refuses, or one stamped before the clock began, is left out and counted in the motion
event'srefused, so the server tells a sensor reporting garbage from a phone that didn't move.
The adapters build against Custos main again, whose motion event gained that count. -
React Native apps that link
@smileid/kamera-testinglaunch on iOS:kamera_spm_link.rblinksKameraTestinginto the app target as well as the testing pod, so Xcode embeds the dynamic framework instead of the app dying at launch with "Library not loaded: @rpath/KameraTesting.framework/KameraTesting". -
@smileid/kamera-testingbuilds on iOS again with React Native 0.86: itsKameraReplaymodule imports React, soRCTPromiseResolveBlockandRCTPromiseRejectBlockresolve and an app with the testing package linked compiles. -
iOS:
KameraReplay.installfinds the kit's manifest: with no registry set, it read only the
app bundle's manifest (fromBundle(.main)), so an app that bundles media but no manifest failed to
install a fixture withmanifestMissing. It now reads the device's fixtures as
FixtureRegistry.fromDevicedoes: pushed, bundled, then the kit's own manifest with the app's media. -
Replay media an app stages is read against the kit's manifest: with no manifest of its own,
FixtureRegistry.fromDeviceused the kit's and looked for the media inside the kit, which never
carries it. It now reads the media from the app's owncapture-fixtures/media: on iOS pushed into
Documents or copied into the app bundle, on Android pushed into the files directory (media in the
app's assets already merged with the kit's manifest). A folder counts once it holds every file the
manifest names, so a stray or half-pushed one never hides complete media elsewhere. -
ML Kit works in a minified app (Android):
kamera-mlkitnow ships a consumer rule keeping
ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
R8 otherwise strips. Without it,FaceDetection.getClient()threw aNullPointerExceptionin a
minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0). -
Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
low-light boost off for its frames where they're on, and the undo turns them back on, as Android
takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
so this matters where a host or the system did. -
Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
slow in a dark room needs more time to apply a write than 500 ms gives it. -
Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
its result lands, and a frame the sink would hear without readings waits for its result (up to 8
frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
80 ms exposures), where none did. -
Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
diopters and never applied before.
Added
- Prompt crops in the Custos adapters (
kamera-custos,KameraCustos): when the server asks
for prompt crops,KameraCustosCapture.askForPromptCrops()says so before the SDK chains its
step.respond(prompt, outcome, frame)then takes the analysed frame the SDK decided a completed
challenge on, still open. It cuts that frame's crop, seals it under roleprompt-cropat the
challenge's schedule index, and chains the response with its hash, naming the frame by the camera
time it was chained with.sealedPromptCropsholds the packets to upload beside the probe crops.
framedefaults to none, so existing calls are unchanged. A capture that asked refuses a
completed response without a frame Kamera can cut (Custos SPEC §6.3 rule 10; docs/design/69, D9),
and one whose frame Kamera's sink never chained, with Custos'sCaptureChainerror naming it. On
Android, where Kamera holds a frame's evidence until its capture result lands,respondfirst
suspends up to a quarter of a second for it.respondis nowsuspendon Android andasyncon
iOS: call it from the SDK's coroutine or task, asnext()is. - Prompt crops for Custos (native cores, for
kamera-custos):KameraFrame.promptCrop()
cuts the face of an open frame losslessly, as Custos SPEC §6.7 windows it (the region of
interest in buffer coordinates, at least 64 and at most 512 a side), and returns a
KameraPromptCropof the frame's own YUV 4:2:0 samples with every stride dropped. Its
description never prints the planes. Restricted to kamera's own adapters
(@RestrictTo(LIBRARY_GROUP)on Android,@_spi(KameraCustos)on iOS); nothing changes for a
host that doesn't call it (docs/design/69, D9). - The replay kits ship the fixture manifest: the kamera-testing AAR carries
capture-fixtures/manifest.jsonas an asset and KameraTesting carries it as a resource, so
FixtureRegistry.fromDevicefinds the fixtures in any...
kamera v1.2.1-SNAPSHOT.32
Snapshot of ee51d159bd56d1dca30936a4c7799280cc3bb101 at ee51d159bd56d1dca30936a4c7799280cc3bb101, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.32" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.32 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.32, dist-tag snapshot |
Changes since 1.2.0
Fixed
-
React Native apps that link
@smileid/kamera-testinglaunch on iOS:kamera_spm_link.rblinksKameraTestinginto the app target as well as the testing pod, so Xcode embeds the dynamic framework instead of the app dying at launch with "Library not loaded: @rpath/KameraTesting.framework/KameraTesting". -
@smileid/kamera-testingbuilds on iOS again with React Native 0.86: itsKameraReplaymodule imports React, soRCTPromiseResolveBlockandRCTPromiseRejectBlockresolve and an app with the testing package linked compiles. -
iOS:
KameraReplay.installfinds the kit's manifest: with no registry set, it read only the
app bundle's manifest (fromBundle(.main)), so an app that bundles media but no manifest failed to
install a fixture withmanifestMissing. It now reads the device's fixtures as
FixtureRegistry.fromDevicedoes: pushed, bundled, then the kit's own manifest with the app's media. -
Replay media an app stages is read against the kit's manifest: with no manifest of its own,
FixtureRegistry.fromDeviceused the kit's and looked for the media inside the kit, which never
carries it. It now reads the media from the app's owncapture-fixtures/media: on iOS pushed into
Documents or copied into the app bundle, on Android pushed into the files directory (media in the
app's assets already merged with the kit's manifest). A folder counts once it holds every file the
manifest names, so a stray or half-pushed one never hides complete media elsewhere. -
ML Kit works in a minified app (Android):
kamera-mlkitnow ships a consumer rule keeping
ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
R8 otherwise strips. Without it,FaceDetection.getClient()threw aNullPointerExceptionin a
minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0). -
Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
low-light boost off for its frames where they're on, and the undo turns them back on, as Android
takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
so this matters where a host or the system did. -
Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
slow in a dark room needs more time to apply a write than 500 ms gives it. -
Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
its result lands, and a frame the sink would hear without readings waits for its result (up to 8
frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
80 ms exposures), where none did. -
Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
diopters and never applied before.
Added
-
Custos adapters run the exposure swaps:
kamera-custosandKameraCustosmap Custos probes
6 and 7 toExposureLongandExposureShort, so a schedule's swaps reach the camera. -
Exposure swap probes (iOS):
KameraProbeKindgainsexposureLongandexposureShort
(Custos probes 6 and 7,KameraCustosonly). The probe sets a custom exposure: the duration
auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case onKameraProbeKindbreaks an exhaustiveswitchover it;
it is behind@_spi(KameraCustos). -
Exposure swap probes (Android):
KameraProbeKindgainsExposureLongandExposureShort
(Custos probes 6 and 7,kamera-custosonly). The probe turns auto-exposure off and trades the
exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
photons behind it: the server judges the pair by its shot noise wherever the processor holds
brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
swap starts from what AE chose. A new case onKameraProbeKindbreaks an exhaustive
whenover it; it is restricted to kamera's own library group. -
Custos adapters:
kamera-custos(Android) andKameraCustos(iOS) connect a Kamera session
to a Custos capture (docs/design/69, D8).KameraCustosCapturefeeds the capture from Kamera's
evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
taken, so the SDK uploads sealed packets and never sees the pixels.attachEvidenceand
runProbeare now the adapters' alone (@RestrictTo(LIBRARY_GROUP)on Android,
@_spi(KameraCustos)on iOS). Each applied probe names its frame by the camera time that frame
was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
the adapters build only with-Pcustos.repo=DIRorKAMERA_CUSTOS_PATH. -
Camera probes (Android and iOS):
KameraSession.runProbe(KameraProbe)runs one of the
server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
and the evidence sink still hears every one. The first frame whose own capture result shows the
probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
probe gives a fourth, the same background window, so the server can check the sensor's noise is
new in every frame (65,536 bytes in all); a probe shown by one frame only isFailed. Then the probe is undone and
analysis resumes. It answersApplied,Unsupported(a fixed-focus lens, no such rate, a
parameter Kamera can't express, or a replay session),Failed(nothing showed it within 500 ms),
orNotRun(another probe or a still was running, or the session wasn't), which is not evidence.
Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
from the sync time AVFoundation's completion reports, a frame duration from the frames' own
intervals, and a zoom on the second frame after the write; face tracking answersunsupported.
On Android a probe's frames are taken withNOISE_REDUCTION_MODE_OFFandEDGE_MODE_OFFwherever
the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
undo restores the camera's processing. iOS probes read the video data output, which never merges
frames the way the photo pipeline does. -
Capture evidence (Android and iOS):
KameraSession.attachEvidence(KameraEvidence?)hands a
session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
callsonFramewith each analysed frame's camera readings: its sensor timestamp, whether that is
onKameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
and callsonMotionwith batches of up to 256 readings. On iOS the clock is
mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
a Swift sink can't throw), andrelease()detaches it. Without a sink nothing changes. The types live in
`com.usesmileid.kamer...
kamera v1.2.1-SNAPSHOT.31
Snapshot of main at 2947cd18da6815c4312bbe4782ba83ab19a26e98, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.31" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.31 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.31, dist-tag snapshot |
Changes since 1.2.0
Fixed
-
iOS:
KameraReplay.installfinds the kit's manifest: with no registry set, it read only the
app bundle's manifest (fromBundle(.main)), so an app that bundles media but no manifest failed to
install a fixture withmanifestMissing. It now reads the device's fixtures as
FixtureRegistry.fromDevicedoes: pushed, bundled, then the kit's own manifest with the app's media. -
Replay media an app stages is read against the kit's manifest: with no manifest of its own,
FixtureRegistry.fromDeviceused the kit's and looked for the media inside the kit, which never
carries it. It now reads the media from the app's owncapture-fixtures/media: on iOS pushed into
Documents or copied into the app bundle, on Android pushed into the files directory (media in the
app's assets already merged with the kit's manifest). A folder counts once it holds every file the
manifest names, so a stray or half-pushed one never hides complete media elsewhere. -
ML Kit works in a minified app (Android):
kamera-mlkitnow ships a consumer rule keeping
ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
R8 otherwise strips. Without it,FaceDetection.getClient()threw aNullPointerExceptionin a
minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0). -
Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
low-light boost off for its frames where they're on, and the undo turns them back on, as Android
takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
so this matters where a host or the system did. -
Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
slow in a dark room needs more time to apply a write than 500 ms gives it. -
Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
its result lands, and a frame the sink would hear without readings waits for its result (up to 8
frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
80 ms exposures), where none did. -
Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
diopters and never applied before.
Added
-
Custos adapters run the exposure swaps:
kamera-custosandKameraCustosmap Custos probes
6 and 7 toExposureLongandExposureShort, so a schedule's swaps reach the camera. -
Exposure swap probes (iOS):
KameraProbeKindgainsexposureLongandexposureShort
(Custos probes 6 and 7,KameraCustosonly). The probe sets a custom exposure: the duration
auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case onKameraProbeKindbreaks an exhaustiveswitchover it;
it is behind@_spi(KameraCustos). -
Exposure swap probes (Android):
KameraProbeKindgainsExposureLongandExposureShort
(Custos probes 6 and 7,kamera-custosonly). The probe turns auto-exposure off and trades the
exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
photons behind it: the server judges the pair by its shot noise wherever the processor holds
brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
swap starts from what AE chose. A new case onKameraProbeKindbreaks an exhaustive
whenover it; it is restricted to kamera's own library group. -
Custos adapters:
kamera-custos(Android) andKameraCustos(iOS) connect a Kamera session
to a Custos capture (docs/design/69, D8).KameraCustosCapturefeeds the capture from Kamera's
evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
taken, so the SDK uploads sealed packets and never sees the pixels.attachEvidenceand
runProbeare now the adapters' alone (@RestrictTo(LIBRARY_GROUP)on Android,
@_spi(KameraCustos)on iOS). Each applied probe names its frame by the camera time that frame
was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
the adapters build only with-Pcustos.repo=DIRorKAMERA_CUSTOS_PATH. -
Camera probes (Android and iOS):
KameraSession.runProbe(KameraProbe)runs one of the
server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
and the evidence sink still hears every one. The first frame whose own capture result shows the
probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
probe gives a fourth, the same background window, so the server can check the sensor's noise is
new in every frame (65,536 bytes in all); a probe shown by one frame only isFailed. Then the probe is undone and
analysis resumes. It answersApplied,Unsupported(a fixed-focus lens, no such rate, a
parameter Kamera can't express, or a replay session),Failed(nothing showed it within 500 ms),
orNotRun(another probe or a still was running, or the session wasn't), which is not evidence.
Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
from the sync time AVFoundation's completion reports, a frame duration from the frames' own
intervals, and a zoom on the second frame after the write; face tracking answersunsupported.
On Android a probe's frames are taken withNOISE_REDUCTION_MODE_OFFandEDGE_MODE_OFFwherever
the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
undo restores the camera's processing. iOS probes read the video data output, which never merges
frames the way the photo pipeline does. -
Capture evidence (Android and iOS):
KameraSession.attachEvidence(KameraEvidence?)hands a
session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
callsonFramewith each analysed frame's camera readings: its sensor timestamp, whether that is
onKameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
and callsonMotionwith batches of up to 256 readings. On iOS the clock is
mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
a Swift sink can't throw), andrelease()detaches it. Without a sink nothing changes. The types live in
com.usesmileid.kamera.integrationon the native cores only: the v12 SDKs connect them to Custos
in their native bridges, and nothing crosses to Flutter or React Native. -
Replay: a fixture can be installed for one use case.
KameraReplay.install(fixture, useCase:)
sets the source a session of thatKameraUseCaseplays, beside the defaultinstall(fixture),
so a document job replays the card for its document step and the face for its selfie.reset()
clears them all. Core reads them throughKameraReplayHooks.sessionFrameSource(useCase:), still
compiled out of release builds....
kamera v1.2.1-SNAPSHOT.30
Snapshot of main at 8f493edaa32907b76abadfe75e63bbd2c7ebd111, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.30" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.30 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.30, dist-tag snapshot |
Changes since 1.2.0
Fixed
-
Replay media an app stages is read against the kit's manifest: with no manifest of its own,
FixtureRegistry.fromDeviceused the kit's and looked for the media inside the kit, which never
carries it. It now reads the media from the app's owncapture-fixtures/media: on iOS pushed into
Documents or copied into the app bundle, on Android pushed into the files directory (media in the
app's assets already merged with the kit's manifest). A folder counts once it holds every file the
manifest names, so a stray or half-pushed one never hides complete media elsewhere. -
ML Kit works in a minified app (Android):
kamera-mlkitnow ships a consumer rule keeping
ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
R8 otherwise strips. Without it,FaceDetection.getClient()threw aNullPointerExceptionin a
minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0). -
Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
low-light boost off for its frames where they're on, and the undo turns them back on, as Android
takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
so this matters where a host or the system did. -
Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
slow in a dark room needs more time to apply a write than 500 ms gives it. -
Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
its result lands, and a frame the sink would hear without readings waits for its result (up to 8
frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
80 ms exposures), where none did. -
Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
diopters and never applied before.
Added
-
Custos adapters run the exposure swaps:
kamera-custosandKameraCustosmap Custos probes
6 and 7 toExposureLongandExposureShort, so a schedule's swaps reach the camera. -
Exposure swap probes (iOS):
KameraProbeKindgainsexposureLongandexposureShort
(Custos probes 6 and 7,KameraCustosonly). The probe sets a custom exposure: the duration
auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case onKameraProbeKindbreaks an exhaustiveswitchover it;
it is behind@_spi(KameraCustos). -
Exposure swap probes (Android):
KameraProbeKindgainsExposureLongandExposureShort
(Custos probes 6 and 7,kamera-custosonly). The probe turns auto-exposure off and trades the
exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
photons behind it: the server judges the pair by its shot noise wherever the processor holds
brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
swap starts from what AE chose. A new case onKameraProbeKindbreaks an exhaustive
whenover it; it is restricted to kamera's own library group. -
Custos adapters:
kamera-custos(Android) andKameraCustos(iOS) connect a Kamera session
to a Custos capture (docs/design/69, D8).KameraCustosCapturefeeds the capture from Kamera's
evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
taken, so the SDK uploads sealed packets and never sees the pixels.attachEvidenceand
runProbeare now the adapters' alone (@RestrictTo(LIBRARY_GROUP)on Android,
@_spi(KameraCustos)on iOS). Each applied probe names its frame by the camera time that frame
was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
the adapters build only with-Pcustos.repo=DIRorKAMERA_CUSTOS_PATH. -
Camera probes (Android and iOS):
KameraSession.runProbe(KameraProbe)runs one of the
server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
and the evidence sink still hears every one. The first frame whose own capture result shows the
probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
probe gives a fourth, the same background window, so the server can check the sensor's noise is
new in every frame (65,536 bytes in all); a probe shown by one frame only isFailed. Then the probe is undone and
analysis resumes. It answersApplied,Unsupported(a fixed-focus lens, no such rate, a
parameter Kamera can't express, or a replay session),Failed(nothing showed it within 500 ms),
orNotRun(another probe or a still was running, or the session wasn't), which is not evidence.
Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
from the sync time AVFoundation's completion reports, a frame duration from the frames' own
intervals, and a zoom on the second frame after the write; face tracking answersunsupported.
On Android a probe's frames are taken withNOISE_REDUCTION_MODE_OFFandEDGE_MODE_OFFwherever
the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
undo restores the camera's processing. iOS probes read the video data output, which never merges
frames the way the photo pipeline does. -
Capture evidence (Android and iOS):
KameraSession.attachEvidence(KameraEvidence?)hands a
session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
callsonFramewith each analysed frame's camera readings: its sensor timestamp, whether that is
onKameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
and callsonMotionwith batches of up to 256 readings. On iOS the clock is
mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
a Swift sink can't throw), andrelease()detaches it. Without a sink nothing changes. The types live in
com.usesmileid.kamera.integrationon the native cores only: the v12 SDKs connect them to Custos
in their native bridges, and nothing crosses to Flutter or React Native. -
Replay: a fixture can be installed for one use case.
KameraReplay.install(fixture, useCase:)
sets the source a session of thatKameraUseCaseplays, beside the defaultinstall(fixture),
so a document job replays the card for its document step and the face for its selfie.reset()
clears them all. Core reads them throughKameraReplayHooks.sessionFrameSource(useCase:), still
compiled out of release builds. The names and theuseCaseparameter are the same on Android and
iOS.
Changed
- Kamera's classes shrink and rename in a partner's release (Android): the consumer rules no
longer keep kamera's packages whole. Nothing reaches kamera by name, and the public API is kept
when the AAR is built, so a partner's R8 now treats kamera like its own code. In a minified app
that...
kamera v1.2.1-SNAPSHOT.29
Snapshot of main at 874be0ceb49c2ca3689f1fe51c406db444df4a75, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.29" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.29 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.29, dist-tag snapshot |
Changes since 1.2.0
Fixed
-
ML Kit works in a minified app (Android):
kamera-mlkitnow ships a consumer rule keeping
ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
R8 otherwise strips. Without it,FaceDetection.getClient()threw aNullPointerExceptionin a
minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0). -
Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
low-light boost off for its frames where they're on, and the undo turns them back on, as Android
takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
so this matters where a host or the system did. -
Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
slow in a dark room needs more time to apply a write than 500 ms gives it. -
Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
its result lands, and a frame the sink would hear without readings waits for its result (up to 8
frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
80 ms exposures), where none did. -
Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
diopters and never applied before.
Added
-
Custos adapters run the exposure swaps:
kamera-custosandKameraCustosmap Custos probes
6 and 7 toExposureLongandExposureShort, so a schedule's swaps reach the camera. -
Exposure swap probes (iOS):
KameraProbeKindgainsexposureLongandexposureShort
(Custos probes 6 and 7,KameraCustosonly). The probe sets a custom exposure: the duration
auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case onKameraProbeKindbreaks an exhaustiveswitchover it;
it is behind@_spi(KameraCustos). -
Exposure swap probes (Android):
KameraProbeKindgainsExposureLongandExposureShort
(Custos probes 6 and 7,kamera-custosonly). The probe turns auto-exposure off and trades the
exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
photons behind it: the server judges the pair by its shot noise wherever the processor holds
brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
swap starts from what AE chose. A new case onKameraProbeKindbreaks an exhaustive
whenover it; it is restricted to kamera's own library group. -
Custos adapters:
kamera-custos(Android) andKameraCustos(iOS) connect a Kamera session
to a Custos capture (docs/design/69, D8).KameraCustosCapturefeeds the capture from Kamera's
evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
taken, so the SDK uploads sealed packets and never sees the pixels.attachEvidenceand
runProbeare now the adapters' alone (@RestrictTo(LIBRARY_GROUP)on Android,
@_spi(KameraCustos)on iOS). Each applied probe names its frame by the camera time that frame
was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
the adapters build only with-Pcustos.repo=DIRorKAMERA_CUSTOS_PATH. -
Camera probes (Android and iOS):
KameraSession.runProbe(KameraProbe)runs one of the
server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
and the evidence sink still hears every one. The first frame whose own capture result shows the
probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
probe gives a fourth, the same background window, so the server can check the sensor's noise is
new in every frame (65,536 bytes in all); a probe shown by one frame only isFailed. Then the probe is undone and
analysis resumes. It answersApplied,Unsupported(a fixed-focus lens, no such rate, a
parameter Kamera can't express, or a replay session),Failed(nothing showed it within 500 ms),
orNotRun(another probe or a still was running, or the session wasn't), which is not evidence.
Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
from the sync time AVFoundation's completion reports, a frame duration from the frames' own
intervals, and a zoom on the second frame after the write; face tracking answersunsupported.
On Android a probe's frames are taken withNOISE_REDUCTION_MODE_OFFandEDGE_MODE_OFFwherever
the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
undo restores the camera's processing. iOS probes read the video data output, which never merges
frames the way the photo pipeline does. -
Capture evidence (Android and iOS):
KameraSession.attachEvidence(KameraEvidence?)hands a
session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
callsonFramewith each analysed frame's camera readings: its sensor timestamp, whether that is
onKameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
and callsonMotionwith batches of up to 256 readings. On iOS the clock is
mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
a Swift sink can't throw), andrelease()detaches it. Without a sink nothing changes. The types live in
com.usesmileid.kamera.integrationon the native cores only: the v12 SDKs connect them to Custos
in their native bridges, and nothing crosses to Flutter or React Native. -
Replay: a fixture can be installed for one use case.
KameraReplay.install(fixture, useCase:)
sets the source a session of thatKameraUseCaseplays, beside the defaultinstall(fixture),
so a document job replays the card for its document step and the face for its selfie.reset()
clears them all. Core reads them throughKameraReplayHooks.sessionFrameSource(useCase:), still
compiled out of release builds. The names and theuseCaseparameter are the same on Android and
iOS.
Changed
-
Kamera's classes shrink and rename in a partner's release (Android): the consumer rules no
longer keep kamera's packages whole. Nothing reaches kamera by name, and the public API is kept
when the AAR is built, so a partner's R8 now treats kamera like its own code. In a minified app
that runs kamera, the dex is 234,656 B smaller and no kamera class keeps a readable name (167
did). A newkamera-android/r8checkapp andscripts/r8-check.shprove a minified app streams
frames, runs ML Kit on a kamera frame and captures a still, and the newr8-checklane runs it on
an emulator on every Android change. -
Breaking (probe crops): a probe crop gains a fifth plane, the face window of the next frame
that still shows the probe (Custos SPEC §6.6), so the server measures the face's noise from frame
to frame.KameraProbeResult.CROP_BYTES/cropBytesi...
kamera v1.2.1-SNAPSHOT.28
Snapshot of main at 260467d3523abdbd9d1bab49827fb5795aec0b19, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.28" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.28 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.28, dist-tag snapshot |
Changes since 1.2.0
Fixed
-
ML Kit works in a minified app (Android):
kamera-mlkitnow ships a consumer rule keeping
ML Kit's component registrars, which ML Kit finds by the class names in its manifest meta-data and
R8 otherwise strips. Without it,FaceDetection.getClient()threw aNullPointerExceptionin a
minified partner build (seen on the Galaxy S25 Ultra, play-services-mlkit-face-detection 17.1.0). -
Camera probes keep the sensor's response (iOS): a probe turns global tone mapping and
low-light boost off for its frames where they're on, and the undo turns them back on, as Android
takes probe frames without noise reduction or sharpening. Tone mapping fits a curve to each frame,
which evens brightness out across an exposure probe, and low-light boost raises the gain by itself
in the dark, which moves the noise with no probe commanding it. Kamera never turns tone mapping on,
so this matters where a host or the system did. -
Camera probes in the dark (iOS): a probe waits at least 15 frames of the camera's measured
period as well as its 500 ms (twice that for an exposure bias), as Android does: a camera running
slow in a dark room needs more time to apply a write than 500 ms gives it. -
Camera probes and capture evidence on cameras whose capture results arrive late (Android): the
Huawei GLA-LX1 (Android 11) delivered every capture result 40 to 100 ms after its image, past the
10 ms Kamera waited, so no frame there carried readings: every probe failed and the evidence sink
heard frames without exposure, ISO or focus. A probe frame is now cut as it arrives and judged when
its result lands, and a frame the sink would hear without readings waits for its result (up to 8
frames, then it goes without). Probes also wait at least 15 frames of the camera's own period, not
only 500 ms: in a dark room that phone ran at 14 fps and its focus and zoom writes showed only after
7 frames. There, five of six probes now apply (a 50 ms frame duration still can't, under 60 to
80 ms exposures), where none did. -
Camera probes: an exposure probe applies only once the sensor has moved and settled, not at
the first frame that carries the new setting. Android needs the frame's exposure time × ISO to have
moved at least half the commanded stops from a frame before the probe, then held within 0.05 stops
from one frame to the next. iOS needs the same, past the bias's sync time, from each frame's EXIF.
Exposure probes get twice the window (1 s), because auto-exposure takes longer to settle a stop
than a lens or a zoom. The server's measurements found this on the first device crops: both phones'
exposure steps had moved no brightness at all. A far-focus probe is held at the lens's hyperfocal
distance, where a lens asked for infinity parks: the Galaxy S25 Ultra's front lens parked at 0.45
diopters and never applied before.
Added
-
Custos adapters run the exposure swaps:
kamera-custosandKameraCustosmap Custos probes
6 and 7 toExposureLongandExposureShort, so a schedule's swaps reach the camera. -
Exposure swap probes (iOS):
KameraProbeKindgainsexposureLongandexposureShort
(Custos probes 6 and 7,KameraCustosonly). The probe sets a custom exposure: the duration
auto-exposure chose times or over a factor, and the ISO the other way, so the frame is as bright with
more, or fewer, photons behind it, and the undo hands exposure back to continuous auto-exposure. A new case onKameraProbeKindbreaks an exhaustiveswitchover it;
it is behind@_spi(KameraCustos). -
Exposure swap probes (Android):
KameraProbeKindgainsExposureLongandExposureShort
(Custos probes 6 and 7,kamera-custosonly). The probe turns auto-exposure off and trades the
exposure time AE chose against its ISO by a factor, so the frame is as bright with more, or fewer,
photons behind it: the server judges the pair by its shot noise wherever the processor holds
brightness. Kamera now reads each capture result's exposure while the evidence sink listens, so a
swap starts from what AE chose. A new case onKameraProbeKindbreaks an exhaustive
whenover it; it is restricted to kamera's own library group. -
Custos adapters:
kamera-custos(Android) andKameraCustos(iOS) connect a Kamera session
to a Custos capture (docs/design/69, D8).KameraCustosCapturefeeds the capture from Kamera's
evidence sink, runs the schedule's camera probes, and seals each crop to the server where it's
taken, so the SDK uploads sealed packets and never sees the pixels.attachEvidenceand
runProbeare now the adapters' alone (@RestrictTo(LIBRARY_GROUP)on Android,
@_spi(KameraCustos)on iOS). Each applied probe names its frame by the camera time that frame
was chained with (Custos SPEC §6.2, kind 8, field 7), which the adapter looks up from the frames
it chained, so the server can read the exposure each crop was taken at. Until Custos publishes,
the adapters build only with-Pcustos.repo=DIRorKAMERA_CUSTOS_PATH. -
Camera probes (Android and iOS):
KameraSession.runProbe(KameraProbe)runs one of the
server schedule's hidden camera-liveness probes (docs/design/69, D2–D3): focus near or far, an
exposure step, a fixed frame duration, or a zoom. While it runs, no frame reaches the collector,
and the evidence sink still hears every one. The first frame whose own capture result shows the
probe applied is cropped to a set of three 128×128 luma planes: the face and a background corner at
native resolution, and a 3×3-averaged context around the face. The next frame that still shows the
probe gives a fourth, the same background window, so the server can check the sensor's noise is
new in every frame (65,536 bytes in all); a probe shown by one frame only isFailed. Then the probe is undone and
analysis resumes. It answersApplied,Unsupported(a fixed-focus lens, no such rate, a
parameter Kamera can't express, or a replay session),Failed(nothing showed it within 500 ms),
orNotRun(another probe or a still was running, or the session wasn't), which is not evidence.
Host controls set during a probe (exposure, zoom, night mode, a tap) apply when it ends, and a
still waits for it. iOS has no per-frame capture results, so a lens-position or bias probe applies
from the sync time AVFoundation's completion reports, a frame duration from the frames' own
intervals, and a zoom on the second frame after the write; face tracking answersunsupported.
On Android a probe's frames are taken withNOISE_REDUCTION_MODE_OFFandEDGE_MODE_OFFwherever
the camera lists them, so the crop keeps the sensor's own noise for the server to measure; the
undo restores the camera's processing. iOS probes read the video data output, which never merges
frames the way the photo pipeline does. -
Capture evidence (Android and iOS):
KameraSession.attachEvidence(KameraEvidence?)hands a
session a sink for a signed capture record (docs/design/69, D1). While one is attached, kamera
callsonFramewith each analysed frame's camera readings: its sensor timestamp, whether that is
onKameraEvidenceClock, exposure, ISO, AF and AE state, and the camera id. It also runs the gyro
and callsonMotionwith batches of up to 256 readings. On iOS the clock is
mach_continuous_time, and sample-buffer and Core Motion times are moved onto it; face tracking's
frames report ARKit's exposure. Replay reports nothing, a sink that throws is detached (Android;
a Swift sink can't throw), andrelease()detaches it. Without a sink nothing changes. The types live in
com.usesmileid.kamera.integrationon the native cores only: the v12 SDKs connect them to Custos
in their native bridges, and nothing crosses to Flutter or React Native. -
Replay: a fixture can be installed for one use case.
KameraReplay.install(fixture, useCase:)
sets the source a session of thatKameraUseCaseplays, beside the defaultinstall(fixture),
so a document job replays the card for its document step and the face for its selfie.reset()
clears them all. Core reads them throughKameraReplayHooks.sessionFrameSource(useCase:), still
compiled out of release builds. The names and theuseCaseparameter are the same on Android and
iOS.
Changed
-
Kamera's classes shrink and rename in a partner's release (Android): the consumer rules no
longer keep kamera's packages whole. Nothing reaches kamera by name, and the public API is kept
when the AAR is built, so a partner's R8 now treats kamera like its own code. In a minified app
that runs kamera, the dex is 234,656 B smaller and no kamera class keeps a readable name (167
did). A newkamera-android/r8checkapp andscripts/r8-check.shprove a minified app streams
frames, runs ML Kit on a kamera frame and captures a still, and the newr8-checklane runs it on
an emulator on every Android change. -
Breaking (probe crops): a probe crop gains a fifth plane, the face window of the next frame
that still shows the probe (Custos SPEC §6.6), so the server measures the face's noise from frame
to frame.KameraProbeResult.CROP_BYTES/cropBytesi...
kamera v1.2.1-SNAPSHOT.27
Snapshot of main at 25dc7a4ced85c7b400ecd83ae5062fa8df2ab6ee, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.27" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.27 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.27, dist-tag snapshot |
Changes since 1.2.0
Changed
- Testing kit (source-breaking for raw ids): the replay face and document fixtures are new
generated recordings, and the old ones are gone.selfie-smile-v1becomes
selfie-neutral-smile-v2, neutral for 2.5 s and then smiling, so iOS's smile score has the
resting mouth it compares against.head-turns-v1becomeshead-turns-v2, a 7 s loop of right,
left and up that brings every direction back inside the enhanced selfie's 8 s timeout.
doc-card-v1becomesdoc-id1-desk-v1, a photoreal fictional card on a desk, sized to the SDKs'
ID-1 outline and stored as a video like the face clips. TheSelfieSmile,HeadTurnsandDocCardenum cases keep their names and
point at the new ids. Code that installs a fixture by its raw id string needs the new id.
Fixed
- Replay: a capture screen that hands over to the next no longer stops the next screen's
replay. Sessions share the one replay source, and the next screen's session begins before the
outgoing one is released, so the outgoing release stopped the run it no longer owned: on iOS a
document flow's selfie step got no frames and never completed. A session now stops the source
only while the run it began is still the one playing. Android and iOS both. - iOS testing kit: replay no longer crashes the app when a session restarts mid-run, as the
document flows do between captures. A run thatbegin()superseded freed its frame gate below
its starting value, which libdispatch traps on ("Semaphore object deallocated while in use"). - Testing kit: the
selfie-smile-v1replay fixture is padded with white instead of black.
Its dark bars made full-length uniform rows and columns, which the server rejects as an image
issue, so an SDK's pre-upload check retook every capture and replay never completed. The face's
size and position are unchanged.
Added
- All four SDKs:
KameraKit.deviceProfilesays how capable kamera reads the device to be
(docs/design/61): aKameraDeviceProfilewith atierofentry,standardorhigh(wire
wire.deviceTier) and the signals it came from, read once per process before any camera opens.
Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
be set from the field. On Android it'sdeviceProfile(context); Flutter and React Native return a
future and a promise that never fail. The glue's shared encoder isKameraDeviceProfileWire. - All four SDKs:
KameraMetric.AnalysisStalled(openForMs)(wireanalysisStalled) reports a
frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
reported a frame when it closed. A frame waiting inKameraFrameHubfor the host's take is not in
analysis and never counts. Each core checks once a second on a thread of its own, only while a
frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
matches: a Kotlinwhen, Swiftswitchor DartswitchoverKameraMetric, or a TypeScript
switchonmetric.namethat ends in anevercheck, needs anAnalysisStalled/
.analysisStalled/KameraAnalysisStalledMetric/KameraMetricName.AnalysisStalledcase (or a
default).
Changed
- Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
kameraMlBackenddefaults tomlkit, notall(docs/design/20 D9). Since 1.2.0, HMS reached
every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
an Expo app's manifest merge onandroid:allowBackup, and turned backup off in a Flutter app.- To keep 1.2.0's behaviour, set
-PkameraMlBackend=all(orKAMERA_ML_BACKEND=all). - An app that opts in settles HMS's
allowBackupwithtools:replace, and adds
-dontwarn org.chromium.net.**if R8 stops on the Cronet classes (docs/guide/install.md). - The Smile ID SDKs' Huawei packages depend on
kamera-huaweithemselves, so they don't need it.
- To keep 1.2.0's behaviour, set
- Flutter (Android): the preview texture is a
SurfaceProducer, not aSurfaceTexture. From
Android 10 Flutter backs it with anImageReader, which Impeller's Vulkan backend imports as it
is; aSurfaceTexturehad to be copied into Vulkan, with the raster thread waiting on the copy,
for every preview frame. AnImageReadershows the camera's buffers as the sensor wrote them, so
KameraPreviewViewturns them by the sensor's orientation and flips a front lens's itself. The
glue works the turn out from CameraX's description of each camera surface and sends it as the
camera changes, showing black in between, so a lens switch never shows the last camera's frame
turned the new camera's way (KameraTextureTransformin the Android core, for the glue). Before
Android 10 the texture is still aSurfaceTexture, and nothing changes. A replay's frames are
drawn into the texture already turned back, so the same transform shows them upright.
Removed
- Android and iOS:
BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
either core or in the SDKs. It wasinternalon iOS. On Android the class itself waspublic,
in thecom.usesmileid.kamera.internalpackage but outside the API lists, so it is deleted rather
than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
Breaking (Android, source): a host that referenced
com.usesmileid.kamera.internal.BestFrameTrackerno longer compiles. Rank recent frames over the
public scorer,frame.qualityScore(region), which stays.
kamera v1.2.1-SNAPSHOT.26
Snapshot of main at 2ac6a0de250ef3d8f6feaa5e5e16d30d7b1fe24e, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.26" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.26 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.26, dist-tag snapshot |
Changes since 1.2.0
Added
- Testing kit: three synthetic portrait document replay fixtures,
doc-greenbook-v1(the
South African Green Book's 320/428 page),doc-booklet-front-v1anddoc-booklet-back-v1(a
two-sided booklet at the default portrait 0.65), with ids on all four kits (GreenBook,
BookletFront,BookletBack). Each is three 720x1280 frames drawn inside the SDKs' portrait
document outline. Until now the only document fixture was the landscapedoc-card-v1, so a
portrait document flow had nothing to replay.scripts/gen_document_fixtures.pyrenders them.
Source-breaking for an exhaustive match: these are new cases on the public Kotlin
ReplayFixtureIdand SwiftReplayFixtureIDenums (greenBook,bookletFront,
bookletBackon iOS), so awhenorswitchover every case needs the three new ones or an
else/defaultbranch to compile.
Fixed
- Replay: a capture screen that hands over to the next no longer stops the next screen's
replay. Sessions share the one replay source, and the next screen's session begins before the
outgoing one is released, so the outgoing release stopped the run it no longer owned: on iOS a
document flow's selfie step got no frames and never completed. A session now stops the source
only while the run it began is still the one playing. Android and iOS both. - iOS testing kit: replay no longer crashes the app when a session restarts mid-run, as the
document flows do between captures. A run thatbegin()superseded freed its frame gate below
its starting value, which libdispatch traps on ("Semaphore object deallocated while in use"). - Testing kit: the
selfie-smile-v1replay fixture is padded with white instead of black.
Its dark bars made full-length uniform rows and columns, which the server rejects as an image
issue, so an SDK's pre-upload check retook every capture and replay never completed. The face's
size and position are unchanged.
Added
- All four SDKs:
KameraKit.deviceProfilesays how capable kamera reads the device to be
(docs/design/61): aKameraDeviceProfilewith atierofentry,standardorhigh(wire
wire.deviceTier) and the signals it came from, read once per process before any camera opens.
Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
be set from the field. On Android it'sdeviceProfile(context); Flutter and React Native return a
future and a promise that never fail. The glue's shared encoder isKameraDeviceProfileWire. - All four SDKs:
KameraMetric.AnalysisStalled(openForMs)(wireanalysisStalled) reports a
frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
reported a frame when it closed. A frame waiting inKameraFrameHubfor the host's take is not in
analysis and never counts. Each core checks once a second on a thread of its own, only while a
frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
matches: a Kotlinwhen, Swiftswitchor DartswitchoverKameraMetric, or a TypeScript
switchonmetric.namethat ends in anevercheck, needs anAnalysisStalled/
.analysisStalled/KameraAnalysisStalledMetric/KameraMetricName.AnalysisStalledcase (or a
default).
Changed
- Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
kameraMlBackenddefaults tomlkit, notall(docs/design/20 D9). Since 1.2.0, HMS reached
every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
an Expo app's manifest merge onandroid:allowBackup, and turned backup off in a Flutter app.- To keep 1.2.0's behaviour, set
-PkameraMlBackend=all(orKAMERA_ML_BACKEND=all). - An app that opts in settles HMS's
allowBackupwithtools:replace, and adds
-dontwarn org.chromium.net.**if R8 stops on the Cronet classes (docs/guide/install.md). - The Smile ID SDKs' Huawei packages depend on
kamera-huaweithemselves, so they don't need it.
- To keep 1.2.0's behaviour, set
- Flutter (Android): the preview texture is a
SurfaceProducer, not aSurfaceTexture. From
Android 10 Flutter backs it with anImageReader, which Impeller's Vulkan backend imports as it
is; aSurfaceTexturehad to be copied into Vulkan, with the raster thread waiting on the copy,
for every preview frame. AnImageReadershows the camera's buffers as the sensor wrote them, so
KameraPreviewViewturns them by the sensor's orientation and flips a front lens's itself. The
glue works the turn out from CameraX's description of each camera surface and sends it as the
camera changes, showing black in between, so a lens switch never shows the last camera's frame
turned the new camera's way (KameraTextureTransformin the Android core, for the glue). Before
Android 10 the texture is still aSurfaceTexture, and nothing changes. A replay's frames are
drawn into the texture already turned back, so the same transform shows them upright.
Removed
- Android and iOS:
BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
either core or in the SDKs. It wasinternalon iOS. On Android the class itself waspublic,
in thecom.usesmileid.kamera.internalpackage but outside the API lists, so it is deleted rather
than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
Breaking (Android, source): a host that referenced
com.usesmileid.kamera.internal.BestFrameTrackerno longer compiles. Rank recent frames over the
public scorer,frame.qualityScore(region), which stays.
kamera v1.2.1-SNAPSHOT.25
Snapshot of main at 555361812ce8a85fa8828d4cf43cc626e7661794, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.25" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.25 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.25, dist-tag snapshot |
Changes since 1.2.0
Fixed
- iOS testing kit: replay no longer crashes the app when a session restarts mid-run, as the
document flows do between captures. A run thatbegin()superseded freed its frame gate below
its starting value, which libdispatch traps on ("Semaphore object deallocated while in use"). - Testing kit: the
selfie-smile-v1replay fixture is padded with white instead of black.
Its dark bars made full-length uniform rows and columns, which the server rejects as an image
issue, so an SDK's pre-upload check retook every capture and replay never completed. The face's
size and position are unchanged.
Added
- All four SDKs:
KameraKit.deviceProfilesays how capable kamera reads the device to be
(docs/design/61): aKameraDeviceProfilewith atierofentry,standardorhigh(wire
wire.deviceTier) and the signals it came from, read once per process before any camera opens.
Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
be set from the field. On Android it'sdeviceProfile(context); Flutter and React Native return a
future and a promise that never fail. The glue's shared encoder isKameraDeviceProfileWire. - All four SDKs:
KameraMetric.AnalysisStalled(openForMs)(wireanalysisStalled) reports a
frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
reported a frame when it closed. A frame waiting inKameraFrameHubfor the host's take is not in
analysis and never counts. Each core checks once a second on a thread of its own, only while a
frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
matches: a Kotlinwhen, Swiftswitchor DartswitchoverKameraMetric, or a TypeScript
switchonmetric.namethat ends in anevercheck, needs anAnalysisStalled/
.analysisStalled/KameraAnalysisStalledMetric/KameraMetricName.AnalysisStalledcase (or a
default).
Changed
- Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
kameraMlBackenddefaults tomlkit, notall(docs/design/20 D9). Since 1.2.0, HMS reached
every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
an Expo app's manifest merge onandroid:allowBackup, and turned backup off in a Flutter app.- To keep 1.2.0's behaviour, set
-PkameraMlBackend=all(orKAMERA_ML_BACKEND=all). - An app that opts in settles HMS's
allowBackupwithtools:replace, and adds
-dontwarn org.chromium.net.**if R8 stops on the Cronet classes (docs/guide/install.md). - The Smile ID SDKs' Huawei packages depend on
kamera-huaweithemselves, so they don't need it.
- To keep 1.2.0's behaviour, set
- Flutter (Android): the preview texture is a
SurfaceProducer, not aSurfaceTexture. From
Android 10 Flutter backs it with anImageReader, which Impeller's Vulkan backend imports as it
is; aSurfaceTexturehad to be copied into Vulkan, with the raster thread waiting on the copy,
for every preview frame. AnImageReadershows the camera's buffers as the sensor wrote them, so
KameraPreviewViewturns them by the sensor's orientation and flips a front lens's itself. The
glue works the turn out from CameraX's description of each camera surface and sends it as the
camera changes, showing black in between, so a lens switch never shows the last camera's frame
turned the new camera's way (KameraTextureTransformin the Android core, for the glue). Before
Android 10 the texture is still aSurfaceTexture, and nothing changes. A replay's frames are
drawn into the texture already turned back, so the same transform shows them upright.
Removed
- Android and iOS:
BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
either core or in the SDKs. It wasinternalon iOS. On Android the class itself waspublic,
in thecom.usesmileid.kamera.internalpackage but outside the API lists, so it is deleted rather
than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
Breaking (Android, source): a host that referenced
com.usesmileid.kamera.internal.BestFrameTrackerno longer compiles. Rank recent frames over the
public scorer,frame.qualityScore(region), which stays.
kamera v1.2.1-SNAPSHOT.22
Snapshot of main at 41108cff36a1487bc18352ad22d96a60ab3f0b5d, for testing only — not for production.
The current release is 1.2.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.2.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.2.1-SNAPSHOT.22" |
| Flutter | usesmileid_kamera: 1.2.1-SNAPSHOT.22 |
| React Native | @smileid/kamera@1.2.1-SNAPSHOT.22, dist-tag snapshot |
Changes since 1.2.0
Added
- All four SDKs:
KameraKit.deviceProfilesays how capable kamera reads the device to be
(docs/design/61): aKameraDeviceProfilewith atierofentry,standardorhigh(wire
wire.deviceTier) and the signals it came from, read once per process before any camera opens.
Android reads the Go-tier switch, the memory, the cores the phone has, the fastest core's top
clock and, on Android 12 and later, the chip and the media performance class; iOS reads the memory
and the model identifier. A signal kamera can't read is null and moves the tier nowhere, and
nothing kamera does depends on the tier yet: a host records it with each capture, so the lines can
be set from the field. On Android it'sdeviceProfile(context); Flutter and React Native return a
future and a promise that never fail. The glue's shared encoder isKameraDeviceProfileWire. - All four SDKs:
KameraMetric.AnalysisStalled(openForMs)(wireanalysisStalled) reports a
frame the host has kept in analysis for 2 s or more (KameraDefaults.analysisStallAfterMs), once
per frame and while it is still out (docs/design/60). Until now a host analyzer that hung froze
analysis silently: the camera delivers no fresh frame until the frame out closes, and kamera only
reported a frame when it closed. A frame waiting inKameraFrameHubfor the host's take is not in
analysis and never counts. Each core checks once a second on a thread of its own, only while a
frame is out and not yet reported, and only with metrics on. Breaking for exhaustive
matches: a Kotlinwhen, Swiftswitchor DartswitchoverKameraMetric, or a TypeScript
switchonmetric.namethat ends in anevercheck, needs anAnalysisStalled/
.analysisStalled/KameraAnalysisStalledMetric/KameraMetricName.AnalysisStalledcase (or a
default).
Changed
- Flutter and React Native: the Android glue ships ML Kit's adapter alone again by default:
kameraMlBackenddefaults tomlkit, notall(docs/design/20 D9). Since 1.2.0, HMS reached
every app, ML Kit-only ones too: it failed a Flutter release build at R8 on Cronet classes, failed
an Expo app's manifest merge onandroid:allowBackup, and turned backup off in a Flutter app.- To keep 1.2.0's behaviour, set
-PkameraMlBackend=all(orKAMERA_ML_BACKEND=all). - An app that opts in settles HMS's
allowBackupwithtools:replace, and adds
-dontwarn org.chromium.net.**if R8 stops on the Cronet classes (docs/guide/install.md). - The Smile ID SDKs' Huawei packages depend on
kamera-huaweithemselves, so they don't need it.
- To keep 1.2.0's behaviour, set
- Flutter (Android): the preview texture is a
SurfaceProducer, not aSurfaceTexture. From
Android 10 Flutter backs it with anImageReader, which Impeller's Vulkan backend imports as it
is; aSurfaceTexturehad to be copied into Vulkan, with the raster thread waiting on the copy,
for every preview frame. AnImageReadershows the camera's buffers as the sensor wrote them, so
KameraPreviewViewturns them by the sensor's orientation and flips a front lens's itself. The
glue works the turn out from CameraX's description of each camera surface and sends it as the
camera changes, showing black in between, so a lens switch never shows the last camera's frame
turned the new camera's way (KameraTextureTransformin the Android core, for the glue). Before
Android 10 the texture is still aSurfaceTexture, and nothing changes. A replay's frames are
drawn into the texture already turned back, so the same transform shows them upright.
Removed
- Android and iOS:
BestFrameTracker, the best-frame ring of frames.md M2. Nothing called it on
either core or in the SDKs. It wasinternalon iOS. On Android the class itself waspublic,
in thecom.usesmileid.kamera.internalpackage but outside the API lists, so it is deleted rather
than deprecated, as a never-used symbol (the owner's exception to deprecate-first).
Breaking (Android, source): a host that referenced
com.usesmileid.kamera.internal.BestFrameTrackerno longer compiles. Rank recent frames over the
public scorer,frame.qualityScore(region), which stays.