A foreground service, a wake lock, and a TECNO that ignores both
StudyPomo 1.0.0 is on Play. Here is what actually keeps the timer counting after you put the phone down, and the one family of phones where none of it is enough.
In this piece
StudyPomo went live on Google Play on 9 August. Version 1.0.0, build 16, which is the honest way of saying the first release took sixteen goes.
The app makes one promise ahead of all the others: start a session, put the phone down, and the timer keeps counting. That sounds like the easy part of a Pomodoro app. It is very nearly the whole engineering problem.
Why a countdown is hard on Android
An app that is not on screen has no guarantee it will keep running. Android reclaims memory by killing processes, and it does not ask first. A countdown that lives in the app’s own process is a countdown that stops the moment the system decides it wants that memory back.
The user does not find out when it happens. They find out forty minutes later, when nothing chimed and the session they thought they were in the middle of turns out to have ended before it started.
So the countdown cannot live in the app.
What actually runs
Three things, and none of them is a package we installed.
A foreground service. SessionForegroundService, written against the Android
APIs directly rather than through a Flutter plugin. It owns the session, and it
keeps running when the Flutter engine is not.
A wake lock, so the CPU does not quietly sleep through the end of a session.
A completion alert owned by the service, not by the Dart side. If the Flutter engine has been torn down, the chime still fires, because the thing that fires it was never the Flutter engine.
The service schedules the completion rather than counting towards it. Its two
methods are scheduleCompletion and postCompletion, which is the design stated
in two names: work out when this session ends, hand that to the system, then wake
up and say so.
The notification in your shade shows a fixed end time, not a live countdown. That is a repair, not a preference. An earlier build ran a chronometer, and when a session ended while the app was dead the chronometer kept going into negative numbers. A notification counting down to minus four minutes is worse than no notification at all, because it is confidently wrong.
What broke on a TECNO KM7
Then we tested on the phones the people around us actually carry.
On Transsion firmware, which is HiOS on Tecno, Infinix and itel, the foreground service is killed outright. Not paused. Not backgrounded. Not handed a lifecycle callback so it can save its state on the way out. Killed, with nothing delivered to the app at all.
We measured it on a TECNO KM7. It happens even with a battery optimisation exemption granted, which is where nearly every answer to this question on the internet stops.
That is the part worth writing down. The standard advice is not wrong so much as incomplete: it describes stock Android, and a very large number of phones are not running stock Android. We are in Nigeria. Transsion is not an exotic edge case here, it is the shelf in the shop.
What we did, which is not a fix
Nothing at application level reaches this. There is no scheduling mechanism we can swap in, no permission we can request, and no clever fallback that survives a process being removed without notice. We spent a while looking for one, which is how we know.
What ships instead is a setup screen inside the app that points you at the manufacturer’s own controls, because those controls are the only thing that actually governs the behaviour. Focus To-Do, the largest app in this category, does the same. Everyone building against these devices arrives at the same wall.
So the accurate version of the promise is this. On stock Android, and on most of what you will meet, the timer survives the screen going off, the app being switched away from, and the process being reclaimed. On Transsion firmware it survives all of that too, right up until the firmware decides otherwise, and then it does not, and we cannot stop it.
What we are not claiming
We do not know how often this happens in the wild. StudyPomo ships with no analytics and no crash reporting, which is a deliberate choice we are happy with and also a cost we should state plainly: it means we have one reproduction on one handset, not a rate across a population.
We also cannot tell you it is fixed in the next release, because we do not think it is fixable from inside the app. If that turns out to be wrong we will write it up here.
The reason this is the first thing we have published is that it is the kind of thing we would want to have read before we started. If you are building anything with a background timer for this market, test on a Transsion handset early, and do not accept a battery optimisation exemption as proof that you are done.
Written by Adeboye Oluwatimileyin and Moses-Azuoru George, the two people who are Emberfig. We make money from AdMob inside the apps. This site carries no ads yet. You never pay us and we never sell your data.