How are past alarms delivered by the AlarmManager? - android

I've got a question about setting alarms in the AlarmManager. I found something in the docs I didn't understand (see below). I'd like to set 10 alarms which trigger the ringer mode alternately silent and normal, all with a different trigger time. Now the device goes asleep, and becomes active again after all 10 alarms are outdated. Does the AlarmManager then immediately broadcast an alarm? Would it be only the 10th (what about the ringer mode)?
Alarm intents are delivered with a
data extra of type int called
Intent.EXTRA_ALARM_COUNT that
indicates how many past alarm events
have been accumulated into this intent
broadcast. Recurring alarms that have
gone undelivered because the phone was
asleep may have a count greater than
one when delivered.

One thing that is most unknown (mainly because the Android documentation tells that its "not used at the moment") is that the PendingIntent will not be reused if the requestCode differs. So instead create the PI with an request code of 0:
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
You could implement a counter and do someting like:
PendingIntent pendingIntent = PendingIntent.getService(context, counter, intent, 0);
I know that this will work for SMS delivered/sent notifications PendingIntents, where you have the same problem: If the PendingIntent is reused and you have more than 1 outstanding notification, you will not know for which SMS it was.
But chances are good that this will also work for outstanding alarm PendingIntent.
Hope this helps.

From what I understand, when scheduling an alarm with an alarm manager, you have to provide a PendingIntent instance.
There are two Types of alarms ones that will wake up and do work even if the phone is asleep or locked and ones that will not.
Also, If you were to schedule 10 things at one time The AlarmManager will replace the existing Scheduled Pending intent with the new one, unless you were giving it different intent actions. When I use Alarms I have always used a sqlite database to queue up jobs that I wanted to execute on some schedule. From there I would schedule one alarm at a time, because they all executed the same Intent when the buzzer went ding.
The EXTRA_ALARM_COUNT extra would come into play if you had a reoccurring alarm scheduled and it went off multiple times when the users device was asleep. When the phone wakes up it will replay anything that it has queued up in the past. In this case your pending intent will fire off and have the value of how many times your Alarm was skipped because it was constructed with RTC or ELAPSED_REALTIME as the type when calling the set method.
Here is a sample of how I usually interact with the AlarmManger
protected void scheduleNext(Context context) {
AlarmManager alarmManager = getAlarmManager();
Intent intent = new Intent(MyIntent.ACTION_DO_WORK);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
String where = Queue.SCHEDULED_DATE + "= (select min(" + Queue.SCHEDULED_DATE + ") from queue where " + Queue.COMPLETED_DATE + " is null)";
Cursor cursor = context.getContentResolver().query(Queue.CONTENT_URI, Queue.PROJECTION, where, null, null);
if (cursor.moveToFirst()) {
int id = cursor.getInt(cursor.getColumnIndex(Queue._ID));
long when = cursor.getLong(cursor.getColumnIndex(Queue.SCHEDULED_DATE));
alarmManager.set(AlarmManager.RTC_WAKEUP, when, pendingIntent);
}
cursor.close();
}

Related

Android: AlarmManager setExact() not firing event in Android 8

I have an Android implementation in which I have to schedule notifications in the long term, I create the PendingIntent and use AlarmManager#setExact() method to do it:
Intent intent = new Intent(mContext, BirthdayReceiver.class);
intent.putExtra(BirthdayReceiver.INTENT_WHO, contact.getName());
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, id, intent, 0);
mAlarmManager.setExact(
AlarmManager.RTC,
notificationCalendar.getTimeInMillis(),
pendingIntent);
I've checked the time passed to setExact() method, if it's a short time (minutes, few hours) then the notification appears correctly.
But if I set a notification to fire, let's say the next day at 12:00 it doesn't work.
I am using an Android 8 device to test it.
Any help?
Seems like flag is not specified.
Could you try it with FLAG_UPDATE_CURRENT .
PendingIntent pendingIntent = PendingIntent.getBroadcast(
mContext, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
Also, in order to confirm if your PendingIntent is still available in the system, you can use FLAG_NO_CREATE.
PendingIntent broadcastIntent = PendingIntent.getBroadcast(mContext, id,
intent, PendingIntent.FLAG_NO_CREATE);
if broadcastIntent returns null then the PendingIntent is not avaialble (possibly cancelled) in the system.
As per the documentation of setexact
This method is like set(int, long, PendingIntent), but does not permit
the OS to adjust the delivery time. The alarm will be delivered as
nearly as possible to the requested trigger time.
So you may not expect the alarm to be triggered immediately.
Note: only alarms for which there is a strong demand for exact-time
delivery (such as an alarm clock ringing at the requested time) should
be scheduled as exact. Applications are strongly discouraged from
using exact alarms unnecessarily as they reduce the OS's ability to
minimize battery use.
So you can really consider whether you really need to call this API or not

Android AlarmManager.set(...): notification never received when battery low

I'm attempting to use AlarmManager to schedule a delayed check in my app. (Specifically, N minutes after a user approaches a location, I want to check whether they're still there, and if so send them a notification.)
I've implemented this by checking to see whether they've entered the region in my location update receiver and, if they have, scheduling like so:
Intent geofenceIntent = new Intent(context, GeofenceReceiver.class)
// ...intent contents not important...
PendingIntent pi = PendingIntent.getBroadcast(context, 0, geofenceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Calendar c = Calendar.getInstance();
c.add(Calendar.SECOND, getGeofenceDelaySeconds());
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(), pi);
Log.v("Scheduling notification check for "+c.getTime());
When the battery level is high (say, 50%), everything works like a charm. But when it's low (say, 10%), I get location updates, they schedule the alarm seemingly-successfully, but that alarm never actually triggers!
What gives? Does Android stop sending certain types of updates when it's trying to conserve power? How can I work around this (short of actually keeping my app active for the duration of the delay, which causes obvious issues with battery life)?
It turns out that this is related to the use of the real-time clock.
Although I could not find the documentation it quotes (it's not in AlarmManager), this StackOverflow answer suggests that AlarmManager.RTC_WAKEUP alarms do not trigger if the phone is in power-saving mode. AlarmManager.ELAPSED_REALTIME_WAKEUP alarms do not seem to suffer this problem, so I was able to fix the issue by switching to:
Intent geofenceIntent = new Intent(context, GeofenceReceiver.class)
// ...intent contents not important...
PendingIntent pi = PendingIntent.getBroadcast(context, 0, geofenceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
long millis = SystemClock.elapsedRealtime() + 1000 * getGeofenceDelaySeconds();
AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, millis, pi);

Android: Resetting repeating AlarmManager from within called service

I've set a repeating alarm on a service and decided that it's most convenient to reset the alarm from within the called service. The reason is that the service already has code to check if it's within a user-defined schedule (time range). When it's outside the time range, it resets the alarm to start at the future time selected by the user. Maybe I'm approaching this wrong but I'll put this question out there and see what you think.
An activity kicks off the service by creating a repeating alarm:
//Activity
Calendar cal = Calendar.getInstance();
Intent intent = new Intent(getApplicationContext(), MyService.class);
intent.setData(Uri.parse("MyService://identifier"));
PendingIntent pIntent = PendingIntent.getService(getApplicationContext(), 0, intent, 0);
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),
intervalInMins*60000, pIntent);
The service has something like this:
//Service
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Uri Action = intent.getData();
try {
if (Action.equals(Uri.parse("MyService://identifier"))) {
//Simplifying the code here: CalculatedOffset is determined from the current time
//and scheduled start time. intervalInMins is read from settings.
if (!WithinSchedule()) {
Calendar cal = Calendar.getInstance();
PendingIntent pIntent = PendingIntent.getService(getApplicationContext(), 0, intent, 0);
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis() + CalculatedOffset,
intervalInMins*60000, pIntent);
}
}
} catch (NullPointerException np) {
np.printStackTrace();
}
return Service.START_REDELIVER_INTENT;
}
I was hoping to re-use the intent to reset the repeating alarm. With this new code, I'm seeing multiple alarms stack up firing rapidly in succession around when the start time hits. It should not spaz out like that, but should fire at regular intervals as it did before the scheduling reset. I need to catch it in the debugger but haven't been able to determine the exact conditions yet. Is my understanding of alarms completely off base here? Is there a better way to do this?
Addendum: A wrinkle in this is that I'm using RootTools to gain superuser privileges in order to work around Android 4.2's airplane mode. This hasn't been a problem before the scheduling, but I'm suspicious whether su is blocking for a long time while the alarms stack up.
Re-using the intent inside the service that receives the alarm does work. I've switched from using a Repeating Alarm to a single-shot alarm which gets re-armed every time the service is called. Unfortunately this didn't fix the problem of the alarms stacking. The culprit is definitely su blocking. It may be RootTools or su itself. I need to update the library from 2.6 to 3.x and see if that makes any difference.

Schedule AlarmManager event to happen a few days from now

I have been playing with the AlarmManagerin order to schedule an IntentServiceto do X task after a specific period of time. So far it works great, here' how I do it:
public static void scheduleNextRefresh (final Context context, long msFromNow) {
Constants.logMessage("Scheduling fetcher alarm to happen within: " + msFromNow/(1000*60) + " minutes");
Intent intent = new Intent(context, AlarmReceiver.class);
intent.putExtra(AlarmReceiver.EXTRA_ACTION, AlarmReceiver.FETCH_NEWS);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (msFromNow != -1) {
Constants.logMessage("Alarm set");
alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime() + msFromNow, pendingIntent);
}
}
That works well, so far; however, I normally only use small intervals such as 30 minutes, or a few hours.
Now I want to schedule an action to happen a few days in the future, and I'm curious as to whether or not it will work appropriately with the AlarmManager, or if it's simpler to use another tool to send a PendingIntent at X time.
Being that the smallest interval would be 3 days, and there may be a few reboots (I know I myself reboot my phone at least once a day) in the process, I'm not sure how practical the AlarmManager would be.
First, the logic of scheduling the refresh I am using is the same as posted above, the only difference is that I added some more code the the BroadcastReceiver in order to listen for android.permission.RECEIVE_BOOT_COMPLETED and re-schedule the alarms as appropriate.
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
...some other code
else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
Constants.logMessage("Re-scheduling alarms after boot completed");
SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(context);
//get the current interval we are using for cleanups
long alarmInterval = Long.valueOf(mPreferences.getString("pref_key_ccleaner_interval", "259200000"));
//get the last time in miliseconds that we set an alarm
//Whenever I schedule an alarm in the IntentService
//I store the time in ms to know when it was last scheduled
long lastAlarm = mPreferences.getLong(CacheCleaner.KEY_LAST_ALARM, 0);
if (lastAlarm == 0) {
//If no previous alarm is set, schedule it normally
CacheCleaner.scheduleNextCleanup(context, alarmInterval);
}
else {
//If there was an alarm set previously
//The difference between the alarmInterval and the amount of ms ellapsed since last alarm
//is the new time we will schedule this for
CacheCleaner.scheduleNextCleanup(context, (alarmInterval - (System.currentTimeMillis() - lastAlarm)));
}
}
I think that can do the trick, but I'm just curious as to there being a better way of scheduling events to happen several days from now, without having to worry about re-scheduling, or other events.
Any ideas?
Rescheduling alarms on RECEIVE_BOOT_COMPLETED is the method I use (and has been working for over 2 years) and doing a quick search seems to validate it. AlarmManager for Android.
Alarms are not persisted across reboots. You need to run at boot time to reschedule your alarms. This is what the built-in Clock application does, for example.
As of Android 5.0, you might want to switch to using the JobScheduler mechanisms instead. With them you don't need to handle your own boot-time execution or wakelocks or anything; you just specify the timing for when you want to run (plus any other interesting constraints like wanting to run only when the device is charging), and the OS takes care of running you at the right time.

about alarm manager in android

I have made an alarm manager to schedule some user defined events and it is working successfully
then I have made a setting screen (from preference activity) to let the user to change some setting like (days before alarm)
my question is If I schedule the event before event start date by 3 days, then the user change the days before from setting to one day only
then I schedule the event again before event start date by 1 day, is that mean the user will be notified twice
one before 3 days
one before 1 day
if that is true so how can I prevent that from happening
Thanks in Advance
Each alarm is accompanied by a PendingIntent which has a unique hash identifier. When using the same identifier, that alarm will overwrite the former as stated in the documentation:
http://developer.android.com/reference/android/app/AlarmManager.html#set(int, long, android.app.PendingIntent)
Schedule an alarm. Note: ...If there is already an alarm scheduled for the same IntentSender, it will first be canceled...
Note that PendingIntent are characterized by their parameters and identifiers.
Intent intent = new Intent(mContext, YourTarget.class);
// The hashcode works as an identifier here. By using the same, the alarm will be overwrriten
PendingIntent sender = PendingIntent.getBroadcast(mContext, hashCode, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
mAlarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
} else {
mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
}
public void cancel (PendingIntent operation)
Remove any alarms with a matching Intent. Any alarm, of any type, whose Intent
matches this one (as defined by filterEquals(Intent)),
will be canceled.
So you can call cancel on your pending intent alarmManager.cancel(myPendingIntent)
and the create a new one with a new time.
But you dont have to call cancel explicitly because as long as filterEquals returns true when comparing your new PendingIntent with the previous one then your alarm will start only 1 day before.
public boolean filterEquals (Intent other)
Determine if two intents are the same for the purposes of intent resolution
(filtering). That is, if their action, data, type, class,
and categories are the same. This does not compare any extra data
included in the intents.

Categories

Resources