DEV Community

Super Funicular
Super Funicular

Posted on

The Architecture Behind a $0 Security Camera — How an Old Android Phone Replaces Ring Without the Cloud

Why This Is a Companion Piece

A few days ago I wrote Turn Your Old Android Phone Into a Free Security Camera (No Subscription Required) — a step-by-step guide to repurposing a drawer phone as a continuous-recording security camera with no cloud, no account, and no monthly fees.

That post answered "how." This one answers "why it works at all."

If you're an Android developer, the question that should be bothering you is: if a drawer phone is hardware-equivalent or hardware-superior to a $300 security camera, why doesn't every Android phone already do this? The answer is software, and it's interesting.

The Three Things Stock Android Won't Do

Out of the box, an Android phone is unusable as a security camera for three specific reasons:

  1. The camera surface is destroyed when the screen turns off. This is a battery decision, not a hardware limitation. The OS assumes that a user-facing app cannot be doing useful work when the display is off, so it tears down the Camera2 session.
  2. There is no built-in remote viewer. A handheld phone has no UX surface for "let other devices on my network connect to me and watch what my camera sees."
  3. OEM dozing kills long-running services. MIUI, OneUI, ColorOS, and friends each have their own aggressive battery-optimization layers on top of stock Android's Doze mode. A naïvely written background-camera app gets killed within an hour on most non-Pixel devices.

A real "old phone as security camera" app has to solve all three. Each one is a different sub-problem.

Problem 1: Surviving Screen-Off

The textbook solution is a foreground service holding a partial wakelock, plus a Camera2 capture session that's owned by the service rather than an Activity.

class CameraRecordingService : Service() {
    private val wakeLock by lazy {
        (getSystemService(POWER_SERVICE) as PowerManager)
            .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BgCam::recording")
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        startForeground(NOTIF_ID, buildPersistentNotification())
        wakeLock.acquire(8 * 60 * 60 * 1000L /* 8h cap */)
        startCamera2Session()
        return START_STICKY
    }

    override fun onDestroy() {
        if (wakeLock.isHeld) wakeLock.release()
        super.onDestroy()
    }
}
Enter fullscreen mode Exit fullscreen mode

Three details that took me weeks to get right:

  • The notification has to actually exist before startForeground is called. Android 12+ kills services that call startForeground from a non-foreground context unless the notification channel is already registered.
  • The wakelock has a max durationacquire(timeout) with a finite timeout is safer than acquire() with no argument, because it bounds the worst case if your stop logic crashes.
  • START_STICKY matters. If the system kills your service for memory pressure, you want it restarted automatically. The Activity that started the service may be long gone.

Problem 2: The Local Web Server

The interesting design choice is that the camera should serve itself — directly, over the LAN, to any browser. No companion app on the viewing device. No cloud relay.

This is where Ktor on Android earns its keep:

embeddedServer(Netty, port = 8080) {
    install(CORS) { anyHost() }
    routing {
        get("/stream") {
            call.respondBytes(
                contentType = ContentType("multipart", "x-mixed-replace; boundary=frame"),
                bytes = mjpegFrameStream()
            )
        }
        get("/snapshot") {
            call.respondBytes(latestJpegFrame(), ContentType.Image.JPEG)
        }
        get("/") { call.respondHtml(controlPanelHtml()) }
    }
}.start(wait = false)
Enter fullscreen mode Exit fullscreen mode

Three architectural payoffs from this pattern:

  1. Zero install on the viewing device. Any browser on the same WiFi can hit http://192.168.1.x:8080/ and see the feed. Laptops, tablets, smart TVs with browsers, anything.
  2. No relay infrastructure. Cloud-camera companies need datacenter scale to relay every customer's video stream. We don't need any of it because the bytes never leave the LAN.
  3. No third-party data exposure. This is the privacy claim's actual technical foundation. There is no "we got breached" failure mode because there is no "we" — the server runs on your phone.

Things to know about putting Ktor on Android:

  • Use Netty, not the OkHttp engine. Netty is more battery-friendly for long-running idle servers.
  • Don't try to bind to port 80 — Android won't let unprivileged apps. Pick something high (8080, 8443).
  • For HTTPS on the LAN, generate a self-signed cert at first launch and accept the browser warning. There is no clean Let's Encrypt path for a host-on-LAN device with a private IP.

Problem 3: OEM Doze

This is the part nobody's article warns you about. You can do everything above correctly and still have your service murdered after 30 minutes on a Xiaomi Redmi.

The mitigations, in order of how much they help:

  • Request REQUEST_IGNORE_BATTERY_OPTIMIZATIONS. This handles stock Doze.
  • Detect the OEM and link the user directly into the manufacturer's whitelist screen. MIUI has a separate "Battery saver" whitelist. OneUI has "Sleeping apps." ColorOS has "Auto-launch." Each is a different intent target. Detecting Build.MANUFACTURER and routing the user there adds about 40 lines and saves about 80% of OEM-doze killings in my logs.
  • Heartbeat from the foreground service notification. A periodic notification update (once every 5 minutes) keeps the service "interesting" to certain OEM watchdogs.
  • Don't use WorkManager for the camera loop. WorkManager is fine for periodic tasks but it's the wrong tool for "stay running for 8 hours without interruption" — it batches work and is subject to all the same OEM-doze constraints you're trying to escape.

What the Architecture Costs

The only feature this architecture can't easily replicate from a Ring/Nest is push notifications when motion is detected and you're not on your home WiFi. Solving that requires either a relay (which destroys the privacy claim) or a self-hosted notification service (which is a non-trivial follow-on project).

For continuous monitoring on the LAN — driveway, baby crib, pet area, garage — the architecture is strictly superior to a cloud camera. Better hardware, no rent, no breach surface, no vendor lock-in.

What This Means for the Industry

Cloud-camera companies sell hardware at near-cost and earn on subscriptions. The drawer-phone architecture undercuts both legs of that business model — the hardware is free because you already own it, and the subscription is gone because the cloud isn't in the picture.

The reason this isn't more widespread isn't technical. The Android APIs to do all of this have been stable since Camera2 landed in Android 5.0 (2014). It's a marketing-distribution gap: Ring spends more on advertising in a quarter than the entire indie-Android-camera-app category will ever see in a decade.

If you're an Android developer reading this, the takeaway is: there's a category of "rent-extracting consumer hardware" — security cameras, baby monitors, pet cams, dashcams — where the underlying technical problem is solved and the business model is held in place by marketing budget. The wedge for indie apps is that you don't need to outspend Ring; you just need to be the answer that comes up when someone Googles "free alternative to Ring."

How We Apply All of This

Background Camera RemoteStream is the implementation of every architecture decision in this article. Free version covers screen-off recording and OEM-doze survival. Pro version adds the embedded Ktor web server for browser-based viewing.

If you're building something adjacent — a different sensor type on a phone, a different content-server pattern over LAN — I'd love to compare notes in the comments. Camera2 quirks specifically are an underdocumented area and every Android dev I talk to has at least one war story.

Companion to: Turn Your Old Android Phone Into a Free Security Camera (No Subscription Required).

Top comments (0)