How to Test Camera Integration on Android (Complete Guide)
Camera features are among the most visible parts of an Android app. Users instantly notice if the preview is frozen, if a captured photo is corrupted, or if the app crashes when they switch lenses. A
Why Camera Integration Testing Matters
Camera features are among the most visible parts of an Android app. Users instantly notice if the preview is frozen, if a captured photo is corrupted, or if the app crashes when they switch lenses. A broken camera flow not only frustrates users but can also lead to negative reviews, increased support tickets, and, in regulated domains (healthcare, finance, identity verification), compliance failures.
From a technical standpoint, the camera subsystem touches several layers of the Android stack: the hardware abstraction layer (HAL), the Camera2 API, CameraX wrappers, UI components that display the preview, and any downstream processing (image compression, ML inference, upload). A defect in any of these layers can manifest as a crash, an Application Not Responding (ANR) event, a dead button, or a subtle usability issue such as delayed focus. Because the camera is a shared resource, tests must also verify correct handling of lifecycle events (pause/resume), permission changes, and interruptions from other apps (incoming calls, picture‑in‑picture).
In production, the most common failure modes include:
- Permission races – the app requests CAMERA permission after the user has already denied it, leading to a silent failure.
- Resource leaks – failing to release the camera device when the activity is paused, causing subsequent launches to throw
CameraAccessException. - Incorrect surface handling – using a
SurfaceViewthat is destroyed before the camera starts, resulting in a black preview. - OEM‑specific extensions – some manufacturers expose extra modes (portrait, night, macro) that behave differently from the generic Camera2 contract.
- Concurrent access – two UI components trying to open the camera simultaneously, which triggers
CameraBusyException.
A thorough test strategy must therefore cover functional correctness, error handling, performance under stress, accessibility, and privacy safeguards. The following sections break down exactly how to achieve that.
---
Test Matrix Overview
A test matrix helps you ensure that every relevant dimension is exercised. Below is a comprehensive matrix that groups test cases by category and sub‑category. Each row represents a distinct scenario; the columns indicate the recommended verification method (manual, automated, or autonomous) and the expected outcome.
| Category | Sub‑category | Test ID | Description | Manual? | Automated? | Autonomous? | Expected Verdict |
|---|---|---|---|---|---|---|---|
| Happy Path | Preview launch | HP1 | Open camera, verify preview fills surface, focus converges within 2 s | ✔ | ✔ | ✔ | PASS |
| Happy Path | Still capture | HP2 | Tap shutter, confirm JPEG saved with correct EXIF orientation | ✔ | ✔ | ✔ | PASS |
| Happy Path | Video record | HP3 | Start recording, capture 5 s, stop, verify file plays and audio‑video sync | ✔ | ✔ | ✔ | PASS |
| Happy Path | Switch lens | HP4 | Toggle front/rear while preview active, ensure seamless transition | ✔ | ✔ | ✔ | PASS |
| Error Handling | Denied permission | EH1 | Launch camera with CAMERA permission denied, observe fallback UI | ✔ | ✔ | ✔ | PASS (graceful handling ) |
| Error Handling | Camera busy | EH2 | Open camera from two activities simultaneously, verify second fails with proper error dialog | ✔ | ✔ | ✔ | PASS |
| Error Handling | Unsupported format | EH3 | Request JPEG output on a device that only supports YUV_420_888, confirm fallback or error | ✔ | ✔ | ❌ | PASS/FAIL per device |
| Edge Cases | Low light | EC1 | Cover sensor, verify exposure compensation increases, no crash | ✔ | ✔ | ✔ | PASS |
| Edge Cases | Rapid lens switch | EC2 | Toggle front/rear 10 times in 5 s, check for leaked resources | ✔ | ✔ | ✔ | PASS |
| Edge Cases | USB OTG camera | EC3 | Connect external UVC camera, select it via CameraManager, preview works | ✔ | ✔ | ❌ (needs hardware) | PASS if supported |
| Edge Cases | Battery saver | EC4 | Enable system battery optimization for the app, start camera, verify no premature kill | ✔ | ✔ | ✔ | PASS |
| Accessibility | TalkBack navigation | A1 | With TalkBack enabled, navigate to shutter button, announce state correctly | ✔ | ✔ | ✔ | PASS |
| Accessibility | Contrast | A2 | Verify UI elements meet WCAG AA contrast ratio (≥4.5:1) | ✔ | ❌ (needs visual check) | ✔ | PASS |
| Accessibility | Switch device | A3 | Operate camera using external switch control, confirm all actions reachable | ✔ | ❌ | ✔ | PASS |
| Security/Privacy | Preview leakage | SP1 | Ensure preview surface is not accessible to other apps while camera is active (use adb shell dumpsys media.camera) | ✔ | ✔ | ✔ | PASS |
| Security/Privacy | Metadata stripping | SP2 | Confirm location tags are removed when user disables geo‑tagging | ✔ | ✔ | ✔ | PASS |
| Security/Privacy | Denial‑of‑service via rapid open/close | SP3 | Rapidly open/close camera 100 times, verify no crash or ANR | ✔ | ✔ | ✔ | PASS |
*The table above is intentionally dense to serve as a reference; you can prune rows that are irrelevant to your specific feature set.*
---
Manual Testing Approach
Even when automation is in place, manual exploration remains valuable for catching UI‑centric issues, OEM quirks, and contextual usability problems. Below is a step‑by‑step guide that you can follow on a physical device or an emulator with camera support.
Setting up a Test Device Matrix
- Device selection – Choose at least three devices representing different hardware tiers:
- Low‑end (e.g., Samsung Galaxy A10) – tests limited sensor modes and slower ISP.
- Mid‑range (e.g., Google Pixel 5) – baseline for Camera2 behavior.
- High‑end/OEM‑specific (e.g., Xiaomi Mi 11 Ultra) – exposes proprietary extensions like ultra‑wide or periscope zoom.
- OS version – Include Android 11 (API 30), Android 12 (API 31), and Android 13 (API 32) to catch API‑level changes.
- Peripherals – Keep a USB OTG cable and a UVC webcam handy for external camera tests.
- Tooling – Install
adb,scrcpy(for real‑time screen mirroring), andCamera2Probe(an open‑source app that logs CameraCharacteristics).
Step‑by‑Step Manual Test Flow
- Permission sanity
- Go to *Settings → Apps → YourApp → Permissions* and set CAMERA to Deny.
- Launch the app and navigate to the camera screen. Verify that a clear permission rationale appears and that the app does not crash.
- Grant permission via the system dialog and repeat the flow; the preview should start within 2 seconds.
- Preview validation
- With the preview active, rotate the device. The preview should rotate smoothly without tearing.
- Use
scrcpyto capture a short video and verify that the preview fills the allocatedTextureVieworSurfaceView.
- Capture and verify
- Tap the shutter button. Observe the UI feedback (shutter animation, sound).
- Immediately check the gallery for the newest file. Confirm:
- File size > 0 bytes.
- JPEG marker (
FF D8 FF E0) present. - Orientation tag matches device rotation (use
exiftool). - Repeat for video: start recording, wait 5 s, stop, and verify playback with
MediaPlayerorVLC.
- Lens switching
- If the device exposes multiple lenses, toggle between them while preview is active.
- Look for any hiccups in the preview (freeze, black frames) and ensure the camera is properly released before reopening.
- Interrupt handling
- While preview is running, trigger an incoming call (using
adb shell am broadcast -a android.intent.action.CALL --es number 12345). - Verify that the camera preview pauses, the call UI appears, and after ending the call the preview resumes without requiring a restart.
- Repeat with a picture‑in‑picture video from another app.
- Accessibility checks
- Enable TalkBack (
Settings → Accessibility → TalkBack). - Navigate to the shutter button using swipe gestures; ensure it announces “Shutter button, double tap to capture”.
- Increase font size to 200 % and confirm that all UI elements remain readable and tappable.
- Battery optimization
- Go to *Settings → Apps → YourApp → Battery → Battery optimization* and set the app to Optimize.
- Launch the camera, start a 30‑second video recording, then put the device in standby (press power button).
- After 2 minutes, wake the device and verify that the recording stopped gracefully and the file is intact.
- External USB camera (if applicable)
- Connect a UVC webcam via OTG.
- Open the camera selector in your app (if you expose one) and choose the external device.
- Verify preview, capture, and that the app correctly releases the USB device when closed.
Tools for Manual Verification
| Tool | Purpose | Typical Command | |
|---|---|---|---|
adb logcat | Monitor Camera2 API logs, catch exceptions | `adb logcat | grep -i Camera` |
dumpsys media.camera | List active camera sessions and clients | adb shell dumpsys media.camera | |
scrcpy | Real‑time | Visual verification without touching device | scrcpy --max-size 1024 |
Camera2Probe | Inspect device‑specific camera characteristics | Install via Play Store, read output | |
exiftool | Validate JPEG metadata (orientation, GPS) | exiftool image.jpg | |
MediaCodecInfo (via adb shell) | Verify video encoder capabilities | adb shell media list --codec video/avc |
Manual Test Checklist
- [ ] Permission flow (deny → grant → deny again) works without crash.
- [ ] Preview starts within 2 s, fills surface, rotates correctly.
- [ ] Still image saved, correct EXIF orientation, non‑zero size.
- [ ] Video records, plays back, audio‑video sync within ±100 ms.
- [ ] Lens switch seamless, no leaked camera handles.
- [ ] Interruptions (call, PiP) pause and resume preview correctly.
- [ ] Accessibility: TalkBack navigation, contrast, font scaling.
- [ ] Battery optimization does not kill ongoing capture.
- [ ] External USB camera (if supported) works and releases cleanly.
- [ ] No preview leakage to other apps (verified via
dumpsys).
---
Automated Testing with Espresso/UIAutomator
Automated instrumented tests give you repeatable verification on CI pipelines. The Android testing ecosystem provides several ways to interact with the camera without requiring a physical lens.
Instrumented Tests Basics
- Use AndroidJUnitRunner with
@RunWith(AndroidJUnit4.class). - Place tests under
src/androidTest/java/.... - Request the CAMERA permission in the test’s
@Beforemethod usinggrantPermission.
Mocking the Camera
Because the emulator’s default camera is often a black frame, you need a deterministic fake. Two popular approaches:
- CameraX Test Library – provides a
CameraXTestRulethat injects a fake image pipeline. - Android Instrumentation Test Orchestrator with a custom
CameraDevicestub – you can extendCameraDeviceand override its methods to return pre‑generatedImageobjects.
Below is a minimal example using CameraX’s test artifact.
// build.gradle (app module)
dependencies {
androidTestImplementation "androidx.camera:camera-testing:1.3.0"
androidTestImplementation "androidx.test.ext:junit:1.1.5"
androidTestImplementation "androidx.test:runner:1.5.2"
}
// CameraCaptureTest.kt
@RunWith(AndroidJUnit4::class)
class CameraCaptureTest {
@get:Rule
val instantTaskExecutorRule = InstantTaskExecutorRule()
@get:Rule
val cameraTestRule = CameraXTestRule(ApplicationProvider.getApplicationContext())
@Before
fun grantPermissions() {
grantPermission(
InstrumentationRegistry.getInstrumentation().targetContext.packageName,
Manifest.permission.CAMERA
)
}
@Test
fun previewAndCapture_useFakeImage_returnsExpectedBitmap() {
// Bind preview use case to a fake Surface
val previewUseCase = Preview.Builder().build().also {
it.setSurfaceProvider(cameraTestRule.previewSurfaceProvider)
}
// ImageCapture use case that will receive a known bitmap
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
cameraTestRule.bindToLifecycle(
lifecycle,
previewUseCase,
imageCapture
)
// Provide a fake bitmap (e.g., a 640x480 red image)
val fakeBitmap = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888).also {
it.eraseColor(Color.RED)
}
cameraTestRule.injectCaptureResult(fakeBitmap)
// Trigger capture
val capturedFuture = imageCapture.takePicture(
ContextCompat.getMainExecutor(ApplicationProvider.getApplicationContext()),
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
// Convert ImageProxy to Bitmap for assertion
val bitmap = image.toBitmap()
assertEquals(Color.red(bitmap.getPixel(0, 0)), 255)
image.close()
}
override fun onError(exc: ImageCaptureException, image: ImageProxy?) {
fail("Capture failed: $exc")
image?.close()
}
}
)
// Wait for result (CameraXTestRule handles synchronization)
capturedFuture.get(5, TimeUnit.SECONDS)
}
}
Explanation
CameraXTestRulecreates a fake camera device that feeds theImageCaptureuse case with whatever bitmap you inject viainjectCaptureResult.- The test verifies that the bitmap returned matches the injected one, confirming that the capture pipeline, EXIF handling, and any post‑processing (e.g., rotation) work as expected.
Using Emulator Virtual Scene
If you prefer to rely on the emulator’s built‑in camera, you can load a virtual scene (a video or image sequence) that the emulator presents as a live feed.
- Start the emulator with
-camera-front virtualsceneand-camera-rear virtualscene. - Place a
.yuvor.mp4file in~/.android/avd/(or rear)..avd/camera-front/ - The emulator will stream that file as camera input, giving you deterministic visual content for assertions based on pixel colors or OpenCV template matching.
Example of launching an emulator with a custom scene:
# Create a simple 640x480 red frame repeated 30 times (1 second at 30 fps)
ffmpeg -f lavfi -i "color=c=red:s=640x480:d=1" -r 30 -vcodec libx264 -pix_fmt yuv420p red_scene.mp4
# Start emulator
emulator -avd Pixel_5_API33 -camera-front virtualscene -camera-rear virtualscene \
-camera-front-file red_scene.mp4 -camera-rear-file red_scene.mp4 \
-no-window -no-audio -gpu swiftshader_indirect
Your UI tests can then assert that the preview contains a dominant red hue, using a small snippet that reads pixels from a ImageProxy.
Handling Permissions Programmatically
In automated tests you must grant the permission before launching the UI; otherwise the system dialog will block the test thread. Use the grantPermission method from androidx.test.core.app.ApplicationProvider.
@Before
public void setUp() {
String pkg = InstrumentationRegistry.getTargetContext().getPackageName();
grantPermission(pkg, Manifest.permission.CAMERA);
}
If your app also needs RECORD_AUDIO for video capture, grant that as well.
Dealing with Camera State Across Tests
Camera is a singleton‑like resource; failing to release it leaks the device and causes subsequent tests to throw CameraAccessException. Ensure each test either:
- Calls
camera.unbindAll()(CameraX) orcamera.close()(Camera2) in an@Aftermethod, or - Uses
ActivityScenarioto launch a fresh activity per test, which automatically tears down the UI and its bound use cases.
@After
fun tearDown() {
// Unbind all use cases tied to the lifecycle
cameraTestRule.unbindAll()
}
CI Integration Tips
- Emulator snapshots – start from a pre‑booted snapshot to cut boot time.
- GPU swiftshader – use
-gpu swiftshader_indirectfor headless CI environments that lack hardware acceleration. - Timeouts – set a generous
@Test(timeout = 30_000)for camera tests because the fake pipeline may add latency. - Artifacts – capture
logcatandscreenshotson failure usingandroidx.test.core.app.ApplicationProvider.getApplicationContext()andActivityScenario.
---
Leveraging CameraX for Testability
CameraX was designed with testing in mind. Its lifecycle‑aware architecture and the separate use‑case model (Preview, ImageCapture, VideoCapture, ImageAnalysis) make it straightforward to substitute fake implementations.
Using the CameraX Test API
The androidx.camera:camera-testing artifact provides:
CameraXTestRule– manages a fake camera device.TestImageReader– suppliesImageProxyobjects with user‑defined pixel data.ExperimentalGetImage– allows direct access to the underlyingImagebuffer for assertions.
Fake Preview and Analysis
You can also inject frames into the Preview and ImageAnalysis use cases to verify that your UI reacts correctly to changes in brightness, face detection results, or barcode data.
@Test
fun imageAnalysis_receivesExpectedBitmap() {
val analyzer = object : ImageAnalysis.Analyzer {
override fun analyze(image: ImageProxy) {
val bitmap = image.toBitmap()
// Assume we expect a green pixel at (10,10) from the injected frame
assertEquals(Color.green(bitmap.getPixel(10,10)), 255)
image.close()
}
}
val analysis = ImageCapture.Builder()
.setTargetAspectRatio(AspectRatio.RATIO_4_3)
.build()
.also { it.setAnalyzer(Executors.newSingleThreadExecutor(), analyzer) }
cameraTestRule.bindToLifecycle(lifecycle, analysis)
// Provide a known bitmap (green at (10,10))
val bitmap = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888).also {
it.eraseColor(Color.BLACK)
it.setPixel(10, 10, Color.GREEN)
}
cameraTestRule.injectCaptureResult(bitmap)
// Give the analyzer a moment to process
Thread.sleep(200)
}
Verifying Capture Use Cases
Beyond simple bitmap equality, you can validate EXIF metadata, compression quality, and output format.
@Test
fun capture_storesCorrectExifOrientation() {
// Simulate device rotated 90° clockwise
val fakeBitmap = Bitmap.createBitmap(480, 640, Bitmap.Config.ARGB_8888).also {
it.eraseColor(Color.BLUE)
}
cameraTestRule.injectCaptureResult(fakeBitmap)
val capture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.setTargetRotation(Surface.ROTATION_90) // tell CameraX we are rotated
.build()
cameraTestRule.bindToLifecycle(lifecycle, capture)
val result = capture.takePicture(
ContextCompat.getMainExecutor(ApplicationProvider.getApplicationContext()),
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
val exif = ExifInterface()
val inputStream = image.image?.let { imageToBytes(it) }
inputStream?.let { exif.load(inputStream) }
val orient = exif.getAttributeInt(
ExifInterface.TAG_ORIENTATION,
ExifInterface.ORIENTATION_NORMAL
)
// Expect ROTATE_90 because we told CameraX the target rotation
assertEquals(ExifInterface.ORIENTATION_ROTATE_90, orient)
image.close()
}
override fun onError(exc: ImageCaptureException, image: ImageProxy?) {
fail("Capture error: $exc")
image?.close()
}
}
)
result.get(5, TimeUnit.SECONDS)
}
*Helper to convert Image to byte array:*
private fun imageToBytes(image: Image): ByteArray {
val buffer = image.planes[0].buffer
val data = ByteArray(buffer.remaining())
buffer.get(data)
return data
}
Benefits of CameraX‑centric Tests
- Deterministic output – you control every pixel that enters the pipeline.
- Fast execution – no reliance on hardware timings; a test runs in sub‑second on CI.
- Coverage of all use cases – Preview, ImageCapture, VideoCapture, and ImageAnalysis can be tested independently.
- Easy integration with existing Espresso/UIAutomator tests – you can bind the fake camera to a real
ActivityScenarioand then drive UI with Espresso gestures.
---
Autonomous Exploration with Persona‑Driven Testing
Even the most comprehensive test matrix can miss bugs that arise from unusual interaction patterns, unexpected timing, or device‑specific quirks that only appear when a real user explores the app freely. Autonomous QA platforms like SUSA address this gap by simulating a variety of user personalities, each with its own behavior model, and letting the platform explore the app without pre‑written scripts.
How SUSA Works (Brief)
- You upload an APK or point SUSA at a web URL.
- The agent installs the app on a fleet of real or virtual devices (you can specify API levels, OEMs, etc.).
- Eight built‑in personas drive interaction:
- Curious – taps every visible element, explores long‑press menus, tries hidden gestures.
- Impatient – performs rapid actions, double‑taps, spams buttons, quickly switches apps.
- Novice – follows on‑screen hints, avoids advanced controls, takes longer to decide.
- Adversarial – attempts to break the app via malformed inputs, rapid permission toggles, and stressing system resources.
- Elderly – uses larger touch targets, prefers voice commands, avoids fast gestures.
- Accessibility – enables TalkBack, Switch Access, font scaling, and high‑contrast mode.
- Power user – utilizes shortcuts, multi‑window, drag‑and‑drop, and external peripherals.
- Privacy‑conscious – regularly checks permissions, revokes them, and monitors data flow.
Each persona maintains a state machine that tracks which screens have been visited, which actions have been tried, and which have resulted in crashes, ANRs, or UI freezes. The agent learns from each run, avoiding previously explored dead ends and focusing on novel states.
Persona Profiles Relevant to Camera
| Persona | Typical Camera Interaction | What It Can Uncover |
|---|---|---|
| Curious | Long‑press on preview to open hidden menus, tries voice command “Take a photo”, experiments with gesture‑based zoom | Hidden UI, undocumented gestures, voice‑command integration bugs |
| Impatient | Rapidly taps shutter 20 times in 2 s, switches front/rear while recording, cancels and re‑starts capture instantly | Race conditions, surface leaks, CameraBusyException handling |
| Novice | Follows on‑screen tooltip, waits for focus lock before tapping, uses default settings only | Clarity of instructions, accessibility of tooltip, default configuration correctness |
| Adversarial | Denies camera permission after granting, then immediately re‑grants, rotates device while permission dialog is up, forces low‑memory condition via background apps | Permission race handling, UI state consistency under interruption |
| Elderly | Increases system font size to 200 %, uses larger touch targets, relies on TalkBack to locate shutter | Touch target size, accessibility label correctness, scaling of preview controls |
| Accessibility | Enables Switch Access, scans with external switch, uses voice‑to‑text to issue “Capture” command | Switch navigation order, voice command parsing, accessibility service compatibility |
| Power user | Opens app in split‑screen with a gallery, drags a photo into the camera preview to test overlay, uses USB OTG mouse to control shutter | Multi‑window lifecycle, external input handling, preview overlay correctness |
| Privacy‑conscious | Launches app, checks permission usage via Android’s permission dashboard, revokes CAMERA after a capture, re‑opens app | Permission persistence, graceful degradation when permission revoked mid‑session |
What Autonomous Exploration Finds That Scripts Miss
- Hidden gesture conflicts – a long‑press on the preview might trigger a system‑level screenshot gesture on certain OEM skins, causing the app to lose focus. Scripted tests rarely simulate long‑presses unless explicitly coded.
- Timing‑dependent leaks – the Impatient persona may open and close the camera 50 times in a few seconds, exposing a scenario where
CameraDevice’sclose()is called before the underlying HAL has finished releasing resources, leading to a delayedIllegalStateExceptionon the next open. - Permission dialog interleaving – the Adversarial persona can trigger a permission dialog while the preview is already started (by rapidly toggling the switch in settings). This can leave the preview surface attached to a
Sessionthat is later destroyed, causing a black screen that only appears under that specific race. - Accessibility overlay clashes – when TalkBack is enabled, the accessibility focus rectangle may cover the shutter button, making it impossible to double‑tap. The Accessibility persona catches this; a typical Espresso test that clicks coordinates would still succeed, missing the real‑world blockage.
- External device interference – the Power user persona may plug in a USB mouse and use its middle button to emulate a shutter press. If the app does not filter out
KeyEventfrom external devices, the camera might receive spurious focus triggers, causing continuous refocusing and excessive battery drain.
Example: Bug Found Only via Persona Exploration
During a SUSA run on a mid‑range device with the Impatient persona, the agent performed the following sequence:
- Launch app → grant CAMERA permission.
- Tap shutter → start video recording.
- While recording, rapidly toggle front/rear camera 12 times in 3 seconds.
- Stop recording.
The resulting video file was corrupted: the first half showed the rear camera feed, then a sudden jump to a green frame, followed by the front camera feed with a noticeable audio‑video drift. Logcat revealed:
E/CameraDevice: Attempt to use a released camera device ID 0
W/CameraCaptureSession: Session 3: failed to submit request list (canceled)
The root cause was that the app’s CameraCaptureSession was being reused without being reconfigured after each switch, causing the HAL to drop frames. The fix involved releasing the old session and creating a new one each time the lens changed.
No scripted test had previously exercised such rapid toggling; the typical automated test switched lenses only once per test case, waiting for UI animations to finish. The Impatient persona’s behavior exposed a concurrency bug that only manifested under stress.
Integrating Autonomous Findings into Your Pipeline
- Export – SUSA can generate a JUnit‑style XML report listing each discovered crash, ANR, or accessibility violation with reproduction steps.
- Replay – the report includes a script (in the form of a sequence of UI actions) that you can import into Espresso or UIAutomator to turn the finding into a regression test.
- Continuous learning – enable cross‑session learning so that subsequent runs focus on newly uncovered states, gradually expanding coverage without blowing up test time.
---
Edge Cases That Appear Only in Production
Even with thorough lab testing, certain issues surface only when the app runs in the wild, on a diverse set of devices, under fluctuating environmental conditions, or alongside other software. Below are the most common production‑only edge cases for camera integration, along with detection strategies.
1. Low‑Light and Dynamic Range Scenarios
- Problem – Automatic exposure algorithms may oscillate, causing flickering preview or missed focus. Some OEMs expose a “night mode” that changes the underlying HAL parameters, which your app might not be aware of.
- Detection –
- Use
CameraManager.getCameraCharacteristics()to readCONTROL_AE_MODEandCONTROL_AE_PRECAPTURE_TRIGGER. - In a test, cover the sensor with a neutral density filter and log AE state changes via
CameraCaptureSession.CaptureCallback. - Verify that exposure compensation stays within a reasonable range (e.g., -2 to +2 EV) and that the preview does not exhibit rapid brightness jumps (>30 % change between consecutive frames).
2. Switching Between Front/Rear While Recording
- Problem – On certain devices, switching media sources mid‑record triggers a temporary drop in output framerate or causes the encoder to drop keyframes, resulting in visible artifacts.
- Detection –
- Record a 10‑second video while toggling lenses every second.
- Use
MediaExtractorto examine the resulting file: check that the time between keyframes does not exceed 2 seconds (assuming a 2 s GOP). - Verify audio continuity (no gaps > 10 ms).
3. External USB Cameras (OTG)
- Problem – USB video class (UVC) devices may expose multiple formats (MJPEG, YUYV, NV12). If your app hard‑codes a format that the device does not support, the preview will be black.
- Detection –
- Enumerate available formats via
CameraManager.getCameraIdList()andgetCameraCharacteristics(cameraId).get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP). - Attempt to open a session with each format; log success/failure.
- Ensure
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free