My little baby, I want you sleep longer….
Finally! I upgraded my four year old Sony Xperia ZR phone to the newer Android Nougat by replacing stock ROM with LineageOS. With custom ROM and root access I can configure my phone pretty as much as I want. The most important thing to me is to have a phone with only useful Apps installed and efficient battery usage. Being satisfied with getting rid of bloatware from stock ROM, I also miss probably one of the most useful Android feature from Sony: STAMINA mode. With STAMINA I could keep my phone never turn off during hiking for almost one week long without charging battery and still having working cell network, GPS and map Apps when I needed them.
To make smart phones, manufacturers use precious raw materials that must be extracted and processed, and consume natural resources and energy that can affect our air, land, and water, as well as plants and animals. — The Secret Life of a Smart Phone
It didn’t take me long to realize that the newer Android has already implemented built-in battery saving features. Besides App Standby the Doze mode is probably mostly mentioned since then. Unfortunately I was very disappointed with the battery usage after using my phone with the new Android feature for several days. The Doze performance just could not compared with STAMINA one! Or probably I misunderstand Doze?
To understand how Doze works there are Google’s document and some great blogs, but they are rather for developers to make a Doze friendly App. There are also several Apps like Doze-Settings-Editor and ForceDoze for users to configure Doze with more intensive battery saving, however it was not really clear and trivial to me how exactly I could tune Doze. There are more than twenty parameters to configure Doze and I had no idea what a good combination should be and what exactly I can achieve. My goal is very simple:
- I try to avoid installing extra Apps.
- I would avoid root access whenever possible.
- I would like my phone stay as less battery usage as possible whenever the screen is locked (and without battery charging).
- I don’t need immediate notification from Email, calendar and messaging Apps. I don’t need location tracking in background and just need location service performing precisely when I read maps on my phone.
Probably there are already a bunch of posts and discussion about the design of Doze, but at the end I still decide to directly review its implementation DeviceIdleController and somehow reconstruct the insight of Doze with following state diagrams (also available on GitHub).
There are a few things worth to know:
- The original Doze, or Deep Doze, is available since Android Marshmallow (6.0) and Light Doze is since Android Nougat (7.0).
- There are actually two state machines running in parallel for both Deep and Light Doze.
- Once either Deep or Light Doze enters its IDLE state, the battery saving actions are enabled. Namely, more time in IDLE less battery is used.
- Whenever Deep Doze enters IDLE state, Light Doze will be overridden, or suspended, (i.e. in OVERRIDE state) until another motion is detected.
- Deep Doze would only work if there is any motion sensor available and would fallback on significant motion sensor if necessary. Without any sensor Deep Doze would never enter IDLE state and there is practically no battery saving feature any more (but Light Doze will still work).
- Some battery saving Apps would disable (i.e. actually restrict) sensors to other Apps, but Deep Doze will still need to depend on motion sensors.
- The illusion that Light Doze always run before Deep Doze kicks in has to depend on proper settings (i.e. device_idle_constants for usual phone or device_idle_constants_watch for wearable watch).
There is one thing really impressive to me. The introduction of Light Doze did not change the existing Deep Doze implementation at all (except that Deep Doze has to override Light Doze). The way to have a second state machine running in parallel and to lift existing limitation without touch existing code is really a good example for any software development lesson.
It is genius to lift limitation built in an existing product and sell the solution with another fancy name again in a newer product, either on purpose or just as a consequence.
I couldn’t understand why the original Deep Doze was designed to be always dependent on motion. It would be for me a useless feature during hiking or just daily commuting. It probably only makes sense for users wearing Android watches and they have to check Email constantly while walking between office and running on treadmill. If it were a bug that Deep Doze always depends on motion, then Light Doze, also sold as Doze on Go, is probably the bugfix instead of a new feature.
Once understanding Doze idle state transition and those more than twenty parameters from the both state machines, now I can tune the Doze on my phone in various ways with adb tool.
To ignore Deep Doze and discard any motion, I can just increase inactive_to (and motion_inactive_to) timeout to a very large number (e.g. 30 days) so that Deep Doze never goes into its IDLE state and only Light Doze can manage to its IDLE state.
$ adb shell settings put global device_idle_constants inactive_to=2592000000,motion_inactive_to=2592000000
To ignore Light Doze for whatever reason, I would do the similar setup with light_after_inactive_to so that Light Doze probably never enters IDLE state.
$ adb shell settings put global device_idle_constants light_after_inactive_to=2592000000
To have Deep Doze without taking motion into consideration (i.e. any motion won’t wake up Doze from idle), I have to ignore the real Deep Doze and more and less apply Deep Doze timing parameters to Light Doze so that Light Doze behaves like Deep Doze but without motion detection. Now it takes about one hour (e.g. light_after_inactive_to is set to 50 minutes) to enter Light Doze’s IDLE state if screen is kept off and battery is not charging. It will stay idle up to 6 hours before running any maintenance task for 30 seconds to maximal 10 minutes.
$ adb shell settings put global device_idle_constants inactive_to=2592000000,motion_inactive_to=2592000000,light_after_inactive_to=3000000,light_max_idle_to=21600000,light_idle_to=3600000,light_idle_maintenance_max_budget=600000,min_light_maintenance_time=30000
To have deep limbo Doze without taking motion into account, I would ignore the real Deep Doze and tune Light Doze so that it will be trapped in IDLE state as long as possible. Maintenance task would be performed about twice a day for maximal 30 seconds and any alarm allowed to wake up Doze from idle would take less effect.
$ adb shell settings put global device_idle_constants inactive_to=2592000000,motion_inactive_to=2592000000,light_after_inactive_to=15000,light_pre_idle_to=30000,light_max_idle_to=86400000,light_idle_to=43200000,light_idle_maintenance_max_budget=30000,min_time_to_alarm=60000
To verify current Doze settings:
$ adb shell dumpsys deviceidle
To reset any customized setting back to default values:
$ adb shell settings delete global device_idle_constants
And it is always a good idea to re-start your phone after applying new settings.
Using adb from another computer to change Doze behavior of your phone is probably not the most convenient solution if you want to tune your Doze frequently and directly on your phone.
Instead of buying another new sustainable ethical smartphone, I would rather use my existing phone for the next five years…