What is platform evolution?
Fuchsia is built from the kernel up to meet the needs of today's growing ecosystem of connected devices. Product owners may create different products on top of the Fuchsia platform to meet user needs. Product requirements evolve over time, and so Fuchsia must be able to evolve with them.
New platform releases may deliver new features and bug fixes. Product owners that integrate with the Fuchsia platform may then need to rebase their existing product assembly on top of the new platform build. Depending on the nature of the product and how updates are delivered, end users might even receive platform updates directly from the Fuchsia project. Depending on the nature of the product being assembled, the product owner may not be able to port existing application software to new platform versions, and must rely on existing application prebuilts continuing to work against new platform prebuilts.
Fuchsia is designed to enable the various elements of the platform to evolve over time while supporting existing and new products. Different software vendors that are involved in the product lifecycle may have their own development and release schedules independent of each other. This document explains the mechanisms that support this decoupled evolution.
How does platform evolution work?
Fuchsia has multiple mechanisms that promote the platform’s ability to evolve over time. Below we survey the most prominent mechanisms and some of their applications.
Strictly defined interfaces
Interfaces act as contracts between different pieces of software. Fuchsia defines such contracts between the platform and the software that it runs. The platform’s Application Binary Interface (ABI) surface is precisely defined and enumerated. The ABI surface includes entry points into the kernel, all interactions with platform services, and other conventions and protocols. Developers can write software to interact with Fuchsia such as by using a Software Development Kit (SDK) that’s based on the Fuchsia Integrator Development Kit (IDK). The Fuchsia IDK includes interface definitions and client libraries that offer an Application Programming Interface (API) derived from the same platform contracts.
Interface definition languages
Fuchsia ABIs are largely defined in terms of the Fuchsia Interface Definition Language (FIDL).
- System calls (syscalls) and the syscall ABI in the kernel vDSO are expressed in FIDL. Note, the structures passed to and from syscalls are not currently defined in FIDL.
- A broad range of FIDL protocols are used to communicate with usermode platform services.
- Common APIs are implemented in terms of FIDL.
The FIDL toolchain can generate binding code for clients and servers in a number of programming languages. The toolchain is designed to make it easy to extend support for more languages.
FIDL is designed to support cross-version compatibility and to ease interface evolution. FIDL has compatibility guarantees and lists exactly what changes are binary-compatible (ABI-stable) and/or source-compatible (API-stable).
As an interface definition language, FIDL has special affordances to help developers change protocols and types over time while maintaining backward and forward compatibility. These are sometimes referred to as soft transitions.
- Developers may add and remove methods over time.
- Developers may use flexible union types, allowing old clients to ignore new data.
- Developers may rename types without breaking ABI compatibility.
By defining much of the Fuchsia system interface in terms of FIDL, especially the parts that are expected to evolve over time, Fuchsia can take greater advantage of FIDL’s special affordances for evolution.
Versioning and compatibility metadata
Fuchsia defines a platform versioning scheme to denote API levels for the Fuchsia IDK and ABI revisions to the Fuchsia platform. Each release of the Fuchsia IDK or of the Fuchsia platform may introduce API or ABI revisions respectively, in which case the release is denoted with an incremented version. Versioned releases may support a range of versions as a backward/forward compatibility window.
Properties of a Fuchsia interface may be annotated with their respective versioning metadata.
- A property is added, and may have been removed, at given versions. This defines a support window for that property in terms of numbered versions.
- A property may be marked as deprecated at a given version. Deprecation indicates an intent to ultimately remove a property, but does not affect the ABI.
- A deprecated property may carry an additional note in human-readable form. Often the note indicates to developers what they should do to avoid breakage once the property is removed.
Different elements of an interface may be annotated differently. For instance different methods in a protocol or fields in an extensible union may have been added, deprecated, or removed at different versions.
FIDL being the common language for interface definition on Fuchsia supports versioning annotations. To help developers who are making changes to platform FIDL files detect that their changes introduce potentially-incompatible revisions to the API, API summaries are generated from FIDL files. Summaries can be compared against saved references (i.e. golden files) to identify breaking changes and to ensure that they are introduced with explicit intent.
Traditionally, a process is a container for threads and protected resources such as virtual memory regions. On Fuchsia, processes may additionally be assigned a local namespace. The namespace is the foundation of the sandbox that ensures that programs can only access the resources that it’s been given, in the terms that they’ve been given - for instance as kernel objects, as FIDL protocols, or as files. These form different capabilities that may be used by the program at runtime.
A sandbox is commonly defined in terms of a component manifest. A collection of component manifests may define what capabilities are present in the sandbox, and how these capabilities are satisfied. However these details are not visible to the component inside the sandbox. The parent of a component may route a capability to its child that’s offered by one component or another, changing the implementation details over time, but leaving the shape of the sandbox unchanged.
Sandboxing promotes loose coupling and allows implementation details to evolve without their clients being aware. For instance when Netstack3 replaces Netstack2, components that use networking capabilities ideally won’t notice the difference. This also helps with testing, since test authors may inject a test double to a component, undetected by the component under test.
Packaging collaborates with namespacing to create a form of sandboxing. A component that's resolved from a package will have access to the packaged contents in its namespace. As a result, component authors may package additional files with their components, such as localized assets. Conversely, they may not normally access files from other packages directly. For instance system fonts are provided via a FIDL protocol, not as direct file access. Alternatively developers may package their own font files.
If the use of a given interface between two systems can be circumvented, then the two systems may become more tightly coupled and lose the ability to evolve independently of each other. Fuchsia uses various mechanisms to enforce that platform interfaces are observed and respected.
- The kernel defines an ABI for entering the kernel from usermode code, i.e. the syscall ABI. However that ABI is between the kernel and the vDSO. Applications are required to call into the vDSO’s exported symbols, not to perform syscalls directly. Since the kernel and the vDSO may be versioned together, requiring applications to call into the vDSO allows the syscall ABI to evolve seamlessly, undetected by application developers. To enforce this property, the kernel checks the caller’s address at syscall entry points and ensures that it falls within the range where the vDSO is mapped in the caller’s address space.
- The kernel offers a syscall for process creation. However the details of program loading are complex, so they're abstracted away behind a process launcher implementation. To ensure that application developers aren't exposed to these details, components are not allowed to create processes directly and instead may be offered a process launcher behind a FIDL protocol. This allows details of program loading to evolve over time.
- Any filesystem directory may be used as the root of a process namespace. These namespaces ensure that programs have access only to a known and enumerated set of files, preventing them from forming unintended ABIs. To ensure that directory paths act as inescapable roots, Fuchsia filesystems do not implement “..”.