Background: I have an Android application running on Android 7.1. A reccurring task is set using AlarmManage to perform a task every day at a specific time. code is like below:
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
long startInMs = getComing9AM();
Intent intent = ...; //an intent to be run when alarm time is up.
PendingIntent ScheduledIntent = PendingIntent.getBroadcast(context, id, intent, 0);
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, startInMs, ScheduledIntent);
private long getComing9AM(){
long now = System.currentTimeMillis();
long today = getToday9AM();
return (now < today? today:getTomorrow9AM());
}
private long getToday9AM(){
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 9);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
return calendar.getTimeInMillis();
}
private long getTomorrow9AM(){
return getToday9AM() + 7 * 24 * 3600 * 1000;
}
- The android device is not a mobile phone, but a small box. Under normal operation, only power cable is plugged with WIFI connectivity.
Problem Description:
- The scheduled task works pretty well every day. After three weeks or more, the task no longer running.
- All other functionality still working, including MQTT client communication, IO operations.
Analysis result:
After checking the pattern of the problem, it is found that around 25 days from last boot up the task will not work. Being a math-geek who is sensitive to number, "25 days" = 2,160,000 second = 2,160,000,000 ms. And this value is very closed to 2^31-1 (Integer.MAX_VALUE), which is equivalent to 24.85 days.
HDMI cable is plugged to the device, the screen shows no input.
Even a new scheduled task is assigned after 25days (thru HTTPS), the task will not be executed.
After a USB device (i tried a mouse) is plugged to the device, Android is wake up (the screen has a display). And the Android has been awakened, the task can be run normally every day until the next 25 days.
using ADB to check the Android setting, I can only find one parameter value is met with Integer.MAX_VALUE. That is (adb shell dumpsys power):
mMaximumScreenOffTimeoutFromDeviceAdmin=2147483647 (enforced=false) Screen off timeout: 2147483647 ms
Based on finding #5, I have tried to set the screen off timeout to 60000 trying to reproduce the problem. But the task can still be run as usual.
It should not be related to Doze mode as the device is always charged and no battery with it.
(New finding) The value of "mWakefulness" in dumpsys power is Asleep after 24.85days. Before 24.85days this value should be Awake.
(New finding) Given that wWakefulness=Asleep arelay, the "mLastSleepTime"=now - (startupTimestamp + 24.85day).
Seek for advice:
I would like to have some direction to further investigate the problem.
I would like to know if there is a way to simulate the waking up event within the application? (as a workaround before finding the root cause).
I would find ways to unit test your code to ensure it works fine. You can fire intents regularly and try that. Either in a Unit test or via adb. You mentioned "via https" but perhaps you need to go straight to the "metal" and trigger the methods yourself to test.
Your device is not a phone, so I'm not sure if it's capable of receiving Firebase Push Notifications (or any form of), since you'd need Google Play services for this, and that comes with another bag of things, but I would try to manually control these events (externally) to make sure your code is not causing issues. Does making a "beep" (or printing to logcat) every day at 9am still work?
If AlarmManager somehow breaks periodically after NN days, then something is either really broken (it would be strange to be honest, 7.1 is rather old and has been in use for a long time, someone would have said something about this, I hope(?)), or your code is misbehaving.
Start isolating things, can you create a simple app that ALL it does is print a log ever day at 9:30 am. Does it still work?
Or would work manager help you here or would there be other dependencies that you cannot use/have? There are tradeoffs of course, for WorkManager is not the same as AlarmManager and is way more constrained.
This is also puzzling, since it appears the AlarmManager stopped working and receiving work altogether. Does the job work if you broadcast the intent manually via
adbat that point? Do you have more than one device of these? does it happen with all?This is a very interesting problem, I wish I had more concrete stuff, but very few people will know the inner workings of the
AlarmManagerin relation to all the new ways google is trying to "save battery" on phones.setExactAndAllowWhenIdleis described as:Could the OS allowing itself to be flexible in some form and ignoring your alarm? hmmm not sure.
Final comment, have you taken a look at the AlarmManager's source code? Perhaps there's a hint in there.
In the end, this long post (that should have been a comment) doesn't really give you a solution, just an open place for me to dump ideas. :) I'll update if need; please let us know what you find :)
As a side-comment: I'd test if you can use Gradle 4.x and have access to Java 8 APIs for
java.time(much better than calendar and even util.Date), or try to leverage the (now archived/deprecated but still usable for some time until Gradle 4.x is "streamlined") ThreeTenABP abstraction from Wharthon to use the same Java8 APIs (the migration from ThreeTen tojava.timeis really a 10 minute find/replace to change the imports).In short, you can replace all that calendar nonsense with
val tomorrowAt9Am = ZonedDateTime.now().plusDays(1).withHour(9).withMinute(0).withSecond(0).toEpochSecond()EDIT: the last two points you found out (mWakefulness) are very interesting, i'll keep looking. In the meantime, I found a few instances of said variable in the Android codebase. The most interesting one is this one from the AttentionDetector (what a name); feel free to browse all the results