Running in the Background on KaiOS
Despite KaiOS operating on resource-constrained hardware, apps can execute tasks in the background using several approaches.
- Background Media
- Activities & System Messages
- Service Workers
- Alarm API
- Request Sync API
Background Media
The simplest way to do anything in the background is by using
Audio Channels to continue audio playback while using another app. Using the content
audio channel on an audio
or video
element, when the user presses Back/ EndCall, it will trigger your application to enter the background.
The property can be set as an HTML attribute:
1<audio mozAudioChannelType="content" />
2<video mozAudioChannelType="content" />
Or via a JavaScript property:
1const audio = document.createElement('audio');
2audio.mozAudioChannelType = 'content';
That’s it! When the user exits your app, audio will continue to play in the background.
Note: due to resource constraints, devices that are experiencing memory pressure may close your app, stopping audio playback. It’s best not to use video for background audio playback, since that requires significantly more memory.
Activities and System Messages
The easiest way to perform work in the background is by having your app respond to activities using MozActivity an the messages manifest.webapp property. Responding apps can either
- Launch fully and become the foreground app
- Launch an interstitial (i.e. to select photos from a gallery)
- Perform a task and return a result.
In the case of system messages, some may be triggered by the system without user interaction, while most activities will be triggered by the user directly.
What it’s good for? Responding to user-triggered events or certain system messages.
⚠️ Warning: Activities are exposed to all apps and the web browser. This is how CVE-2023-27108 works: the Communications app exposes an activity that returns the user’s call log without authorization checks.
Service Workers
Like all modern web browsers, one way to performa limited set of background tasks is through Service Workers, most notably via
push notifications. That said, service workers execute in a
worker scope with no access to the Document Object Model (DOM) and without many common APIs like XMLHttpRequest (XHR). However, service workers can process information off the main thread and can return transferable objects (i.e. String
and ArrayBuffer
) using
message passing.
What it’s good for? Responding to push notifications; basic background processing via message passing.
On KaiOS 3.0, all background processing is routed via a Service Worker that, in some cases (i.e. when called via notificationclick
), can launch a UI.
ℹ️ Important: Service Workers have a default 30 second idle timeout for Events like PusgEvent
. It’s controlled by dom.serviceWorkers.idle_timeout
, and
while this may seem short, longer-running tasks are best moved to the main thread.
Alarm API
The
Alarm API wakes up the phone at a specified time. The OS dispatches system messages to an app with both the
alarm
permission and alarm
message declared in manifest.webapp. The Alarm API is accessible via the navigator.mozAlarms
(KaiOS 2.5) and navigator.b2g.alarmManager
(KaiOS 3.0).
ℹ️ Important: unlike the RequestSync API, alarms do not have timeouts. If you use the Alarm API for background synchronization, it’s important to call window.close
to terminate your application once completed.
Apps first need to declare the permission and message in their manifest:
1"permissions": {
2 "alarms": { },
3},
4"messages": [
5 { "alarm": "/index.html" }
6]
Registering for Notifications
Then register for incoming system messages via navigator.mozSetMessageHandler
, or navigator.mozSetMessageHandlerPromise
if they want to wait until some asychronous process is completed. On KaiSO 3.0, you need to register a ServiceWorker than subscribe for the alarm
systems messages.
1// KaiOS 2.5
2navigator.mozSetMessageHandler('alarm', (mozAlarm) => {
3 console.log('alarm fired:', JSON.stringify(mozAlarm.data));
4});
5
6// KaiOS 3.0
7navigator.serviceWorker.ready.then(() => {
8 registration.systemMessageManager.subscribe('alarm');
9});
Adding Alarms
Finally, the app then needs to set an alarm at a specific time:
1let tomorrow = new Date();
2tomorrow.setMinutes(0);
3tomorrow.setSeconds(0);
4tomorrow.setDate(new Date().getDate() + 1);
5
6let data = { timestamp: tomorrow.valueOf() };
7
8// KaiOS 2.5, returns DOMRequest
9navigator.mozAlarms.add(
10 tomorrow,
11 'honorTimezone',
12 data
13);
14
15// KaiOS 3.0, returns Promise
16navigator.b2g.alarmManager.add({
17 date: date,
18 data: data,
19 ignoreTimezone: false,
20});
The first parameter is the Date
for the alarm to trigger, the second is an enum to honorTimezone
or ignoreTimezone
, and the third data
parameter is optional, and must be JSON-serializable.
Honoring timezones means that the alarm remains the same in UTC Coordinated Universal Time. If the alarm was set for 1PM Pacific Standard Time (PST) but the user has travelled to the east coast, it will run at 4PM Eastern Standard Time (EST).
Ignoring timezones means that the alarm remains at the same time of day. If the alarm was set for 1PM Pacific Standard Time (PST), it will also run at 1PM Eastern Standard Time (EST).
Getting and Removing Alarms
You can get a list of all the alarms that your app set using getAll
. It returns a Promise
or DOMRequest
that resolves to an Array of objects, each with an id
property unique to that alarm.
1// KaiOS 2.5, returns DOMRequest
2navigator.mozAlarms.getAll();
3
4// KaiOS 3.0, returns Promise
5navigator.b2g.alarmManager.getAll();
You can remove the alarm with remove
by passing the alarm id
as the parameter. These methods return void
, so there’s no need to wait or check their results.
1// KaiOS 2.5
2navigator.mozAlarms.remove(alarm.id);
3
4// KaiOS 3.0
5navigator.b2g.alarmManager.remove(alarm.id);
Alarm API Use Cases
One downside of the Alarm API is that you don’t have information on the device state, so if you need internet access and your alarm triggers when internet is unavailable, you’ll have to alert the user (likely with a notification) and try again later. I have also found that it can be unreliable when setting intervals, with alarms not running at the time set or not running at all.
Intervals: apps can set multiple alarms, so to replicate regular intervals (i.e. daily, weekly) they can set an alarm for every day in the next X months, or when triggered, set an alarm for the next day. For an example of this, check out my Daily Bing Wallpaper app.
What it’s good for? The Alarm API is good for waking up the device and launching your app at a specific time (i.e. an alarm clock). With a bit more effort, it can also be used for periodic background polling (i.e. auto-update, checking for new emails).
Note: the Alarm API is only available on privileged packaged apps, and excessive use will drain the battery. It’s available based on the dom.mozAlarms.enabled
device preference, which is enabled by default.
Request Sync API
The final approach executing background tasks on KaiOS is the Request Sync API. Note that is only works on KaiOS 2. (was removed in KaiOS 3.0) and requires the app be certified. Since there’s more book keeping involved, and the RequestSyncService needs to obtain a cpu
wakelock for tasks, RequestSync may drain the battery more than the Alarm API.
That said, it’s a fairly simple API and, in my experience, has proved more reliable than the Alarm API. The RequestSync API was originally introduced in Boot2Gecko (B2G) as a background sync schedule for the email app to periodically poll for new email messages. It was meant as a substitute for the BackgroundSync API, which is only available for installed Progressive Web Apps (PWAs) and Firefox has not implemented to this day.
ℹ️ Important: because the RequestSync API is meant for background synchronization, it has a minimum internal of 100 seconds (customizable via dom.requestSync.minInterval
), and a timeout of 2 minutes (customizable via dom.requestSync.maxTaskTimeout
).
In your manifest.webapp, declare the
requestsync-manager
permission and the request-sync
message.
1"permissions": {
2 "requestsync-manager": { },
3},
4"messages": [
5 { "request-sync": "/index.html" }
6]
Note: the RequestSync API is available behind the dom.requestSync.enabled
device preference, which is enabled by default on KaiOS 2.5.
Register Tasks
Registering RequestSync tasks is done via the navigator.sync
API.
1let taskName = 'myapp-dailytask';
2
3// KaiOS 2.5 only, returns Promise
4return navigator.sync.register(taskName, {
5 minInterval: 60 * 60 * 24, // 1 day, in seconds
6 oneShot: false, // run repeatedly
7 wifiOnly: false, // run with or without WiFi
8 wakeUpPage: location.href // launch this page
9});
In this example, we run a task daily. When the app launches, we register the task in the background like in my app Quotez.
Unregister Tasks
Tasks can also be unregistered easily via task name.
1let taskName = 'myapp-dailytask';
2
3// KaiOS 2.5 only
4navigator.sync.unregister(taskName);
Enumerating Tasks
You can easily enumerate all tasks that your app registered. This resolves to an Array of task objects with all the same configuration parameters, as well as some metadata on execution history.
Here is a snippet of the WebIDL definition for a RequestSync Task:
1interface RequestSyncTask {
2 // This object describes the app that is owning the task.
3 readonly attribute RequestSyncApp app;
4
5 // These attributes are taken from the configuration of the task:
6 readonly attribute USVString task;
7 readonly attribute DOMTimeStamp lastSync;
8 readonly attribute USVString wakeUpPage;
9 readonly attribute boolean oneShot;
10 readonly attribute long minInterval;
11 readonly attribute boolean wifiOnly;
12 readonly attribute any data;
13
14 Promise<void> runNow();
15}
Enumerating all registration is simple:
1navigator.sync.registrations()
2 .then((registrations) => {
3 if (Array.isArray(registrations) && registrations.length) {
4 // Registrations are present
5 }
6 });
Note: it’s a good idea to check if you’ve already registered the tasks, to avoid double registration.
What it’s good for? For certified apps targeting KaiOS 2.5 only: running tasks at specific intervals (i.e. daily) or only running tasks when WiFi is available.
ServiceWorker | Alarm | RequestSync | |
Type | Push Notification | One-Off Alarm | Sync Intervals |
Trigger | push event | alarm system message | request-sync system message |
Min Permission | Hosted | Privileged | Certified |
Timeout | 30 seconds | N/A | 2 minutes |
Scope | SW Global Scope | Window (2.5) SW (3.0) | Window |
Works Offline? | X | ☑️ | ☑️ |
WiFi-Only Option? | X | X | ☑️ |
KaiOS 2.5 | ☑️ | ☑️ | ☑️ |
KaiOS 3.0 | ☑️ | ☑️ | X |
Conclusion
Despite hardware constraints, background task execution is available in KaiOS. In fact, it’s an important component of certain services like the Email app, which needs to poll for new email messages without the user opening the app. If you’re looking for a partner to ensure the best user experience and deliver background synchronization on KaiOS, you can find the author’s contact info on the About page.