Alarms on Android - Notes from a Stroll through the Android Source

I need to implement a simple alarm as part of an Android app.  The app needs to set a time for the alarm to go off - say twenty minutes from now - and in twenty minutes the app needs to be informed of this so it can play a tone or whatever.  The internets and stackoverflow contain a scattering of approaches.  Hasn't this been done in exactly the way I need already?

I remember reading somewhere that in chess, after the first few moves, the chances are that the players are in a game that no one has ever been to before.  Just a few moves.  As a species, we are poor estimators of the magnitude of possibilities for "branching" processes.  I guess it hasn't been particularly important so far for survival purposes.  And maybe that is what is at play every time I want to do something that seems simple, that seems to have been done a million times by someone, but there's something just unique enough about my scenario that brings unexpected work to "adjust" an existing solution, or forces an entirely new solution altogether.  Simple apps that are no more than "text boxes over databases" seem to be a similar thing.  Haven't we completely solved that problem?  The number of frameworks devoted to this mundane but practical need is staggering.  And the current javascript boom (which I welcome and am enjoying) is opening up whole new pastures of purported panacea for this in that realm.  And maybe the universe of "simple" app needs - even for text boxes over databases - is even more complicated than chess.

Anyway.

As for a simple alarm, I ended up just going to the source: the AlarmClock implementation in the Android source itself.   And the purpose of this note is to summarize some of the things you've got to deal with when implementing an alarm on Android, some of which I had not known about until a stroll through the Android code itself, the 2.3 version of which is available on github here: https://github.com/android/platform_packages_apps_alarmclock.  You can get the latest Android source at http://source.android.com/source/downloading.html, but it requires a few extra steps (I've been waiting more than an hour for the source to be completely downloaded with their "repo" tool - and will update my notes here if anything noteworthy has changed in the their AlarmClock implementation).

This particular note is not a code-snippet post, but a more general summarization for what on the surface seems a simple thing to do, and the mildly surprising degree one has to go so that the user has a smooth and uneventful experience.


Basic Idea of the Implementation

The basic idea in implementing any kind of solid alarm is to use the android.app.AlarmManager to set the alarm, which will call your custom android.content.BroadcastReceiver at the desired time.  The system will "wake up" your Broadcast receiver for this, which is nice.  Your app itself does not need to be running.  I will refer to this particular BroadcastReceiver as the "Alarm Receiver".

You probably shouldn't put the Alarm Receiver in an activity of your app

It is possible to define a BroadcastReceiver in one of your activities and register it there.  You can even use the AlarmManager to have this BroadcastReceiver called at some point in the future.  This works as long as  your activity is running.  As soon as your activity goes away, however, any receivers defined in it have to go away, too.  In fact, if you don't unregister a receiver, you'll get an error about IntentReceiverLeaked.  And if you have to unregister the receiver for the alarm when the activity's onDestroy is called, this means that your app itself won't be woken up when the alarm goes off.  Wonderful.

The Alarm Receiver only has a short window to do its work

Your Alarm Receiver has only a short time do its work before the system will kill it.  So this means you need to set up other things to happen right away, or deal with wakelocks.  I don't know what the exact time limit is.  In the relevant Android AlarmClock source, they first get a wakelock (via static methods of their AlarmAlertWakeLock class) before doing  some transactions against a content provider, and a number of other things.

What is the state of the KeyGuard?

The Android AlarmClock shows different activities based on whether the keyboard is in restricted input mode or not (it uses a full screen activity if it is).  This can be the case when screen is locked.  The Android folks uses a special full screen activity in this case.

When the user is to be notified that alarm has gone off, start your "Alert Activity" properly

In the Android source, when they launch the UI activity, they make sure and use Intent.FLAG_ACTIVITY_NO_USER_ACTION. Per the Android docs:


Typically, an activity can rely on that callback to indicate that an explicit user action has caused their activity to be moved out of the foreground. The callback marks an appropriate point in the activity's lifecycle for it to dismiss any notifications that it intends to display "until the user has seen them," such as a blinking LED.
If an activity is ever started via any non-user-driven events such as phone-call receipt or an alarm handler, this flag should be passed to Context.startActivity, ensuring that the pausing activity does not think the user has acknowledged its notification.

So, this is one more scenario that must be dealt with.

Who plays the alarm?

All you want to do is play the alarm, right?  Shouldn't the activity do it?  Well, that's not what the Android folks ultimately decided to do.  In fact, they play the alarm (and optionally vibrate) in a service so that it can continue to play if another activity overrides the AlarmAlert dialog/screen.

What if the user is on the phone when the alarm goes off?

The user might be on the phone when the alarm goes off, so you've got to decide what you're gonna do.  You want the alarm to still be active, but not disrupt the ongoing call.  The Android folks play the a special "in call alarm" (R.raw.in_call_alarm), and at a lower volume: a "volume suggested by [the] media team for in-call alarms".

What if the ringtone/alarm file is on the SD card, which is busy?

I guess this can happen.  Basically, if playing the alarm with the MediaPlayer fails, they try to play the "fallback ringtone", which is wrapped up in the Android source at com.android.internal.R.raw.fallbackring... and don't forget to reset the MediaPlayer first before doing this.

How long do you play the alarm if the user doesn't stop it?

You've got to deal with this situation.  The Android folks use a hard-coded ten minutes.

What if the device is rebooted before the alarm goes off?

The Android AlarmClock stores all of the alarms in an sqlite database, and these are checked after the device reboots by the AlarmInitReceiver, which is registered to be notified when the boot process is completed.  You will need to decide what you're gonna do.

What if the screen is locked between time that the alarm is fired but before the default activity is shown has finished handling user input?

The Android AlarmClock specifically treats the case where

  • the alarm has gone off
  • the screen is not locked
  • the AlarmAlert activity has been created (this is a dialog), and then
  • the screen is locked
In this case, they explicitly call the full-screen version (which is a superclass of AlarmAlert) and kill the one that started first.


There may be some cases missed above - my own little walk in the forest will reveal as much - but the point is that it is perhaps more involved than one might think to have something as boring as an alarm clock work properly.

No comments:

Post a Comment

Popular Posts