Android: a webradio stream alarm
In the morning, I want to be woken up by a webradio, not by the same dummy alarm (or audio file) again and again.
Because I care about freedom and privacy, I try not to use apps from the Play Store1. I usually find everything I need from the F-Droid repository: IceCat, OpenKeychain, Password Store, OsmAnd, Plumble, VLC… There's plenty of alarm apps in the F-Droid repository, but none that offer to use an internet stream. ServeStream claims to support alarm clocks, but I couldn't get it to work.
Shortly before giving up I realised I didn't need an alarm app,
since VLC can already play webradios extremely well from a playlist. I
just need a way to "open" the playlist file with VLC at a
predetermined time. So I looked for "scheduled tasks" and found
crond. As the name implies, it will run whatever you put in
/data/crontab
. So I have to figure out how to run stuff from the
command line.
Opening a file from the CLI
(The commands below can be tested from any terminal-like app, or from
an adb shell
.)
That's the relatively easy part, Android has a am
binary that's
essentially an interface to its ActivityManager.
am start -a android.intent.action.VIEW \
-d file:///sdcard/foo.pls \
-t audio/wav \
-p org.videolan.vlc
The value of -t
isn't the right content type, but it's only needed
for VLC to figure out what it should do with the opened file (in this
case, play the audio).
If -p
is omitted, Android will prompt a "open with" popup, not what
we want in a non-interactive use case.
Okay, now that it works, we can use it to play the radio with crond, but there's still several things that can go wrong:
- Wifi and SIM Data could both be disabled;
- A network error could prevent the stream from playing;
- The volume could be too low (or worse, do not disturb mode set to mute everything).
Setting music volume from the CLI
This is where it gets tricky. There is a simple approach, that uses
input keyevent
to simulate pressing the volume-up or volume-down
buttons:
input keyevent 24 # volume-up
input keyevent 25 # volume-down
This method sucks for many reasons. First, it's quite slow (every call takes a solid 2 seconds to complete), you can't set the volume at a specific level and you can't control which volume is being changed. If no music is currently playing, these calls will change the ringtone volume. So, play the music first, at whatever volume, then fiddle with slowly changing its volume. It works, but it's messy and doesn't make for a good alarm clock.
Thankfully there is anohter, messier approach: service call
. But it
requires knowing much of the internals of whatever version of Android
you're running. Thanks to this gist, I finally got it working (on
my device, if you run a different version of Android then it will not
work!):
service call audio 3 i32 3 i32 10
Okay, so call the third function of the audio service, which
implements the IAudioService
interface, defined here for
Android 6.0.1. Let's look at the prototype:
void setStreamVolume(int streamType, int index,
int flags, String callingPackage);
First parameter is which volume we want to change. For changing the media (music) volume, it's 3 (again, on my device). Get a list by running
dumpsys audio
.Second parameter is the new volume level: 0 is mute, 15 is the loudest (may change on your device).
Third and fourth parameters are probably important as well, but the command works without them.
Putting it all together
% cat >/sdcard/alarm.sh <<EOT
#!/bin/sh
service call audio 3 i32 3 i32 10
am start -a android.intent.action.VIEW \
-d file:///sdcard/foo.pls \
-t audio/wav \
-p org.videolan.vlc
EOT
% echo "59 6 * * * sh /sdcard/alarm.sh" >/data/crontab
Because cron is fairly flexible, it's possible to setup many alarms at
different times based on, for example, the day of the week. Read
crontab(5)
for more info.
UPDATE 9/11: sometimes, the phone will randomly decide not to wake up for the alarm. This is a known issue. A workaround is to set up a silent alarm in the clock app at the same time as the cron job. It will force the phone to wake up.
Possible improvements
Restore old music volume after the alarm.
A snooze feature would be nice, but I have no idea how it could be done.
A backup alarm (in case VLC can't play the stream) would be very nice as well. This can easily be done if:
- There is a way to enable/disable alarms from the CLI (there probably is)
- There is a way to query the success of
am start
(or time of last user wakeup? how isidle time
defined in/proc/uptime
?)
Of course, the cleanest solution would be to write an app, but that's a hornet's nest I don't want to get into at the moment.
-
Yes, I know, I am still using Gmail. Sadly, self-hosting email requires a huge amount of work and maintenance time, and you still have no control over other people's SMTP servers ; email privacy is a huge can of worms. You can always use my OpenPGP public key. ↩︎