Android AlarmManager sometimes triggers late if phone runs on battery - android

* Updated: The issue has likely been resolved. Please refer directly to the update below, as AlarmManager was only partially to blame. *
I'm currently developing an android app with alarm clock functionality. Unfortunately it turned out that there seem to be rather specific cases for which AlarmManager doesn't seem to work as expected .
When I initially tested the app, I did so by means of the Android Studio Emulator (Nexus 5, API 21) as well as an old phone of mine (Galaxy S2, API 16), with the result that all alarms were delivered in time. As soon as I switched
to my Xperia Z1 Compact (API 19) though, alarms suddenly triggered minutes late occasionally.
Interestingly this seems to be the case especially when the phone is currently running on battery (i.e. not connected to the pc or an outlet). It kind of feels like AlarmManager would suddenly act super sluggish in a desperate attempt to spare the battery, completely oblivious to the fact that it was utilized by means of .setExact(). If the device is not asleep, delivery is always in time.
Either way, the behaviour that results from my code doesn't seem to be deterministic at all, which is what really boggles my mind.
Simplified version of my code:
First, I schedule AlarmManager depending on the API. According to the logs calendar is set to the correct date and time.
public abstract class AlarmScheduler {
//...
private static void schedule(AlarmManager am, PendingIntent pi, Calendar calendar){
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
} else {
am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pi);
}
}
//...
}
The pending intent that is scheduled is a WakefulBroadcastReceiver. According to the logs, every time the alarm is late the call of
onReceive() is as well, at least as far as I've seen. So it seems likely that the problem is located here.
public class AlarmReceiver extends WakefulBroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent service = new Intent(context, AlarmService.class);
service.putExtras(intent);
startWakefulService(context, service);
}
}
For completeness, this is the service that is started by the BroadcastReceiver. The activity that the service starts acquires its own wakelock in onResume(). I've also tried to forcefully delay the release of the receiver wakelock up to 1000 ms, in order to guarantee that one is active at all times, but that didn't yield different results.
public class AlarmService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent alarmDisplay = new Intent(getBaseContext(), AlarmActivity.class);
alarmDisplay.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmDisplay.putExtras(intent);
getApplication().startActivity(alarmDisplay);
/** Some simple database operations here */
AlarmReceiver.completeWakefulIntent(intent);
return super.onStartCommand(intent, flags, startId);
}
}
I'm honestly kind of lost where I did go wrong here. But since an alarm clock that might trigger minutes late on some devices is not acceptable of course, I'd
really appreciate any input - thanks in advance.
UPDATE
Alright, so first of all AlarmManager.setExact() does indeed seem to deliver late occasionally, but only for seconds, not minutes. According to the documentation this seems to be intended, as it states:
"The alarm will be delivered as nearly as possible to the requested trigger time."
Unfortunately this does sound like a matter of milliseconds rather than seconds, which is why I did assume that AlarmManager was behaving faultily in the first place . If it was evidently delivering seconds late, I thought it capable of delivering minutes late as well. Still, my mistake ultimately.
Secondly, the issue described initially was a wakelock issue that, at least in my case, did only show on my API 19+ phone when it was currently running on battery. While API 19 apparently introduced very aggressive power management for the sake of battery life, the management appears to be even more aggressive if the phone is running on battery. This seems also to be the reason why on the emulator everything was behaving normal (per default it is always in the process of being charged, if the icons are to be believed).
The problem with the code initially posted is that the receiver wakelock is released before the activity acquires its own wakelock in onResume(). Just a matter of milliseconds, but still sufficient for the CPU to be pushed back to sleep apparently. As I did state in my original post, I already thought of this as a potential source of the problem, which is why I did try to forcefully delay the release of the wakelock, in order to guarantee that at least one would be active at all times. For testing purposes I did this quick & dirty by means of wait() though, which the OS seemingly wasn't very fond of and thus forcefully shut down my service at some point, which was something that didn't show in regular logcat output. From this point on subsequent alarms tended to be faulty, although I'm not sufficiently knowledgeable in terms of Android to actually explain why.
Either way, the issue could be resolved by acquiring and releasing only one single wakelock instead of two separate ones (please mind that, due to the very nature of the problem, there is no absolute certainty, but testing results are looking fine).

Related

Is there any way to run service continuously?

There are few questions similar to this on Stack Overflow but none of the solutions are working for me
The problem is with only few devices like OnePlus and MI, The service is getting killed as soon as the user swipes away app from recent app.
I read that these OEM'S use some aggressive strategies to kill services. I just want to know is there any way to keep service running or start it as soon as it gets killed.
I need to run a service which will give location continuously (24/7) in background (This app is only for specific people so no worries about battery life).
I've tried:
running foreground service.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent)
} else {
startService(intent)
}
Also in service onCreate method started with notification
#Override
public void onCreate() {
Log.i("Service", "onCreate");
startForeground(NOTIFICATION_ID, getnotification());
}
returning START_STICKY in onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
initLocationClient();
initLocationSyncThread();
return START_STICKY;
}
re-initiating service in onDestroy and onTaskRemoved but they are not getting called.
binding a service
scheduling alarm manager and start service frequently but play store will give warning that our app is using alarm manager too frequently and its a bad practice. And there is now way using workmanager to schedule for less than 15 min and its still not guaranteed to start after 15 min.
So is there any way to keep running a service other than above options?
If you go through THE LINK, you will find:
Unfortunately, some devices implement killing the app from the recents menu as a force stop. Stock Android does not do this. When an app is force stopped, it cannot execute jobs, receive alarms or broadcasts, etc. So unfortunately, it's infeasible for us to address it - the problem lies in the OS and there is no workaround.
It is a known issue. To save battery, many manufacturers force close the app, thus cancelling all the period tasks, alarms, and broadcast receivers etc. Major manufacturers being OnePlus (you have option to toogle), Redmi, Vivo, Oppo, Huwaei.
Each of these devices have AutoStartManagers/AutoLaunch/StartManager type of optimization managers. Which prevent the background activities to start again. You will have to manually ask the user to whitelist your application, so that app can auto start its background processess. Follow THIS and THIS link, for more info.
The methods to add to whitelist for different manufactures are give in this Stack Overflow answer. Even after adding to whitelist, your app might not work because of DOZE Mode, for that you will have to ignore battery otimizations
Also in case you might be wondering, apps like Gmail/Hangout/WhatsApp/Slack/LinkedIn etc. are already whitelisted by these AutoStart Managers. Hence, there is no effect on their background processes. You always receive timely updates & notifications.
Here are few things which helped little .
In AndroidManifest.xml add line android:enabled="true"
<service
android:name=".service.TrackingService"
android:exported="false"
android:enabled="true"
/>
Inside service Add alarm to wake up again after 2 seconds .
#Override
public void onTaskRemoved(Intent rootIntent) {
initAlarm();
super.onTaskRemoved(rootIntent);
}
private void initAlarm() {
AlarmManager alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, StartServiceReceiver.class);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() +
2000, alarmIntent);
}
Create a receiver StartServiceReceiver and in it just start service again .
For Mi devices we need to allow a permission inside setting to allows service to start in background
The right way to listen to location on the background of to use a fused location API from play services.
Look at the samples here.
https://github.com/googlesamples/android-play-location?files=1
These API's are power efficient, and I would recommend it too.

Network call on alarm times out

I have an app that uses AlarmManager to schedule a repeating alarm every X amount of time. When my receiver receives the Intent, it has to make a http request.
The alarm itself works fine and triggers when it should. The network call, however, starts timing out when the phone is not in use. To be more specific:
When I schedule it to fire every minute (bad practise, I know, but just to illustrate), the first 5-8 minutes the request succeeds. After that, I get a java.net.SocketTimeoutException: connect timed out. Sometimes it does succeed, but mostly this happens.
I tried setting the connect/read/write timeouts to a minute, but then I get this exception instead of the one above: java.net.ConnectException: Failed to connect to myapp.example.com/123.45.67.89:80.
My code:
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Consider mApi and myBody to be initialised and valid
mApi.myPostRequest(myBody).enqueue(new Callback<Void> {
#Override
public void onResponse(Call<Void> call, Response<Void> response) {
//Does not get here
}
#Override
public void onFailure(Call<Void> call, Throwable t) {
t.printStackTrace();
}
}
}
}
Things I've tried:
as stated before, increasing timeouts
acquiring a WakeLock in
onReceive and releasing it when the call is done (added the
permission)
Other information:
The alarm is set using
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(), interval, pendingIntent); from my
Activity.
I'm using Retrofit (2.1.0) for the network communication, but you could probably have guessed that from my code ;)
Any ideas on how to get the network call working when the phone is sleeping?
You Should use JobService here, it has many constraint to handle different scenarios and also your Job is guaranteed to be executed by the system.
The problem here is of doze mode, and using JobService this can be resolved easily.
Implementation is also easy all you need to do is create a JobService and inside it's onStartJob() start your network thread and then just dispatch your job.
For more detail
https://developer.android.com/reference/android/app/job/JobService.html
From https://developer.android.com/reference/android/content/BroadcastReceiver.html
As a general rule, broadcast receivers are allowed to run for up to 10
seconds before they system will consider them non-responsive and ANR
the app. Since these usually execute on the app's main thread, they
are already bound by the ~5 second time limit of various operations
that can happen there (not to mention just avoiding UI jank), so the
receive limit is generally not of concern. However, once you use
{#goAsync }, though able to be off the main thread, the broadcast
execution limit still applies, and that includes the time spent
between calling this method and ultimately PendingResult.finish().
Further reading says
If you are taking advantage of this method to have more time to
execute, it is useful to know that the available time can be longer in
certain situations. In particular, if the broadcast you are receiving
is not a foreground broadcast (that is, the sender has not used
FLAG_RECEIVER_FOREGROUND), then more time is allowed for the receivers
to run, allowing them to execute for 30 seconds or even a bit more.
(long work should be punted to another system facility such as
JobScheduler, Service, or see especially JobIntentService),
You can try using #goAsync. Or you can switch your logic to JobIntentService
I haven't tested any of these.
You have a basic mistake in your code - you cannot make requests (or any long running operations) in your broadcast receiver - it dies after ~10 sec so that might be the reason for some of your failures.
You should move the request logic to a service (IntentService) which you'll start from your broadcast receiver and make the request there.
That should work just fine.
From developer docs : https://developer.android.com/reference/android/app/AlarmManager.html
The Alarm Manager holds a CPU wake lock as long as the alarm
receiver's onReceive() method is executing. This guarantees that the
phone will not sleep until you have finished handling the broadcast.
Once onReceive() returns, the Alarm Manager releases this wake lock.
This means that the phone will in some cases sleep as soon as your
onReceive() method completes
In your code onReceive will return before mApi.myPostRequest(myBody).enqueue ...task will be executed, then this task probably will be never executed due the CPU will stop as soon onReceive returns.
You said you tested acquiring a WakeLock, but new Android 6.0 Doze mode ignores wakelocks
It seems that OnReceive will have to wait for the task to end
Some ideas:
Checking for some terminate flag in a loop with thread.sleep ?
If the task uses a Thread object then using thread.join() ?
You should use AlarmManager.RTC_WAKEUP, not AlarmManager.ELAPSED_REALTIME_WAKEUP to wake up device and you should use a service to do your job started in Received in onReceive with startWakefulService(context, service.class). This will ensure that device will fully wake up and do your network call without timeout.
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime(), interval, pendingIntent);
If the problem is caused by Doze and Wakelocks being ignored, you should try the tips offered in https://developer.android.com/training/monitoring-device-state/doze-standby.html:
Standard AlarmManager alarms (including setExact() and setWindow()) are >deferred to the next maintenance window.
If you need to set alarms that fire while in Doze, use setAndAllowWhileIdle() >or setExactAndAllowWhileIdle().
Alarms set with setAlarmClock() continue to fire normally — the system exits Doze shortly before those alarms fire.
there are 2 problems in your implementation:
1) if broadcast receiver doesn't get finished executing within 10 seconds then ANR will happen.
2) All network calls are optimized in the background, so when device is wake it may work else will not fire HTTP request to conserve battery and other resources.
what you should do is inside service create a loop (with sleep time of few seconds) that checks time on each iteration and when that time is reached then perform task, I have also faced such issues when tried to upload files to server at an interval of 1 hour, so I decided to work on my own rather than using AlarmManager class...
I hope this will help you...
Use Foreground service to get your job done.
Refer the official link for dive into Foreground service
By using Foreground service, we can set the interval to start doing an API call in the background without getting affected by the doze mode. follow this simple example that includes complete code and walkthrough.
happy coding. Thanks ;)

Is there some way to do something similar to startForeground for an activity?

I finally finished my app, but it doesn't work properly on my phone - it seems that the app is always being re-created when I open it after the device has been put to sleep, which (in spite of my best efforts) causes a LOT of trouble for me.
Looking for a way to prevent the app from being killed, I came across startForeground... which, as I understand it, applies only to services (if not, I can't figure out how to apply it to an Activity - I've tried).
Is there an option which will save me from having to make a lot of serious changes? (I barely understand what I've done so far as it is...)
I've read somewhere that having an ongoing notification work, or worked until froyo - I'm not even sure.
If this is true, would I have to be concerned about the energy consumption? I am using an AlarmManager with a partial wake lock on the BroadcastReciever, so until the alarm is fired, I don't actually need to be doing anything - just keeping the app alive.
If you return Service.START_STICKY; in your Service's onStartCommand, it it should restart in case your app gets terminated. So if you have any code that you want to run for a longer period of time, put it in a service.
Here's an example:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
registerAlarmBroadcast();
return Service.START_STICKY;
}

Track per app usage on android on pre-Lollipop devices

I would like to build an app that can track how long a user spends on certain apps (mainly for proof of concept and to implement in other apps.) I know that Lollipop implemented the API UsageStatsManager that enables you to track the "total length of time an app was in the foreground for a time interval (by day, week, month, or year)."
I have also read that one can poll the AcvtivtyManager continuously, however that would waste battery life and take up CPU and RAM.
What I am wonder is, is there an efficient way to keep track of the amount of time an app is open on devices running an android version lower than Lollipop. Specifically how apps like Aptrax and App Usage Tracker as well as many others, do it with very little or "No battery drain (even on high frequency tracking)."
Is there another method that these apps use, to get app usage data on pre-Lollipop devices, with little battery usage, or is polling the Activity manger not as resource heavy as they source I read lead me to believe.
i have tried to do the same but found no better way, basicklly poll the activity manager, now to make sure not to waste battary use the alarmManager and a recieverto set the waking times of the pollin so that when the phone is asleep it wont be woken for your poll (assume that user uses apps when the screen is on and phone is active) and poll like every 5 seconds or so
here is basic sample code
Intent alarmIntent = new Intent(context, MyReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC, Calendar.getInstance().getTimeInMillis(), pendingIntent);
public class MyReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, Intent intent) {
// poll activity manager and any thing else you want here
// including figuring out the next time you want to run
// and scheduling another PendingIntent with the AlarmManager
}
}
using AlarmManager.RTC makes sure the reciever is only polling when the phone is awake.

AlarmManager.setExact is still inexact in Kitkat when phone sleeps

I'm struggling with AlarmManager.setExact() for couple of days. My application needs exact alarms and works fine on older Androids with AlarmManager.setRepeating().
I've read that it has changed since API 19 and updated my code accordingly.
Here is the code responsible for setting an alarm:
PendingIntent pi = PendingIntent.getBroadcast(context.getApplicationContext(), alarmOrder+1, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getAlarmManager(context).setExact(AlarmManager.RTC_WAKEUP, alarmTimeMillis, pi);
}
else {
getAlarmManager(context).setRepeating(AlarmManager.RTC_WAKEUP, alarmTimeMillis, AlarmManager.INTERVAL_DAY, pi);
}
Broadcast receiver starts new activity with alarm screen:
#Override
public void onReceive(Context context, Intent intent) {
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "AlarmTimeReceiver");
wl.acquire();
Intent alarmIntent = new Intent(context, AlarmActivity.class);
alarmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
context.startActivity(alarmIntent);
//Release the lock
wl.release();
}
The thing is that while testing on Android 4.4 device connected via USB cable all works fine. Alarm starts on specified time all the time (with screen turned on/off). Unfortunately, when I disconnect device from computer, alarm starts correctly only when screen is turned on. When I turn screen off, alarms starts in inexact time.
Have I missed something? Have any of you experienced similar situation?
It runs correctly on Android < 19
Not really.
A _WAKEUP alarm guarantees -- via a framework-managed WakeLock -- that the device will stay awake until onReceive() returns. Then, the framework releases the WakeLock, and the device can fall back asleep... if there are no other outstanding WakeLocks.
Your WakeLock, as written, is pointless. It merely duplicates the framework-managed WakeLock without adding value.
startActivity() is asynchronous. The activity will not be anywhere near started by the time onReceive() ends and the framework-managed WakeLock (and your additional one) are released. Now, sometimes, your activity will get a chance to start up anyway, because the device does not fall back asleep quickly. I presume that you are using android:keepScreenOn or the equivalent in the activity, so once you get to that point, there is another WakeLock outstanding, and the device cannot fall back asleep automatically.
However, sometimes the device will indeed fall back asleep before your activity starts up. That might have changed with Android 5.0 -- it would not surprise me in the least if Android is more aggressive about putting the device back to sleep more quickly. So, while your previous approach might have worked 90% of the time, it may be a lot less now. However, your previous approach did not work 100% of the time.
We see the same thing with services. In fact, this scenario is a lot more common there. That's why, back in April 2009, I created the WakefulIntentService, and why in August 2013, Google released WakefulBroadcastReceiver. Both offer a pattern for acquiring the WakeLock in onReceive() but then not releasing it until the service's work is finished.
Neither of those solutions will work out of the box for you, as both are tied inextricably to services. However, you can use those as a source of ideas for rolling your own. You need to move your WakeLock into a static data member (ick), and only release() it once your activity is far enough along that it has its own WakeLock. For example, if you are calling setKeepScreenOn() on a View in Java code, once that is done, I would expect that it is safe to release your original WakeLock. Then, the framework can take over and release the keep-screen-on WakeLock based upon user input.

Categories

Resources