AlarmManager not waking phone up in Air Mode (onReceive delayed) - android

Problem is that onReceive method of BroadcastReceiver usually "called" by AlarmManager is delayed until device is awoken by the user.
This has never happened to me, only information I have was is from the report sent by user. In the log I saw that in the first case onReceive method call was delayed by almost 2 hours and in the second one by about 20 minutes. In both situations alarm (and onReceive) has started just after the phone was awoken by the user.
Problem has occured twice in two consecutive days and user states it has never happened before. Only distinctive change in phone's settings was that Air Mode was enabled.
My code:
Alarm is set like:
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_UPDATE_CURRENT);
am.set(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
Logger.log("posting alarm " + id + " for " + formatTime(timeInMillis);
Broadcast Receiver's onReceiveMethod:
#Override
public void onReceive(Context context, Intent intent) {
Logger.initialize(context, "AlarmReceiver");
...
}
Logs received from the user:
481. 20/05 13:00:04 v89: posting alarm 4 for 7:0 (in 17:59)
486. 21/05 08:58:00 v89: logger initialized again from AlarmReceiver
536. 21/05 09:04:54 v89: posting alarm 4 for 7:0 (in 21:55)
541. 22/05 07:22:24 v89: logger initialized again from AlarmReceiver
Is it possible for Air Mode to block phone's awakening somehow? Can I prevent it? Or maybe it is something completely different? Any help is welcomed.
Device is Samsung Galaxy SIII (GT-I9305) with Android 4.1.2
Edit:
Just in case that delay could be somehow caused by the Logger, here's its code. mHandler is created with use of HandlerThread, so I believe it can't block onReceive, right?
public synchronized static void initialize(Context context, String src) {
if (mInstance == null) {//wasn't null
...
} else {
Logger.log("logger initialized again from " + src);
}
}
public synchronized static void log(final String text) {
Log.d(TAG, text);
if (mInstance != null && mInstance.mLoggingEnabled) {
mInstance.mHandler.post(new Runnable() {
#Override
public void run() {
//some database operations
}
});
}
}

If you want to schedule your tasks with the specified interval don't use flags AlarmManager.RTC and AlarmManager.RTC_WAKEUP with method alarm.setRepeating(...). Because in this case alarm will be bounded to the device's real time clock. So changing the system time may cause alarm to misbehave. You must use flags AlarmManager.ELAPSED_REALTIME or AlarmManager.ELAPSED_REALTIME_WAKEUP. In this case SystemClock.elapsedRealtime() will serve as a basis for scheduling an alarm.
The code will look like:
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + checkIntervalMillis, checkIntervalMillis, pendingIntent);
If you want your long running task to be executed when device in the sleep mode I recommend to use WakefulIntentService library by CommonsWare: https://github.com/commonsguy/cwac-wakeful

Related

Alarm intent entering rapid infinite loop

I'm trying to code a block of code that has to wake up a few times a day and notify a server. I'm trying to use an alarm intent and a broadcast receiver but the receiver is being rapidly triggered infinitely and I can't seem to stop this.
All of my code sits in one file. The process flow is simple.
Wake up on boot, check if we should communicate, attempt to communicate else set up one of two waiting conditions, activate alarm.
Wake up on alarm, attempt to communicate, re-activate alarm if necessary, otherwise kill it.
When I build and deploy this apk on my device the following process flow happens:
reboot
receiver receives boot intent just fine
alarm gets scheduled
alarm intent gets triggered after 80 seconds as intended
then after the next 80 seconds,
then log-cat shows the broadcast receiver being triggered very rapidly. Several times a second as if its being spammed.
I am completely baffled at why its behaving like this
private PendingIntent pendingIntent;
/**
* Receive a signal, in our case, the device has booted up
* #param context The Context in which the receiver is running.
* #param intent The Intent being received.
*/
#SuppressLint("UnsafeProtectedBroadcastReceiver")
#Override
public void onReceive(Context context, Intent intent) {
Log.d("autostart", "broadcast received");
if(intent.getAction()==null)return;
Intent alarmIntent = new Intent(context, autostart.class);
alarmIntent.setAction("device.activation.alarm");
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);//cancel flag seems to be ignored
cancel(context);//cancel command seems to be ignored
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
// Set the alarm here.
Log.d("autostart","We have booted");
RegisterActivation registerActivation = new RegisterActivation(context);
if(registerActivation.AlreadyActivated())
return;//no need
if(registerActivation.ActivationPending()){
//perform regular activation
return;
}
if(registerActivation.canComplete()){
boolean success = registerActivation.sendActivation();//talk to server
if(success) {
registerActivation.markCompleted();
cancel(context);
}
else {
registerActivation.markFileWaiting();
startPending(context);
}
return;
}
if(registerActivation.shouldWait()){//if can complete fails, then shouldWait will immediately return true
Log.d("autostart", "waiting");
registerActivation.markFileSimWait();
startWait(context);
return;
}
}
if(intent.getAction().equals("device.activation.alarm")){
Log.d("autostart","alarm triggered");
cancel(context);
RegisterActivation registerActivation = new RegisterActivation(context);
if(registerActivation.AlreadyActivated()){//for now always false
cancel(context);
return;
}
if(registerActivation.ActivationPending()){//for now always false
//same as before
return;
}
if(registerActivation.canComplete()){//glitch happens here
if(registerActivation.sendActivation()){
registerActivation.markCompleted();
cancel(context);
}else{
registerActivation.markFileWaiting();
startPending(context);//this immediatly triggers the broadcast recieve
}
return;
}
if(registerActivation.shouldWait()){
registerActivation.markFileSimWait();
startWait(context);
}
}
}
public void startPending(Context context) {
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
int interval = 80000;//will later become 4 hours
manager.set(AlarmManager.RTC_WAKEUP,interval,pendingIntent);
Log.d("autostart", "alarm activated");
}
public void startWait(Context context) {//same function but different time interval
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
int interval = 90000;// will later become 12 hours
manager.set(AlarmManager.RTC_WAKEUP, interval, pendingIntent);
}
public void cancel(Context context) {
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.cancel(pendingIntent);
}
thank you to Mike M.
https://stackoverflow.com/users/2850651/mike-m
I'd mark your comment as the answer but you only commented
I went from
manager.set(AlarmManager.RTC_WAKEUP,interval,pendingIntent);
to:
manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+interval, interval, pendingIntent);
I had to add the interval to the intervalMillis paramater.
And as tested, the manager.set(...) function is really not responding all too well to what I want it to do so I just have to trigger a cancel at the start.
Thanks again Mike :)

Notification Listener Service does not work after app is crashed

I have a problem on my app and I want to report this bug.
I develope the app which can crawls notifications using NotificationListenerService.
It works well.
But NotificationListenerService class has the problem I think.
Because, If the app is crashed, app can't crawl the notification at all,
UNTIL the phone reboots.
Is anyone who can solve this problem??
Please help me.
The bug is very clear!! But It is not easy to find the solution ....
If do you have already permissions then:
In your service class or another service/activity you can switch the "component hability" to listen notifications:
public void tryReconnectService() {
toggleNotificationListenerService();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
ComponentName componentName =
new ComponentName(getApplicationContext(), NotificationReaderV2Service.class);
//It say to Notification Manager RE-BIND your service to listen notifications again inmediatelly!
requestRebind(componentName);
}
}
/**
* Try deactivate/activate your component service
*/
private void toggleNotificationListenerService() {
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(this, NotificationReaderV2Service.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
Your notification listener, is a SERVICE, it can be killed by System, you can do your service as FOREGROUND to drastically decrease the probability that the system will kill your service.
#Override
public void onListenerConnected() {
super.onListenerConnected();
Log.d(TAG, "Service Reader Connected");
Notification not = createNotification();
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.notify(NOTIFICATION_ID, not);
}
startForeground(NOTIFICATION_ID, not);
//Alarm to auto - send Intents to Service to reconnect, you can ommit next line.
alarmIt();
}
If do you like so more "safe", you can to programming not-friendly battery alarms, try to use inexact alarms please, the user's battery will be happy:
private void alarmIt() {
Log.d(TAG, "ALARM PROGRAMMATED at"+HotUtils.formatDate(new Date()));
Calendar now = Calendar.getInstance();
now.setTimeInMillis(System.currentTimeMillis());
now.set(Calendar.MINUTE, now.get(Calendar.MINUTE) + 1);
Intent intent = new Intent(this, NotificationReaderV2Service.class);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
intent.setAction(REBIND_ACTION);
PendingIntent pendingIntent = PendingIntent.getService(this, 0,
intent, 0);
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//The alarms that are repeated are inaccurate by default, use RTC_WAKE_UP at your convenience.
//Alarm will fire every minute, CHANGE THIS iF DO YOU CAN, you can't use less than 1 minute to repeating alarms.
manager.setRepeating(AlarmManager.RTC_WAKEUP, now.getTimeInMillis(), 1000 * 60 * 1, pendingIntent);
}
and next read the Intent to reconnect service binding:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Notification service onStartCommandCalled");
if (intent!=null && !HotUtils.isNullOrEmpty(intent.getAction()) && intent.getAction().equals(REBIND_ACTION)){
Log.d(TAG, "TRYING REBIND SERVICE at "+HotUtils.formatDate(new Date()));
tryReconnectService();//switch on/off component and rebind
}
//START_STICKY to order the system to restart your service as soon as possible when it was killed.
return START_STICKY;
}
Keep in mind that doing all these steps you can sure that your service will be killed anyway by the system but this code will restart the service and make it harder to kill it.
Maybe, you should consider using PARTIAL_WAKE_LOCK with your service and execute it in a process independently (:remote) if you want even more certainty (Maybe this is useless)
I would like to add a common error that is often followed, NEVER override the onBind and onUnbind method or overwrite the INTENT ACTION.
This will cause your service to not be connected and never run onListenerConnected
Keep the Intent as it is, in most cases you do not need to edit it.
I see exactly the same on this. The only "solution" I've found was to have the notification listener running in a separate process. Then if the rest of the app crashes it doesn't stop the listener. So it's only then specifically notification listener service crashes that require the reboot.
Seems a terrible and over complicated solution though.
I had the same problem. Here are few things that I did and now it works wonderfully for me.
Override onStartCommand, call super and return START_STICKY;
Override onNotificationRemoved, call super and add a toast so that you know in android itself that you service has not died yet whenever you swipe a notification.
Exclude your app from Battery saving list (Settings-> Battery-> Power Saving Exclusion)
Post this the service never dies even after the main app's crash. I dont need to reboot now to restart it.

AlarmManager not waking from deep sleep

The code for setting the alarm is:
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), interval, getPendingIntent());
I tried also
mAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, cal.getTimeInMillis(), interval, getPendingIntent());
Where
interval = type long of ~60000
cal = Calendar.getInstance() then modified DAY, HOUR etc.
I've noticed that my alarm doesn't trigger if phone in deep sleep at all.
I also checked command
adb shell dumpsys alarm
And alarm is written in and should start at time selected. Where's the catch then that alarm sometimes does, sometimes doesn't trigger?
BroadcastReceiver works as it should since i'm logging other possible actions too:
public class Receiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
// some things here...
}
}
AndroidManifest.xml includes permissions for:
android.alarm.permission.SET_ALARM
This is really confusing and Serious issue for my app...

Android Service won't run from AlarmManager

I have a problem with running a service from Alarm manager.
I am building an app that notifies the owner on the namedays of his facebook friends. It all works nicely, but the notification won't show up.
I've set up an AlarmTask that creates the PendingIntent and sets the AlarmManager, like this:
public void run() {
// Request to start are service when the alarm date is upon us
Intent intent = new Intent(context, NotifyService.class);
intent.putExtra(NotifyService.INTENT_NOTIFY, true);
intent.putExtra("notifyID", ID);
PendingIntent pendingIntent = PendingIntent.getService(context, ID, intent, 0);
// Sets an alarm - note this alarm will be lost if the phone is turned off and on again
am.set(AlarmManager.RTC_WAKEUP, date.getTimeInMillis(), pendingIntent);
}
The ID is specific for every nameday.
Now in my NotifyService, I have set up these:
#Override
public void onCreate() {
super.onCreate();
System.out.println("NOTIFICATION SERVICE onCreate()");
mNM = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
System.out.println("INTENT RECIEVED: " + intent + " " + flags + " " + startId);
// If this service was started by out AlarmTask intent then we want to show our notification
if(intent.getBooleanExtra(INTENT_NOTIFY, false)){
int ID = intent.getIntExtra("notifyID", -1);
showNotification(ID);
}
// We don't care if this service is stopped as we have already delivered our notification
return START_STICKY;
}
Both the methods are executed once when I start the app, but when the notification should come up, nothing happens.
Is there a way to test if the AlarmManager really executes the PendingIntent?
Should I rather use IntentService? Why/how?
Thanks a lot.
I tried to change it to BroadcastReciever, looking like this:
public class NotificationBroadcastReciever extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
System.out.println("BROADCAST RECIEVED");
}
}
The AlarmTask bit is changed to this:
Intent intent = new Intent("NotificationBroadcast");
intent.putExtra(NotifyService.INTENT_NOTIFY, true);
intent.putExtra("notifyID", ID);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context.getApplicationContext(), ID, intent, 0);
System.out.println("date for notification: " + date.get(Calendar.DAY_OF_MONTH) + "." + date.get(Calendar.MONTH) + "." + date.get(Calendar.YEAR));
System.out.println("epoch time in milils: " + date.getTimeInMillis());
// Sets an alarm - note this alarm will be lost if the phone is turned off and on again
am.set(AlarmManager.RTC_WAKEUP, date.getTimeInMillis(), pendingIntent);
and relevant manifest part looks like this:
<receiver
android:name="cz.cvut.kubispe2.jmeniny.NotificationBroadcastReciever"
android:exported="false">
<intent-filter>
<action android:name="NotificationBroadcast" />
</intent-filter>
</receiver>
I checked if the date that is to be set is equal to the epoch time and it is, but still, the onRecieve method is never called.
Both the methods are executed once when I start the app, but when the notification should come up, nothing happens.
_WAKEUP alarms are only guaranteed to wake up the device if they route to a BroadcastReceiver, not a Service. So long as what you are doing is very short (1-2 milliseconds), you can safely do that work in onReceive() of a BroadcastReceiver. The work you are presently doing in your Service would qualify.
Beyond that, use adb shell dumpsys alarm to confirm that your alarm is scheduled for when you think it is.
Should I rather use IntentService?
It would certainly be a better option than a regular Service, which you are leaking in your current implementation. However, the _WAKEUP limitation still holds, which is why I wrote WakefulIntentService, to help bridge the gap. Again, though, with the current limited work you are doing, just using a BroadcastReceiver should suffice.
try using application context.
PendingIntent pendingIntent = PendingIntent.getService(context.getApplicationContext(), ID, intent, 0);
And work with android logs. Then you will see if it's running in your console
Seems like I finally resolved it, I used the broadcast reciever, and found out where the error was - Calendar takes the month argument from 0 to 11, instead of 1-12, which I thought, since all the other arguments are dealt with normally. So I was just putting up a notification for the end of May, instead of today, when testing.
Anyway, thank you all for help, it was very appreciated.

Alarm Manager Reliability

I've been struggling with this problem for days. I've also checked the documentation and several topics but didn't find any solution / explanation.
I am testing my application on LG p500 but I did a few test on Droid too and I get the same result.
My application uses AlarmHandler to schedule alarm. The application works correctly on the emulator and also on the device until the device has enough free memory.
When I start several other applications on the device and the memory is low the alarm will not fire anymore. As soon as I stop the "other" application the alarm works fine again.
Let me report the test and the result.
I set an alarm on my application 10 minute later.
I start several application (browser, google map, gmail, K9Mail,....)
I start the catlog to see the log of my application
Wait 15 minute without working on the phone
After 10 minutes the alarm should be fired but nothing happen until I wakeup my phone pressing a button
When I wake-up my phone the alarm immediatly fires and all the notificatin happen.
I stop the "other" application I previously started (browser, google map,...)
Set again an alarm 10 minute later
I start the catlog to see the log of my application
Wait without working on the phone
10 minutes later the alarm fires and I get notified.
I did this test several time and I get the same result.
Then I tried to set an alarm using the "Catch" application I previously downloaded from the market and I get the same behaviour so it looks like this is not a problem of my application.
Looking at the log of my application I do not see any error / exception but it looks like that when the system is low on memory something happen and the broadcast receiver does not start until the phone is waked up throught the keyboard. As soon as I wake-up the phone the receiver start and all the notification happen.
Here the code I used:
The Receiver:
public class NotificationReceiver extends BroadcastReceiver
{
public static final String LOG_TAG = "YAAS - Notification Receiver";
#Override
public void onReceive(Context context, Intent intent)
{
ScheduleActivityService.acquireStaticLock(context);
Log.i(LOG_TAG, "Received alarm - id: " + intent.getIntExtra("id", -1));
Intent intent2 = new Intent(context, ScheduleActivityService.class);
intent2.putExtra("id", intent.getIntExtra("id", -1));
context.startService(intent2);
}
}
The Service
public class ScheduleActivityService extends Service
{
public static final String LOCK_NAME_STATIC="it.hp.yaas.AppService.Static";
public static final String LOG_TAG = "YAAS - ActivityService";
private static PowerManager.WakeLock lockStatic = null;
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder
{
public ScheduleActivityService getService()
{
return ScheduleActivityService.this;
}
}
#Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
public static void acquireStaticLock(Context context) {
getLock(context).acquire();
}
synchronized private static PowerManager.WakeLock getLock(Context context)
{
if (lockStatic == null)
{
PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOCK_NAME_STATIC);
lockStatic.setReferenceCounted(true);
}
return(lockStatic);
}
/**
* This method is called when an alarm fires that is its alarm time is reached.
* The system assume that the alarm fired match the alarm time of the first
* activity.
* #param intent intent fired
* #param flag
* #param startId
*/
#Override
public int onStartCommand(Intent intent, int flag, int startId)
{
super.onStartCommand(intent, flag, startId);
try {
Log.i(LOG_TAG, "Alarm fired: " + startId + " - id: " + intent.getIntExtra("id", -1));
AlarmHandler.getInstance().onAlarmFired(intent.getIntExtra("id", -1));
}
finally { getLock(this).release(); }
return START_STICKY;
}
#Override
public void onDestroy()
{
super.onDestroy();
Log.i(LOG_TAG, "Destroy");
}
}
An piece of code from AlarmHandler, the routine called to schedule the alarm:
public synchronized void onAlarmFired(int alarmId)
{
scheduledAlarmId = -1;
Alarm alarmFired = pop();
if (alarmFired == null) return;
Log.i(LOG_TAG, "onAlarmFired (Alarm: " + alarmFired + ") at (time: " + Utilities.convertDate(new Date(), "HH:mm:ss") + ")");
notifyAlarmListener(alarmFired);
if (alarmFired.reschedule(null) != null) add(alarmFired);
Alarm alarm = peek();
if (alarm != null && scheduledAlarmId != alarm.getId()) scheduleEvent(alarm);
}
/**
* Schedule an alarm through AlarmManager that trigger next activity notification
* #param alarm alarm to be scheduled
*/
private void scheduleEvent(Alarm alarm)
{
Log.i(LOG_TAG, "scheduleEvent - (Alarm: " + alarm + ")");
Intent intent = new Intent(context, NotificationReceiver.class);
intent.putExtra("id", alarm.getId());
// In reality, you would want to have a static variable for the request code instead of 192837
PendingIntent sender = PendingIntent.getBroadcast(context, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, alarm.getTime().getTime(), sender);
scheduledAlarmId = alarm.getId();
}
And finally this is a piece of Manifest file:
<activity android:name=".ListActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".EditActivity"/>
<activity android:name=".SettingsActivity"/>
<service android:name="ScheduleActivityService"
android:label="YAAS Service"/>
<receiver android:name="NotificationReceiver" />
Are you sure your process doesn't get killed when you start all those applications? If it does, the alarms you set will die with it. It's not exactly clear who and when schedules the alarm in your code, but if it's the service, since it's sticky, it will eventually gets re-started, and you will get an alarm at some point (when you wake the device).
An easy way to check what alarms are registered at different points of your testing:
# adb shell dumpsys alarm
My code is very similar to yours on an alarm app that I wrote and use regularly. I haven't been able to reproduce the problem that you describe. I can't seem to get my phone to a state of extremely low memory. I opened every app I have installed and still have 260M free on my HTC Rezound.
As a safeguard in my app I used alarmmanager.setRepeating() instead of .set(). I set the repeat interval to 20 seconds. I passed the alarm ID as an intent extra just as you have. When my service starts it immediately cancels the pending intent using the alarm ID. My logic here is that if for any reason my alarm fails it will continue to try every 20 seconds until it succeeds.
In your code is AlarmManager.set(), which is not guaranteed to fire at the time you specify. It may fire 30 minutes or even 6 hours later, which I've seen happen on devices like the Xiaomi POCO F1.
Instead use AlarmManager.setExact() to schedule your code to run at a specific time.
Android 12 introduces an exact alarms permisison. If you don't want to deal with that, you can instead use AlarmManager.setWindow() with a small window like 15 minutes.

Categories

Resources