← Back to Blog

Why I Built an App That Replaces Your Camera Feed

Why I Built an App That Replaces Your Camera Feed

Camera apps on Android look simple on the surface. You open the app, point it at something, and take a picture. But underneath that simple-looking interface, there are at least three completely different ways an app can talk to the camera hardware — and they all work differently.

I wanted to understand all three. Not just in theory, but well enough to intercept them.

The Starting Point

I was working on a different project at the time and needed to test how an app reacted to different camera inputs. Like, what happens if the camera returns a specific image instead of the real feed? Does the app crash? Does it handle it gracefully? Does it do something unexpected?

The problem was, there was no easy way to do this. I couldn't just "mock" the camera on a real device without going deep into the system. So I started digging.

Three Camera APIs, Three Headaches

Android has accumulated three camera APIs over the years:

  • Legacy Camera API (android.hardware.Camera) — the original, deprecated but still used by a surprising number of apps
  • Camera2 API (android.hardware.camera2) — the modern replacement, much more powerful and much more complex
  • CameraX (androidx.camera) — a Jetpack library that wraps Camera2 with a simpler interface

If you want to intercept the camera in every app, you need to handle all three. And they each store and transfer image data in completely different formats.

How the Interception Works

CameraInterceptor is an LSPosed module. If you're not familiar with it, LSPosed is a framework that lets you modify how Android apps behave at the system level — without changing the apps themselves. It works by hooking into the Zygote process (the parent of all Android apps) and intercepting method calls.

For each camera API, the approach is different:

With the legacy Camera API, you intercept the takePicture callback and swap out the byte array that gets returned. With Camera2, you need to intercept the ImageReader that receives frames from the camera pipeline. With CameraX, you hook into the image analysis use case.

The result is the same in all three cases: instead of the real camera feed, the app sees whatever image you've selected from your gallery.

What It's Good For

Before anyone asks — this isn't for faking photos on social media or tricking people. It's a development and testing tool. Here's what I actually use it for:

  • Testing camera-dependent apps — How does your app handle a specific image? Now you can find out without physically pointing the camera at a printout.
  • Learning the camera APIs — Building this forced me to deeply understand all three APIs. If you want to learn how Android cameras really work, try intercepting them.
  • Research — Understanding how apps handle camera input is valuable for security research and quality assurance.

The Root Requirement

I'll be upfront: CameraInterceptor requires a rooted device with LSPosed installed. That's not something most people have, and that's okay. This is a specialist tool for developers and researchers who need to test camera-related functionality.

If you're in that category, the source code is on GitHub. It works on Android 8.0+ and supports per-app configuration — so you can choose exactly which apps get intercepted.

What I Took Away

Building CameraInterceptor was one of those projects where the journey taught me more than the destination. I now have a deep understanding of how Android handles camera hardware at every layer of the stack — from framework-level APIs down to native buffers.

And honestly? That knowledge has come in handy in places I never expected. When you understand a system at that level, debugging regular camera issues becomes almost trivial.