**1. Approach (brief)**- Select camera with RAW capability, create CameraDevice + CaptureSession with ImageReaders for RAW and JPEG, build a manual CaptureRequest.Builder (CONTROL_MODE_OFF) exposing exposureTime, sensitivity, lensFocusDistance, submit repeating requests, handle single RAW capture, process images with a software stabilization step on a background thread.**2. Pseudo-Kotlin implementation (high-level)**kotlin
// Threading
val cameraThread = HandlerThread("CameraThread").apply { start() }
val cameraHandler = Handler(cameraThread.looper)
val ioThread = HandlerThread("IOThread").apply { start() }
val ioHandler = Handler(ioThread.looper)
// Camera selection
fun chooseCamera(manager: CameraManager): String {
for (id in manager.cameraIdList) {
val chars = manager.getCameraCharacteristics(id)
val caps = chars.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES) ?: continue
if (caps.contains(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) return id
}
return manager.cameraIdList.first() // fallback
}
// ImageReaders
val rawReader = ImageReader.newInstance(width, height, ImageFormat.RAW_SENSOR, 2)
val jpegReader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 2)
// Open camera
manager.openCamera(chosenId, object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) {
cameraDevice = device
createSession()
}
override fun onDisconnected(d: CameraDevice) { d.close() }
override fun onError(d: CameraDevice, err: Int) { d.close() }
}, cameraHandler)
// Create capture session
fun createSession() {
val targets = listOf(rawReader.surface, jpegReader.surface, previewSurface)
cameraDevice.createCaptureSession(targets, object: CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
captureSession = session
startPreviewWithManualControls()
}
override fun onConfigureFailed(s: CameraCaptureSession) {}
}, cameraHandler)
}
// Build manual request
fun startPreviewWithManualControls() {
val req = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW).apply {
addTarget(previewSurface)
// Manual controls
set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_OFF)
set(CaptureRequest.SENSOR_EXPOSURE_TIME, 10_000_000L) // 10ms example
set(CaptureRequest.SENSOR_SENSITIVITY, 400)
set(CaptureRequest.LENS_FOCUS_DISTANCE, 0.5f) // manual focus distance
set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, CaptureRequest.STATISTICS_FACE_DETECT_MODE_SIMPLE)
}
captureSession.setRepeatingRequest(req.build(), captureCallback, cameraHandler)
}
// Single RAW+JPEG capture
fun captureRawAndJpeg() {
val req = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
addTarget(rawReader.surface)
addTarget(jpegReader.surface)
set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_OFF)
// tighten manual exposure for still
set(CaptureRequest.SENSOR_EXPOSURE_TIME, desiredExposureNs)
set(CaptureRequest.SENSOR_SENSITIVITY, desiredIso)
set(CaptureRequest.LENS_FOCUS_DISTANCE, focusDistance)
// enable RAW if available via target
set(CaptureRequest.JPEG_ORIENTATION, getOrientation())
}
captureSession.capture(req.build(), captureCallback, cameraHandler)
}
// Image processing & software stabilization
rawReader.setOnImageAvailableListener({ reader ->
val image = reader.acquireNextImage()
ioHandler.post {
val rawBuffer = extractRawBuffer(image)
val stabilized = runSoftwareStabilization(rawBuffer) // custom algorithm: motion estimate + warp
saveDng(stabilized, image) // write DNG using Exif + metadata
image.close()
}
}, cameraHandler)
// Capture callbacks
val captureCallback = object: CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
// read AE/AF state, meter extents, update UI
}
override fun onCaptureFailed(session: CameraCaptureSession, request: CaptureRequest, failure: CaptureFailure) { }
}
**3. Key concepts & reasoning**- Choose camera supporting RAW to enable RAW_SENSOR capture and full manual controls.- CONTROL_MODE_OFF + SENSOR_* keys give manual exposure and ISO.- Use ImageReader for async delivery of RAW (RAW_SENSOR) and JPEG.- Run CPU/GPU-based software stabilization on IO thread: estimate motion (IMU-assisted if available), compute inverse transform, warp raw Bayer or RGB buffer before saving to avoid motion blur artifacts.- Always manage threads and close Image objects to avoid leaks.**4. Complexity & edge cases**- Edge: Some devices clamp manual ranges — query min/max SENSOR_INFO_EXPOSURE_TIME_RANGE and SENSITIVITY_RANGE.- RAW might be unavailable; fall back to highest-quality JPEG.- Synchronize sensor timestamps with IMU if doing IMU-assisted stabilization.- Ensure session targets match template expectations; recreate session when adding/removing targets.This sketch demonstrates camera selection, manual request construction, RAW/JPEG ImageReader usage, capture callbacks, threading, and a post-capture software stabilization step appropriate for a mobile developer interview.