Android emulator mask testing
Skill simiancraft/simiancraft-skills/skills/android-emulator-mask-testing
Specialization of android-emulator-harness for CAMERA / segmentation testing: get a real PERSON in front of the emulator camera so MediaPipe / ML Kit selfie segmentation produces an actual mask, then verify background-replacement / blur / shader effects and tune mask threshold by vision. Use when the task is "test the mask", "verify segmentation", "tune mask threshold", "check a camera/background effect on Android", or any time an app must SEE a human through the emulator camera without a physical device. Read android-emulator-harness FIRST for bring-up, drive, and assert; this skill only overrides the camera-specific pieces. Validated on Linux/WSL with a 32-bit x86 emulator image and MediaPipe/ML Kit segmentation.From its SKILL.md
npx -y skills add simiancraft/simiancraft-skills --skill android-emulator-mask-testingAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 6 stars6 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 7 commands, including `unzip -l app.apk | grep mediapipe` and 6 more.
SKILL.md
6.4 KB, ~1.5k tokens by cl100k_base, as published. Nobody here has run it
Camera / Mask Testing: specialization of android-emulator-harness
Read android-emulator-harness first. That base covers KVM, AVD creation,
headless boot, app install/launch, Maestro driving, logcat/screenshot assertion,
and teardown. This skill changes only what's needed to put a segmentable human in
the camera and run GPU segmentation. Everything else (drive with Maestro, assert on
logcat + screenshot) is identical to the base.
This skill ships one fixture: fixtures/person-framed.png, a full-body subject
pre-positioned for the emulator's camera-feed crop (see override 3). Feed it directly,
or swap in your own subject framed the same way.
The three camera-specific overrides (each cost real debugging time)
-
Use a 32-bit
x86system image, NOT the base'sx86_64. MediaPipe Tasks Vision shipslibmediapipe_tasks_vision_jni.soforarm64-v8a,armeabi-v7a, andx86, notx86_64. On x86_64 it fails withUnsatisfiedLinkErrorand segmentation silently falls through to the raw frame. API 30 is the highest 32-bit x86google_apisimage. Verify in the APK:unzip -l app.apk | grep mediapipe(expect alib/x86/…entry).yes | "$SDKMGR" "system-images;android-30;google_apis;x86" echo no | "$AVDMGR" create avd -n harness_x86 -k "system-images;android-30;google_apis;x86" -d pixel_3 --force -
Boot with
-gpu swangle_indirect, NOT the base'sswiftshader_indirect. MediaPipe runs through TFLite's GPU delegate, which needs GLES 3.1 compute shaders. Raw SwiftShader is only GLES 3.0 →[GL_INVALID_ENUM] glCreateShaderinTensorsToSegmentationCalculator.swangle(ANGLE over SwiftShader-Vulkan) exposes GLES 3.1. Confirm after boot; this is the make-or-break check:adb shell dumpsys SurfaceFlinger | grep -m1 "GLES:" # MUST say "OpenGL ES 3.1 ... ANGLE" -
Feed the framed subject straight into the camera (
-camera-back imagefile:). A recent emulator presents a still image as the back camera. Confirm support first ("$EMU" -help-camera-backlistsimagefile:/videofile:); upgrade if missing (yes | "$SDKMGR" emulator). Feed the pre-framed image, not a bare cutout: the imagefile-to-sensor path does not present the image 1:1; it crops and shifts, so a subject centered in the source lands off to the right with the head clipped.fixtures/person-framed.pngis pre-compensated. The framing that lands the subject centered and fully in frame:- Frame: 9:16 portrait (e.g. 1080 x 1920).
- Subject height: ~0.42 of the frame height (full body, not a close-up).
- Subject center: x = 0.25 W, y = 0.58 H. The left-quarter x is deliberate; it cancels the sensor path's rightward shift so the subject reads centered on screen.
- Background: opaque and contrasting (a light neutral gray works); segmentation needs a clean figure/ground split, and the background is what gets replaced.
These offsets are emulator/AVD/version specific. Calibrate once: feed the image, select no effect to see the raw camera preview, screenshot it, and nudge the subject's x-center until it reads centered before trusting a run. To re-frame for a different crop, recompose from your own transparent subject using the offsets above.
Full launch (base boot + the three overrides)
sg kvm -c "nohup $EMU -avd harness_x86 \
-no-window -no-audio -no-boot-anim -no-snapshot \
-gpu swangle_indirect \
-camera-back imagefile:$PWD/fixtures/person-framed.png \
-accel on -port 5554 > /tmp/emulator.log 2>&1 &"
# then base boot-wait, then dumpsys SurfaceFlinger GLES check MUST be 3.1/ANGLE
-no-snapshot forces a clean boot so the camera feed takes effect. Pass an absolute
path to the imagefile; the example uses $PWD assuming you launch from the skill dir.
Asserting the mask (beyond the base's logcat gate)
Add these to the base's HARD logcat gate (all must be ABSENT):
UnsatisfiedLink, GL_INVALID_ENUM, glCreateShader, CalculatorGraph::Run() failed.
Then drive an effect (Maestro: tapOn: "Dark Office") and Read the screenshot. A
working mask shows the person kept and the background replaced. Failure
modes: unchanged room (segmentation fell through) or person gone (empty mask). For
threshold tuning, iterate the app's maskThreshold/hardness controls and re-Read.
What this adds to the base's scope
- CAN now also validate: mask SHAPE + compositing (person carved, bg swapped).
- Still CANNOT: temporal mask quality (flicker/edge stability) from a STATIC
image; that needs motion. Use
-camera-back videofile:<abs>/subject.mp4for a full-frame moving subject when you need to test edge stability over time.
Older emulators without imagefile:
Builds whose -help-camera-back lacks imagefile: can still put a subject in view via
the virtualscene wall poster. The headless camera pose is not settable (telnet
sensor set is ignored; only the gRPC physical model moves it), so move the poster
into the camera's fixed view (camera sits near origin looking down −Z): feed your own
transparent full-body cutout as -camera-back virtualscene -virtualscene-poster wall=<subject.png>
and set geometry in $SDK/emulator/resources/Toren1BD.posters (back it up first; the
wall anchor is guaranteed to render). Upgrading the emulator to get imagefile: is
simpler; prefer that.
Teardown
adb -s emulator-5554 emu kill
# only if you used the virtualscene fallback and edited resources:
# cp "$SDK/emulator/resources/Toren1BD.posters.bak" "$SDK/emulator/resources/Toren1BD.posters" 2>/dev/null || true
What ships with it: 2 files
211.2 KB alongside SKILL.md
fixtures/
- person-framed.png210.4 KB
- README.md838 B
Gives 0 of the 12 instructions most quality gates skills give in ~1.5k tokens
Counted across 1,524 of the 2,830 authors here whose files we hold, read 2026-09-06
- Read full output and check exit codein 45 of 1524, across 40 files
- Verify output confirms the claimin 44 of 1524, across 39 files
- Identify the command that proves the claimin 43 of 1524, across 39 files
- Execute the full verification commandin 36 of 1524, across 30 files
- Produce a verification reportin 34 of 1524, across 18 files
- Review git diff changesin 30 of 1524, across 16 files
- Fix build failures immediatelyin 29 of 1524, across 9 files
- Group findings by severityin 28 of 1524
- State claim only with evidencein 27 of 1524, across 22 files
- Verify regression tests with red-green cyclein 26 of 1524, across 22 files
- Run the full test suitein 26 of 1524, across 25 files
- Run test suite with coveragein 25 of 1524, across 10 files
Said here and by no other author read
- use a 32-bit x86 system image
- boot with gpu swangle_indirect
- feed a pre-framed image into the camera
- verify GLES 3.1 ANGLE support after boot
- use absolute paths for camera image files
- force a clean boot without snapshots
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.