0

Update service-related documentation

This wipes out obsolete references to Service Manager in service-related
documentation and replaces it with simpler docs reflecting the new
service model.

Bug: None
Change-Id: Iab5662bcc48b34954a9003532cd407058e470282
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2063773
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: Oksana Zhuravlova <oksamyt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#742677}
This commit is contained in:
Ken Rockot
2020-02-19 17:09:55 +00:00
committed by Commit Bot
parent a9ad696264
commit 216eb5de77
5 changed files with 118 additions and 1529 deletions

@ -368,8 +368,6 @@ used when committed.
to Mojo and services in Chromium, with examples
* [Mojo API Reference](/mojo/README.md) - Detailed reference documentation for
all things Mojo
* [The Service Manager &amp; Services](/services/service_manager/README.md) -
Services system overview, API references, example services
* [Service Development Guidelines](/services/README.md) - Guidelines for
service development in the Chromium tree
* [Servicifying Chromium Features](servicification.md) - General advice for

@ -242,51 +242,23 @@ Content browser/renderer/gpu/utility process split.
A **service** is a self-contained library of code which implements one or more
related features or behaviors and whose interaction with outside code is done
*exclusively* through Mojo interface connections facilitated by the **Service
Manager.**
*exclusively* through Mojo interface connections, typically brokered by the
browser process.
The **Service Manager** is a component which can run in a dedicated process
or embedded within another process. Only one Service Manager exists globally
across the system, and in Chromium the browser process runs an embedded Service
Manager instance immediately on startup. The Service Manager spawns
**service instances** on-demand, and it routes each interface request from a
service instance to some destination instance of the Service Manager's choosing.
Each service instance implements the
[**`Service`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h)
interface to receive incoming interface requests brokered by the Service
Manager, and each service instance has a
[**`Connector`**](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h)
it can use to issue interface requests to other services via the
Service Manager.
Every service has a **manifest** which declares some static metadata about the
service. This metadata is used by the Service Manager for various purposes,
including as a declaration of what interfaces are exposed to other services in
the system. This eases the security review process.
Inside its manifest every service declares its **service name**, used to
identify instances of the service in the most general sense. Names are free-form
and usually short strings which must be globally unique. Some services defined
in Chromium today include `"device"`, `"identity"`, and `"network"` services.
For more complete and in-depth coverage of the concepts covered here and other
related APIs, see the
[Service Manager documentation](/services/service_manager/README.md).
Each service defines and implements a main Mojo interface which can be used
by the browser to manage an instance of the service.
## Example: Building a Simple Out-of-Process Service
There are multiple steps required to get a new service up and running in
Chromium. You must:
There are multiple steps typically involved to get a new service up and running
in Chromium:
- Define the `Service` implementation
- Define the service's manifest
- Tell Chromium's Service Manager about the manifest
- Tell Chromium how to instantiate the `Service` implementation when it's needed
- Define the main service interface and implementation
- Hook up the implementation in out-of-process code
- Write some browser logic to launch a service process
This section walks through these steps with some brief explanations. For more
thorough documentation of the concepts and APIs used herein, see the
[Service Manager](/services/service_manager/README.md) and
[Mojo](/mojo/README.md) documentation.
### Defining the Service
@ -299,20 +271,10 @@ define a new service for use by Chrome specifically, so we'll define it within
We can create the following files. First some mojoms:
``` cpp
// src/chrome/services/math/public/mojom/constants.mojom
// src/chrome/services/math/public/mojom/math_service.mojom
module math.mojom;
// These are not used by the implementation directly, but will be used in
// following sections.
const string kServiceName = "math";
const string kArithmeticCapability = "arithmetic";
```
``` cpp
// src/chrome/services/math/public/mojom/divider.mojom
module math.mojom;
interface Divider {
interface MathService {
Divide(int32 dividend, int32 divisor) => (int32 quotient);
};
```
@ -323,46 +285,32 @@ import("//mojo/public/tools/bindings/mojom.gni")
mojom("mojom") {
sources = [
"constants.mojom",
"divider.mojom",
"math_service.mojom",
]
}
```
Then the actual `Service` implementation:
Then the actual `MathService` implementation:
``` cpp
// src/chrome/services/math/math_service.h
#include "services/service_manager/public/cpp/service.h"
#include "base/macros.h"
#include "chrome/services/math/public/mojom/divider.mojom.h"
#include "chrome/services/math/public/mojom/math_service.mojom.h"
namespace math {
class MathService : public service_manager::Service,
public mojom::Divider {
class MathService : public mojom::MathService {
public:
explicit MathService(service_manager::mojom::ServiceRequest request);
explicit MathService(mojo::PendingReceiver<mojom::MathService> receiver);
~MathService() override;
private:
// service_manager::Service:
void OnBindInterface(const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override;
// mojom::Divider:
// mojom::MathService:
void Divide(int32_t dividend,
int32_t divisor,
DivideCallback callback) override;
service_manager::ServiceBinding service_binding_;
// You could also use a Receiver. We use ReceiverSet to conveniently allow
// multiple clients to bind to the same instance of this class. See Mojo
// C++ Bindings documentation for more information.
mojo::ReceiverSet<mojom::Divider> divider_receivers_;
mojo::Receiver<mojom::MathService> receiver_;
DISALLOW_COPY_AND_ASSIGN(MathService);
};
@ -376,23 +324,11 @@ class MathService : public service_manager::Service,
namespace math {
MathService::MathService(service_manager::ServiceRequest request)
: service_binding_(this, std::move(request)) {}
MathService::MathService(mojo::PendingReceiver<mojom::MathService> receiver)
: receiver_(this, std::move(receiver)) {}
MathService::~MathService() = default;
void MathService::OnBindInterface(
const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) {
// Note that services typically use a service_manager::BinderRegistry if they
// plan on handling many different interface request types.
if (interface_name == mojom::Divider::Name_) {
divider_receivers_.Add(
this, mojo::PendingReceiver<mojom::Divider>(std::move(interface_pipe)));
}
}
void MathService::Divide(int32_t dividend,
int32_t divisor,
DivideCallback callback) {
@ -415,321 +351,81 @@ source_set("math") {
deps = [
"//base",
"//chrome/services/math/public/mojom",
"//services/service_manager/public/cpp",
]
}
```
Now we have a fully defined `math` service implementation, including a nice
little `Divider` interface for clients to play with. Next we need to define the
service's manifest to declare how the service can be used.
### Defining the Manifest
Manifests are defined as
[`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h)
objects, typically built using a
[`ManifestBuilder`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h). As a general rule, services should define their manifest
in a dedicated `source_set` or `component` target under their `public/cpp`
subdirectory (typically referred to as the service's **C++ client library**).
We can create the following files for this purpose:
``` cpp
// src/chrome/services/math/public/cpp/manifest.h
#include "services/service_manager/public/cpp/manifest.h"
namespace math {
const service_manager::Manifest& GetManifest();
} // namespace math
```
``` cpp
// src/chrome/services/math/public/cpp/manifest.cc
#include "chrome/services/math/public/cpp/manifest.h"
#include "base/no_destructor.h"
#include "chrome/services/math/public/mojom/constants.mojom.h"
#include "chrome/services/math/public/mojom/divider.mojom.h"
#include "services/service_manager/public/cpp/manifest_builder.h"
namespace math {
const service_manager::Manifest& GetManifest() {
static base::NoDestructor<service_manager::Manifest> manifest{
service_manager::ManifestBuilder()
.WithServiceName(mojom::kServiceName)
.ExposeCapability(
mojom::kArithmeticCapability,
service_manager::Manifest::InterfaceList<mojom::Divider>())
.Build()};
return *manifest
}
} // namespace math
```
We also need to define a build target for our manifest sources:
``` python
# src/chrome/services/math/public/cpp/BUILD.gn
source_set("manifest") {
sources = [
"manifest.cc",
"manifest.h",
]
deps = [
"//base",
"//chrome/services/math/public/mojom",
"//services/service_manager/public/cpp",
]
}
```
The above `Manifest` definition declares that the service is named `math` and
that it **exposes** a single **capability** named `arithmetic` which allows
access to the `Divider` interface.
Another service may **require** this capability from its own manifest in order
for the Service Manager to grant it access to a `Divider`. We'll see this a
few sections below. First, let's get the manifest and service implementation
registered with Chromium's Service Manager.
### Registering the Manifest
For the most common out-of-process service cases, we register service manifests
by **packaging** them in Chrome. This can be done by augmenting the value
returned by
[`GetChromePackagedServiceManifests`](https://cs.chromium.org/chromium/src/chrome/app/chrome_packaged_service_manifests.cc?rcl=af43cabf3c01e28be437becb972a7eae44fd54e8&l=133).
We can add our manifest there:
``` cpp
// Deep within src/chrome/app/chrome_packaged_service_manifests.cc...
const std::vector<service_manager::Manifest>
GetChromePackagedServiceManifests() {
...
math::GetManifest(),
...
```
And don't forget to add a GN dependency from
[`//chrome/app:chrome_packaged_service_manifests`](https://cs.chromium.org/chromium/src/chrome/app/BUILD.gn?l=564&rcl=a77d5ba9c4621cfe14e7e1cd03bbae16904f269e) onto
`//chrome/services/math/public/cpp:manifest`!
We're almost done with service setup. The last step is to teach Chromium (and
thus the Service Manager) how to launch an instance of our beautiful `math`
service.
Now we have a fully defined `MathService` implementation that we can make
available in- or out-of-process.
### Hooking Up the Service Implementation
There are two parts to this for an out-of-process Chrome service.
First, we need
to inform the embedded Service Manager that this service is an out-of-process
service. The goofiness of this part is a product of some legacy issues and it
should be eliminated soon, but for now it just means teaching the Service
Manager how to *label* the process it creates for this service (e.g. how the process will
appear in the system task manager). We modify
[`ChromeContentBrowserClient::RegisterOutOfProcessServices`](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=960886a7febcc2acccea7f797d3d5e03a344a12c&l=3766)
for this:
For an out-of-process Chrome service, we simply register a factory function
in [`//chrome/utility/services.cc`](https://cs.chromium.org/chromium/src/chrome/utility/services.cc).
``` cpp
void ChromeContentBrowserClient::RegisterOutOfProcessServices(
OutOfProcessServicesMap* services) {
...
(*services)[math::mojom::kServiceName] =
base::BindRepeating([]() -> base::string16 {
return "Math Service";
});
...
auto RunMathService(mojo::PendingReceiver<math::mojom::MathService> receiver) {
return std::make_unique<math::MathService>(std::move(receiver));
}
mojo::ServiceFactory* GetMainThreadServiceFactory() {
// Existing factories...
static base::NoDestructor<mojo::ServiceFactory> factory {
RunFilePatcher,
RunUnzipper,
// We add our own factory to this list
RunMathService,
//...
```
And finally, since nearly all out-of-process services run in a "utility" process
today, we need to add a dependency on our actual `Service` implementation to
Chrome's service spawning code within the utility process.
With this done, it is now possible for the browser process to launch new
out-of-process instances of MathService.
For this step we just modify
[`ChromeContentUtilityClient::MaybeCreateMainThreadService`](https://cs.chromium.org/chromium/src/chrome/utility/chrome_content_utility_client.cc?rcl=7226adebd6e8d077d673a82acf1aab0790627178&l=261)
by adding a block of code as follows:
### Launching the Service
If you're running your service in-process, there's really nothing interesting
left to do. You can instantiate the service implementation just like any other
object, yet you can also talk to it via a Mojo Remote as if it were
out-of-process.
To launch an out-of-process service instance after the hookup performed in the
previous section, use Content's
[`ServiceProcessHost`](https://cs.chromium.org/chromium/src/content/public/browser/service_process_host.h?rcl=e7a1f6c9a24f3151c875598174a05167fb12c5d5&l=47)
API:
``` cpp
std::unique_ptr<service_manager::Service> ChromeContentUtilityClient::MaybeCreateMainThreadService(
const std::string& service_name,
service_manager::mojom::ServiceRequest request) {
...
if (service_name == math::mojom::kServiceName)
return std::make_unique<math::MathService>(std::move(request));
...
}
mojo::Remote<math::mojom::MathService> math_service =
content::ServiceProcessHost::Launch<math::mojom::MathService>(
content::ServiceProcessHost::LaunchOptions()
.WithSandboxType(content::SandboxType::kUtility)
.WithDisplayName("Math!")
.Pass());
```
And we're done!
Except in the case of crashes, the launched process will live as long as
`math_service` lives. As a corollary, you can force the process to be torn
down by destroying (or resetting) `math_service`.
As one nice follow-up step, let's use our math service from the browser.
### Using the Service
We can grant the browser process access to our `Divider` interface by
**requiring** the `math` service's `arithmetic` capability within the
`content_browser` service manifest.
*** aside
NOTE: See the following section for an elaboration on what `content_browser` is.
For the sake of this example, it's magic.
***
For Chrome-specific features such as our glorious new `math` service, we can
amend the `content_browser` manifest by modifying
[GetChromeContentBrowserOverlayManifest](https://cs.chromium.org/chromium/src/chrome/app/chrome_content_browser_overlay_manifest.cc?rcl=38db90321e8e3627b2f3165cdb051fa8d668af48&l=100)
as follows:
We can now perform an out-of-process division:
``` cpp
// src/chrome/app/chrome_content_browser_overlay_manifest.cc
...
const service_manager::Manifest& GetChromeContentBrowserOverlayManifest() {
...
.RequireCapability(math::mojom::kServiceName,
math::mojom::kArithmeticCapability)
...
}
```
Finally, we can use the global `content_browser` instance's `Connector` to send
an interface request to our service. This is accessible from the main thread of
the browser process. Somewhere in `src/chrome/browser`, we can write:
``` cpp
// This gives us the system Connector for the browser process, which has access
// to most service interfaces.
service_manager::Connector* connector = content::GetSystemConnector();
// Recall from the earlier Mojo section that BindNewPipeAndPassReceiver()
// creates a message pipe and yields a PendingReceiver as its return value.
// Connector passes the receiver endpoint to the Service Manager along with
// the name of our target service, "math".
mojo::Remote<math::mojom::Divider> divider;
connector->Connect(math::mojom::kServiceName,
divider.BindNewPipeAndPassReceiver());
// As a client, we do not have to wait for any acknowledgement or confirmation
// of a connection. We can start queueing messages immediately and they will be
// delivered as soon as the service is up and running.
divider->Divide(
// NOTE: As a client, we do not have to wait for any acknowledgement or
// confirmation of a connection. We can start queueing messages immediately and
// they will be delivered as soon as the service is up and running.
math_service->Divide(
42, 6, base::BindOnce([](int32_t quotient) { LOG(INFO) << quotient; }));
```
*** aside
NOTE: To ensure the execution of the response callback, the
mojo::Remote<Divider> object must be kept alive (see
`mojo::Remote<math::mojom::MathService>` object must be kept alive (see
[this section](/mojo/public/cpp/bindings/README.md#A-Note-About-Endpoint-Lifetime-and-Callbacks)
and [this note from an earlier section](#sending-a-message)).
***
This should successfully spawn a new process to run the `math` service if it's
not already running, then ask it to do a division, and ultimately log the result
after it's sent back to the browser process.
Finally it's worth reiterating that every service instance in the system has
its own `Connector` and there's no reason we have to limit ourselves to
`content_browser` as the client, as long as the appropriate manifest declares
that it requires our `arithmetic` capability.
If we did not update the `content_browser` manifest overlay as we did in this
example, the `Divide` call would never reach the `math` service (in fact the
service wouldn't even be started) and instead we'd get an error message (or in
developer builds, an assertion failure) informing us that the Service Manager
blocked the `Connect` call.
## Content-Layer Services Overview
Apart from very early initialization steps in the browser process, every bit of
logic in Chromium today is effectively running as part of one service instance
or another.
Although we continue to migrate parts of the browser's privileged
functionality to more granular services defined below the Content layer, the
main services defined in Chromium today continue to model the Content layer's
classical multiprocess architecture which defines a handful of
**process types**: browser, renderer, gpu, utility, and plugin processes. For
each of these process types, we now define corresponding services.
Manifest definitions for all of the following services can be found in
`//content/public/app`.
### The Browser Service
`content_browser` is defined to encapsulate general-purpose browser process
code. There are multiple instances of this service, all running within the
singular browser process. There is one shared global instance as well an
additional instance for each `BrowserContext` (*i.e.* per Chrome profile).
The global instance exists primarily so that arbitrary browser process code can
reach various system services conveniently via a global `Connector` instance
on the main thread.
Each instance associated with a `BrowserContext` is placed in an isolated
instance group specific to that `BrowserContext`. This limits the service
instances with which its `Connector` can make contact. These instances are
used primarily to facilitate the spawning of other isolated per-profile service
instances, such as renderers and plugins.
### The Renderer Service
A `content_renderer` instance is spawned in its own sandboxed process for every
site-isolated instance of Blink we require. Instances are placed in the same
instance group as the renderer's corresponding `BrowserContext`, *i.e.* the
profile which navigated to the site being rendered.
Most interfaces used by `content_renderer` are not brokered through the Service
Manager but instead are brokered through dedicated interfaces implemented by
`content_browser`, with which each renderer maintains persistent connections.
### The GPU Service
Only a single instance of `content_gpu` exists at a time and it always runs in
its own isolated, sandboxed process. This service hosts the code in content/gpu
and whatever else Content's embedder adds to that for GPU support.
### The Plugin Service
`content_plugin` hosts a plugin in an isolated process. Similarly to
`content_renderer` instances, each instance of `content_plugin` belongs to
an instance group associated with a specific `BrowserContext`, and in general
plugins get most of their functionality by talking directly to `content_browser`
rather than brokering interface requests through the Service Manager.
### The Utility Service
`content_utility` exists only nominally today, as there is no remaining API
surface within Content which would allow a caller to explicitly create an
instance of it. Instead, this service is used exclusively to bootstrap new
isolated processes in which other services will run.
## Exposing Interfaces Between Content Processes
Apart from the standard Service Manager APIs, the Content layer defines a number
of additional concepts for Content and its embedder to expose interfaces
specifically between Content processes in various contexts.
### Exposing Browser Interfaces to Renderer Documents and Workers
Documents and workers are somewhat of a special case since interface access
decisions often require browser-centric state that the Service Manager cannot
know about, such as details of the current `BrowserContext`, the origin of the
renderered content, installed extensions in the renderer, *etc.* For this
reason, interface brokering decisions are increasingly being made by the
browser.
#### Interface Brokers
### Interface Brokers
We define an explicit mojom interface with a persistent connection
between a renderer's frame object and the corresponding
@ -760,53 +456,6 @@ void PopulateFrameBinders(RenderFrameHostImpl* host,
TODO: add information about workers and embedders.
### Exposing Browser Interfaces to Render Processes
Sometimes (albeit rarely) it's useful to expose a browser interface directly to
a renderer process. This can be done as for any other interface exposed between
two services. In this specific instance, the `content_browser` manifest exposes
a capability named `"renderer"` which `content_renderer` requires. Any interface
listed as part of that capability can be accessed by a `content_renderer`
instance by using its own `Connector`. See below.
### Exposing Browser Interfaces to Content Child Processes
All Content child process types (renderer, GPU, and plugin) share a common API
to interface with the Service Manager. Their Service Manager connection is
initialized and maintained by `ChildThreadImpl` on process startup, and from
the main thread, you can access the process's `Connector` as follows:
``` cpp
auto* connector = content::ChildThread::Get()->GetConnector();
// For example...
connector->Connect(content::mojom::kBrowserServiceName,
std::move(some_receiver));
```
### Exposing Content Child Process Interfaces to the Browser
Content child processes may also expose interfaces to the browser, though this
is much less common and requires a fair bit of caution since the browser must be
careful to only call `Connector.BindInterface` in these cases with an exact
`service_manager::Identity` to avoid unexpected behavior.
Every child process provides a subclass of ChildThreadImpl, and this can be used
to install a new `ConnectionFilter` on the process's Service Manager connection
before starting to accept requests.
This behavior should really be considered deprecated, but for posterity, here is
how the GPU process does it:
1. [Disable Service Manager connection auto-start](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=62)
2. [Register a new ConnectionFilter impl to handle certain interface requests](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=255)
3. [Start the Service Manager connection manually](https://cs.chromium.org/chromium/src/content/gpu/gpu_child_thread.cc?rcl=6b85a56334c0cd64b0e657934060de716714ca64&l=257)
It's much more common instead for there to be some primordial interface
connection established by the child process which can then be used to facilitate
push communications from the browser, so please consider not duplicating this
behavior.
## Additional Support
If this document was not helpful in some way, please post a message to your

@ -28,26 +28,15 @@ command-line switch. Client code using the Network Service stays the same,
independent of that switch.
This document focuses on helpful guidelines and patterns for servicifying parts
of Chromium, taking into account some nuances in how Chromium models its core
services as well as how it embeds and configures the Service Manager. Readers
are strongly encouraged to first read some basic
[Service Manager documentation](/services/service_manager/README.md), as it will
likely make the contents of this document easier to digest.
of Chromium.
Also see general [Mojo &amp; Services](/docs/README.md#Mojo-Services)
documentation for other introductory guides, API references, *etc.*
## Setting Up The Service
There are three big things you must decide when building and hooking up a shiny
new service:
- Where should the service live in the tree?
- Do you need an instance of your service per BrowserContext?
- Can Content depend on your service, or must Content embedders like Chrome do
so independently?
This section aims to help you understand and answer those questions.
This section briefly covers early decisions and implementation concerns when
introducing a new service.
### Where in the Tree?
@ -72,115 +61,51 @@ Other common places where developers place services, and why:
just Chrome itself (for example, if the `ash` service must also connect to
them for use in system UI).
### Inside Content or Not?
### Launching Service Processes
The next decision you need to make is whether or not Content will wire in your
service directly -- that is, whether or not your service is necessary to support
some subsystem Content makes available to either the web platform or to Content
embedders like Chrome, Android WebView, Cast Shell, and various third-party
applications.
Content provides a simple
[`ServiceProcessHost`](https://cs.chromium.org/chromium/src/content/public/browser/service_process_host.h?rcl=723edf64a56ef6058e886afc67adc786bea39e78&l=47)
API to launch a new Service Process. The Mojo Remote corresponding to each
process launch is effectively a lifetime control for the launched process.
For example, Content cannot function at all without the Network Service being
available, because Content depends heavily on the Network Service to issue and
process all of its network requests (imagine that, right?). As such, the
Network Service is wired up to the Service Manager from within Content directly.
In general, services which will be wired up in Content must live either in
`//services` or `//components/services` but ideally the former.
You may choose to maintain only a single concurrent instance of your service
at a time, similar to the Network or Storage services. In this case, typically
you will have some browser code maintain a lazy Mojo Remote to the service
process, and any clients of the service will have their connections brokered
through this interface.
Conversely there are a large number of services used only by Chrome today,
such as the `unzip` service which safely performs sandboxed unpacking of
compressed archive files on behalf of clients in the browser process. These
can always be placed in `//chrome/services`.
In other cases you may want to manage multiple independent service processes.
The Data Decoder service, for example, allows for arbitrary browser code
to launch a unique isolated instance to process a single decode operation or
a batch of related operations (e.g. to decode a bunch of different objects
from the same untrusted origin).
### Per-BrowserContext or Not?
Insofar as the browser can use ServiceProcessLauncher however it likes, and the
corresponding Mojo Remotes can be owned just like any other object, developers
are free to manage their service instances however they like.
Now that you've decided on a source location for your service and you know
whether it will be wired into Content or hooked up by Content embedder code, all
that's left left is to decide whether or not you want an instance of your service
per BrowserContext (*i.e.* per user profile in Chrome).
### Hooking Up the Service Implementation
The alternative is for you to manage your own instance arity, either as a
singleton service (quite common) or as a service which supports multiple
instances that are *not* each intrinsically tied to a BrowserContext. Most
services choose this path because BrowserContext coupling is typically
unnecessary.
For out-of-process service launching, Content uses its "utility" process type.
As a general rule, if you're porting a subsystem which today relies heavily
on `BrowserContextKeyedService`, it's likely that you want your service
instances to have a 1:1 correspondence with BrowserContext instances.
For services known to content, this is accomplished by adding an appropriate
factory function to
[`//content/utility/services.cc`](https://cs.chromium.org/chromium/src/content/utility/services.cc)
### Putting It All Together
For other services known only to Chrome, we have a similar file at
[`//chrome/utility/services.cc`](https://cs.chromium.org/chromium/src/chrome/utility/services.cc).
Let's get down to brass tacks. You're a developer of action. You've made all the
important choices you need to make and you've even built a small and extremely
well-tested prototype service with the help of
[this glorious guide](/services/service_manager/README.md#Services). Now you
want to get it working in Chromium while suffering as little pain as possible.
Once an appropriate service factory is registered for your main service
interface in one of these places, `ServiceProcessHost::Launch` can be used to
acquire a new isolated instance from within the browser process.
You're not going to believe it, but this section was written *just for YOU*.
To run a service in-process, you can simply instantiate your service
implementation (e.g. on a background thread) like you would any other object,
and you can then bind a Mojo Remote which is connected to that instance.
For services which are **are not** isolated per BrowserContext and which **can**
be wired directly into Content:
- Include your service's manifest in the `content_packaged_services` manifest
directly, similar to
[these ones](https://cs.chromium.org/chromium/src/content/public/app/content_packaged_services_manifest.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=63).
- If you want to run your service embedded in the browser process, follow the
examples using `RegisterInProcessService`
[here](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=589).
- If you want to run your service out-of-process, update
`out_of_process_services`
[like so](https://cs.chromium.org/chromium/src/content/browser/service_manager/service_manager_context.cc?rcl=0e8ac57eec2acfaa6f44b06eaa2fa667fe84a293&l=642)
and hook up your actual private `Service` implementation exactly like the
many examples
[here](https://cs.chromium.org/chromium/src/content/utility/utility_service_factory.cc?rcl=2bdcc80a55c72a26ffe9778681f98dc4b6a565c0&l=114).
For services which are **are** isolated per BrowserContext and which **can**
be wired directly into Content:
- Include your service's manifest in the `content_browser` manifest directly,
similar to
[these ones](https://cs.chromium.org/chromium/src/content/public/app/content_browser_manifest.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=277).
- If you want to run your service embedded in the browser process, follow the
example
[here](https://cs.chromium.org/chromium/src/content/browser/browser_context.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=250)
- If you want to run your service out-of-process, you are doing something that
hasn't been done yet and you will need to build a new thing.
For services which **are not** isolated per BrowserContext but which **can not**
be wired directly into Content:
- Include your service's manifest in Chrome's `content_packaged_services`
manifest overlay similar to
[these ones](https://cs.chromium.org/chromium/src/chrome/app/chrome_packaged_service_manifests.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=135)
- If you want to run your service embedded in the browser process, follow the
examples in `ChromeContentBrowserClient::HandleServiceRequest`
[here](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=3891)
- If you want to run your service out-of-process, modify
`ChromeContentBrowserClient::RegisterOutOfProcessServices` like the examples
[here](https://cs.chromium.org/chromium/src/chrome/browser/chrome_content_browser_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=3785)
and hook up your `Service` implementation in
`ChromeContentUtilityClient::HandleServiceRequest` like the ones
[here](https://cs.chromium.org/chromium/src/chrome/utility/chrome_content_utility_client.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=237).
For services which **are** isolated per BrowserContext but which **can not** be
wired directly into Content:
- Include your service's manifest in Chrome's `content_browser` manifest overlay
similar to
[these ones](https://cs.chromium.org/chromium/src/chrome/app/chrome_content_browser_overlay_manifest.cc?rcl=a651619623a7b56d0c21083463ef8e61bf0a6058&l=247)
- If you want to run your service embedded in the browser process, follow the
examples in `ProfileImpl::HandleServiceRequest`
[here](https://cs.chromium.org/chromium/src/chrome/browser/profiles/profile_impl.cc?rcl=350aea1a0242c2ea8610c9f755acee085c74ea7d&l=1263)
- If you want to run your service out-of-process, you are doing something that
hasn't been done yet and you will need to build a new thing.
*** aside
The non-Content examples above are obviously specific to Chrome as the embedder,
but Chrome's additions to supported services are all facilitated through the
common `ContentBrowserClient` and `ContentUtilityClient` APIs that all embedders
can implement. Mimicking what Chrome does should be sufficient for any embedder.
***
This is useful if you want to avoid the overhead of extra processes in some
scenarios, and it allows the detail of where and how the service runs to be
fully hidden behind management of the main interface's Mojo Remote.
## Incremental Servicification
@ -259,30 +184,9 @@ doubt, look approximately near the recommended bits of code and try to find
relevant prior art.
***
Services are supported on iOS, with the usage model in //ios/web being very
close to the usage model in //content. More specifically:
* To embed a global service in the browser service, override
[WebClient::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::Register&sq=package:chromium&l=136). For an example usage, see
[ShellWebClient](https://cs.chromium.org/chromium/src/ios/web/shell/shell_web_client.mm?q=ShellWebClient::RegisterS&sq=package:chromium&l=91)
and the related integration test that
[connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=89).
* To embed a per-BrowserState service, override
[BrowserState::RegisterServices](https://cs.chromium.org/chromium/src/ios/web/public/browser_state.h?q=BrowserState::RegisterServices&sq=package:chromium&l=89). For an
example usage, see
[ShellBrowserState](https://cs.chromium.org/chromium/src/ios/web/shell/shell_browser_state.mm?q=ShellBrowserState::RegisterServices&sq=package:chromium&l=48)
and the related integration test that
[connects to the embedded service](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=110).
* To register a per-frame Mojo interface, override
[WebClient::BindInterfaceRequestFromMainFrame](https://cs.chromium.org/chromium/src/ios/web/public/web_client.h?q=WebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=148).
For an example usage, see
[ShellWebClient](https://cs.chromium.org/chromium/src/ios/web/shell/shell_web_client.mm?type=cs&q=ShellWebClient::BindInterfaceRequestFromMainFrame&sq=package:chromium&l=115)
and the related integration test that
[connects to the interface](https://cs.chromium.org/chromium/src/ios/web/shell/test/service_manager_egtest.mm?q=service_manager_eg&sq=package:chromium&l=130).
Note that this is the equivalent of
[ContentBrowserClient::BindInterfaceRequestFromFrame()](https://cs.chromium.org/chromium/src/content/public/browser/content_browser_client.h?type=cs&q=ContentBrowserClient::BindInterfaceRequestFromFrame&sq=package:chromium&l=667),
as on iOS all operation "in the content area" is implicitly operating in the
context of the page's main frame.
Services are supported on iOS insofar as Mojo is supported. However, Chrome on
iOS is strictly single-process, and all services thus must run in-process on
iOS.
If you have a use case or need for services on iOS, contact
blundell@chromium.org. For general information on the motivations and vision for

@ -4,11 +4,6 @@
## Overview
If you're looking for general documentation on the Service Manager, what a
"service" is, and how to build one, see the
[Service Manager &amp; Services](/services/service_manager/README.md)
documentation instead of this document.
The top-level `//services` directory contains the sources, public Mojo interface
definitions, and public client libraries for a number of essential services,
designated as **Chrome Foundation Services**. If you think of Chrome as a
@ -17,9 +12,6 @@ services of that OS.
Each subdirectory here corresponds to a service that:
- implements
[`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h?rcl=ebec02bcca6e327b2e01855ce43ab1bec0aeef27&l=21)
and is thus a client of the Service Manager
- generally focuses on a subset of functionality or features which are
thematically or functionally related in a way that makes sense given the name
of the service
@ -29,16 +21,12 @@ Each subdirectory here corresponds to a service that:
*** aside
Note that there are other parts of the tree which aggregate
slightly-less-than-foundational service definitions, such as services specific
to the Chrome browser, defined in `//chrome/services`. The motivations, advice,
and standards discussed in this document apply to all service definitions in the
Chromium tree.
to the Chrome browser defined in `//chrome/services` or reusable services for
Content or its embedders, defined in `//components/services`. The motivations,
advice, and standards discussed in this document apply to all service
definitions in the Chromium tree.
***
The `//services/service_manager` directory contains the implementation and
public APIs of the Service Manager itself, including an embedder API with which
software applications (such as Chrome) can embed the Service Manager to manage
their own multiprocess service architecture.
One of the main motivations for expressing Chromium as a collection of services
is long-term maintainability and code health. Because service API boundaries are
strictly limited to Mojo interfaces, state owned and managed by each service is
@ -47,10 +35,10 @@ strongly isolated from other components in the system.
Another key motivation is general modularity and reusability: in the past there
have been a number of missed opportunities for potential new features or
Chromium-based products due to the browser's generally monolothic and inflexible
system design. With the Service Manager &amp; services providing scaffolding for
system components, it becomes progressively easier to build out newer use cases
with *e.g.* a smaller resource footprint, or a different process model, or even
a more granular binary distribution.
system design. With the services providing scaffolding for system components, it
becomes progressively easier to build out newer use cases with *e.g.* a smaller
resource footprint, or a different process model, or even a more granular binary
distribution.
## Service Standards
@ -126,19 +114,13 @@ expose `GoatTeleporterFactory` instead. Now it's impossible for a client to
acquire a functioning `GoatTeleporter` pipe without also providing a
corresponding client pipe to complement it.
### Service &amp; Interface Naming
### Interface Naming
Just some basic tips for service and interface naming:
- Strive to give your service a name that makes it immediately obvious what the
service is for (*e.g.*, `"network"`, `"metrics"`) rather than a meaningless
codename like `"cromulator_3000"`.
- Avoid the usage of `"Service"` in interface names. While the term "service"
is overloaded in Chromium and certainly has plenty of valid interpretations,
in the context of Service Manager services it has a very specific meaning
and should not be overloaded further if possible. An interface which exposes a
control API for a goat teleporter can just be called `GoatTeleporter`, not
`GoatTeleporterService`.
- Strive to give your service's main interface a name that directly conveys the
general purpose of the service (*e.g.*, `NetworkService`, `StorageService`)
rather than a meaningless codename like `Cromulator`.
- Strive to avoid conceptual layering violations in naming and documentation --
*e.g.*, avoid referencing Blink or Content concepts like "renderers" or
@ -170,9 +152,7 @@ system.
Generally the language-specific client libraries are built against only the
public mojom API of the service (and usually few other common dependencies like
`//base` and `//mojo`), and they bootstrap connections to those interfaces by
using public Service Manager APIs like
[`Connector`](/services/service_manager/README.md#Connectors).
`//base` and `//mojo`).
Even in the private service implementation, services should not depend on very
large components like Content, Chrome, or Blink.
@ -219,21 +199,8 @@ actually required as part of the service implementation. For example
what expectations and guarantees are supposed to be upheld by *any*
implementation of the service's APIs.
- Take advantage of the
[test support library](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/test/)
provided by the Service Manager. In particular, `TestConnectorFactory` is
useful for driving public API tests with the service running inside the test
process, and `TestServiceManager` makes it possible to easily cover
out-of-process testing scenarios while faking out as little of the system as
possible.
## Adding a New Service
See the [Service Manager documentation](/services/service_manager/README.md) for
more details regarding how to define a service and expose or consume interfaces
to and from it, as well as how to make the service available to an application's
runtime environment.
Please start a thread on
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
if you want to propose the introduction of a new service.

@ -1,929 +0,0 @@
# The Service Manager &amp; Services
[TOC]
## Overview
The Service Manager is a component which large applications like Chromium can
use support a cross-platform, multi-process, service-oriented,
hyphenated-adjective-laden architecture.
This document covers how to embed
the Service Manager into an application as well as how to define and register
services for it to manage. If you just want to read about defining services and
using common service APIs, skip to the main [Services](#Services) section.
## Embedding the Service Manager
To embed the Service Manager, an application should link against the code in
`//services/service_manager/embedder`. This defines a main entry point for
most platforms, with a relatively small
[`service_manager::MainDelegate`](https://cs.chromium.org/chromium/src/services/service_manager/embedder/main_delegate.h)
interface for the application to implement. In particular, the application
should at least implement
[`GetServiceManifests`](https://cs.chromium.org/chromium/src/services/service_manager/embedder/main_delegate.h?rcl=734122d6a01196706dfc1c252fa09ed933778f8f&l=80) to provide
metadata about the full set of services comprising the application.
*** aside
Note that Chromium does not currently implement `GetServiceManifests` for
production use of the Service Manager. This is because a bunch of process
launching and management logic still lives at the Content layer. As more of this
code moves into Service Manager internals, Chromium will start to look more like
any other Service Manager embedder.
***
*TODO: Improve embedder documentation here, and include support for in-process
service launching once MainDelegate supports it.*
## Services
A **service** in this context can be defined as any self-contained body of
application logic which satisfies *all* of the following constraints:
- It defines a single [implementation](#Implementation) of
[`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h)
to receive interface requests brokered by the
Service Manager, and it maintains a connection between this object and the
Service Manager using a
[`ServiceBinding`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h).
- Its API surface in from or out to other services is restricted exclusively to
[Mojo](/mojo/README.md) interfaces and self-contained client libraries built
on those Mojo interfaces. This means no link-time or run-time exposure of
the service implementation's internal heap or global state.
- It defines a [service manifest](#Manifests) to declare how the Service Manager
should identify and manage instances of the service, as well as what
interfaces are exposed to or required from other services in the system.
The Service Manager is responsible for managing the creation and interconnection
of individual service instances, whether they are embedded within an existing
process or each isolated within dedicated processes. Managed service processes
may be sandboxed with any of various supported
[sandbox configurations](#Sandbox-Configurations).
This section walks through important concepts and APIs throughout service
development, and builds up a small working example service in the process.
### A Brief Note About Service Granularity
Many developers fret over what the right "size" or granularity is for a service
or set of services. This makes sense, and there is always going to be some
design tension between choosing a simpler and potentially more efficient,
monolithic implementation, versus choosing a more modular but often more complex
one.
One classic example of this tension is in the origins of Chromium's
`device` service. The service hosts a number of independent device interfacing
subsystems for things like USB, Bluetooth, HID, battery status, etc. You could
easily imagine justifying separate services for each of these features, but it
was ultimately decided keep them merged together as one service thematically
related to hardware device capabilities. Some factors which played into this
decision:
- There was no clear **security** benefit to keeping the features isolated from
each other.
- There was no clear **code size** benefit to keeping the features isolated from
each other -- environments supporting any one of the device capabilities are
fairly likely to support several others and would thus likely include all or
most of the smaller services anyway.
- There isn't really any coupling between the different features in the service,
so there would be few **code health** benefits to building separate services.
Given all of the above conditions, opting for a smaller overall number of
services seems likely to have been the right decision.
When making these kinds of decisions yourself, use your best judgment. When in
doubt, start a bike-shedding centithread on
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum#!forum/services-dev).
### Implementation
The central fixture in any service implementation is, well, its
[`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h)
implementation. This is a small interface with really only three virtual methods
of practical interest, all optional to implement:
``` cpp
class Service {
public:
virtual void OnStart();
virtual void OnBindInterface(const BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe);
virtual void OnDisconnected();
};
```
Services implement a subclass of this to work in conjunction with a
[`ServiceBinding`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h)
so the Service Manager can call into the service with lifecycle events and
interface requests from other services.
*** aside
NOTE: As discussed in [Instance Sharing](#Instance-Sharing) below, your service
configuration may allow for the Service Manager to manage many concurrent
instances of your service. Whether these instances run in the same shared
process or in separate processes, each instance is comprised of exactly one
dedicated instance of your actual `Service` subclass.
***
Through the rest of this document we'll build out a basic working service
implementation, complete with a manifest and simple tests. We'll call it the
`storage` service, and it will provide the basis for all persistent storage
capabilities in our crappy operating system hobby project that is doomed to
languish forever in an unfinished state.
*** aside
NOTE: Sheerly for the sake of brevity, example code written here is inlined in
headers where it would typically be moved out-of-line.
***
The first step is usually to imagine and define some mojom API surface to start
with. We'll limit this example to two mojom files. It's conventional to keep
important constants defined in a separate `constants.mojom` file:
``` cpp
// src/services/storage/public/mojom/constants.mojom
module storage.mojom;
// This string will identify our service to the Service Manager. It will be used
// in our manifest when registering the service, and clients can use it when
// sending interface requests to the Service Manager if they want to reach our
// service.
const string kServiceName = "storage";
// We'll use this later, in service manifest definitions.
const string kAllocationCapability = "allocation";
```
And some useful interface definitions:
``` cpp
// src/services/storage/public/mojom/block.mojom
module storage.mojom;
interface BlockAllocator {
// Allocates a new block of persistent storage for the client. If allocation
// fails, |receiver| is discarded.
Allocate(uint64 num_bytes, pending_receiver<Block> receiver);
};
interface Block {
// Reads and returns a small range of bytes from the block.
Read(uint64 byte_offset, uint16 num_bytes) => (array<uint8> bytes);
// Writes a small range of bytes to the block.
Write(uint64 byte_offset, array<uint8> bytes);
};
```
And finally we'll define our basic `Service` subclass:
``` cpp
// src/services/storage/storage_service.h
#include "base/macros.h"
#include "services/service_manager/public/cpp/service.h"
#include "services/service_manager/public/cpp/service_binding.h"
#include "services/storage/public/mojom/block.mojom.h"
namespace storage {
class StorageService : public service_manager::Service,
public mojom::BlockAllocator {
public:
explicit StorageService(service_manager::mojom::ServiceRequest request)
: service_binding_(this, std::move(request)) {}
~StorageService() override = default;
private:
// service_manager::Service:
void OnBindInterface(const service_manager::BindSourceInfo& source,
const std::string& interface_name,
mojo::ScopedMessagePipeHandle interface_pipe) override {
if (interface_name == mojom::BlockAllocator::Name_) {
// If the Service Manager sends us a request with BlockAllocator's
// interface name, we should treat |interface_pipe| as a
// PendingReceiver<BlockAllocator> that we can bind.
allocator_receivers_.Add(
this, mojo::PendingReceiver<mojom::BlockAllocator>(std::move(interface_pipe)));
}
}
// mojom::BlockAllocator:
void Allocate(uint64_t num_bytes, mojo::PendingReceiver<mojom::Block> receiver) override {
// This space intentionally left blank.
}
service_manager::ServiceBinding service_binding_;
mojo::ReceiverSet<mojom::BlockAllocator> allocator_receivers_;
DISALLOW_COPY_AND_ASSIGN(StorageService);
};
} // namespace storage
```
Great. This is a basic service implementation. It does nothing useful, but we
can come back and fix that some other time.
First, notice that the `StorageService` constructor takes a
`service_manager::mojom::ServiceRequest` and immediately passes it to the
`service_binding_` constructor. This is a nearly universal convention among
service implementations, and your service will probably do it too. The
`ServiceRequest` is an interface pipe that the Service Manager uses to drive
your service, and the `ServiceBinding` is a helper class which translates
messages from the Service Manager into the simpler interface methods of the
`Service` class you've implemented.
`StorageService` also implements `OnBindInterface`, which is what the Service
Manager invokes (via your `ServiceBinding`) when it has decided to route another
service's interface request to your service instance. Note that because this is
a generic API intended to support arbitrary interfaces, the request comes in the
form of an interface name and a raw message pipe handle. It is the service's
responsibility to inspect the name and decide how (or even if) to bind the pipe.
Here we recognize only incoming `BlockAllocator` requests and drop anything
else.
*** aside
NOTE: Because interface receivers are just strongly-type message pipe endpoint
wrappers, you can freely construct any kind of interface receiver over a raw
message pipe handle. If you're planning to pass the endpoint around, it's good
to do this as early as possible (i.e. as soon as you know the intended interface
type) to benefit from your compiler's type-checking and avoid having to pass
around both a name and a pipe.
***
The last piece of our service that we need to lay down is its manifest.
### Manifests
A service's manifest is a simple static data structure provided to the Service
Manager early during its initialization process. The Service Manager combines
all of the manifest data it has in order to form a complete picture of the
system it's coordinating. It uses all of this information to make decisions
like:
- When service X requests interface Q from service Y, should it be allowed?
- Were all of the constraints specified in X's request valid, and is X allowed
to specify them as such?
- Do I need to spawn a new instance of Y to satisfy this request or can I re-use
an existing one (assuming there are any)?
- If I have to spawn a new process for a new Y instance, how should I configure
its sandbox, if at all?
All of this metadata is contained within different instances of the
[`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h)
class.
#### A Basic Manifest
The most common way to define a service's manifest is to place it in its own
source target within the service's C++ client library. To combine the
convenience of inline one-time initialization with the avoidance of static
initializers, typically this means using a function-local static with
`base::NoDestructor` and `service_manager::ManifestBuilder` as below. First the
header:
``` cpp
// src/services/storage/public/cpp/manifest.h
#include "services/service_manager/public/cpp/manifest.h"
namespace storage {
const service_manager::Manifest& GetManifest();
} // namespace storage
```
And for the actual implementation:
``` cpp
// src/services/storage/public/cpp/manifest.cc
#include "services/storage/public/cpp/manifest.h"
#include "base/no_destructor.h"
#include "services/storage/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/manifest_builder.h"
namespace storage {
const service_manager::Manifest& GetManifest() {
static base::NoDestructor<service_manager::Manifest> manifest{
service_manager::ManifestBuilder()
.WithServiceName(mojom::kServiceName)
.Build()};
return *manifest;
};
} // namespace storage
```
Here we've specified only the **service name**, matching the constant defined
in `constants.mojom` so that other services can easily locate us without a
hard-coded string.
With this manifest definition there is no way for our service to reach other
services, and there's no way for other services to reach us; this is because
we neither **expose** nor **require** any capabilities, thus the Service Manager
will always block any interface request from us or targeting us.
#### Exposing Interfaces
Let's expose an "allocator" capability that grants permission to bind a
`BlockAllocator` pipe. We can augment the above manifest definition as follows:
``` cpp
...
#include "services/storage/public/mojom/block.mojom.h"
...
...
.WithServiceName(mojom::kServiceName)
.ExposeCapability(
mojom::kAllocatorCapability,
service_manager::Manifest::InterfaceList<mojom::BlockAllocator>())
.Build()
...
```
This declares the existence of an `"allocator"` capability exposed by our
service, and specifies that granting a client this capability means granting it
the privilege to send our service `storage.mojom.BlockAllocator` interface
requests.
You can list as many interfaces as you like for each exposed capability, and
multiple capabilities may list the same interface.
**NOTE**: You only need to expose an interface through a capability if you want
other services to be able to be able to request it *through the Service
Manager* (see [Connectors](#Connectors)) -- that is, if you handle requests for
it in your `Service::OnBindInterface` implementation.
Contrast this with interfaces acquired transitively, like `Block` above. The
Service Manager does not mediate the behavior of existing interface connections,
so once a client has a `BlockAllocator` they can use `BlockAllocator.Allocate`
to send as many `Block` requests as they like. Such requests go directly to
the service-side implementation of `BlockAllocator` to which the pipe is bound,
and so manifest contents are irrelevant to their behavior.
#### Getting Access to Interfaces
We don't need to add anything else to our `storage` manifest, but if another
service wanted to enjoy access to our amazing storage block allocation
facilities, they would need to declare in their manifest that they **require**
our `"allocation"` capability. For ease of maintenance they would utilitize our
publicly defined constants to do this. It's pretty straightforward:
``` cpp
// src/services/some_other_pretty_cool_service/public/cpp/manifest.cc
... // Somewhere along the chain of ManifestBuilder calls...
.RequireCapability(storage::mojom::kServiceName,
storage::mojom::kAllocationCapability)
...
```
Now `some_other_pretty_cool_service` can use its [Connector](#Connectors) to ask
the Service Manager for a `BlockAllocator` from us, like so:
``` cpp
mojo::Remote<storage::mojom::BlockAllocator> allocator;
connector->Connect(storage::mojom::kServiceName,
allocator.BindNewPipeAndPassReceiver());
mojo::Remote<storage::mojom::Block> block;
allocator->Allocate(42, block.BindNewPipeAndPassReceiver());
// etc..
```
#### Other Manifest Elements
There are a handful of other optional elements in a `Manifest` structure which
can affect how your service behaves at runtime. See the current
[`Manifest`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest.h)
definition and comments as well as
[`ManifestBuilder`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h)
for the most complete and current information, but some of the more common
properties specified by manifests are:
- **Display Name** - This is the string the Service Manager will use to name
any new process created to run your service. This string would appear in the
Windows Task Manager to identify the service process, for example.
- **Options** - A few miscellaneous options are stuffed into a `ManifestOptions`
field. These include sandbox type (see
[Sandbox Configurations](#Sandbox-Configurations)),
[instance sharing policy](#Instance-Sharing), and various behavioral flags to
control a few [special capabilities](#Additional-Capabilities).
- **Preloaded Files** - On Android and Linux platforms, the Service Manager can
open specified files on the service's behalf and pass the corresponding open
file descriptor(s) to each new service process on launch.
- **Packaged Services** - A service may declare that it **packages** another
service by including a copy of that service's own manifest. See
[Packaging](#Packaging) for details.
### Running the Service
Hooking the service up so that it can be run in a production environment is
actually outside the scope of this document at the moment, only because it still
depends heavily on the environment in which the Service Manager is embedded. For
now, if you want to get your great little service hooked up in Chromium for
example, you should check out the sections on this in the very Chromium-centric
[Intro to Mojo &amp; Services](/docs/mojo_and_services.md#Hooking-Up-the-Service-Implementation)
and/or
[Servicifying Chromium Features](/docs/servicification.md#Putting-It-All-Together)
documents.
For the sake of this document, we'll focus on running the service in test
environments with the service both in-process and out-of-process.
### Testing
There are three primary approaches used when testing services, applied in
varying combinations:
#### Standard Unit-testing
This is ideal for covering details of your service's internal components and
making sure they operate as expected. There is nothing special here regarding
services. Code is code, you can unit-test it.
#### Out-of-process End-to-end Tests
These are good for emulating a production environment as closely as possible,
with your service implementation isolated in a separate process from the test
(client) code.
The main drawback to this approach is that it limits your test's ability to poke
at or observe internal service state, which can sometimes be useful in test
environments (for *e.g.* faking out some behavior in a predictable manner). In
general, supporting such controls means adding test-only interfaces to your
service.
The
[`TestServiceManager`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/test/test_service_manager.h)
helper and
[`service_executable`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_executable.gni)
GN target type make this fairly easy to accomplish. You simply define a new
entry point for your service:
``` cpp
// src/services/storage/service_main.cc
#include "base/message_loop.h"
#include "services/service_manager/public/cpp/service_executable/main.h"
#include "services/storage/storage_service.h"
void ServiceMain(service_manager::ServiceRequest request) {
base::SingleThreadTaskExecutor main_task_executor;
storage::StorageService(std::move(request)).RunUntilTermination();
}
```
and a GN target for this:
``` python
import "services/service_manager/public/cpp/service_executable.gni"
service_executable("storage") {
sources = [
"service_main.cc",
]
deps = [
# The ":impl" target would be the target that defines our StorageService
# implementation.
":impl",
"//base",
"//services/service_manager/public/cpp",
]
}
test("whatever_unittests") {
...
# Include the executable target as data_deps for your test target
data_deps = [ ":storage" ]
}
```
And finally in your test code, use `TestServiceManager` to create a real
Service Manager instance within your test environment, configured to know about
your `storage` service.
`TestServiceManager` allows you to inject an artificial service instance to
treat your test suite as an actual service instance. You can provide a manifest
for your test to simulate requiring (or failing to require) various capabilities
and get a `Connector` with which to reach your service-under-test. This looks
something like:
``` cpp
#include "services/service_manager/public/cpp/manifest_builder.h"
#include "services/service_manager/public/cpp/test/test_service.h"
#include "services/service_manager/public/cpp/test/test_service_manager.h"
#include "services/storage/public/cpp/manifest.h"
#include "services/storage/public/mojom/constants.mojom.h"
#include "services/storage/public/mojom/block.mojom.h"
...
TEST(StorageServiceTest, AllocateBlock) {
const char kTestServiceName[] = "my_inconsequentially_named_test_service";
service_manager::TestServiceManager service_manager(
// Make sure the Service Manager knows about the storage service.
{storage::GetManifest,
// Also make sure it has a manifest for our test service, which this
// test will effectively act as an instance of.
service_manager::ManifestBuilder()
.WithServiceName(kTestServiceName)
.RequireCapability(storage::mojom::kServiceName,
storage::mojom::kAllocationCapability)
.Build()});
service_manager::TestService test_service(
service_manager.RegisterTestInstance(kTestServiceName));
mojo::Remote<storage::mojom::BlockAllocator> allocator;
// This Connector belongs to the test service instance and can reach the
// storage service through the Service Manager by virtue of the required
// capability above.
test_service.connector()->Connect(storage::mojom::kServiceName,
allocator.BindNewPipeAndPassReceiver());
// Verify that we can request a small block of storage.
mojo::Remote<storage::mojom::Block> block;
allocator->Allocate(64, block.BindNewPipeAndPassReceiver());
// Do some stuff with the block, etc...
}
```
#### In-Process Service API Tests
Sometimes you want to poke at your service primarily through its client API,
but you also want to be able to -- either for convenience or out of necessity --
observe or manipulate its internal state within the test code. Running the
service in-process is ideal in this case, and in that case there's not much use
in involving the Service Manager or dealing with manifests.
Instead you can use a
[`TestConnectorFactory`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/test/test_connector_factory.h)
to give yourself a working `Connector` object which routes interface requests
directly to specific service instances which you wire up directly. For a quick
example, suppose we had some client library helper function for allocating a
block of storage when given a `Connector`:
``` cpp
// src/services/storage/public/cpp/allocate_block.h
namespace storage {
// This helper function can be used by any service which is granted the
// |kAllocationCapability| capability.
mojo::Remote<mojom::Block> AllocateBlock(service_manager::Connector* connector,
uint64_t size) {
mojo::Remote<mojom::BlockAllocator> allocator;
connector->Connect(mojom::kServiceName, allocator.BindNewPipeAndPassReceiver());
mojo::Remote<mojom::Block> block;
allocator->Allocate(size, block.BindNewPipeAndPassReceiver());
return block;
}
} // namespace storage
```
Our test could look something like:
``` cpp
TEST(StorageTest, AllocateBlock) {
service_manager::TestConnectorFactory test_connector_factory;
storage::StorageService service(
test_connector_factory.RegisterInstance(storage::mojom::kServiceName));
constexpr uint64_t kTestBlockSize = 64;
mojo::Remote<storage::mojom::Block> block = storage::AllocateBlock(
test_connector_factory.GetDefaultConnector(), kTestBlockSize);
block.FlushForTesting();
// Verify that we have the expected number of bytes allocated within the
// service implementation.
EXPECT_EQ(kTestBlockSize, service.GetTotalAllocationSizeForTesting());
}
```
### Connectors
While the
[`Service`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service.h)
interface is what the Service Manager uses to drive a service instance's
behavior, a
[`Connector`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h)
is what the service instance uses to send requests to the Service Manager. This
interface is connected when your instance is launched, and `ServiceBinding`
maintains and
[exposes](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h?rcl=887b934e0d979f3da81c41cadc396b4ef587257a&l=66)
it on your behalf.
#### Sending Interface Receivers
By far the most common and useful method on `Connector` is `Connect`,
which allows your service to send an interface receiver to another service in the
system, configuration permitting.
Supposing the `storage` service actually depended on an even lower-level storage
service to get at its disk, you could imagine its block allocation code doing
something like:
``` cpp
mojo::Remote<real_storage::mojom::ReallyRealStorage> storage;
service_binding_.GetConnector()->Connect(
real_storage::mojom::kServiceName, storage.BindNewPipeAndPassReceiver());
storage->AllocateBytes(...);
```
Note that the first argument to this particular overload of `Connect` is
a string, but the more generalized form of `Connect` takes a
`ServiceFilter`. See more about these in the section on
[Service Filters](#Service-Filters).
#### Registering Service Instances
One of the superpowers services can be granted is the ability to forcibly inject
new service instances into the Service Manager's universe. This is done via
[`Connector::ServiceInstance`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/connector.h?rcl=ec509adfa3ac85fab3cd51422b8aaf9cbb6b43cb&l=108) and is still
used pretty heavily by Chromium's browser process. Most services don't need to
touch this API.
#### Usage in Multithreaded Environments
Connectors are **not** thread-safe, but they do support **cloning**. There are
two useful ways you can associate a new Connector with an existing one on a
different thread.
You can `Clone` the `Connector` on its own thread and then pass the clone to
another thread:
``` cpp
std::unique_ptr<service_manager::Connector> new_connector = connector->Clone();
base::PostTask(...[elsewhere]...,
base::BindOnce(..., std::move(new_connector)));
```
Or you can fabricate a brand new `Connector` right from where you're standing,
and asynchronously associate it with one on another thread:
``` cpp
mojo::PendingReceiver<service_manager::mojom::Connector> receiver;
std::unique_ptr<service_manager::Connector> new_connector =
service_manager::Connector::Create(&receiver);
// |new_connector| can be used to start issuing calls immediately, despite not
// yet being associated with the establshed Connector. The calls will queue as
// long as necessary.
base::PostTask(
...[over to the correct thread]...,
base::BindOnce([](
mojo::PendingReceiver<service_manager::Connector> receiver) {
service_manager::Connector* connector = GetMyConnectorForThisThread();
connector->BindConnectorReceiver(std::move(receiver));
}));
```
### Identity
Every service instance started by the Service Manager is assigned a globally
unique (across space *and* time) identity, encapsulated by the
[`Identity`](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/identity.h)
type. This value is communicated to the service and retained and
[exposed](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/service_binding.h?rcl=b8bc0ab281f2cb5cd567dc994692c6022845fb89&l=62)
by `ServiceBinding` immediately before `Service::OnStart` is invoked.
There are *four* components to an `Identity`:
- Service name
- Instance ID
- Instance group ID
- Globally unique ID
You're already quite familiar with the **service name**: this is whatever the
service declared in its manifest, *e.g.*, `"storage"`.
#### Instance ID
**Instance ID** is a `base::Token` qualifier which is simply used to
differentiate multiple instances of the service if multiple instances are
desired for whatever arbitrary reason. By default instances get an instance ID
of zero when started unless a connecting client *explicitly* requests a specific
instance ID. Doing so requires a special manifest-declared capability covered by
[Additional Capabilities](#Additional-Capabilities).
*** aside
A good example of how instance ID can be useful: the `"unzip"` service in
Chrome is used to safely unpack untrusted Chrome extensions (CRX) archives, but
we don't want multiple extensions being unpacked by the same process. To support
this, Chrome generates a random `base::Token` for the instance ID it uses when
connecting to the `"unzip"` service, and this elicits the creation of a new
service instance in a new isolated process for each such connection. See
[Service Filters](#Service-Filters) for how this can be done.
***
#### Instance Group ID
All created service instances implicitly belong to an **instance group**, which
is also identified by a `base::Token`. Unless either specially privileged by
[Additional Capabilities](#Additional-Capabilities), or the target service is
a [singleton or shared across groups](#Instance-Sharing), the service sending out
an interface request can only reach other service instances in the same instance
group. See [Instance Groups](#Instance-Groups) for more information.
#### Globally Unique ID
Finally, the **globally unique ID** is a cryptographically secure, unguessably
random `base::Token` value which can be considered unique across all time and
space. This can never be controlled by an instance or even by a highly
privileged service, and its sole purpose is to ensure that `Identity` itself
can be treated as unique across time and space. See
[Service Filters](#Service-Filters) and
[Observing Service Instances](#Observing-Service-Instances) for why this
property of uniqueness is useful and sometimes necessary.
### Instance Sharing
Assuming the Service Manager has decided to allow an interface request due to
sufficient capability requirements, it must consider a number of factors to
decide where exactly to route the request. The first factor is the **instance
sharing policy** of the target service, declared in its manifest. There are
three supported policies:
- **No sharing** - This means the precise identity of the target instance
depends on both the instance ID provided by the request's `ServiceFilter`,
as well as the instance group either provided by the `ServiceFilter` or
inherited from the source instance's group.
- **Shared across groups** - This means the precise identity of the target
instance still depends on the instance ID provided by the request's
`ServiceFilter`, but the instance group of both the `ServiceFilter` and the
source instance are completely ignored.
- **Singleton** - This means there can be only one instance of the service at
a time, no matter what. Instance ID and group are always ignored when
connecting to the service.
Based on one of the policies above, the Service Manager determines whether or
not an existing service instance matches the parameters specified by the given
`ServiceFilter` in conjunction with the source instance's own identity. If so,
that Service Manager will forward the interface request to that instance via
`Service::OnBindInterface`. Otherwise, it will spawn a new instance which
sufficiently matches the constraints, and it will forward the request to that
new instance.
### Instance Groups
Service instances are organized into **instance groups**. These are arbitrary
partitions of instances which can be used by the host application to impose
various kinds of security boundaries.
Most services in the system do not have the privilege of specifying the
instance group they want to connect into when passing a `ServiceFilter` to
`Connector::Connect` (see
[Additional Capabilities](#Additional-Capabilities)). As such, most
`Connect` calls implicitly inherit the group ID of the caller and only
cross outside of the caller's instance group when targeting a service which
adopts either a singleton or shared-across-groups
[sharing policy](#Instance-Sharing) in its manifest.
Singleton and shared-across-groups services are themselves always run in their
own isolated groups.
### Service Filters
The most common form of `Connect` calls passes a simple string as the
first argument. This is essentially telling the Service Manager that the caller
doesn't care about any details regarding the target instance's identity -- it
only cares about talking to *some* instance of the named service.
When a client *does* care about other details, they can explicitly construct and
pass a `ServiceFilter` object, which essentially provides some subset of the
desired target instance's total `Identity`.
Specifying an instance group or instance ID in a `ServiceFilter` requires a
service to declare [additional capabilities](#Additional-Capabilities) in its
manifest options.
A `ServiceFilter` can also wrap a complete `Identity` value, including the
globally unique ID. This filter always *only* matches a specific instance unique
in space and time. So if the identified instance has died and been replaced by
a new instance with the same service name, same instance ID, and same instance
group, the request will still *fail*, because the globally unique ID component
will *never* match this or any future instance.
One useful property of targeting a specific `Identity` is that the client can
connect without any risk of eliciting new target instance creation: either
the target exists and the request can be routed, or the target doesn't exist
and the request will be dropped.
### Additional Capabilities
Service manifests can use `ManifestOptionsBuilder` to set a few additional
boolean options controlling their Service Manager privileges:
- `CanRegisterOtherServiceInstances` - If this is `true` the service can call
`RegisterServiceInstance` on its `Connector` to forcibly introduce new service
instances into the environment.
- `CanConnectToInstancesWithAnyId` - If this is `true` the service can specify
an instance ID in any `ServiceFilter` it passes to `Connect`.
- `CanConnectToInstancesInAnyGroup` - If this is `true` the service can specify
an instance group ID in any `ServiceFilter` it passes to `Connect`.
### Packaging
A service can declare that it **packages** another service by
[nesting](https://cs.chromium.org/chromium/src/services/service_manager/public/cpp/manifest_builder.h?rcl=7839843db1ccdf13c3f1b8cb90a763989dde83a8&l=87) that
service's manifest within its own.
This signals to the Service Manager that it should defer to the packaging
service when it needs a new instance of the packaged service. For example, if
we offered up the manifest:
``` cpp
service_manager::ManifestBuilder()
.WithServiceName("fruit_vendor")
...
.PackageService(service_manager::ManifestBuilder()
.WithServiceName("banana_stand")
.Build())
.Build()
```
And someone wanted to connect to a new instance of the `"banana_stand"` service
(there's always money in the banana stand), the Service Manager would ask an
appropriate `"fruit_vendor"` instance to do this on its behalf.
*** aside
NOTE: If an appropriate instance of `"fruit_vendor"` wasn't already running --
as determined by the rules described in [Instance Sharing](#Instance-Sharing)
above -- one would first be spawned by the Service Manager.
***
In order to support this operation, the `fruit_vendor` must expose a capability
named exactly `"service_manager:service_factory"` which includes the
`"service_manager.mojom.ServiceFactory"` interface. Then it must handle requests
for the `service_manager.mojom.ServiceFactory` interface in its implementation
of `Service::OnBindInterface`. The implementation of `ServiceFactory` provided
by the service must then handle the `CreateService` that will be sent by
the Service Manager. This call will include the name of the service and the
`ServiceRequest` the new service instance will need to bind.
*** aside
NOTE: It is this complicated for historical reasons. Expect it to be less
complicated soon.
***
Services can use this for example if, in certain runtime environments, they want
to share their process with another service.
*** aside
FUN FACT: This is actually how Chromium manages *all* services today, because
the Content layer still owns much of the production-ready process launching
logic. We have a singleton `content_packaged_services` service which packages
nearly all other registered services in the system, and so the Service Manager
defers (via `ServiceFactory`) nearly all service instance creation operations
to Content.
***
### Sandbox Configurations
Service manifests support specifying a fixed sandbox configuration for the
service to be launched with when run out-of-process. Currently these values
are strings which must match one of the defined constants
[here](https://cs.chromium.org/chromium/src/services/service_manager/sandbox/switches.cc?rcl=2e6a3bddac0aff89c5ff415e9c1cd4da804280ef&l=23).
The most common and default value is `"utility"`, which is a restrictive sandbox
configuration and generally a safe choice. For services which must run
unsandboxed, use the value `"none"`. Use of other sandbox configurations should
be done under the advisory of Chrome's security reviewers.
### Observing Service Instances
Services which require the `"service_manager:service_manager`" capability from
the `"service_manager"` service can connect to the `"service_manager"` service
to request a
[`ServiceManager`](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/service_manager.mojom?rcl=765c18ee7c317535594ba37520a23c11f0cef008&l=82)
interface. This can in turn be used to register a new
[`ServiceManagerListener`](https://cs.chromium.org/chromium/src/services/service_manager/public/mojom/service_manager.mojom?rcl=765c18ee7c317535594ba37520a23c11f0cef008&l=44) to
observe lifecycle events pertaining to all service instances hosted by the
Service Manager.
There are several
[examples](https://cs.chromium.org/search/?q=mojo::Binding%3Cservice_manager::mojom::ServiceManagerListener%3E&type=cs)
of this throughout the tree.
## Additional Support
If this document was not helpful in some way, please post a message to your
friendly
[services-dev@chromium.org](https://groups.google.com/a/chromium.org/forum/#!forum/services-dev)
mailing list.
Also don't forget to take a look at other
[Mojo &amp; Services](/docs/README.md#Mojo-Services) documentation in the tree.