Android AlarmManager incomprehensible behavior - android

i know AlarmManager was discussed several times, but i really can't find an answer that can help me. I have an app which needs to start a service at a certain time and make some stuff, so after some research work, i decided that AlarmManager is what i need.
I use this code to do the job
Intent myIntent=new Intent();
ComponentName cn=new ComponentName("my.package.name", "my.package.name.AlarmService");
myIntent.setComponent(cn);
PendingIntent pendingIntent= PendingIntent.getService(alarm._context, alarm.id, myIntent, 0);
AlarmManager alarmManager = (AlarmManager) _context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, alarm.time, pendingIntent);
Where "alarm" is an object written by me.
Now, i am sure like the death that this code works, because if i set one (or more) action for 2 minutes or hours forward, it works (i write a log on file in the first instruction of the service). If i execute
adb shell dumpsys alarm
I can see all of my pending intents.Ok, happy to see everything working, i schedule my actions at:
01:00 AM
08:00 AM
08:40 AM
09:15:AM
01:00 PM
02:00 PM
18:00 PM
after setting this actions, i execute
adb shell dumpsys alarm
and i can see all of the pending intents. Then i go to sleep and..... when i wake up in the morning at 07:30 AM, the action scheduled at 01:00 AM has not been executed and if i execute
adb shell dumpsys alarm
all of my pending intents are disappeared!!!!!!!
I am really frustrated of this behavior, because i spent a lot of time writing this application and i can't get it working properly. I'm posting this question after weeks of researchs, because i tryed every thing, but i still have this problem. Please help me

From the docs for AlarmManager (link here):
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. If your alarm receiver called
Context.startService(), it is possible that the phone will sleep
before the requested service is launched. To prevent this, your
BroadcastReceiver and Service will need to implement a separate wake
lock policy to ensure that the phone continues running until the
service becomes available.
If your Service does not also hold set up proper wake locks, the device will go right back to sleep when the AlarmManager is finished, you need to manage this as well in your code.
HTH

Related

Service is killed in sleep mode.Why?

I've read just about every Stackoverflow answer that exists on this topic, but none of them worked.
Goal: Keep my service running 24/7, all the time
Problem: Whenever my device is on sleep mode for an hour or more, the service is killed
What I've tried to fix it:
Returning START_STICKY from onStartCommand() and using startForeground()
public int onStartCommand(Intent intent, int flags, int startId) {
notification = makeStickyNotification(); //I've simplified the irrelevant code, obviously this would be a real notification I build
startForeground(1234, notification);
return START_STICKY;
}
This works fine, and it even restarts my service whenever the device is low on memory, but it is not enough to fix the problem that occurs when my device goes to sleep for a while.
Using Alarm Manager in onCreate() of my Activity and in onStartCommand() of my Service to call a Broadcast Receiver that calls my service
Intent ll24 = new Intent(this, AlarmReceiver.class);
PendingIntent recurringLl24 = PendingIntent.getBroadcast(this, 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 1000*60, recurringLl24); // Every minute
This helps keep my service active, but again, doesn't solve my problem
Using Schedule Task Executor to keep it alive
if (scheduleTaskExecutor == null) {
scheduleTaskExecutor = Executors.newScheduledThreadPool(1);
scheduleTaskExecutor.scheduleAtFixedRate(new mainTask(), 0, 1, TimeUnit.SECONDS);
}
...
class mainTask implements Runnable {
public void run() {
// 1 Second Timer
}
}
This also just keeps the service active but doesn't keep it alive after a long sleep.
Separate task Manifest
android:launchMode="singleTop"
This did nothing
How can I (1) test this issue without having to put my phone to sleep and check every hour and (2) keep my service running despite the device going to sleep?
Your service was killed by Doze or Standby mode of Android. That was introduced in Android 6.0 (API level 23).
Doze restrictions
The following restrictions apply to your apps while in Doze:
Network access is suspended.
The system ignores wake locks.
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.
The system does not perform Wi-Fi scans.
The system does not allow sync adapters to run. The system does not allow JobScheduler to run.
So system ignored your Alarm Clocks, Scheduler, etc.
In Android Oreo release Android defined limits to background services.
To improve the user experience, Android 8.0 (API level 26) imposes
limitations on what apps can do while running in the background.
Still if app need to run its service always, then we can create foreground service.
Background Service Limitations: While an app is idle, there are limits
to its use of background services. This does not apply to foreground
services, which are more noticeable to the user.
So create a foreground service. In which you will put a notification for user while your service is running. See this answer (There are many others)
Now what if you don't want a notification for your service. A solution is for that.
You can create some periodic task that will start your service, service will do its work and stops itself. By this your app will not be considered battery draining.
You can create periodic task with Alarm Manager, Job Scheduler, Evernote-Jobs or Work Manager.
Instead of telling pros & cons of each one. I just tell you best. Work manager is best solution for periodic tasks. Which was introduced with Android Architecture Component.
Unlike Job-Scheduler(only >21 API) it will work for all versions.
Also it starts work after a Doze-Standby mode.
Make a Android Boot Receiver for scheduling service after device boot.
I created forever running service with Work-Manager, that is working perfectly.
The murder mystery has been solved, and I know what killed my service. Here's what I did:
After I realized that startsticky, startforeground, alarmmanager, scheduleTaskExecutor, and even wakelock were unable to save my service, I realized the murderer couldn't be the Android system, because I had taken every measure possible to prevent the system from killing my service and it still would get killed.
I realized I needed to look for another suspect, since the service wasn't dying because of the system. For that, I had to run an investigation. I ran the following command:
adb shell dumpsys activity processes > tmp.txt
This would give me a detailed log of all the processes running and their system priorities. Essentially, tmp.txt would be the detective in this murder mystery.
I looked through the file with lots of detail. It looked like my service was prioritized properly by the system:
Proc #31: adj=prcp /FS trm= 0 2205:servicename.service/uID (fg-service)
The above line indicates the exact priority of a process running on the Android device. adj=prcp means the service is a visible foreground service.
At this point, I realized that my service must be encountering some error a couple hours after running, so I let it run and die. After it died, I produced a dumpsys again to examine the error:
At this point, my service wasn't listed as a task in the tmp.txt file. Excited, I scrolled to the bottom of the dumpsys and solved the mystery!
com.curlybrace.ruchir.appName.MyService$2.onForeground(MyService.java:199)
at com.rvalerio.fgchecker.AppChecker$2.run(AppChecker.java:118)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6123)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:757)
The stack trace that caused the killing of my service was displayed right there! Essentially, a variable that would check for the foreground app being used would become null after a few hours of inactivity, which would cause an exception, and kill the service!
Key Takeaways:
If your service is getting killed, and you've done everything you can to make sure that it shouldn't be killed, perform a dumpsys and examine the nitty gritty of your device's activity process. I guarantee you will find the issue that way.
I still would like to have the bounty awarded to #Khemraj since his answer could be a great solution for someone who hasn't started their service properly. However, I am accepting this answer since it is the solution that actually fixed the issue.
onDestroy() is really unreliable and won't be called often that you want. Same for onLowMemory() callbacks. There is no way to take a guaranteed callback if android decides to kill your process or if user decides to Force Stop your app.
That's normal that than user device go to sleep mode, your service dies. Read about wakelocks. Try something like that in your service:
In manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
In service:
PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
"tag");
wakeLock.acquire();
But it's rly tricky for user and totally anti-pattern in android world, cuz of battery consumption.
Another option is to trigger service something like every 10 mins. Make pending intent on WakefulBroadcastReceiver(where you can start your service) and schedule it with alarm manager with flag RTC_WAKE_UP
Starting from SDK 26 a Service should have its relative "MainActivity" in foreground OR this Service should be started as in foreground using "startForegroundService()". The "startForeground()" doesn't work as expected if the target SDK is 26+ but need the other way I just explained.
After this you can use following code to Kill and restart the App from scratch (yes, even the Service is killed in this way):
Intent mStartActivity = new Intent(context, StartActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(context, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
Doze mode kills services to save battery. Only valid solution for you is to create a foreground service in Oreo and above.
https://developer.android.com/about/versions/oreo/background

Does AlarmManager require the PendingIntent to be of Type BroadcastReceiver?

The documentation for AlarmManager seems to imply (but does not outright explicitly require) that the PendingIntent you pass in to any of the set() methods should be of the type BroadcastReceiver, but I tested passing in other component types (like an IntentService) and it seemed to work fine.
Is it safe to use non-BroadcastReceiver Intents with AlarmManager?
Yes, and it has always worked, but I suspect not in the way that you're thinking. You can use any PendingIntent with an alarm; this could indeed be an activity or service PendingIntent. If it's a service PendingIntent, then the OS will call startService() for you when the alarm fires. The hidden catch is about the behavior of wakeup alarms.
When any alarm fires, the OS holds a wakelock on the sender's behalf for as long as it takes to deliver the PendingIntent, at which point the wakelock is released and the device is allowed to go back to sleep. The exact meaning of "as long as it takes to deliver" depends on which kind of PendingIntent is being used.
Broadcast delivery is essentially treated as synchronous: the wakelock is held by the Alarm Manager until the recipient's onReceive() callback returns. This gives you a hard guarantee that whatever processing you want to do in onReceive() is guaranteed to proceed without the device sleeping.
However, activity and service PendingIntent delivery does not wait for the recipient in the same way. With those kinds of alarm PendingIntents, the device remains awake long enough to begin the process of starting the target activity or service, but then it can (and does) go back to sleep immediately after that launch has begun, before the target code actually has a chance to run. In practice this means that with a service PendingIntent, even if the alarm is a wakeup alarm, the service will often not actually execute until the device as a whole is woken up normally, e.g. the next time the user turns on the screen manually.
Sometimes this is okay, if your code doesn't actually care that even though the alarm fired at 3am, the service didn't start running until 7am when the alarm clock went off and lit up the phone for an extended period. More often, though, what apps need to do is use a broadcast alarm, then in their onReceive() -- knowing that the device will sleep as soon as they return -- acquire their own wakelock and start up the service under that wakelock, etc.
There is a terrific support library class called WakefulBroadcastReceiver that encapsulates this alarm-wakelock-service dance and makes it both easy and bulletproof; it's https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html. Use that if you ever want to start a service in response to a wakeup alarm.

AlarmManager with BroadcastReceiver - how is it meant to work?

I'm trying to make a task schedule app and I made an Alarm app trying to learn how to do that part at least. It uses AlarmManager and it makes an alarm go off at a time chosen by a TimePicker. But it doesn't work when the emulator is turned off and on again.
So I'm trying to use BroadcastReceiver but I don't understand any of the guides...I mean am I supposed to set the intent that the alarm manager does to the BroadcastReciever? Or can I just start up the app and then the alarm exists again or what? How are the alarms stored in android?
But it doesn't work when the emulator is turned off and on again.
That is the correct behavior -- AlarmManager's schedule is cleared on a reboot. You need to specifically register to receive the ACTION_BOOT_COMPLETED broadcast, in order to re-establish your alarm events after a reboot.
I mean am I supposed to set the intent that the alarm manager does to the BroadcastReciever?
Well, if you are using a _WAKEUP-style alarm, the recipe is to use a getBroadcast() PendingIntent with AlarmManager, where the BroadcastReceiver is either a WakefulBroadcastReceiver (and follows those instructions) or passes control to my WakefulIntentService.
I have somewhat-contrived examples of using WakefulIntentService and WakefulBroadcastRecevier.
How are the alarms stored in android?
AFAIK, they are held in the memory of a core OS process and are not persisted.

Will AlarmManager work if my application is not running?

I have an alarm that works fine if i am interacting(using) with my application but it dose not works if I set it for next day and not interacting with my app.Therefore I am getting doubt is this because my application process is not running at that time.
here is what I am doing
Calendar calSet = Calendar.getInstance();
calSet.set(Calendar.HOUR_OF_DAY, selectedhour);
calSet.set(Calendar.MINUTE, selectedminute);
calSet.set(Calendar.YEAR, year);
calSet.set(Calendar.MONTH, monthOfYear);
calSet.set(Calendar.DATE, dayOfMonth);
alarm = new Intent(ActivityA.this, Service.class);
pendingIntent = PendingIntent.getService(getApplicationContext(), i++,alarm, 1);
alarmanager.set(AlarmManager.RTC_WAKEUP, calSet.getTimeInMillis(),pendingIntent);
From AlarmManager
AlarmManager provides access to the system alarm services. These allow you to schedule your application to be run at some point in the future. When an alarm goes off, the Intent that had been registered for it is broadcast by the system, automatically starting the target application if it is not already running. Registered alarms are retained while the device is asleep (and can optionally wake the device up if they go off during that time), but will be cleared if it is turned off and rebooted.
In simple way, it will work until your device has been rebooted.
You can read Android AlarmManager after reboot where #CommonsWare has been given a link of his sample application which persists Alarm even after device reboot.
Please ignore below section, it seems not valid. I will remove in future
You can read more about application kill at How to create a persistent AlarmManager, and How to save Alarm after app killing? can give you the idea about how to handle such issue (to persist alarm if application has been killed).
Yes it worked but proper understanding see doc.
Note: The Alarm Manager is intended for cases where you want to have your application code run at a specific time, even if your application is not currently running. For normal timing operations (ticks, timeouts, etc) it is easier and much more efficient to use Handler.
see here http://developer.android.com/reference/android/app/AlarmManager.html
Looking at the AlarmManager documentation..
http://developer.android.com/reference/android/app/AlarmManager.html
I don't see anywhere where it states that killing your app will remove all alarms that have been scheduled by that app. More specifically it states if your app is not started, it will start it for you.
I have done my own testing and can validate this by..
Setting an alarm 5 sec in the future.
Then closing app from recents.
Then watching logs for my broadcast to be received.
Keeping in mind this was done with a signed apk.
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MILLISECOND, 5000);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
I would also keep in mind what Pankaj Kumar said about the restarting alarms on boot. That is the one place you need to cover yourself, because AlarmManager does clear all alarms on device restart.
We need to enable our app in autostart manager in app manager, some handsets like Vivo v5,
In Vivo v5, we can find out this menu in
iManager > App Manager > Auto Start Manager > Enable our app here.
Then your alarm / alarm manager will trigger alarm if the app is killed or closed.

Sleeping service with contentobserver

I've created a service (S) which hosts a ContentObserver (C).
C keeps track of some data and logs changes made to a database (D).
S also has a timer which dumps the content of D to a file on timed intervals.
I have my service running on my phone and all seems fine for a while (a couple of hours or so). Then suddenly it stops recording changes made to the data and stops dumping to a file.
I tried changing the data (provoking onChange on C) and browsing around in the phone to keep the device awake but nothing happened - service stil inactive.
S is still running and when I connect to my laptop I see no errors in the log.
My guess is that the service is sleeping (gets no CPU slice). I've read a bit about a wakelock but I don't want to force the phone to be awake all the time. ().
An alternative is to create a repeated alarm which carry out the work of C every T minutes (polling).
I like the idea with ContentObservers more because the user can alter (especially delete) data between the updates. With a ContentObserver I will get ALL changes, not just snapshots in timed intervals.
Any ideas on how I can wake my service on a regularbasis?
Best regards
Frederik
You can use the AlarmManager to broadcast a pending intent at a set interval. If you use the RTC_WAKEUP type, it will even get sent if the device is asleep.
Intent intent = new Intent("android.intent.action.DO_SOMETHING");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(Service.ALARM_SERVICE);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), 60*60*1000, pendingIntent);
Then simply register a broadcast receiver for the intent and do what you must do.
Note that broadcast receivers timeout after aprox. 10 sec, so if you got a long task, use a service

Categories

Resources