Repository navigation
Releases: smileidentity/kamera-spm
Release list
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.
kamera v1.2.0
kamera v1.1.1-SNAPSHOT.19
Snapshot of main at 969433ed213109465635386dda33cd4ef06ffcef, for testing only — not for production.
The current release is 1.1.0. This snapshot runs ahead of it, and the next release replaces it.
| Platform | This snapshot |
|---|---|
| Android | com.usesmileid:kamera:1.1.1-SNAPSHOT from Maven Central's snapshot repository, https://central.sonatype.com/repository/maven-snapshots/ |
| iOS | kamera-spm exact: "1.1.1-SNAPSHOT.19" |
| Flutter | usesmileid_kamera: 1.1.1-SNAPSHOT.19 |
| React Native | @smileid/kamera@1.1.1-SNAPSHOT.19, dist-tag snapshot |
Changes since 1.1.0
Added
-
All four SDKs: the face's depth map (docs/design/59).
AnalysisConfig.faceDepth(off in every
preset) asks a face-tracking session to map the face, andKameraCapabilities.faceDepthsays
whether it can: a TrueDepth iPhone. There each face'sKameraFaceDepthaddsflatnessMm(how far
the surface lies off a plane: 13–15 mm for a face on an iPhone 16, about zero for a picture, flat
or tilted),meshFitMm(how far it lies from ARKit's mesh),coverageand up to 256
KameraFaceDepthPoints, andKameraExifwritesflat,fitandcovfor the server. Android
has the same config, capability and types, and reports no depth: the capability is false and the
flag a documented no-op. The new fields default, so existingKameraFaceDepthinitialisers keep
compiling.- The Lab has a Face depth tool on all four samples: on iOS it draws the depth points over the
face with a DEPTH panel (distance, relief, flatness, mesh fit, coverage and a 3D-or-flat
verdict); elsewhere it shows as unavailable.
- The Lab has a Face depth tool on all four samples: on iOS it draws the depth points over the
-
All four SDKs:
KameraKit, the front door (docs/design/58):KameraKit.version,KameraKit.prewarm
andKameraKit.sessionforward toKameraSession's staticprewarmand its constructor, and
versionis the coordinate the package was published as, the same string on all four (a
snapshot reports its own, such as1.1.1-SNAPSHOT.12). Nothing moves:KameraSessionand the
presets stay where they are. The conformance file pins the members asapi.frontDoor, and
set-version.sh,stamp-publish-version.shand the lockstep check write and read each
package'sKameraKit.version(scripts/front-door-versions.sh).- It isn't
Kamera, the iOS module's name: a type named after its module breaks Swift code
that qualifies a type by module (Kamera.KameraFrame), and Xcode 26's library-evolution
interfaces.
- It isn't
-
All four SDKs:
setDocumentGuide(widthMm, frameWidthFraction)tells a document session's
close-focus zoom floor (docs/design/43, D5) the document the host frames and the share of the
displayed frame's width its guide spans. The floor then zooms just enough that the document filling
the guide sits at the lens's closest focus distance, instead of assuming an ID-1 card across 0.9
of the frame, which put the floor at its 2x cap on an iPhone 16 and made users hold a passport
about twice as far away as the lens needs. A width or fraction of 0 or less, or a fraction above 1
(a percentage such as 44.6 where 0.446 was meant), clears it; the guide outlives a reconfigure.
The fraction is of the camera frame's width as displayed, asKameraPreviewGuide.frameWidthFraction
holds it, not of the view's. A guide applies on the display axis it was measured on: after the
display turns, the floor keeps the ID-1 default until the host sets the guide again. Conformancebehavior.closeFocusgains guided cases. -
All four SDKs: camera-integrity readings (docs/design/57).
AnalysisConfig.integrityasks for
them, off in every preset; a new wire field (integrity), so Flutter and React Native pass it too.
On the two native cores each frame then carriesKameraFrame.integrity(andKameraImage.integrity),
aKameraIntegrity.- Its first reading,
videoEffects: the system video effects active on the frame, as
KameraVideoEffect(backgroundReplacement,centerStage,portrait,reactions,
studioLight). iOS reads them from the bound device; face tracking's frames report nil, since
ARKit holds the camera. Android enables no camera extension, so its frames report none. KameraExifwrites them asfx, sorted wire names:[]when none was active, left out when
the frame carries no reading.- Why: Portrait, Studio Light, Background Replacement and Reactions edit the face and its
background before the app sees the frame, and an app can't turn most of them off. Reporting them
lets a host ask the user to turn one off, and lets the server weigh a frame it would otherwise
take as the camera's own. Readings only: kamera never refuses a frame on them. - Which apps they reach on iOS: Portrait, Studio Light and Reactions apply only to a
video-conferencing app (voipinUIBackgroundModes), or to an app that opts in through
Info.plist (NSCameraPortraitEffectEnabled,NSCameraStudioLightEnabled,
NSCameraReactionEffectsEnabled; AVCaptureDevice.h, iOS 27 SDK). A typical host never gets them,
and its Control Center panel offers only Center Stage, which applies to every app (iPhone 17 Pro
Max and iPhone 11, iOS 27.0).
- Its first reading,
-
Both native cores: three more
KameraIntegrityreadings (docs/design/57, I6, I5 and I2), on
the frames of anAnalysisConfig.integritysession.metadataAttached: whether the frame came with the camera's own metadata: the sample
buffer's EXIF attachment on iOS, a capture result joined by timestamp on Android. A hook that
builds new buffers drops it. Null under ARKit, whose frames always carry their exposure and
intrinsics, so their presence would prove nothing.deliveryLatencyMs: how long after its sensor timestamp the frame reached kamera. Android
reports it only where the camera stamps frames onelapsedRealtimeNanos(REALTIME).faceWidthMm: the face's physical width, its box over the focal length times its distance.
Only face tracking has a distance today, so only iOS face tracking reports it. A face held up on
a phone measures 40–70 mm, a real one about 140–160 mm.KameraExifwrites them asmeta(1 or 0),lat(ms) andfw(mm).- Why: each is a value the frame already carries, the cheapest of design 57's detection
readings. A replaced buffer loses the camera's metadata, a slow hook adds latency, and a phone
held up to the camera has a face too small to be real. They are readings only, unenforced until
the rigs (D5) show what each is worth.
-
iOS: kamera turns Center Stage off while it holds a camera that supports it
(AVCaptureDevice.centerStageControlMode = .app,isCenterStageEnabled = false), and puts back
the app-wide setting it found, switch and mode, onstop()andrelease()(docs/design/57, P2). Center Stage crops
and pans the frame to follow the people in it, so the frames the server checks stopped being the
lens's whole view (docs/design/42). The front cameras of recent iPads and the iPhone 17 support it.
Control Center's Audio & Video panel still offers the switch while kamera holds it, and a tap there
turns Center Stage on mid-session (iPhone 17 Pro Max, iOS 27.0). So kamera turns it off again on
the next camera frame. -
iOS: the camera's own faces on every frame (docs/design/45, D3). With
AnalysisConfig.cameraFaceson, each camera frame carries the largest face the camera's metadata
output reported asKameraFrame.face,sourcecamera, in display coordinates: the box and the
camera'sfaceIDas itstrackingId. Android has done this since design 53.- How a frame finds its faces: the metadata output and the video output report a frame on
separate callbacks, in no promised order. A frame takes its own report, else the newest one from
within 70 ms before it (a frame of lag); a frame with neither carries no face. - On an iPhone 17 Pro Max (iOS 27.0, front camera): every frame carried the face, with one
trackingIdthroughout. The video output's transform reports a box in the active format's pixels
(1920×1440 for a 640×480 buffer), not 0..1, so kamera divides by the format's size. Before that,
every box fell off the buffer, and no frame carried a face. - Not yet: the camera's roll and yaw. Their convention against kamera's (the person's own
point of view) waits for a device run, so they stay nil. - Why: the analyzers see a frame about every 400 ms on the floor phone, and the camera reports
faces on every frame it streams, at no ML cost: face-priority exposure at the camera's rate, and
design 57's I1 (the camera's faces against the analyzer's).
- How a frame finds its faces: the metadata output and the video output report a frame on
-
All four SDKs:
KameraPreviewGuide(docs/design/56, D1) works out where to lay a preview out so
a host's guide (a selfie oval, a card's outline) shows the same part of the frame on every screen.
previewRecthas the frame's own aspect, scaled soframeWidthFractionof the frame's width spans
the guide's width and centred on it.regionInFramemaps a container rectangle into the frame, and
rectInContainermaps a frame region back. It only computes layout: no preview changes.- Why: a host's fill limits are a share of the frame, but its guide was a share of the
screen. A face at 13% of a 3:4 frame spanned 84% of ios-v12's oval on a 6.1" iPhone, 69% on an
iPhone SE and 56% on an 11" iPad. - With the guide: 13% spans 70% of the oval on all three.
- Conformance: checked against
behavior.previewGuideon all four SDKs.
- Why: a host's fill limits are a share of the frame, but its guide was a share of the
-
All four SDKs: device motion (docs/design/55).
AnalysisConfig.deviceMotionasks for the
device's motion on every frame, off in every preset. On both native coresKameraFrame.motion(and
KameraImage.motion) carries the frame's e...