DEV Community

Super Funicular
Super Funicular

Posted on

How an Android Phone Keeps Recording Video With the Screen Off: Foreground Services, Camera2, and the OS Fighting You (2026)

"Record with the screen off" sounds like a checkbox. It is, in fact, the single hardest thing a phone-as-camera app has to do, because almost every part of modern Android is designed to stop exactly this behavior. The OS wants to kill background work, throttle CPU when the screen is dark, and reclaim the camera the moment your app stops being the visible foreground. If you want a spare Android phone to sit on a windowsill and quietly record for hours with a black screen, you are working directly against the grain of the platform's power model.

I build Background Camera RemoteStream, an app whose entire reason to exist is screen-off recording and live viewing, so I've spent a lot of time in this particular fight. This is an architecture-level walkthrough of how screen-off capture actually works on Android in 2026 — the Camera2 pieces, the foreground-service rules that changed under Android 14, and the background-execution limits you have to design around. It's aimed at the r/androiddev / Hacker News reader who wants to understand the mechanism, not a marketing tour. I'll be honest about the tradeoffs and the things the OS simply won't let you do.

Why "screen off" is the hard part, not "record"

Recording video on Android is a solved problem when your app is in the foreground with a visible preview. The camera delivers frames, you route them to an encoder, you write an MP4. The difficulty appears the instant the screen goes dark, because two independent OS subsystems change their behavior:

  1. Process lifecycle / background execution limits. Since Android 8 (Oreo), an app that isn't visible is a background app, and background apps get their services killed, their network restricted, and their wakeups batched. A plain Service started from a backgrounded app is a candidate for termination within minutes.
  2. Power management (Doze and App Standby). When the device is stationary with the screen off, Doze progressively defers background CPU, network, and alarms. This is great for battery and terrible for something that is supposed to keep working while nothing appears to be happening.

So the real engineering question isn't "how do I record video," it's "how do I convince the OS that this dark-screen phone is doing legitimate, user-visible work that it must not interrupt." The answer is a foreground service, and getting that right in 2026 is more involved than it used to be.

Foreground services: the contract with the OS

A foreground service is Android's mechanism for saying "the user knows this is happening, keep it alive." The visible sign is the persistent notification you can't swipe away — that notification is not decoration, it's the price of admission. In exchange for showing it, the system dramatically lowers the priority with which it will kill your process and exempts a lot of your work from the harshest background limits.

The lifecycle, in the order it has to happen:

  • The app starts the service (from an allowed context) and, within a few seconds, calls startForeground() with a notification.
  • If you don't call startForeground() in time, the system throws ForegroundServiceDidNotStartInTimeException and kills you. Slow initialization is a real trap here — you want the notification up before you touch the camera.
  • The service holds the process at foreground priority for as long as it runs, which is what lets capture survive the screen turning off.

This part is old and stable. What changed — and what trips up anyone porting an older app forward — is that as of Android 14 (API 34), every foreground service must declare a type.

The Android 14 foreground-service-type rule

Beginning with Android 14, you must declare an appropriate foregroundServiceType for each foreground service in your manifest, request the matching permission, and — for Play distribution — justify the type in the Play Console. For a camera app the relevant type is camera, and it comes with a specific permission:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.CAMERA" />

<service
    android:name=".CaptureService"
    android:foregroundServiceType="camera"
    android:exported="false" />
Enter fullscreen mode Exit fullscreen mode

Two consequences worth internalizing:

  • If you call startForeground() for a service whose type you didn't declare in the manifest, the system throws MissingForegroundServiceTypeException. There is no graceful degradation — declare it or crash.
  • The camera type is what lets your foreground service keep accessing the camera while running in the background. That's the whole point: without the correct FGS type, the moment the app is no longer visible, camera access is on borrowed time.

If you also record audio, the microphone type has a sharper edge: the RECORD_AUDIO runtime permission is subject to while-in-use restrictions, so you generally can't start a microphone foreground service while your app is already in the background. The practical design rule that falls out of this: start capture while you're still the visible foreground app (the user taps "start"), then let the screen go off. Trying to cold-start sensor capture from a truly backgrounded state is where things quietly stop working on modern OS versions.

Camera2 without a preview: the part people get wrong

Here's the insight that makes screen-off recording possible at all: Camera2 doesn't require a preview surface. People assume the camera needs something on screen to render into. It doesn't. Camera2 delivers frames to whatever output Surfaces you hand it in the capture session, and those surfaces don't have to be attached to any visible view.

A screen-off capture session, conceptually:

  • Open the camera via CameraManager.openCamera() and hold the CameraDevice.
  • Create your output target — commonly a MediaRecorder surface (for writing an MP4) and/or a MediaCodec/ImageReader surface (for encoding frames you'll stream). None of these is a SurfaceView or TextureView, so none of them needs the display.
  • Build a repeating capture request (TEMPLATE_RECORD) targeting those surfaces and set it repeating on the CameraCaptureSession.

Because there's no TextureView in the pipeline, the display can turn off and the capture session keeps producing frames into your encoder surface. The screen being dark is, from the camera pipeline's perspective, irrelevant — you were never drawing to the screen in the first place. That decoupling is the core trick, and it's why "screen off" and "recording" are not actually in tension once the architecture is right: the tension was only ever with the process lifecycle, which the foreground service solves.

The complications that remain are the real-world ones:

  • Lifecycle races. The camera is a single-consumer resource. If another app (or your own preview Activity being destroyed) tears down a surface while the session is live, you get CameraAccessException or a silent stall. You have to treat surface creation/teardown and session state as a careful state machine, not a set of independent callbacks.
  • Thermal and power reality. Continuous encode is genuinely warm work. A phone doing this on a windowsill needs to be on a charger — not because the software is inefficient, but because sustained H.264/HEVC encoding plus sensor draw is simply a real power load. This is a hardware truth, not a bug to optimize away.
  • OEM variance. Camera2's guaranteed capabilities are a floor, not a ceiling; different chipsets expose different INFO_SUPPORTED_HARDWARE_LEVEL and resolution/FPS combos. Defensive code queries the CameraCharacteristics and degrades gracefully rather than assuming a fixed profile.

Keeping it alive: Doze, wake locks, and battery optimizations

The foreground service earns you a lot of protection, but Doze and aggressive OEM battery managers can still interfere with a long-running session, especially over many hours. The honest state of things in 2026:

  • A properly typed foreground service is the primary defense, and for an actively-capturing camera service it covers most of what you need — the system treats an ongoing FGS as user-visible work.
  • Some deployments still request the user exempt the app from battery optimization (REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) for maximum reliability on OEMs with heavy-handed task killers. This is a permission you should ask for sparingly and explain, not sprinkle on reflexively — Play scrutinizes it.
  • Aggressive PARTIAL_WAKE_LOCK usage is mostly a legacy pattern here; the foreground service model is meant to replace hand-rolled wake locks for this class of work. Where the camera pipeline is active and the FGS is up, the CPU work needed to keep encoding proceeds.

The uncomfortable truth is that there is no single API that guarantees "run forever with the screen off on every phone." What you get is a stack of correct choices — right service type, notification up in time, capture started from the foreground, sensible power expectations — that together make it reliable in practice. Anyone promising more than that on the modern platform is overselling.

Where the frames go next

Recording to a local MP4 is only half of what a phone-as-camera app usually wants; the other half is seeing the feed live. That's a separate subsystem, and I wrote a full deep-dive on how the phone serves its own live camera feed over your LAN with an embedded Ktor server, including the single-latest-frame buffer that decouples capture rate from delivery rate so multiple browser viewers don't back-pressure the encoder. For watching from outside the network, the frames get muxed to RTMP instead — the screen-off YouTube Live path, end to end, is written up here. Both of those consume the exact same capture session described above; the FGS-plus-Camera2 foundation is what everything else stands on. If you want the wider tour of the hard parts of building this kind of app, the overview of recording-with-the-screen-off and streaming-over-LAN is here.

The architecture in one paragraph

Screen-off recording on Android is a foreground service of type camera (declared in the manifest with FOREGROUND_SERVICE_CAMERA, or you crash on startForeground()), started while the app is still visible so the while-in-use rules are satisfied, holding a Camera2 CameraCaptureSession whose outputs are encoder/recorder surfaces rather than on-screen views — which is precisely why the display can go dark without stopping the pipeline. Doze and OEM battery managers are handled by the FGS priority itself, occasionally supplemented by a battery-optimization exemption, and the whole thing lives on a charger because sustained encoding is real power. That's the shape of it. There's no magic — just a small number of platform contracts that all have to be honored at once, and a design that respects the camera as a single-consumer, lifecycle-sensitive resource.

Why this design is also the privacy story

There's a nice property that falls out of this architecture almost for free: because the capture pipeline writes to a local MediaRecorder surface by default, the footage lives on the device, not in a vendor cloud. The "record with the screen off" plumbing and the "your video doesn't leave your phone" guarantee are the same design decision viewed from two angles. That matters more than it sounds, because what happens to your footage when a free camera app shuts down or gets acquired is a real risk with cloud-first apps and a non-issue when the bytes never left local storage.

If you want to see the whole thing in action rather than in prose, Background Camera RemoteStream is on Google Play and there's more about the approach at superfunicular.com. And if you're building your own, the summary is short: the camera was never the hard part. The OS was.

Top comments (0)