Da Real Fragmentation - Doze
In the last post, we learnt how Android Alarms work. We analyzed the different options, types and methods available and realized that scheduling tasks in Android implies fragmentation issues. In this post, we’ll dive into Doze and App Standby modes, explaining how these two power-saving features work in detail. Furthermore, we’ll go deeper, describing how alarms are affected when devices enter Doze mode and the problems it causes on apps with a strong background component. Stay awake!
Starting from Android 6.0 (API level 23), Android introduced a new way for devices to preserve battery life by going idle. Doze reduces battery consumption by deferring background CPU and network activity for apps when the device is unused for long periods of time. While in Doze, your scheduled alarms (with
AlarmManager), jobs (with
JobScheduler), and syncs (with
SyncManager) will be ignored by default, except during occasional idle maintenance windows. Periodically, the system exits Doze for a brief time to let apps complete their deferred activities. During these maintenance windows, the system runs all pending syncs, jobs, and alarms, and lets apps access the network. Note that activating the screen or plugging in the device exits Doze and removes these processing restrictions.
First of all, we have to know the different stages of Doze mode and when they will be active on devices. Let’s review them in depth.
Deep-Doze was introduced with Android Marshmallow and it’s activated when the device is unplugged, when the screen is off, and when no motion has been detected for some time. At this point, the OS tags internally when these conditions were met, waits about 30 minutes to be sure, and then jumps into the Deep-Doze mode. As I already mentioned, Deep-Doze provides short maintenance windows where apps can sync up their latest data. At the conclusion of each maintenance window, the system again enters Doze, suspending network access and deferring jobs, syncs, and alarms. Over time, the system schedules maintenance windows less and less frequently, helping to reduce battery consumption in cases of longer-term inactivity when the device is not connected to a charger.
A picture is worth a thousand words!
Maybe not this time…
Android 7.0 introduced Light-Doze, a more lightweight version of Doze. This mode brings a subset of CPU and network restrictions while the device is unplugged with the screen turned off, but not necessarily stationary. In this case, Android checks these conditions during a couple minutes before applying the first subset of restrictions: It shuts off app network access, and defers jobs and syncs. If you depend on network access and you are using
AlarmManager for your recurring background work, consider using
JobScheduler so that you only start up when the network is available in the maintenance windows. As you can see in the following diagram, the period between data-syncing maintenance windows is shorter than in Deep-Doze.
If the device is stationary for a certain time after entering Light-Doze, the system applies the rest of the Deep-Doze restrictions to
AlarmManager, GPS, and Wi-Fi scans. Regardless of whether some or all Doze restrictions are being applied, again, the system wakes the device for brief maintenance windows, during which applications are allowed network access and can execute any deferred jobs/syncs.
It is also important to note that Deep-Doze can be interrupted and stepped up to Light-Doze if the device stops being stationary but still not charging with the screen off.
App Standby lets the system determine that an app which has not been in the foreground (or is showing a
Notification), after some undefined period of time not being used by the user, is idle. While the app is in Standby, and the device unplugged, the app behaves similarly to Doze mode, which means that the OS doesn’t permit it to access the network and to execute jobs and syncs. If the device is idle for long periods of time, Android only allows idle apps network access around once a day. In the moment the user plugs the device into a power supply, everything returns to normal.
Warning: Doze and App Standby manage the behavior of all apps running on Android 6.0 or higher, regardless of whether they are specifically targeting API level 23.
What restrictions does Doze add?
Now that we know how Doze works, we are going to analyze the limitations that it introduces and how to wake the device from it, so that your scheduled and background work is not affected.
Some argue that Doze shouldn’t disrupt your app flow too much, because in Light-Doze maintenance windows occur frequently and Deep-Doze only affects your app when the user is not using their device at all. Of course, in favor of battery life…
But, what about real-time apps or time-sensitive apps that do their job behind the scenes without user interaction? If you are working on any of these kind of apps, you definitely need a way to get around Doze’s effects. Let’s check the options available.
As you may already know, Doze is particularly likely to affect activities that
AlarmManager alarms and timers manage. Be careful, because if you use the alarms methods from Android 5.1 (API level 22) or lower when the system is in Doze (Android 6.0+ devices), alarms won’t fire.
As explained in the previous post, Android 6.0 (API level 23) introduced two new
setExactAndAllowWhileIdle(). With these methods, you can set alarms that will fire even if the device is in Doze and not yet in a maintenance window. Remember that you only have 10 seconds to capture further wake locks in which to complete your work.
setExactAndAllowWhileIdle() can fire alarms more than once per 9 minutes, per app.
From the point of view of power management, Doze mode leaves
setAlarmClock() events alone. Using this method, Doze exits completely shortly before the alarm goes off regardless of device state, so the app is able to fetch fresh data before the user is back. The major problem with
setAlarmClock() is that it is visible to the user, because it represents an alarm clock (as its name suggests). Which implies that the user will see the alarm clock icon in the status bar and the scheduled time at which the phone will alert the user if the notification shade is fully opened.
Firebase Cloud Messaging
If your app requires a persistent connection to the network to receive critical messages, you could use Firebase Cloud Messaging (formerly GCM). FCM high-priority messages let you reliably wake your app to access the network, even if the user’s device is in Doze or the app is in App Standby mode. The system delivers the message and gives the app temporary access to network services and partial wakelocks, then returns the device or app to idle state. As you can imagine, this option introduces some difficulties, e.g. you have to setup FCM, the app needs network connectivity, etc.
The system provides a configurable whitelist of apps that are partially exempt from Doze and App Standby optimizations. This whitelist of apps allows you to hold wakelocks and access the network. However, other restrictions still apply to the whitelisted app, just as they do to other apps. It does not change the behavior of
SyncManager. You can call
isIgnoringBatteryOptimizations() to check if your app is already on the whitelist. As described in the documentation, users can manually configure the whitelist in Settings > Battery > Battery Optimization and, alternatively, the system provides ways for apps to ask users to whitelist them.
An app can fire the
ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGSintent to take the user directly to the Battery Optimization, where they can add the app.
An app holding the
REQUEST_IGNORE_BATTERY_OPTIMIZATIONSpermission can trigger a system dialog to let the user add the app to the whitelist directly, without going to settings. The app fires a
ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONSintent to trigger the dialog.
Warning: You have to be careful because Google Play policies prohibit apps from requesting direct exemption from Power Management features in Android 6.0+ (Doze and App Standby) unless the core function of the app is adversely affected. So, if in doubt, make sure your app is in the acceptable use cases for whitelisting.
Although is not officially documented, apps that have been running foreground services (with the associated notification) are not restricted by Doze.
Doze is for the entire device, so one last case is hoping somebody else interrupts it. You may be thinking…
Obviously, it’s unlikely, but it’s still an option :)
We’ve learnt how to live with Doze and App Standby modes and a bunch of workarounds to avoid the issues caused by them. Apart from that and in my opinion, a more philosophical problem has been clearly shown: Android is adding more and more restrictions, especially regarding background processes. Like ours, lots of other apps are being severely compromised by these changes. I’m starting to feel that we are loosing that openness that we used to have… We’ll see how it ends…
Anyway, say Boo! today and…
In the next and final article, we’ll talk about sensors. We’ll explain how they work. Besides, we’ll introduce some peculiarities when you are using them and battery optimization modes show up and show how to deal with such problems.
P.S. I hope you’re enjoying the series but I would love to hear your thoughts! As always, feel free to leave a comment below and/or send a Pull Request on DaRealFragmentation repository!