The System app is the first web app loaded by Gecko during the Firefox OS bootup procedure, and it handles numerous responsibilities that are required generally for the running of the system, and are therefore not scoped per individual web app. This article explains in detail how System works.
Any application that can be written in JavaScript, will eventually be written in JavaScript. -- Atwood's Law
Note: You can find the source code for the System app in the Gaia Github repo.
How the system app boots up
When Gecko tries to start the System app, it references System's manifest.webapp and loads the index.html file referenced in the launch_path
parameter (the launch_path
is always /index.html for Gaia apps.) index.html
links to all system-wide styles and JavaScript. To manage a full mobile system, the System app has to load a lot of resources.
The booting process is started in bootstrap.js
, with the following code:
window.addEventListener('load', function startup() { // define safelyLaunchFTU function safelyLaunchFTU() { ... } if (Applications.ready) { safelyLaunchFTU(); } else { ... } window.addEventListener('ftudone', function doneWithFTU() { window.removeEventListener('ftudone', doneWithFTU); var lock = window.navigator.mozSettings.createLock(); lock.set({ 'gaia.system.checkForUpdates': true }); } ... // With all important event handlers in place, we can now notify // Gecko that we're ready for certain system services to send us // messages (e.g. the radio). var evt = new CustomEvent('mozContentEvent', { bubbles: true, cancelable: false, detail: { type: 'system-message-listener-ready' } }); window.dispatchEvent(evt); }
The code works like so:
- System is a real web app running in a browser engine, and needs all its dependent resources to be loaded — including images and styles. We therefore start everything off once the
window.onload
handler triggers. - First, we prepare to launch the First Time Use (FTU) experience and Homescreen with the
safelyLaunchFTU()
function. As its name implies, FTU is only shown when the user starts Firefox OS for the first time. - When the user presses DONE inside the FTU, the
ftudone
custom event is fired, andbootstrap.js
listens for and handles this event. - Next, we use the
Settings API
(navigator.mozSettings
) to set thegaia.system.checkForUpdates
setting totrue
, meaning that the system will check for updates. - Finally, we run the customed
window.dispatchEvent
, viaCustomEvent
. This is a very important pattern used by Gaia for system notifications and communication with Gecko. In this case, the Gaia System app is notifying Gecko that it is ready to listen to and handle events.
Instantiable modularization
The system itself is constantly evolving to achieve better modularization and flexibility. From version 1.4 there’s an initiative underway to refactor the system module as instantiable object.
After all the above code has been run, the references to each system component in bootstrap.js
will be in the following form:
window.telephonySettings = new TelephonySettings(); window.telephonySettings.start();
The source code skeleton for TelephonySettings()
would be:
(function(exports) { 'use strict'; function TelephonySettings() { this.init_param = false; } TelephonySettings.prototype = { // Initialzes all settings. start: function() { ... }, // Clean all settings. stop: function() { ... }, 1st_method: function ts_1st_method() { ... }, 2nd_method: function ts_2nd_method() { ... } }; exports.TelephonySettings = TelephonySettings; }(window));
This pattern helps modularize each system component and makes them more testable.
Boot up and shut down animations
This section explains how the System app controls the bootup and shut down animations. init_logo_handler.js
and sleep_menu.js
handle the system boot up and shut down animations.
Boot up animations
The bootup animation action is not actually contained in the main bootup procedure, but is instead hooked by check EventListeners
.
The boot up animation code in init_logo_handler.js
looks like so:
To paint the logo or animation once Gecko is ready to paint something onto the screen, we run the appropriate system handler once the DOM is loaded, then hide the logo once a ftudone
or ftuskip
event is fired. The _appendCarrierPowerOn
method, contained in init_logo_handler.js
, shows how Gaia prepares to launch the animation or boot logo by listening to the DOMContentLoaded
event. The logoLoader
is defined in logo_loader.js
.
var self = this; document.addEventListener('DOMContentLoaded', function() { self.carrierLogo.appendChild(self.logoLoader.element); self._setReady(); });
Once the logo is prepared, the system calls the _setReady()
method, which sets up a listener to listen for the special mozChromeEvent
event with a type of system-first-paint
to denote that the system is ready to paint on to the screen.
var elem = this.logoLoader.element; ... window.addEventListener('mozChromeEvent', function startVideo(e) { if (e.detail.type == 'system-first-paint') { window.removeEventListener('mozChromeEvent', startVideo); if (elem && elem.ended === false) { elem.play(); } } });
At this point the graphic element is now playing. Once a ftuopen
or ftuskip
event is fired, init_logo_handler.js
invokes the default handleEvent()
method that in turn triggers the animate()
method to hide the animation with a fadeout transition effect.
init: function ilh_init(logoLoader) { window.addEventListener('ftuopen', this); window.addEventListener('ftuskip', this); ... }, handleEvent: function ilh_handleEvent() { this.animate(); },
Shut down animations
Once the system is ready, long pressing the power button fires a holdsleep
event, as defined in hardware_button.js
. This script handles all of "hardware button pressed" events, including power (sleep), home, volume up/down, and so on.
HardwareButtonsSleepState.prototype.enter = function() { this.timer = setTimeout(function() { / * When the user holds Sleep button more than HOLD_INTERVAL. */ this.hardwareButtons.publish('holdsleep'); this.hardwareButtons.setState('base'); }.bind(this), this.hardwareButtons.HOLD_INTERVAL); };
The shut down animation is handled by sleep_menu.js
; this script listens for the holdsleep
event and shows the menu dialog when it is fired.
init: function sm_init() { ... window.addEventListener('holdsleep', this.show.bind(this)); ... }
If user chooses to shut down the device via the restart or power off menu options, the startPowerOff()
function is triggered, which in turn triggers the LogoLoader()
function to handle displaying the shutdown animation.
handleEvent: function sm_handleEvent(evt) { switch (evt.type) { case 'click': ... this.handler(action); break; ... } } handler: function sm_handler(action) { switch (action) { ... case 'restart': this.startPowerOff(true); break; case 'power': this.startPowerOff(false); break; ... } }
System functions
There are plenty of functions and responsibilities handled by the System app, some of which you might be surprised to find under its remit. The System app handles statusbar and utility tray management, SIM lock, update manager, homescreen launcher, webapp window management, and more. This section looks into some of the most important functions that it serves.
Statusbar and utility tray
The System status bar and dropdown menu (Gaia calls it the utility tray; Android uses the term notification bar) are handled by statusbar.js
and utility_tray.js
. Inside the app's index.html
, the status bar items are defined inside <div id="statusbar" data-z-index-level="statusbar">
whereas the utility tray items reside within the following structure:
<div id="utility-tray" data-z-index-level="utility-tray"> <div id="notifications-container"> ... </div> </div>
There are some special panels within the utility tray such as update Manager, emergency callback manager, storage watcher notification, media playback notification and controls, Bluetooth transfer
status, and keyboard input method (IME) switcher. The related handlers and styles are located in the js
/ and style/
directories.
Special app launchers
The System app has three special launchers, which invoke separate web apps when required:
- The homescreen launcher: Runs the Homescreen app, which displays the homescreen when the user presses the Home button, or if a webapp crashes or is exited in some other way.
- The lockscreen launcher: Runs the Lockscreen app, which displays the lockscreen every time a user turns on the screen.
- The FTU launcher: Runs the FTU experience app. This is unique because every user will see the FTU experience the first time they run a new FirefoxOS device (or return their device to factory settings). In addition, the FTU webapp doesn't allow the user to use their Home button to escape out of the app.
Lockscreen
The main entrypoint for the Lockscreen app is system/js/lockscreen.js
. From this screen the user can swipe unlock, trigger the secure camera and control the music player.
Emergency dialer
The code for the emergency dialer is contained in the gaia/apps/system/emergency-call/
directory. It’s a simplified version of the Dialer app that allows the user can access from the SIM PIN unlock dialog to dial emergency services such as police.
System-wide UI
The System app handles most of the system-wide UI, which mostly consists of dialogs such as the volume warning dialog, SIM PIN unlock dialog, cell broadcast
dialog, and UI elements affecting window behavior, such as the software home button.
z-index level
with the System app's index.html
file, many components are bundled with data-z-index-level
attribute, for example:
<div id="sleep-menu" data-z-index-level="sleep-menu"> ... </div>
The corresponding z-index-levels are defined inside system/style/zindex.css
, for example:
#screen > [data-z-index-level="sleep-menu"] { z-index: 65536; } ... #screen > [data-z-index-level="app"] > .appWindow { z-index: 3; }
The z-index settings are arranged according to the order the elements are displayed on screen — elements that need to appear higher in the visual hierarchy are given a higher number. This is how the System app handles the window management at a basic level.
The software home button
The software home button is an alternative home button, automatically enabled on devices without a hardware home button, e.g. Nexus 4. To control it's appearence Gecko provides a proprietary media feature called -moz-physical-home-button
, which can be used inside a media query to apply styles based on the presence of a hardware home button. The window manager allocates some screen space for the software home button if required.
In system/style/window.css
(and many other system style files), you will see this:
@media not all and (-moz-physical-home-button) { #screen:not(.software-button-disabled) > #windows > .appWindow { bottom: 5rem; } }
Home gesture (slide up from bottom of the screen)
The home gesture is another hardware home button alternative; it can be enabled in developer settings, and the code that controls it is found in system/js/home_gesture.js
. The gesture involves swiping from the bottom of the screen, and can be used to close apps, for example.
The gesture will be automatically enabled on Firefox OS tablet devices. The threshold defined in gaia/shared/js/screen_layout.js
is used to detect whether the device is a tablet or not.
Volume warning dialog
The code that controls the volume warning dialog is found in system/js/sound_manager.js
. The volume warning dialog will appear when all of the following conditions are satisfied:
- The earphones are plugged in.
- A volume threshold of 85dB is exceeded.
- Content channel audio is playing.
SIM PIN unlock dialog
The code that controls the SIM PIN unlock dialog is in system/js/simcard_dialog.js
— this dialog shows when the Passcode lock option is enabled, the lock screen is showing. The opening app also has to have telephony permissions contained in its manifest.webapp
file (which the System app does).
Note: the SIM PIN unlock dialog is not shown while the phone is in Airplane mode.