|
|
| Line 1: |
Line 1: |
| == Warning == | | == Warning == |
| This page is no longer maintained - please see this content on https://developer.mozilla.org/en-US/Firefox_OS/Platform/Architecture | | This page is no longer maintained - please see this content on https://developer.mozilla.org/en-US/Firefox_OS/Platform/Architecture |
|
| |
|
| |
|
| |
|
| |
| This document describes at a high level how [[Gaia]] and [[B2G]] work in the port of [[Gecko]] to [[Gonk]]. If you don't know what these things are yet, don't worry, they'll be introduced below.
| |
|
| |
| Gaia/B2G/Gecko are under heavy development, so some items below discuss how the code '''will''' work, instead of how it '''currently''' works. This will be noted.
| |
|
| |
| == Terminology ==
| |
|
| |
| '''''[[Gaia]]''''' : The user interface of b2g. Everything drawn to screen after b2g starts up is some part of Gaia. Gaia implements a lock screen, home screen, telephone dialer, text-messaging application, camera app, ... and many more. Gaia is written entirely in HTML, CSS, and JavaScript. Its only interface to the underlying operating system is through Open Web APIs, which are implemented by Gecko. Gaia works ''well'' when run on top of b2g; however, since it only uses standard web APIs, it works on other OSes and in other web browsers (albeit with degraded functionality). Third-party applications can be installed alongside Gaia.
| |
|
| |
| '''''[[Gecko]]''''' : The "application runtime" of b2g. At a high level, Gecko implements the open standards for HTML, CSS, and JS and makes those interfaces run well on all the OSes that Gecko supports. This means that Gecko consists of, among other things, a networking stack, graphics stack, layout engine, virtual machine (for JS), and porting layers.
| |
|
| |
| '''''[[Gonk]]''''' : The lower-level "operating system" of b2g. Gonk consists of a linux kernel and userspace hardware abstraction layer (HAL). The kernel and several userspace libraries are common open-source projects: linux, libusb, bluez, etc. Some other parts of the HAL are shared with the android project: GPS, camera, among others. You could say that Gonk is an extremely simple linux distribution. Gonk is a ''porting target'' of Gecko; there is a port of Gecko to Gonk, just like there is a port of Gecko to OS X, and a port of Gecko to Android. Since the b2g project has full control over Gonk, we can expose interfaces to Gecko that aren't possible to expose on other OSes. For example, Gecko has direct access to the full telephony stack and display framebuffer on Gonk, but doesn't have this access on any other OS.
| |
|
| |
| == Booting ==
| |
|
| |
| After turning on a b2g phone, execution starts in the primary bootloader. From there, the process of loading the main OS kernel happens in the usual way: a succession of higher-level bootloaders bootstrap the next loader in the chain. At the end of the process, execution is handed off to the linux kernel.
| |
|
| |
| There's not a lot to say about the boot process, but there are a few things worth knowing
| |
| * The bootloaders usually show the first "splash screen" seen during device boot, which usually displays a vendor logo.
| |
| * The bootloaders implement flashing an image onto the device. Different devices use different protocols. Most phones use the fastboot protocol, but the Galaxy S II uses the "odin" protocol.
| |
| * By the end of the bootstrapping process, the modem image is usually loaded and running on the modem processor. How this happens is highly device-specific and possibly proprietary.
| |
|
| |
| == Kernel (Linux) ==
| |
|
| |
| The linux kernel(s) in Gonk is reasonably close to upstream linux. There are a few modifications made by AOSP that are not in upstream yet. Vendors also modify the linux kernel and upstream those modifications on their own schedule. But in general, the linux kernel is close to stock.
| |
|
| |
| The startup process for linux is well documented elsewhere on the internet, so it's not covered here. At the end of kernel startup, a userspace "init" process is launched, like in most other UNIX-like OSes. At this point in execution, only a ramdisk is mounted. The ramdisk is built during the b2g build process, and consists of critical utilities (like <code>init</code>), other startup scripts, and loadable kernel modules.
| |
|
| |
| After launching the <code>init</code> process, the linux kernel services system calls from userspace and interrupts etc. from hardware devices. Many devices are exposed to userspace through sysfs (documented elsewhere on the internet). For example, here's some code that reads the battery state in Gecko ([https://github.com/cgjones/mozilla-central/blob/master/hal/gonk/GonkHal.cpp#L267 link to original code])
| |
| <pre>
| |
| FILE *capacityFile = fopen("/sys/class/power_supply/battery/capacity", "r");
| |
| double capacity = dom::battery::kDefaultLevel * 100;
| |
| if (capacityFile) {
| |
| fscanf(capacityFile, "%lf", &capacity);
| |
| fclose(capacityFile);
| |
| }
| |
| </pre>
| |
|
| |
| == init ==
| |
|
| |
| The <code>init</code> process in Gonk mounts necessary file systems, spawns system services, and acts a process manager after that. This is very similar to <code>init</code> on other UNIX-like OSes. <code>init</code> "interprets" scripts (init*.rc), that consist of commands. Here's the main init script for the Galaxy S II
| |
|
| |
| [https://github.com/michaelwu/clockworkmod_galaxys2_initramfs/blob/gingerbread/init.rc Main init script for the Galaxy S II]
| |
|
| |
| In particular, <code>init</code> is responsible for launching the <code>b2g</code> process, described below. Here's the snippet of code that launches <code>b2g</code>
| |
| <pre>
| |
| service b2g /system/b2g/b2g
| |
| onrestart restart media
| |
| </pre>
| |
|
| |
| == Userspace process architecture ==
| |
|
| |
| At this point, it's best to step back a bit and look at how the various components of b2g fit together, at a very high level.
| |
| [[File:B2G-processes.png|thumb|650px|B2G processes]]
| |
|
| |
| This diagram shows the main userspace processes in b2g. ('''NOTE''': this diagram is subject to change, and not guaranteed to be 100% accurate.) The dotted lines show processes that are spawned by <code>init</code>. The solid lines show other communication channels.
| |
|
| |
| The <code>b2g</code> process is the main system process. It runs with high privileges: it has access to most hardware devices. <code>b2g</code> communicates with the modem, draws to the display framebuffer, and talks to GPS, cameras, and other devices. Internally, <code>b2g</code> runs Gecko code (libxul.so). The details of how it communicates with hardware are discussed below.
| |
|
| |
| The <code>b2g</code> process may spawn a number of low-rights "content processes". These are where web applications and other web content are loaded. These process communicate with the main Gecko server process through [https://developer.mozilla.org/en/IPDL IPDL], a message-passing system.
| |
|
| |
| The <code>rild</code> process is the interface to the modem processor. "RIL" is the "radio interface layer", "rild" is the "RIL daemon". It's a proprietary "blob" of code implemented by hardware vendors. <code>rild</code> allows a client to connect to a [https://github.com/michaelwu/clockworkmod_galaxys2_initramfs/blob/gingerbread/init.rc#L656 UNIX-domain socket it binds to].
| |
| <pre>
| |
| service ril-daemon /system/bin/rild
| |
| socket rild stream 660 root radio
| |
| </pre>
| |
|
| |
| In b2g, the rild client is the <code>rilproxy</code> process. It just acts as a dumb forwarding proxy between <code>rild</code> and <code>b2g</code>. The reason we need this proxy is an implementation detail, not important. The code lives [https://github.com/mozilla-b2g/rilproxy here].
| |
|
| |
| The <code>mediaserver</code> process controls audio and video playback. Gecko talks to it through an android RPC mechanism. The code for <code>mediaserver</code> lives [https://github.com/cgjones/android-frameworks-base/tree/gingerbread-b2g/media/libmediaplayerservice here]. Some of the media that Gecko can play (OGG Vorbis audio, OGG Theora video, and WebM video) are decoded by Gecko and sent directly to the mediaserver. Other media files are decoded by [https://github.com/cgjones/android-frameworks-base/tree/gingerbread-b2g/media/libstagefright libstagefright], which is capable of accessing proprietary codecs and hardware decoders. ('''NOTE''': B2G will not use the mediaserver process, in its current incarnation, in the long run.)
| |
|
| |
| The <code>netd</code> process is used to configure network interfaces. <code>wpa_supplicant</code> is the standard UNIX-ish daemon that connects to WiFi access points. '''TODO: document other processes'''
| |
|
| |
| == Gecko: Processing input events ==
| |
|
| |
| Most action inside of Gecko is triggered by input events (button presses, touch-screen touches, and so forth). Input events enter Gecko through the Gonk [https://github.com/cgjones/mozilla-central/blob/master/widget/gonk/nsAppShell.cpp#L532 "app shell"] (on b2g). For example
| |
| <pre>
| |
| void
| |
| GeckoInputDispatcher::notifyKey(nsecs_t eventTime,
| |
| int32_t deviceId,
| |
| int32_t source,
| |
| uint32_t policyFlags,
| |
| int32_t action,
| |
| int32_t flags,
| |
| int32_t keyCode,
| |
| int32_t scanCode,
| |
| int32_t metaState,
| |
| nsecs_t downTime)
| |
| {
| |
| UserInputData data;
| |
| data.timeMs = nanosecsToMillisecs(eventTime);
| |
| data.type = UserInputData::KEY_DATA;
| |
| data.action = action;
| |
| data.flags = flags;
| |
| data.metaState = metaState;
| |
| data.key.keyCode = keyCode;
| |
| data.key.scanCode = scanCode;
| |
| {
| |
| MutexAutoLock lock(mQueueLock);
| |
| mEventQueue.push(data);
| |
| }
| |
| gAppShell->NotifyNativeEvent();
| |
| }
| |
| </pre>
| |
|
| |
| These input events originate from the standard linux input_event system, dispatched by input-device drivers. We use [https://github.com/cgjones/android-frameworks-base/blob/gingerbread-b2g/libs/ui/InputReader.cpp a light abstraction over that] which provides some nice features like filtering of events. [https://github.com/cgjones/android-frameworks-base/blob/gingerbread-b2g/libs/ui/EventHub.cpp#L450 Here's where input events originate]
| |
| <pre>
| |
| if (pfd.revents & POLLIN) {
| |
| int32_t readSize = read(pfd.fd, mInputBufferData,
| |
| sizeof(struct input_event) * INPUT_BUFFER_SIZE);
| |
| if (readSize < 0) {
| |
| if (errno != EAGAIN && errno != EINTR) {
| |
| LOGW("could not get event (errno=%d)", errno);
| |
| }
| |
| } else if ((readSize % sizeof(struct input_event)) != 0) {
| |
| LOGE("could not get event (wrong size: %d)", readSize);
| |
| } else {
| |
| mInputBufferCount = readSize / sizeof(struct input_event);
| |
| mInputBufferIndex = 0;
| |
| }
| |
| }
| |
| }
| |
| </pre>
| |
|
| |
| After being read by Gecko, inputs are dispatched into the DOM from [https://github.com/cgjones/mozilla-central/blob/master/widget/gonk/nsAppShell.cpp#L213 here]
| |
| <pre>
| |
| static nsEventStatus
| |
| sendKeyEventWithMsg(PRUint32 keyCode,
| |
| PRUint32 msg,
| |
| uint64_t timeMs,
| |
| PRUint32 flags)
| |
| {
| |
| nsKeyEvent event(true, msg, NULL);
| |
| event.keyCode = keyCode;
| |
| event.time = timeMs;
| |
| event.flags |= flags;
| |
| return nsWindow::DispatchInputEvent(event);
| |
| }
| |
| </pre>
| |
|
| |
| From here on, the events are either consumed by Gecko internally, or dispatched to web applications as DOM events.
| |
|
| |
| == Gecko: Graphics ==
| |
|
| |
| At the very lowest level, Gecko uses OpenGL ES 2.0 to draw to a glcontext that wraps the hardware framebuffers. Gecko sets up its graphics context [https://github.com/cgjones/mozilla-central/blob/master/widget/gonk/nsWindow.cpp#L79 here]
| |
| <pre>
| |
| gNativeWindow = new android::FramebufferNativeWindow();
| |
| sGLContext = GLContextProvider::CreateForWindow(this);
| |
| </pre>
| |
| The FramebufferNativeWindow is implemented [https://github.com/cgjones/android-frameworks-base/blob/gingerbread-b2g/libs/ui/FramebufferNativeWindow.cpp here]. It uses the "gralloc" hardware interface to the graphics driver to map buffers from the framebuffer device.
| |
|
| |
| Gecko uses its [[Layers]] system to composite drawn content to the screen. A full description of layers is beyond the scope of this document, but what approximately happens is
| |
| * Gecko draws separate regions of pages into memory buffers. Sometimes, these buffers are system memory. Other times, they're textures mapped into Gecko's address space, meaning Gecko draws directly to VRAM. Drawing happens [https://github.com/cgjones/mozilla-central/blob/master/gfx/layers/basic/BasicLayers.cpp#L750 here], in the common case.
| |
| * Gecko composites these textures to screen using GL commands. Composition of the pixels painted above would happen [https://github.com/cgjones/mozilla-central/blob/master/gfx/layers/opengl/ThebesLayerOGL.cpp#L288 here].
| |
|
| |
| The details of how Gecko actually draws web content are beyond the scope of this document.
| |
|
| |
| == Gecko: Hardware Abstraction Layer (hal) ==
| |
|
| |
| (The Gecko hardware abstraction layer is not to be confused with the Gonk hardware abstraction layers. Where possible, the Gecko hardware abstraction layer will be referred to as "hal", and the Gonk hardware abstraction layers will be referred to as "HAL".)
| |
|
| |
| "hal" is one on the porting layers of Gecko. It's used for low-level access to system interfaces across multiple platforms. hal offers a cross-platform C++ API to the higher levels of Gecko, with implementations of those APIs per platform inside of hal itself. hal is not exposed directly to anything other than C++ code in Gecko.
| |
|
| |
| It's easiest to understand hal through an example. We'll use the vibration API. The hal API for vibration is [https://github.com/cgjones/mozilla-central/blob/master/hal/Hal.h#L55 here]
| |
| <pre>
| |
| void Vibrate(const nsTArray<uint32>& pattern);
| |
| </pre>
| |
| (Note: the real API is slightly more complicated, for reasons that aren't relevant to this discussion. The complexities are omitted in this presentation.)
| |
|
| |
| This API tells the vibration motor to turn on/off according to the pattern specified in <code>pattern</code>. The API is implemented for Gonk [https://github.com/cgjones/mozilla-central/blob/master/hal/gonk/GonkHal.cpp#L171 here]
| |
| <pre>
| |
| void
| |
| Vibrate(const nsTArray<uint32> &pattern)
| |
| {
| |
| EnsureVibratorThreadInitialized();
| |
| sVibratorRunnable->Vibrate(pattern);
| |
| }
| |
| </pre>
| |
|
| |
| which sends the request off to another thread [https://github.com/cgjones/mozilla-central/blob/master/hal/gonk/GonkHal.cpp#L107 here], where the real work is done
| |
| <pre>
| |
| while (!mShuttingDown) {
| |
| if (mIndex < mPattern.Length()) {
| |
| uint32 duration = mPattern[mIndex];
| |
| if (mIndex % 2 == 0) {
| |
| vibrator_on(duration);
| |
| }
| |
| mIndex++;
| |
| mMonitor.Wait(PR_MillisecondsToInterval(duration));
| |
| }
| |
| else {
| |
| mMonitor.Wait();
| |
| }
| |
| }
| |
| </pre>
| |
|
| |
| <code>vibrator_on()</code> is the Gonk HAL API for turning on the vibrator motor. Internally, <code>vibrator_on()</code> writes a value to a kernel object exposed through sysfs, which in effect sends the kernel driver a message.
| |
|
| |
| The hal APIs are supported across all platforms. When Gecko is built for a platform that doesn't expose an interface to vibrator motors, then the "fallback" implementation of the hal API is used. For vibration, it's [https://github.com/cgjones/mozilla-central/blob/master/hal/fallback/FallbackHal.cpp#L22 here]
| |
| <pre>
| |
| void
| |
| Vibrate(const nsTArray<uint32>& pattern)
| |
| {}
| |
| </pre>
| |
|
| |
| Most web content runs in low-rights content processes (see above). We can't assume those content processes have the necessary privileges to turn on the vibration motor, and in addition we want to resolve race conditions in a central location. In hal, this is done through the "sandbox" implementation of hal. The sandbox implementation merely proxies requests from content processes to the "Gecko server" process. The proxy requests are sent through IPDL. For vibration, the request originates [https://github.com/cgjones/mozilla-central/blob/master/hal/sandbox/SandboxHal.cpp#L42 here]
| |
| <pre>
| |
| void
| |
| Vibrate(const nsTArray<uint32>& pattern)
| |
| {
| |
| Hal()->SendVibrate(pattern);
| |
| }
| |
| </pre>
| |
| which sends a message that's defined by [https://github.com/cgjones/mozilla-central/blob/master/hal/sandbox/PHal.ipdl#L94 this interface]
| |
| <pre>
| |
| Vibrate(uint32[] pattern);
| |
| </pre>
| |
| to a receiver of the message defined [https://github.com/cgjones/mozilla-central/blob/master/hal/sandbox/SandboxHal.cpp#L238 here]
| |
| <pre>
| |
| NS_OVERRIDE virtual bool
| |
| RecvVibrate(const InfallibleTArray<unsigned int>& pattern)
| |
| {
| |
| // Forward to hal::, not hal_impl::, because we might be a
| |
| // subprocess of another sandboxed process. The hal:: entry point
| |
| // will do the right thing.
| |
| hal::Vibrate(pattern);
| |
| return true;
| |
| </pre>
| |
| (omitting some details that aren't relevant to this discussion). So a vibration request sent by a content process on the Gecko port to Gonk eventually ends up in the GonkHal implementation of <code>Vibrate()</code>, discussed above.
| |
|
| |
| == Gecko: DOM APIs ==
| |
|
| |
| "DOM interfaces", approximately, are how web content communicates with Gecko. There's a lot more to the DOM than that, but the longer discussion is beyond the scope of this document. DOM interfaces are defined in [https://developer.mozilla.org/en/XPIDL IDL], which comprises both a foreign function interface (ffi) and object model (OM) between JavaScript and C++. Again, there are more details that could be discussed here which are beyond the scope of this document. Let's learn a bit of IDL by example.
| |
|
| |
| The very simple vibration API is exposed to web content through [https://mxr.mozilla.org/mozilla-central/source/dom/interfaces/base/nsIDOMNavigator.idl?rev=b405f493e834#80 an IDL interface].
| |
| <pre>
| |
| [implicit_jscontext]
| |
| void mozVibrate(in jsval aPattern);
| |
| </pre>
| |
| The <code>jsval</code> argument indicates that <code>mozVibrate</code> (our vendor-prefixed implementation of the vibration specification) accepts any JS value. Details of working with jsvals are beyond the scope of this document. The IDL compiler generates a C++ interface that's implemented in [https://mxr.mozilla.org/mozilla-central/source/dom/base/Navigator.cpp?rev=af40e0f0ce26#775 the Navigator class]
| |
| <pre>
| |
| NS_IMETHODIMP
| |
| Navigator::MozVibrate(const jsval& aPattern, JSContext* cx)
| |
| {
| |
| // ...
| |
| hal::Vibrate(pattern);
| |
| return NS_OK;
| |
| }
| |
| </pre>
| |
| There's quite a lot of code that's hidden here by the ellipsis ("..."), but let's ignore for it now.
| |
|
| |
| The call to <code>hal::Vibrate()</code> transfers control from the DOM to hal. From there, we enter the hal implementation discussed above. A key point to note here is that the DOM implementation doesn't care what platform it's running on (Gonk or Windows or OS X or ...), nor does it care whether the code is running in a "content process" or the Gecko "server process". This is all left to lower levels of the system.
| |
|
| |
| The vibration API happens to be quite simple, so it's a good example. [https://mxr.mozilla.org/mozilla-central/source/dom/sms/ The SMS API], for instance, is much more complicated and has its own "remoting" layer from content processes to the server.
| |
|
| |
| == Radio Interface Layer (RIL) ==
| |
|
| |
| The RIL was discussed briefly above. In this section, we'll how the various pieces interact in a little more detail. The main actors are
| |
| * <code>rild</code>: the proprietary bit of code that talks to the proprietary modem firmware
| |
| * <code>rilproxy</code>: the daemon that proxies messages between <code>rild</code> and Gecko (the <code>b2g</code> process) to overcome the permission problem of talking to rild directly (the process has to be in the <code>radio</code> group).
| |
| * Gecko's <code>b2g</code> process, also called the <em>chrome process</em>
| |
| ** <code>ril_worker.js</code>: worker thread that talks to <code>rild</code> (via <code>rilproxy</code>) and implements the radio state machine.
| |
| ** <code>nsIRadioInterfaceLayer</code>: main thread XPCOM service that mostly acts as a message exchange between the <code>ril_worker.js</code> thread and various other Gecko components, including the Gecko content processes.
| |
| * Gecko <em>content process</em>
| |
| ** <code>nsIRILContentHelper</code>: XPCOM service that let's DOM implementations such as Telephony, SMS, etc. talk to <code>nsIRadioInterfaceLayer</code> in the chrome process.
| |
|
| |
| === Example: from rild to the DOM ===
| |
|
| |
| Let's start with an example that demonstrates the lower-level parts of the system. When the modem receives an incoming call, it notifies the <code>rild</code> using a proprietary mechanism. The rild then prepares a message for its client according to the "open" protocol [https://github.com/mozilla-b2g/android-hardware-ril/blob/master/include/telephony/ril.h here]. In this case, an incoming call generates the [https://github.com/mozilla-b2g/android-hardware-ril/blob/master/include/telephony/ril.h#L3290 <code>RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED</code>] message. This message is sent by rild to its client and received by rilproxy [https://github.com/mozilla-b2g/rilproxy/blob/master/src/rilproxy.c#L214 here]
| |
| <pre>
| |
| ret = read(rilproxy_rw, data, 1024);
| |
| if(ret > 0) {
| |
| writeToSocket(rild_rw, data, ret);
| |
| }
| |
| </pre>
| |
| where it's forwarded along to Gecko, on the socket connecting rilproxy and Gecko. Gecko receives these forwarded bytes [https://mxr.mozilla.org/mozilla-central/source/ipc/ril/Ril.cpp#231 on the IPC thread] like so:
| |
| <pre>
| |
| int ret = read(fd, mIncoming->mData, 1024);
| |
| // [handle errors]
| |
| mIncoming->mSize = ret;
| |
| sConsumer->MessageReceived(mIncoming.forget());
| |
| </pre>
| |
| The consumer is [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/SystemWorkerManager.cpp#160 SystemWorkerManager] which repackages the message and dispatches it to the <code>ril_worker.js</code> thread that implements the RIL state machine:
| |
| <pre>
| |
| virtual void MessageReceived(RilRawData *aMessage) {
| |
| nsRefPtr<DispatchRILEvent> dre(new DispatchRILEvent(aMessage));
| |
| mDispatcher->PostTask(dre);
| |
| }
| |
| </pre>
| |
| The task posted to that thread [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/SystemWorkerManager.cpp#181 calls a function] <code>onRILMessage()</code> in the JS code on the RIL worker:
| |
| <pre>
| |
| return JS_CallFunctionName(aCx, obj, "onRILMessage", NS_ARRAY_LENGTH(argv),
| |
| argv, argv);
| |
| </pre>
| |
| That function is [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/ril_worker.js#4482 defined in <code>ril_worker.js</code>]. The message bytes are processed a bit and chopped into parcels. Each complete parcel is then [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/ril_worker.js#2525 dispatched to individual handler methods]:
| |
| <pre>
| |
| handleParcel: function handleParcel(request_type, length) {
| |
| let method = this[request_type];
| |
| if (typeof method == "function") {
| |
| if (DEBUG) debug("Handling parcel as " + method.name);
| |
| method.call(this, length);
| |
| }
| |
| }
| |
| };
| |
| </pre>
| |
| For our example, <code>RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED</code>, this calls [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/ril_worker.js#3217 the following handler]:
| |
| <pre>
| |
| RIL[UNSOLICITED_RESPONSE_CALL_STATE_CHANGED] = function UNSOLICITED_RESPONSE_CALL_STATE_CHANGED() {
| |
| this.getCurrentCalls();
| |
| };
| |
| </pre>
| |
| As you can see, upon being notified that the call state changed, the state machine simply fetches the current call state with a <code>REQUEST_GET_CURRENT_CALLS</code> request:
| |
| <pre>
| |
| getCurrentCalls: function getCurrentCalls() {
| |
| Buf.simpleRequest(REQUEST_GET_CURRENT_CALLS);
| |
| },
| |
| </pre>
| |
| This sends a request ''back'' to the rild to request the state of all currently-active calls. The request follows a similar path back to rild (out from ril_worker.js, to SystemWorkerManager.cpp, off to Ril.cpp, then to rilproxy.c and finally written to the rild socket). The response from rild is processed similarly to above, in [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/ril_worker.js#2619 the <code>REQUEST_GET_CURRENT_CALLS</code> handler]. The call state is then [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/ril_worker.js#2057 processed and compared to the previous one]. If there's a change of state, we [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/ril_worker.js#2117 notify the <code>nsIRadioInterfaceLayer</code> service] on the main thread:
| |
| <pre>
| |
| _handleChangedCallState: function _handleChangedCallState(changedCall) {
| |
| let message = {type: "callStateChange",
| |
| call: changedCall};
| |
| this.sendDOMMessage(message);
| |
| },
| |
| </pre>
| |
|
| |
| [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/RadioInterfaceLayer.js#268 This message is processed] by the <code>nsIRadioInterfaceLayer</code> service:
| |
| <pre>
| |
| onmessage: function onmessage(event) {
| |
| let message = event.data;
| |
| debug("Received message from worker: " + JSON.stringify(message));
| |
| switch (message.type) {
| |
| case "callStateChange":
| |
| // This one will handle its own notifications.
| |
| this.handleCallStateChange(message.call);
| |
| break;
| |
| </pre>
| |
| bit really all that <code>RadioInterfaceLayer</code> does is [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/RadioInterfaceLayer.js#536 dispatch the message to the content process] using the <em>parent process message manager</em> (<code>ppmm</code>):
| |
| <pre>
| |
| handleCallStateChange: function handleCallStateChange(call) {
| |
| debug("handleCallStateChange: " + JSON.stringify(call));
| |
| [some internal state updating]
| |
| ppmm.sendAsyncMessage("RIL:CallStateChanged", call);
| |
| },
| |
| </pre>
| |
|
| |
| In the content process, [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/RILContentHelper.js#277 this message is received by the <code>nsIRILContentHelper</code> service] from the <em>child process message manager</em> (<code>cpmm</code>):
| |
| <pre>
| |
| receiveMessage: function receiveMessage(msg) {
| |
| let request;
| |
| debug("Received message '" + msg.name + "': " + JSON.stringify(msg.json));
| |
| switch (msg.name) {
| |
| case "RIL:CallStateChanged":
| |
| this._deliverTelephonyCallback("callStateChanged",
| |
| [msg.json.callIndex, msg.json.state,
| |
| msg.json.number, msg.json.isActive]);
| |
| break;
| |
| </pre>
| |
|
| |
| This ends up calling [https://mxr.mozilla.org/mozilla-central/source/dom/system/gonk/nsIRadioInterfaceLayer.idl#15 the <code>nsIRILTelephonyCallback::callStateChanged</code> method] of all registered telephony callback objects. Every web app that accesses the <code>navigator.mozTelephony</code> API has registered one such callback object that [https://mxr.mozilla.org/mozilla-central/source/dom/telephony/Telephony.cpp#372 dispatches events to the JavaScript code in the web app], either as a state change of an existing call object or a new <code>incoming</code> event:
| |
| <pre>
| |
| NS_IMETHODIMP
| |
| Telephony::CallStateChanged(PRUint32 aCallIndex, PRUint16 aCallState,
| |
| const nsAString& aNumber, bool aIsActive)
| |
| {
| |
| [...]
| |
|
| |
| if (modifiedCall) {
| |
| // Change state.
| |
| modifiedCall->ChangeState(aCallState);
| |
|
| |
| // See if this should replace our current active call.
| |
| if (aIsActive) {
| |
| mActiveCall = modifiedCall;
| |
| }
| |
|
| |
| return NS_OK;
| |
| }
| |
|
| |
| nsRefPtr<TelephonyCall> call =
| |
| TelephonyCall::Create(this, aNumber, aCallState, aCallIndex);
| |
| nsRefPtr<CallEvent> event = CallEvent::Create(call);
| |
| nsresult rv =
| |
| event->Dispatch(ToIDOMEventTarget(), NS_LITERAL_STRING("incoming"));
| |
| NS_ENSURE_SUCCESS(rv, rv);
| |
| return NS_OK;
| |
| }
| |
| </pre>
| |
|
| |
| Apps such as the <em>Dialer</em> [https://github.com/mozilla-b2g/gaia/blob/master/apps/dialer/js/dialer.js#L389 can receive these events] and update their UI etc.:
| |
| <pre>
| |
| handleEvent: function fm_handleEvent(evt) {
| |
| switch (evt.call.state) {
| |
| case 'connected':
| |
| this.connected();
| |
| break;
| |
| case 'disconnected':
| |
| this.disconnected();
| |
| break;
| |
| default:
| |
| break;
| |
| }
| |
| },
| |
| </pre>
| |
|
| |
| === 3G Data ===
| |
|
| |
| There is a RIL message that places a "data call" to the cellular tower, which enables data-transfer mode in the modem. This data call ends up creating/activating a PPP interface device in the linux kernel that can be configured through usual interfaces. '''TODO WRITEME'''
| |
|
| |
| === Related DOM APIs ===
| |
|
| |
| * Telephony
| |
| ** [https://wiki.mozilla.org/WebAPI/WebTelephony Web API discussion/proposal]
| |
| ** [https://mxr.mozilla.org/mozilla-central/source/dom/telephony/ Implementation]
| |
| * SMS
| |
| ** [https://wiki.mozilla.org/WebAPI/WebSMS Web API discussion/proposal]
| |
| ** [https://mxr.mozilla.org/mozilla-central/source/dom/sms/src/ril Implementation]
| |
| * Mobile Connection info
| |
| ** [https://wiki.mozilla.org/WebAPI/WebMobileConnection Web API discussion/proposal]
| |
| ** [https://mxr.mozilla.org/mozilla-central/source/dom/network Implementation]
| |
|
| |
| == WiFi ==
| |
|
| |
| Note: Much of the interesting stuff here depends deeply on the possible
| |
| state changes in wpa_supplicant.
| |
|
| |
| === Overview ===
| |
|
| |
| The wifi backend for B2G simply uses wpa_supplicant to do all of the
| |
| heavy lifting. That means that its main purpose is to simply manage
| |
| supplicant (and do some auxiliary tasks like loading the wifi driver and
| |
| enabling or disabling the network interface). Effectively, this means
| |
| that the backend is a state machine, with the states following the state
| |
| of the supplicant. Bugs in the backend tend to stem from the supplicant
| |
| following a state change that the code wasn't prepared to deal with.
| |
|
| |
| The implementation of the wifi component is broken up in two files:
| |
| - DOMWifiManager.js - The implementation of the API exposed to web
| |
| pages (defined in nsIWifi.idl).
| |
| - WifiWorker.js - The implementation of the state machine and the code
| |
| that drives the supplicant.
| |
|
| |
| The two files talk to each other via the message manager. The backend
| |
| listens for messages requesting certain actions, such as associate and
| |
| responds with a message when it's done. The DOM side listens for the
| |
| response methods as well as several "event" messages indicating state
| |
| changes and information updates. Note that one side effect of this
| |
| communication is that any synchronous DOM APIs are implemented by
| |
| caching data on that side of the pipe. In general, we avoid synchronous
| |
| messages.
| |
|
| |
| === WifiWorker.js ===
| |
|
| |
| This file implements the main logic behind the wifi. That means that it
| |
| runs in the chrome process (in e10s builds) and is instantiated by the
| |
| SystemWorkerManager. The file is generally broken into two sections: a
| |
| giant anonymous function and WifiWorker (and its prototype). The giant
| |
| anonymous function, ends up being the WifiManager. It provides a local
| |
| API including notifications for events like connection to the supplicant
| |
| and scan results being available. In general, it contains relatively
| |
| little logic, letting its one consumer "drive" while it notifies it of
| |
| events and controls the details of the connection with the supplicant.
| |
|
| |
| The second part of WifiWorker.js sits between the WifiManager and the
| |
| DOM. It reacts to events and forwards them to the DOM and it receives
| |
| requests from the DOM and performs the appropriate actions on the
| |
| supplicant. It also maintains state about the wpa_supplicant and what
| |
| it needs to do next.
| |
|
| |
| === DOMWifiManager.js ===
| |
|
| |
| This file implements the DOM API, ferrying messages back and forth to
| |
| the actual worker. There is very little logic in this file. That being
| |
| said: one note: in order to avoid synchronous messages to the chrome
| |
| process, we do need to cache the state based on the event that came in.
| |
| There is a single synchronous message, sent at the time that the DOM API
| |
| is instantiated in order to get the current state of the supplicant.
| |
|
| |
| === MISC ===
| |
|
| |
| DHCP: DHCP (and DNS) is handled by dhcpcd (the standard Linux DHCP
| |
| client). However, it isn't able to react when we lose the connection to
| |
| a network. So we kill and restart dhcpcd for each time we connect to a
| |
| given wireless network. dhcpcd is also responsible for setting the default
| |
| route. We call into the network manager in order to tell the kernel about
| |
| DNS servers.
| |
|
| |
| == Network Manager ==
| |
|
| |
| The network manager configures network interfaces opened by the 3g-data and wifi components. '''TODO'''
| |
|
| |
| == Threading ==
| |
|
| |
| Gabriele Svelto had an informative post about threading on the dev-b2g
| |
| mailing list [https://groups.google.com/d/msg/mozilla.dev.platform/dmip1GpD5II/WRitvuj4b2gJ here]:
| |
|
| |
| <pre>
| |
| FxOS uses the regular Linux scheduler for handling threads; this includes
| |
| the main thread of each application as well as DOM workers and other helper
| |
| threads.
| |
|
| |
| Currently we adjust nice values to prioritize process and thread
| |
| execution. Depending on the status of a process we assign it a different
| |
| nice level. We've currently got 7 levels:
| |
|
| |
| MASTER - nice 0, used for the main b2g process
| |
| FOREGROUND_HIGH - nice 0, used for processes holding a CPU wakelock
| |
| FOREGROUND - nice 1, used for foreground processes
| |
| FOREGROUND_KEYBOARD - nice 1, used for the keyboard app
| |
| BACKGROUND_PERCEIVABLE - nice 7, used for background processes playing audio
| |
| BACKGROUND_HOMESCREEN - nice 18, used for the homescreen app
| |
| BACKGROUND - nice 18, used for all other background apps
| |
|
| |
| As you can see some levels share the same nice values, that's because
| |
| those levels currently differ in the way they're treated by the
| |
| out-of-memory killer. All those values can be adjusted at build time via
| |
| preferences, you can find those here:
| |
|
| |
| http://hg.mozilla.org/mozilla-central/file/54e8c6492dc4/b2g/app/b2g.js#l610
| |
|
| |
| Within a process the main thread receives the nice value of the process
| |
| whilst DOM worker threads receive a nice value that is one point higher
| |
| than the main thread (thus they run at a lower priority than the main
| |
| thread).
| |
|
| |
| If you're interested how process priorities are handled you can find the
| |
| relevant code here:
| |
|
| |
| http://hg.mozilla.org/mozilla-central/file/54e8c6492dc4/hal/HalTypes.h#l79
| |
| http://hg.mozilla.org/mozilla-central/file/54e8c6492dc4/dom/ipc/ProcessPriorityManager.h
| |
| http://hg.mozilla.org/mozilla-central/file/54e8c6492dc4/dom/ipc/ProcessPriorityManager.cpp
| |
|
| |
| We don't use cgroups as Thomas mentioned because we found them to be
| |
| hopelessly broken on certain kernels and we couldn't rely on them for a
| |
| solid implementation.
| |
| </pre>
| |