Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(498)

Side by Side Diff: services/shell/README.md

Issue 2419723002: Move services/shell to services/service_manager (Closed)
Patch Set: rebase Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « services/shell/OWNERS ('k') | services/shell/background/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Service Manager User Guide
2
3 ## What is the Service Manager?
4
5 The Service Manager is a tool that brokers connections and capabilities between
6 and manages instances of components, referred to henceforth as “services”.
7
8 The Service Manager performs the following functions:
9
10 * Brokering connections between services, including communicating policies such
11 as capabilities (which include access to interfaces), user identity, etc.
12 * Launching and managing the lifecycle services and processes (though services
13 may also create their own processes and tell the Service Manager about them).
14 * Tracks running services, and provides an API that allows services to
15 understand what’s running.
16
17 The Service Manager presents a series of Mojo interfaces to services, though in
18 practice interacting with the Service is made simpler with a client library.
19 Currently, there is only a client library written in C++, since that meets the
20 needs of most of the use cases in Chrome.
21
22 ## Details
23
24 ### Mojo Recap
25
26 The Mojo system provides two key components of interest here - a lightweight
27 message pipe concept allowing two endpoints to communicate, and a bindings layer
28 that allows interfaces to be described to bind to those endpoints, with
29 ergonomic bindings for languages used in Chrome.
30
31 Mojo message pipes are designed to be lightweight and may be read from/written
32 to and passed around from one process to the next. In most situations however
33 the developer won’t interact with the pipes directly, rather with a generated
34 types encapsulating a bound interface.
35
36 To use the bindings, a developer defines their interface in the Mojo IDL format,
37 **mojom**. With some build magic, the generated headers can then be included and
38 used from C++, JS and Java.
39
40 It is important to note here that Mojo Interfaces have fully qualified
41 identifiers in string form, generated from the module path and interface name:
42 “**`module.path.InterfaceName`**”. This is how interfaces are referenced in
43 Service Manifests, and how they will be referenced throughout this document.
44
45 This would be a good place for me to refer to this in-depth Mojo User Guide,
46 which spells all of this out in great detail.
47
48 ### Services
49
50 A Service is any bit of code the Service Manager knows about. This could be a
51 unique process, or just a bit of code run in some existing process.
52
53 The Service Manager disambiguates services by their **Identity**. Every service
54 has its own unique Identity. From the Service Manager’s perspective, a service’s
55 Identity is represented by the tuple of the its Name, UserId and Instance Name.
56 The Name is a formatted string that superficially represents a scheme:host pair,
57 but actually isn’t a URL. More on the structure of these names later. The UserId
58 is a string GUID, representing the user the service is run as. The Instance Name
59 is a string, typically (but not necessarily) derived from the Name, which can be
60 used to allow multiple instances of a service to exist for the same Name,UserId
61 pair. In Chrome an example of this would be multiple instances of the renderer
62 or the same profile.
63
64 A Service implements the Mojo interface shell.mojom.Service, which is the
65 primary means the Service Manager has of communicating with its service. Service
66 has two methods: OnStart(), called once at when the Service Manager first learns
67 about the service, and OnConnect(), which the Service Manager calls every time
68 some other service tries to connect to this one.
69
70 Services have a link back to the Service Manager too, primarily in the form of
71 the shell.mojom.Connector interface. The Connector allows services to open
72 connections to other services.
73
74 A unique connection from the Service Manager to a service is called an
75 “instance,” each with its own unique identifier, called an instance id. Every
76 instance has a unique Identity. It is possible to locate an existing instance
77 purely using its Identity.
78
79 Services define their own lifetimes. Services in processes started by other
80 services (rather than the Service Manager) may even outlive the connection with
81 the Service Manager. For processes launched by the Service Manager, when a
82 service wishes to terminate it closes the Service pipe with the Service Manager
83 and the Service Manager destroys its corresponding instance and asks the process
84 to exit.
85
86 #### A simple Service example
87
88 Consider this simple application that implements the Service interface:
89
90 **app.cc:**
91
92 #include “"mojo/public/c/system/main.h”"
93 #include “"services/shell/public/cpp/application_runner.h”"
94 #include “"services/shell/public/cpp/connector.h”"
95 #include “"services/shell/public/cpp/connection.h”"
96 #include “"services/shell/public/cpp/identity.h”"
97 #include “"services/shell/public/cpp/service.h”"
98
99 class Service : public shell::Service {
100 public:
101 Service() {}
102 ~Service() override {}
103
104 // Overridden from shell::Service:
105 void OnStart(const shell::Identity& identity) override {
106 }
107 bool OnConnect(shell::Connection* connection) override {
108 return true;
109 }
110 };
111
112 MojoResult ServiceMain(MojoHandle service_request_handle) {
113 return shell::ServiceRunner(new Service).Run(service_request_handle);
114 }
115
116 app_manifest.json:
117
118 {
119 "“manifest_version"”: 1,
120 "“name"”: "“mojo:app”",
121 "“display_name"”: “"Example App”",
122 "“capabilities"”: {}
123 }
124
125 **BUILD.gn:**
126
127 import(“"//mojo/public/mojo_application.gni”")
128
129 service(“"app"”) {
130 sources = [ "“app.cc"” ]
131 deps = [ "“//base"”, "“//mojo/shell/public/cpp”" ]
132 data_deps = [ “":manifest"” ]
133 }
134
135 service_manifest(“"manifest"”) {
136 name = "“app"”
137 source = “"app_manifest.json”"
138 }
139
140 What does all this do? Building the app target produces two files in the output
141 directory: app/app.library and app/manifest.json. app.library is a DSO loaded by
142 the Service Manager in its own process when another service connects to the
143 “mojo:app” name. This is not the only way (nor even the most likely one) you can
144 implement a Service, but it’s the simplest and easiest to reason about.
145
146 This service doesn’t do much. Its implementation of OnStart() is empty, and its
147 implementation of OnConnect just returns true to allow the inbound connection to
148 complete. Let’s study the parameters to these methods though, since they’ll be
149 important as we begin to do more in our service.
150
151 ##### OnStart Parameters
152
153 ###### const shell::Identity& identity
154 This is the identity this service is known to the Service Manager as. It
155 includes the service’s Name, User ID and Instance Name.
156
157 ##### OnConnect Parameters
158
159 ###### shell::Connection* connection
160 This is a pointer to an object that encapsulates the connection with a remote
161 service. The service uses this object to learn about the service at the remote
162 end, to bind interfaces from it, and to expose interfaces to it. The
163 “Connection” concept is implemented under the hood by a pair of
164 shell.mojom.InterfaceProviders - this is the physical link between the service
165 that give the Connection its utility. The Connection object is owned by the
166 caller of OnConnect, and will outlive the underlying pipes.
167
168 The service can decide to block the connection outright by returning false from
169 this method. In that scenario the underlying pipes will be closed and the remote
170 end will see an error and have the chance to recover.
171
172 Before we add any functionality to our service, such as exposing an interface,
173 we should look at how we connect to another service and bind an interface from
174 it. This will lay the groundwork to understanding how to export an interface.
175
176 ### Connecting
177
178 Once we have a Connector, we can connect to other services and bind interfaces
179 from them. In the trivial app above we can do this directly in OnStart:
180
181 void OnStart(const shell::Identity& identity) override {
182 scoped_ptr<shell::Connection> connection =
183 connector()->Connect(“"mojo:service"”);
184 mojom::SomeInterfacePtr some_interface;
185 connection->GetInterface(&some_interface);
186 some_interface->Foo();
187 }
188
189 This assumes an interface called “mojo.SomeInterface” with a method “Foo()”
190 exported by another Mojo client identified by the name “mojo:service”.
191
192 What is happening here? Let’s look line-by-line
193
194
195 scoped_ptr<shell::Connection> connection =
196 connector->Connect("“mojo:service”");
197
198 This asks the Service Manager to open a connection to the service named
199 “mojo:service”. The Connect() method returns a Connection object similar to the
200 one received by OnConnect() - in fact this Connection object binds the other
201 ends of the pipes of the Connection object received by OnConnect in the remote
202 service. This time, the caller of Connect() takes ownership of the Connection,
203 and when it is destroyed the connection (and the underlying pipes) is closed. A
204 note on this later.
205
206 mojom::SomeInterfacePtr some_interface;
207
208 This is a shorthand from the mojom bindings generator, producing an
209 instantiation of a mojo::InterfacePtr<mojom::SomeInterface>. At this point the
210 InterfacePtr is unbound (has no pipe handle), and calling is_bound() on it will
211 return false. Before we can call any methods, we need to bind it to a Mojo
212 message pipe. This is accomplished on the following line:
213
214 connection->GetInterface(&some_interface);
215
216 Calling this method allocates a Mojo message pipe, binds the client handle to
217 the provided InterfacePtr, and sends the server handle to the remote service,
218 where it will eventually (asynchronously) be bound to an object implementing the
219 requested interface. Now that our InterfacePtr has been bound, we can start
220 calling methods on it:
221
222 some_interface->Foo();
223
224 Now an important note about lifetimes. At this point the Connection returned by
225 Connect() goes out of scope, and is destroyed. This closes the underlying
226 InterfaceProvider pipes with the remote client. But Mojo methods are
227 asynchronous. Does this mean that the call to Foo() above is lost? No. Before
228 closing, queued writes to the pipe are flushed.
229
230 ### Implementing an Interface
231
232 Let’s look at how to implement an interface now from a client and expose it to
233 inbound connections from other clients. To do this we’ll need to implement
234 OnConnect() in our Service implementation, and implement a couple of other
235 interfaces. For the sake of this example, we’ll imagine now we’re writing the
236 “mojo:service” client, implementing the interface defined in this mojom:
237
238 **some_interface.mojom:**
239
240 module mojom;
241
242 interface SomeInterface {
243 Foo();
244 };
245
246 To build this mojom we need to invoke the mojom gn template from
247 `//mojo/public/tools/bindings/mojom.gni`. Once we do that and look at the
248 output, we can see that the C++ class mojom::SomeInterface is generated and can
249 be #included from the same path as the .mojom file at some_interface.mojom.h.
250 In our implementation of the mojo:service client, we’ll need to derive from this
251 class to implement the interface. But that’s not enough. We’ll also have to find
252 a way to bind inbound requests to bind this interface to the object that
253 implements it. Let’s look at a snippet of a class that does all of this:
254
255 **service.cc:**
256
257 class Service : public shell::Service,
258 public shell::InterfaceFactory<mojom::SomeInterface>,
259 public mojom::SomeInterface {
260 public:
261 ..
262
263 // Overridden from shell::Service:
264 bool OnConnect(shell::Connection* connection) override {
265 connection->AddInterface<mojom::SomeInterface>(this);
266 return true;
267 }
268
269 // Overridden from shell::InterfaceFactory<mojom::SomeInterface>:
270 void Create(shell::Connection* connection,
271 mojom::SomeInterfaceRequest request) override {
272 bindings_.AddBinding(this, std::move(request));
273 }
274
275 // Overridden from mojom::SomeInterface:
276 void Foo() override { /* .. */ }
277
278 mojo::BindingSet<mojom::SomeInterface> bindings_;
279 };
280
281 Let’s study what’s going on, starting with the obvious - we derive from
282 `mojom::SomeInterface` and implement `Foo()`. How do we bind this implementation
283 to a pipe handle from a connected service? First we have to advertise the
284 interface to the client through the inbound connection. This is accomplished in
285 OnConnect():
286
287 connection->AddInterface<mojom::SomeInterface>(this);
288
289 This adds the `mojom.SomeInterface` interface name to the inbound Connection
290 object’s InterfaceRegistry, and tells the InterfaceRegistry to consult this
291 object when it needs to construct an implementation to bind. Why this object?
292 Well in addition to Service and SomeInterface, we also implement an
293 instantiation of the generic interface InterfaceFactory. InterfaceFactory is the
294 missing piece - it binds a request for SomeInterface (in the form of a message
295 pipe server handle) to the object that implements the interface (this). This is
296 why we implement Create():
297
298 bindings_.AddBinding(this, std::move(request));
299
300 In this case, this single instance binds requests for this interface from all
301 connected clients, so we use a mojo::BindingSet to hold them all. Alternatively,
302 we could construct an object per request, and use mojo::Binding.
303
304 ### Capabilities
305
306 While the code above looks like it should work, if we were to type it all in,
307 build it and run it it still wouldn’t. In fact, if we ran it, we’d see this
308 error in the console:
309
310 `Capabilities prevented connection from: mojo:app to mojo:service`
311
312 The answer lies in an omission in one of the files I didn’t discuss earlier, the
313 manifest.json, specifically the empty “capabilities” dictionary.
314
315 You can think of an interface (and its underlying client handle) as a
316 capability. If you have it, and it’s bound, you can call methods on it and
317 something will happen. If you don’t have a bound InterfacePtr, you (effectively)
318 don’t have that capability.
319
320 At the top level, the Service Manager implements the delegation of capabilities
321 in accordance with rules spelled out in each service’s manifest.
322
323 Each service produces a manifest file with some typical metadata about itself,
324 and a “capability spec”. A capability spec describes classes of
325 capabilities offered by the service, classes of capabilities and individual
326 capabilities consumed by the service. Let’s study a fairly complete capability
327 spec from another service’s manifest:
328
329 "capabilities": {
330 "provided": {
331 "web": ["if1", "if2"],
332 "uid": []
333 "“god-mode"”: [“"*”"]
334 },
335 "required": {
336 "*": { "classes": ["c1", "c2"], "interfaces": ["if3", "if4"] },
337 "mojo:foo": { "classes": ["c3"], "interfaces": ["if5"] }
338 }
339 }
340
341 At the top level of the capabilities dictionary are two sub-dictionaries.
342
343 #### Provided Capability Classes
344
345 The provided dictionary enumerates the capability classes provided by the
346 service. A capability class is an alias, either to some special behavior exposed
347 by the service to remote services that request that class, or to a set of
348 interfaces exposed by the service to remote services. In the former case, in the
349 dictionary we provide an empty array as the value of the class name key, in the
350 latter case we provide an array with a list of the fully qualified Mojo
351 interface names (module.path.InterfaceName). A special case of array is one that
352 contains the single entry “*”, which means “all interfaces”. In the example
353 above, when another service connects to this one and requests the “god-mode”
354 class in its manifest, it can connect to all interfaces exposed by this service.
355
356 #### Required Capabilities
357
358 The required dictionary enumerates the capability classes and interfaces
359 required by the service. The keys into this dictionary are the names of the
360 services it intends to connect to, and the values for each key are “capability
361 specs” that describe the capability classes and individual interfaces that this
362 class needs to operate correctly. Here again, an array value for the
363 “interfaces” key in the capability spec consisting of a single “*” means the
364 service needs to bind all interfaces exposed by that service. Additionally, a
365 “*” key in the “required” dictionary allows the service to provide a capability
366 spec that must be adhered to by all applications it connects to.
367
368 Note that a service need not enumerate every interface it provides in the
369 provided dictionary. This is done effectively at runtime when the service calls
370 AddInterface() on inbound connections. The service merely describes groups of
371 interfaces in capability classes as an ergonomic measure. Without capability
372 classes, services would have to explicitly state every interface they intended
373 to bind, which would make the manifests very cumbersome to author.
374
375 Armed with this knowledge, we can return to app_manifest.json from the first
376 example and fill out the capability spec:
377
378 {
379 “"manifest_version"”: 1,
380 "“name"”: "“mojo:app"”,
381 “"display_name"”: “"Example App"”,
382 "“capabilities"”: {
383 "“required"”: {
384 “"mojo:service"”: [],
385 }
386 }
387 }
388
389 If we just run now, it still won’t work, and we’ll see this error:
390
391 Connection CapabilitySpec prevented binding to interface mojom.SomeInterface
392 connection_name: mojo:service remote_name: mojo:app
393
394 The connection was allowed to complete, but the attempt to bind
395 `mojom.SomeInterface` was blocked. We need to add that interface to the array in
396 the manifest:
397
398 "“required"”: {
399 "“mojo:service"”: [ “"mojom::SomeInterface"” ],
400 }
401
402 Now everything should work.
403
404 (Note that we didn’t write a manifest for mojo:service. We’d need to do that
405 too, though for this example we wouldn’t have to describe mojom.SomeInterface in
406 the provided section of its capability spec, since it wasn’t part of a class.
407 Connecting services like mojo:app just need to state that interface.)
408
409 ### Testing
410
411 Now that we’ve built a simple application and service, it’s time to write a test
412 for them. The Shell client library provides a gtest base class
413 **shell::test::ServiceTest** that makes writing integration tests of services
414 straightforward. Let’s look at a simple test of our service:
415
416 #include "“base/bind.h”"
417 #include “"base/run_loop.h”"
418 #include “"mojo/shell/public/cpp/service_test.h”"
419 #include “"path/to/some_interface.mojom.h”"
420
421 void QuitLoop(base::RunLoop* loop) {
422 loop->Quit();
423 }
424
425 class Test : public shell::test::ServiceTest {
426 public:
427 Test() : shell::test::ServiceTest(“exe:service_unittest”) {}
428 ~Test() override {}
429 }
430
431 TEST_F(Test, Basic) {
432 mojom::SomeInterface some_interface;
433 connector()->ConnectToInterface(“"mojo:service"”, &some_interface);
434 base::RunLoop loop;
435 some_interface->Foo(base::Bind(&QuitLoop, &loop));
436 loop.Run();
437 }
438
439 The BUILD.gn for this test file looks like any other using the test() template.
440 It must also depend on //services/shell/public/cpp:shell_test_support.
441
442 ServiceTest does a few things, but most importantly it register the test itself
443 as a Service, with the name you pass it via its constructor. In the example
444 above, we supplied the name “exe:service_unittest”. This name is has no special
445 meaning other than that henceforth it will be used to identify the test service.
446
447 Behind the scenes, ServiceTest spins up the Service Manager on a background
448 thread, and asks it to create an instance for the test service on the main
449 thread, with the name supplied. ServiceTest blocks the main thread while the
450 Service Manager thread does this initialization. Once the Service Manager has
451 created the instance, it calls OnStart() (as for any other service), and the
452 main thread continues, running the test. At this point accessors defined in
453 service_test.h like connector() can be used to connect to other services.
454
455 You’ll note in the example above I made Foo() take a callback, this is to give
456 the test something interesting to do. In the mojom for SomeInterface we’d have
457 the Foo() method return an empty response. In mojo:service, we’d have Foo() take
458 the callback as a parameter, and run it. In the test, we spin a RunLoop until we
459 get that response. In real world cases we can pass back state & validate
460 expectations. You can see real examples of this test framework in use in the
461 Service Manager’s own suite of tests, under //services/shell/tests.
462
463 ### Packaging
464
465 By default a .library statically links its dependencies, so having many of them
466 will yield an installed product many times larger than Chrome today. For this
467 reason it’s desirable to package several Services together in a single binary.
468 The Service Manager provides an interface **shell.mojom.ServiceFactory**:
469
470 interface ServiceFactory {
471 CreateService(Service& service, string name);
472 };
473
474 When implemented by a service, the service becomes a “package” of other
475 services, which are instantiated by this interface. Imagine we have two services
476 mojo:service1 and mojo:service2, and we wish to package them together in a
477 single package mojo:services. We write the Service implementations for
478 mojo:service1 and mojo:service2, and then a Service implementation for
479 mojo:services - the latter implements ServiceFactory and instantiates the other
480 two:
481
482 using shell::mojom::ServiceFactory;
483 using shell::mojom::ServiceRequest;
484
485 class Services : public shell::Service,
486 public shell::InterfaceFactory<ServiceFactory>,
487 public ServiceFactory {
488
489 // Expose ServiceFactory to inbound connections and implement
490 // InterfaceFactory to bind requests for it to this object.
491 void CreateService(ServiceRequest request,
492 const std::string& name) {
493 if (name == “mojo:service1”)
494 new Service1(std::move(request));
495 else if (name == “mojo:service2”)
496 new Service2(std::move(request));
497 }
498 }
499
500 This is only half the story though. While this does mean that mojo:service1 and
501 mojo:service2 are now packaged (statically linked) with mojo:services, as it
502 stands to connect to either packaged service you’d have to connect to
503 mojo:services first, and call CreateService yourself. This is undesirable for a
504 couple of reasons, firstly in that it complicates the connect flow, secondly in
505 that it forces details of the packaging, which are a distribution-level
506 implementation detail on clients wishing to use a service.
507
508 To solve this, the Service Manager actually automates resolving packaged service
509 names to the package service. The Service Manager considers the name of a
510 service provided by some other package service to be an “alias” to that package
511 service. The Service Manager resolves these aliases based on information found,
512 you guessed it, in the manifests for the package client.
513
514 Let’s imagine mojo:service1 and mojo:service2 have typical manifests of the form
515 we covered earlier. Now imagine mojo:services, the package service that combines
516 the two. In the application install directory rather than the following
517 structure:
518
519 service1/service1.library,manifest.json
520 service2/service2.library,manifest.json
521
522 Instead we’ll have:
523
524 package/services.library,manifest.json
525
526 The manifest for the package service describes not only itself, but includes the
527 manifests of all the services it provides. Fortunately there is some GN build
528 magic that automates generating this meta-manifest, so you don’t need to write
529 it by hand. In the service_manifest() template instantiation for services, we
530 add the following lines:
531
532 deps = [ “":service1_manifest”", "“:service2_manifest”" ]
533 packaged_services = [ “"service1”", “"service2"” ]
534
535 The deps line lists the service_manifest targets for the packaged services to be
536 consumed, and the packaged_services line provides the service names, without the
537 “mojo:” prefix. The presence of these two lines will cause the Manifest Collator
538 script to run, merging the dependent manifests into the package manifest. You
539 can study the resulting manifest to see what gets generated.
540
541 At startup, the Service Manager will scan the package directory and consume the
542 manifests it finds, so it can learn about how to resolve aliases that it might
543 encounter subsequently.
544
545 ### Executables
546
547 Thus far, the examples we’ve covered have packaged Services in .library files.
548 It’s also possible to have a conventional executable provide a Service. There
549 are two different ways to use executables with the Service Manager, the first is
550 to have the Service Manager start the executable itself, the second is to have
551 some other executable start the process and then tell the Service Manager about
552 it. In both cases, the target executable has to perform a handshake with the
553 Service Manager early on so it can bind the Service request the Service Manager
554 sends it.
555
556 Assuming you have an executable that properly initializes the Mojo EDK, you add
557 the following lines at some point early in application startup to establish the
558 connection with the Service Manager:
559
560 #include “"services/shell/public/cpp/service.h”"
561 #include “"services/shell/public/cpp/service_context.h”"
562 #include “"services/shell/runner/child/runner_connection.h”"
563
564 class MyClient : public shell::Service {
565 ..
566 };
567
568 shell::mojom::ServiceRequest request;
569 scoped_ptr<shell::RunnerConnection> connection(
570 shell::RunnerConnection::ConnectToRunner(
571 &request, ScopedMessagePipeHandle()));
572 MyService service;
573 shell::ServiceContext context(&service, std::move(request));
574
575 What’s happening here? The Service/ServiceContext usage should be familiar from
576 our earlier examples. The interesting part here happens in
577 `RunnerConnection::ConnectToRunner()`. Before we look at what ConnectToRunner
578 does, it’s important to cover how this process is launched. In this example,
579 this process is launched by the Service Manager. This is achieved through the
580 use of the “exe” Service Name type. The Service Names we’ve covered thus far
581 have looked like “mojo:foo”. The “mojo” prefix means that the Shell should look
582 for a .library file at “foo/foo.library” alongside the Service Manager
583 executable. If the code above was linked into an executable “app.exe” alongside
584 the Service Manager executable in the output directory, it can be launched by
585 connecting to the name “exe:app”. When the Service Manager launches an
586 executable, it passes a pipe to it on the command line, which the executable is
587 expected to bind to receive a ServiceRequest on. Now back to ConnectToRunner.
588 It spins up a background “control” thread with the Service Manager, binds the
589 pipe from the command line parameter, and blocks the main thread until the
590 ServiceRequest arrives and can be bound.
591
592 Like services provided from .library files, we have to provide a manifest for
593 services provided from executables. The format is identical, but in the
594 service_manifest template we need to set the type property to “exe” to cause the
595 generation step to put the manifest in the right place (it gets placed alongside
596 the executable, with the name <exe_name>_manifest.json.)
597
598 ### Service-Launched Processes
599
600 There are some scenarios where a service will need to launch its own process,
601 rather than relying on the Service Manager to do it. The Connector API provides
602 the ability to tell the Shell about a process that the service has or will
603 create. The executable that the service launches (henceforth referred to as the
604 “target”) should be written using RunnerConnection as discussed in the previous
605 section. The connect flow in the service that launches the target (henceforth
606 referred to as the driver) works like this:
607
608 base::FilePath target_path;
609 base::PathService::Get(base::DIR_EXE, &target_path);
610 target_path = target_path.Append(FILE_PATH_LITERAL("“target.exe"”));
611 base::CommandLine target_command_line(target_path);
612
613 mojo::edk::PlatformChannelPair pair;
614 mojo::edk::HandlePassingInformation info;
615 pair.PrepareToPassClientHandleToChildProcess(&target_command_line, &info);
616
617 std::string token = mojo::edk::GenerateRandomToken();
618 target_command_line.AppendSwitchASCII(switches::kPrimordialPipeToken,
619 token);
620
621 mojo::ScopedMessagePipeHandle pipe =
622 mojo::edk::CreateParentMessagePipe(token);
623
624 shell::mojom::ServiceFactoryPtr factory;
625 factory.Bind(
626 mojo::InterfacePtrInfo<shell::mojom::ServiceFactory>(
627 std::move(pipe), 0u));
628 shell::mojom::PIDReceiverPtr receiver;
629
630 shell::Identity target(“"exe:target”",shell::mojom::kInheritUserID);
631 shell::Connector::ConnectParams params(target);
632 params.set_client_process_connection(std::move(factory),
633 GetProxy(&receiver));
634 scoped_ptr<shell::Connection> connection = connector->Connect(&params);
635
636 base::LaunchOptions options;
637 options.handles_to_inherit = &info;
638 base::Process process = base::LaunchProcess(target_command_line, options);
639 mojo::edk::ChildProcessLaunched(process.Handle(), pair.PassServerHandle());
640
641 That’s a lot. But it boils down to these steps:
642 1. Creating the message pipe to connect the target process and the Service
643 Manager.
644 2. Putting the server end of the pipe onto the command line to the target
645 process.
646 3. Binding the client end to a ServiceFactoryPtr, constructing an Identity for
647 the target process and passing both through Connector::Connect().
648 4. Starting the process with the configured command line.
649
650 In this example the target executable could be the same as the previous example.
651
652 A word about process lifetimes. Processes created by the shell are managed by
653 the Service Manager. While a service-launched process may quit itself at any
654 point, when the Service Manager shuts down it will also shut down any process it
655 started. Processes created by services themselves are left to those services to
656 manage.
657
658 ***
659
660 TBD:
661
662 Instances & Processes
663
664 Client lifetime strategies
665
666 Process lifetimes.
667
668 Writing tests (ShellTest)
669 Under the Hood
670 Four major components: Shell API (Mojom), Shell, Catalog, Shell Client Lib.
671 The connect flow, catalog, etc.
672 Capability brokering in the shell
673 Userids
674
675 Finer points:
676
677 Mojo Names: mojo, exe
678 Exposing services on outbound connections
OLDNEW
« no previous file with comments | « services/shell/OWNERS ('k') | services/shell/background/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698