Engineering diary

It's 2026, and keeping a sleep app playing all night is still surprisingly hard

I wanted my phone to make a fan noise until morning. That seemed like a reasonably settled problem in 2026, but it has taken up a surprising amount of my time as a developer.

For years I used White Noise Lite. It had ads and an interface that felt ancient, but it mostly worked, which was what mattered. Then it started stopping on me regularly. I thought of these as crashes, although I didn’t have the logs to know what was happening. I also wanted something with a modern interface that generated sound on the device rather than playing a recording, with controls to get the sound just right. Eventually I built my own app, Veil, and got a much better appreciation for the reliability problem.

On iOS, background playback has been relatively straightforward, and in my experience Veil has been very stable from the beginning. Android became a longer project. There’s a wide range of hardware, including older, lower-memory phones, manufacturer-specific battery management, and the occasional update installed right in the middle of a listening session. There were also plenty of problems in my own code. To someone waking up at 3 a.m., the distinction doesn’t matter much.

I started with crash reporting, but quickly needed more context. A killed process may never report an exception, and a live process can have a silent audio stream. Even a foreground service and playback notification don’t establish that sound is coming out of the speaker. I added a native playback journal, process-exit information, memory measurements, service state, audio-stream events, and explicit records of intentional stops. Relevant evidence survives the process and gets collected on a subsequent launch.

The goal was to reconstruct enough of a night to distinguish possible causes. Was playback still intended? Had the timer finished? Was memory growing? Was the phone plugged in? Had Android replaced the package? More signals helped, but only when I understood what each one could actually tell me.

One of the first things I had to debug was the measurement itself. Swiping Veil away could stop playback without properly closing its recorded session. Android might reclaim the cached process hours later, and my reporting would count that as a playback failure. Someone intentionally stopping the app in the morning could become an “overnight kill.” Meanwhile, updates that interrupted real playback were being excluded as benign system exits.

Correcting those definitions changed the failure picture before I fixed any more audio code. Since then, I’ve been careful to distinguish an improvement in playback from an improvement in how I measure it. I revisit the data regularly by app version, device family, session length, and exit reason, excluding test devices and deduplicating reports. Delayed reporting matters too: a few short sessions on a new release don’t tell me whether it survives a night.

Those patterns guide the device testing. I’ve run extended and overnight tests on physical phones, alongside Samsung remote-lab sessions, with continuous logcat, memory and pressure sampling, and snapshots of audio and service state when something fails. The setup takes care of collecting evidence so I don’t have to catch the phone misbehaving in person.

I also learned that the test setup can change the result. USB powers the phone, so a USB-connected run isn’t an unplugged battery test. Repeatedly running dumpsys meminfo could itself cause audio glitches. And a clean overnight run only establishes that I didn’t reproduce the failure under those conditions. It’s useful evidence, but it doesn’t clear every device or system state.

One of the stranger discoveries was Android Auto Backup. On my Pixel, the system logs captured BackupManager killing Veil’s process during a backup while audio was playing. Looking back through the fleet, I found 190 protected SIGKILLs across 93 installs, with exits from August 14 through September 11, 2026, and recurring intervals clustered around three days. That pattern supported the backup explanation without proving the cause of every individual exit.

I disabled Auto Backup for Veil, accepting the cloud-restore tradeoff. Android documents that backup shuts down the app while normally excluding foreground apps; capturing what happened on the device was what made this particular case actionable. Android backup documentation

Other problems were mine to fix. Audio recovery could start building a replacement context before the old one finished closing, overlapping resources when the app was already having trouble. I changed teardown to be awaited and bounded, and serialized recovery with playback operations. Recovery also needs to respect newer user intent: if someone presses stop while a repair is queued, the repair must not bring the sound back.

One attempted fix introduced another failure. I added a 150 ms probe to check whether an audio context’s clock was advancing, which helped detect a context that claimed to be running but was stuck. Unfortunately, the probe awaited a JavaScript timer. In the background, that wait could stall until the app returned to the foreground. The probe now runs only in the foreground, and I have another reason to test recovery paths with the screen off.

Some of the most useful changes were product decisions. On-device measurements showed about 90 MB of additional resident memory associated with the advertising WebView process. Earlier investigation had also uncovered serious SDK-related memory failures. I initially tried containing that cost, then removed the ads and SDK entirely. For something expected to run all night, reliability mattered more than keeping the ads.

There was still an important gap in the telemetry: whether the person expected playback to stop. After a relevant interruption, Veil can show a small card with the time and ask, “Were you expecting this?” That wording is deliberate. “Did you stop it?” misses situations where a partner stopped it or an interruption was expected even though the user didn’t touch anything.

If the stop was expected, the user doesn’t get sent through battery troubleshooting. If it wasn’t, the app acknowledges the interruption and offers guidance matched to the available evidence. Their answer gives me direct information about the experience without pretending to identify the technical cause. It also means I don’t have to greet every new Android user with alarming permissions and a battery-settings scavenger hunt.

Those settings are awkward from both sides. I can open settings or request an exemption where appropriate, but they remain user decisions. Android’s exemption flow also isn’t a universal control for every manufacturer’s cleanup behavior. Giving someone a specific next step after a problem is more useful than asking them to navigate unfamiliar settings before they’ve even played a sound. Android power-management guidance

Updates are especially frustrating. I can guide someone to Play’s per-app auto-update setting, but Veil doesn’t control that preference. Asking people to manage updates manually adds another chore, and Google reserves exceptions for critical security updates. An update intended to improve overnight reliability can itself interrupt someone’s night. Google Play update settings

Automatic restoration after an update is still unfinished work. Rebuilding the audio graph is only part of it; timer state and user intent have to survive correctly too. I also have manufacturer-specific patterns that need better reproduction before I can confidently explain them. I want the app to recover from failures, but I don’t want it fighting someone who deliberately stopped it.

The most useful progress has come from combining recurring analysis, long device runs, specific technical fixes, and direct user feedback. Each catches something the others miss. I’ve become much less interested in whether the app says it’s playing, and much more interested in what evidence supports that claim. Someone pressing play before bed should be able to take that for granted, even if getting there has been considerably more work than I expected.

Looking for practical steps for your phone? Read the companion Android audio troubleshooting guide.