OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "services/shell/service_manager.h" | |
6 | |
7 #include <stdint.h> | |
8 | |
9 #include <utility> | |
10 | |
11 #include "base/bind.h" | |
12 #include "base/command_line.h" | |
13 #include "base/debug/alias.h" | |
14 #include "base/guid.h" | |
15 #include "base/logging.h" | |
16 #include "base/macros.h" | |
17 #include "base/process/process.h" | |
18 #include "base/process/process_handle.h" | |
19 #include "base/stl_util.h" | |
20 #include "base/strings/string_util.h" | |
21 #include "base/trace_event/trace_event.h" | |
22 #include "mojo/public/cpp/bindings/binding.h" | |
23 #include "mojo/public/cpp/bindings/binding_set.h" | |
24 #include "services/shell/connect_util.h" | |
25 #include "services/shell/public/cpp/connector.h" | |
26 #include "services/shell/public/cpp/names.h" | |
27 #include "services/shell/public/cpp/service_context.h" | |
28 #include "services/shell/public/interfaces/connector.mojom.h" | |
29 #include "services/shell/public/interfaces/service.mojom.h" | |
30 #include "services/shell/public/interfaces/service_manager.mojom.h" | |
31 | |
32 namespace shell { | |
33 | |
34 namespace { | |
35 | |
36 const char kCatalogName[] = "service:catalog"; | |
37 const char kServiceManagerName[] = "service:shell"; | |
38 const char kCapabilityClass_UserID[] = "shell:user_id"; | |
39 const char kCapabilityClass_ClientProcess[] = "shell:client_process"; | |
40 const char kCapabilityClass_InstanceName[] = "shell:instance_name"; | |
41 const char kCapabilityClass_AllUsers[] = "shell:all_users"; | |
42 const char kCapabilityClass_ExplicitClass[] = "shell:explicit_class"; | |
43 const char kCapabilityClass_ServiceManager[] = "shell:service_manager"; | |
44 | |
45 } // namespace | |
46 | |
47 Identity CreateServiceManagerIdentity() { | |
48 return Identity(kServiceManagerName, mojom::kRootUserID); | |
49 } | |
50 | |
51 Identity CreateCatalogIdentity() { | |
52 return Identity(kCatalogName, mojom::kRootUserID); | |
53 } | |
54 | |
55 CapabilitySpec GetPermissiveCapabilities() { | |
56 CapabilitySpec capabilities; | |
57 Interfaces interfaces; | |
58 interfaces.insert("*"); | |
59 capabilities.required["*"] = interfaces; | |
60 return capabilities; | |
61 } | |
62 | |
63 Classes GetRequestedClasses(const CapabilitySpec& source_spec, | |
64 const Identity& target) { | |
65 Classes classes; | |
66 | |
67 // Start by looking for specs specific to the supplied identity. | |
68 auto it = source_spec.required.find(target.name()); | |
69 if (it != source_spec.required.end()) { | |
70 std::copy(it->second.begin(), it->second.end(), | |
71 std::inserter(classes, classes.begin())); | |
72 } | |
73 | |
74 // Apply wild card rules too. | |
75 it = source_spec.required.find("*"); | |
76 if (it != source_spec.required.end()) { | |
77 std::copy(it->second.begin(), it->second.end(), | |
78 std::inserter(classes, classes.begin())); | |
79 } | |
80 return classes; | |
81 } | |
82 | |
83 void GetClassesAndInterfacesForConnection( | |
84 const CapabilitySpec& source_spec, | |
85 const Identity& target, | |
86 const CapabilitySpec& target_spec, | |
87 Classes* classes, | |
88 Interfaces* interfaces) { | |
89 DCHECK(classes && interfaces); | |
90 *classes = GetRequestedClasses(source_spec, target); | |
91 // Flatten all interfaces from classes requested by the source into the | |
92 // allowed interface set in the request. | |
93 for (const auto& class_name : *classes) { | |
94 auto it = target_spec.provided.find(class_name); | |
95 if (it != target_spec.provided.end()) { | |
96 for (const auto& interface_name : it->second) | |
97 interfaces->insert(interface_name); | |
98 } | |
99 } | |
100 } | |
101 | |
102 bool HasClass(const CapabilitySpec& spec, const std::string& class_name) { | |
103 auto it = spec.required.find(kServiceManagerName); | |
104 if (it == spec.required.end()) | |
105 return false; | |
106 return it->second.find(class_name) != it->second.end(); | |
107 } | |
108 | |
109 // Encapsulates a connection to an instance of a service, tracked by the | |
110 // Service Manager. | |
111 class ServiceManager::Instance | |
112 : public mojom::Connector, | |
113 public mojom::PIDReceiver, | |
114 public Service, | |
115 public InterfaceFactory<mojom::ServiceManager>, | |
116 public mojom::ServiceManager { | |
117 public: | |
118 Instance(shell::ServiceManager* service_manager, | |
119 const Identity& identity, | |
120 const CapabilitySpec& capability_spec) | |
121 : service_manager_(service_manager), | |
122 id_(GenerateUniqueID()), | |
123 identity_(identity), | |
124 capability_spec_(capability_spec), | |
125 allow_any_application_(capability_spec.required.count("*") == 1), | |
126 pid_receiver_binding_(this), | |
127 weak_factory_(this) { | |
128 if (identity_.name() == kServiceManagerName || | |
129 identity_.name() == kCatalogName) { | |
130 pid_ = base::Process::Current().Pid(); | |
131 } | |
132 DCHECK_NE(mojom::kInvalidInstanceID, id_); | |
133 } | |
134 | |
135 ~Instance() override { | |
136 // Shutdown all bindings before we close the runner. This way the process | |
137 // should see the pipes closed and exit, as well as waking up any potential | |
138 // sync/WaitForIncomingResponse(). | |
139 service_.reset(); | |
140 if (pid_receiver_binding_.is_bound()) | |
141 pid_receiver_binding_.Close(); | |
142 connectors_.CloseAllBindings(); | |
143 service_manager_bindings_.CloseAllBindings(); | |
144 | |
145 // Notify the ServiceManager that this Instance is really going away. | |
146 service_manager_->OnInstanceStopped(identity_); | |
147 | |
148 // Release |runner_| so that if we are called back to OnRunnerCompleted() | |
149 // we know we're in the destructor. | |
150 std::unique_ptr<NativeRunner> runner = std::move(runner_); | |
151 runner.reset(); | |
152 } | |
153 | |
154 Instance* parent() { return parent_; } | |
155 | |
156 void AddChild(std::unique_ptr<Instance> child) { | |
157 child->parent_ = this; | |
158 children_.insert(std::make_pair(child.get(), std::move(child))); | |
159 } | |
160 | |
161 void RemoveChild(Instance* child) { | |
162 auto it = children_.find(child); | |
163 DCHECK(it != children_.end()); | |
164 | |
165 // Deletes |child|. | |
166 children_.erase(it); | |
167 } | |
168 | |
169 bool ConnectToService(std::unique_ptr<ConnectParams>* connect_params) { | |
170 if (!service_.is_bound()) | |
171 return false; | |
172 | |
173 std::unique_ptr<ConnectParams> params(std::move(*connect_params)); | |
174 if (!params->connect_callback().is_null()) { | |
175 params->connect_callback().Run(mojom::ConnectResult::SUCCEEDED, | |
176 identity_.user_id()); | |
177 } | |
178 | |
179 Classes classes; | |
180 Interfaces interfaces; | |
181 Instance* source = service_manager_->GetExistingInstance(params->source()); | |
182 if (source) { | |
183 GetClassesAndInterfacesForConnection(source->capability_spec_, | |
184 identity_, capability_spec_, | |
185 &classes, &interfaces); | |
186 } else { | |
187 interfaces.insert("*"); | |
188 } | |
189 | |
190 // The target has specified that sources must request one of its provided | |
191 // classes instead of specifying a wild-card for interfaces. | |
192 if (HasClass(capability_spec_, kCapabilityClass_ExplicitClass) && | |
193 (interfaces.count("*") != 0)) { | |
194 interfaces.erase("*"); | |
195 } | |
196 | |
197 service_->OnConnect(params->source(), params->TakeRemoteInterfaces(), | |
198 interfaces, classes); | |
199 return true; | |
200 } | |
201 | |
202 void StartWithService(mojom::ServicePtr service) { | |
203 CHECK(!service_); | |
204 service_ = std::move(service); | |
205 service_.set_connection_error_handler( | |
206 base::Bind(&Instance::OnServiceLost, base::Unretained(this), | |
207 service_manager_->GetWeakPtr())); | |
208 service_->OnStart(identity_, | |
209 base::Bind(&Instance::OnInitializeResponse, | |
210 base::Unretained(this))); | |
211 } | |
212 | |
213 void StartWithClientProcessConnection( | |
214 mojom::ClientProcessConnectionPtr client_process_connection) { | |
215 mojom::ServicePtr service; | |
216 service.Bind(mojom::ServicePtrInfo( | |
217 std::move(client_process_connection->service), 0)); | |
218 pid_receiver_binding_.Bind( | |
219 std::move(client_process_connection->pid_receiver_request)); | |
220 StartWithService(std::move(service)); | |
221 } | |
222 | |
223 void StartWithFilePath(const base::FilePath& path) { | |
224 CHECK(!service_); | |
225 runner_ = service_manager_->native_runner_factory_->Create(path); | |
226 bool start_sandboxed = false; | |
227 mojom::ServicePtr service = runner_->Start( | |
228 path, identity_, start_sandboxed, | |
229 base::Bind(&Instance::PIDAvailable, weak_factory_.GetWeakPtr()), | |
230 base::Bind(&Instance::OnRunnerCompleted, weak_factory_.GetWeakPtr())); | |
231 StartWithService(std::move(service)); | |
232 } | |
233 | |
234 mojom::ServiceInfoPtr CreateServiceInfo() const { | |
235 mojom::ServiceInfoPtr info(mojom::ServiceInfo::New()); | |
236 info->id = id_; | |
237 info->identity = identity_; | |
238 info->pid = pid_; | |
239 return info; | |
240 } | |
241 | |
242 const CapabilitySpec& capability_spec() const { | |
243 return capability_spec_; | |
244 } | |
245 const Identity& identity() const { return identity_; } | |
246 uint32_t id() const { return id_; } | |
247 | |
248 // Service: | |
249 bool OnConnect(const Identity& remote_identity, | |
250 InterfaceRegistry* registry) override { | |
251 Instance* source = service_manager_->GetExistingInstance(remote_identity); | |
252 DCHECK(source); | |
253 if (HasClass(source->capability_spec_, kCapabilityClass_ServiceManager)) { | |
254 registry->AddInterface<mojom::ServiceManager>(this); | |
255 return true; | |
256 } | |
257 return false; | |
258 } | |
259 | |
260 private: | |
261 // mojom::Connector implementation: | |
262 void Connect(const shell::Identity& in_target, | |
263 mojom::InterfaceProviderRequest remote_interfaces, | |
264 mojom::ClientProcessConnectionPtr client_process_connection, | |
265 const ConnectCallback& callback) override { | |
266 Identity target = in_target; | |
267 if (target.user_id() == mojom::kInheritUserID) | |
268 target.set_user_id(identity_.user_id()); | |
269 | |
270 if (!ValidateIdentity(target, callback)) | |
271 return; | |
272 if (!ValidateClientProcessConnection(&client_process_connection, target, | |
273 callback)) { | |
274 return; | |
275 } | |
276 if (!ValidateCapabilities(target, callback)) | |
277 return; | |
278 | |
279 std::unique_ptr<ConnectParams> params(new ConnectParams); | |
280 params->set_source(identity_); | |
281 params->set_target(target); | |
282 params->set_remote_interfaces(std::move(remote_interfaces)); | |
283 params->set_client_process_connection(std::move(client_process_connection)); | |
284 params->set_connect_callback(callback); | |
285 service_manager_->Connect( | |
286 std::move(params), nullptr, weak_factory_.GetWeakPtr()); | |
287 } | |
288 | |
289 void Clone(mojom::ConnectorRequest request) override { | |
290 connectors_.AddBinding(this, std::move(request)); | |
291 } | |
292 | |
293 // mojom::PIDReceiver: | |
294 void SetPID(uint32_t pid) override { | |
295 PIDAvailable(pid); | |
296 } | |
297 | |
298 // InterfaceFactory<mojom::ServiceManager>: | |
299 void Create(const Identity& remote_identity, | |
300 mojom::ServiceManagerRequest request) override { | |
301 service_manager_bindings_.AddBinding(this, std::move(request)); | |
302 } | |
303 | |
304 // mojom::ServiceManager implementation: | |
305 void AddListener(mojom::ServiceManagerListenerPtr listener) override { | |
306 // TODO(beng): this should only track the instances matching this user, and | |
307 // root. | |
308 service_manager_->AddListener(std::move(listener)); | |
309 } | |
310 | |
311 bool ValidateIdentity(const Identity& identity, | |
312 const ConnectCallback& callback) { | |
313 if (!IsValidName(identity.name())) { | |
314 LOG(ERROR) << "Error: invalid Name: " << identity.name(); | |
315 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
316 mojom::kInheritUserID); | |
317 return false; | |
318 } | |
319 if (!base::IsValidGUID(identity.user_id())) { | |
320 LOG(ERROR) << "Error: invalid user_id: " << identity.user_id(); | |
321 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
322 mojom::kInheritUserID); | |
323 return false; | |
324 } | |
325 return true; | |
326 } | |
327 | |
328 bool ValidateClientProcessConnection( | |
329 mojom::ClientProcessConnectionPtr* client_process_connection, | |
330 const Identity& target, | |
331 const ConnectCallback& callback) { | |
332 if (!client_process_connection->is_null()) { | |
333 if (!HasClass(capability_spec_, kCapabilityClass_ClientProcess)) { | |
334 LOG(ERROR) << "Instance: " << identity_.name() << " attempting " | |
335 << "to register an instance for a process it created for " | |
336 << "target: " << target.name() << " without the " | |
337 << "service:shell{client_process} capability class."; | |
338 callback.Run(mojom::ConnectResult::ACCESS_DENIED, | |
339 mojom::kInheritUserID); | |
340 return false; | |
341 } | |
342 | |
343 if (!(*client_process_connection)->service.is_valid() || | |
344 !(*client_process_connection)->pid_receiver_request.is_valid()) { | |
345 LOG(ERROR) << "Must supply both service AND " | |
346 << "pid_receiver_request when sending " | |
347 << "client_process_connection."; | |
348 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
349 mojom::kInheritUserID); | |
350 return false; | |
351 } | |
352 if (service_manager_->GetExistingInstance(target)) { | |
353 LOG(ERROR) << "Cannot client process matching existing identity:" | |
354 << "Name: " << target.name() << " User: " | |
355 << target.user_id() << " Instance: " << target.instance(); | |
356 callback.Run(mojom::ConnectResult::INVALID_ARGUMENT, | |
357 mojom::kInheritUserID); | |
358 return false; | |
359 } | |
360 } | |
361 return true; | |
362 } | |
363 | |
364 bool ValidateCapabilities(const Identity& target, | |
365 const ConnectCallback& callback) { | |
366 // TODO(beng): Need to do the following additional policy validation of | |
367 // whether this instance is allowed to connect using: | |
368 // - a non-null client_process_connection. | |
369 if (target.user_id() != identity_.user_id() && | |
370 target.user_id() != mojom::kRootUserID && | |
371 !HasClass(capability_spec_, kCapabilityClass_UserID)) { | |
372 LOG(ERROR) << "Instance: " << identity_.name() << " running as: " | |
373 << identity_.user_id() << " attempting to connect to: " | |
374 << target.name() << " as: " << target.user_id() << " without " | |
375 << " the service:shell{user_id} capability class."; | |
376 callback.Run(mojom::ConnectResult::ACCESS_DENIED, | |
377 mojom::kInheritUserID); | |
378 return false; | |
379 } | |
380 if (!target.instance().empty() && | |
381 target.instance() != GetNamePath(target.name()) && | |
382 !HasClass(capability_spec_, kCapabilityClass_InstanceName)) { | |
383 LOG(ERROR) << "Instance: " << identity_.name() << " attempting to " | |
384 << "connect to " << target.name() << " using Instance name: " | |
385 << target.instance() << " without the " | |
386 << "service:shell{instance_name} capability class."; | |
387 callback.Run(mojom::ConnectResult::ACCESS_DENIED, mojom::kInheritUserID); | |
388 return false; | |
389 | |
390 } | |
391 | |
392 if (allow_any_application_ || | |
393 capability_spec_.required.find(target.name()) != | |
394 capability_spec_.required.end()) { | |
395 return true; | |
396 } | |
397 LOG(ERROR) << "Capabilities prevented connection from: " << | |
398 identity_.name() << " to: " << target.name(); | |
399 callback.Run(mojom::ConnectResult::ACCESS_DENIED, mojom::kInheritUserID); | |
400 return false; | |
401 } | |
402 | |
403 uint32_t GenerateUniqueID() const { | |
404 static uint32_t id = mojom::kInvalidInstanceID; | |
405 ++id; | |
406 CHECK_NE(mojom::kInvalidInstanceID, id); | |
407 return id; | |
408 } | |
409 | |
410 void PIDAvailable(base::ProcessId pid) { | |
411 if (pid == base::kNullProcessId) { | |
412 service_manager_->OnInstanceError(this); | |
413 return; | |
414 } | |
415 pid_ = pid; | |
416 service_manager_->NotifyPIDAvailable(identity_, pid_); | |
417 } | |
418 | |
419 void OnServiceLost(base::WeakPtr<shell::ServiceManager> service_manager) { | |
420 service_.reset(); | |
421 OnConnectionLost(service_manager); | |
422 } | |
423 | |
424 void OnConnectionLost(base::WeakPtr<shell::ServiceManager> service_manager) { | |
425 // Any time a Connector is lost or we lose the Service connection, it | |
426 // may have been the last pipe using this Instance. If so, clean up. | |
427 if (service_manager && !service_) { | |
428 if (connectors_.empty()) | |
429 service_manager->OnInstanceError(this); | |
430 else | |
431 service_manager->OnInstanceUnreachable(this); | |
432 } | |
433 } | |
434 | |
435 void OnInitializeResponse(mojom::ConnectorRequest connector_request) { | |
436 if (connector_request.is_pending()) { | |
437 connectors_.AddBinding(this, std::move(connector_request)); | |
438 connectors_.set_connection_error_handler( | |
439 base::Bind(&Instance::OnConnectionLost, base::Unretained(this), | |
440 service_manager_->GetWeakPtr())); | |
441 } | |
442 } | |
443 | |
444 // Callback when NativeRunner completes. | |
445 void OnRunnerCompleted() { | |
446 if (!runner_.get()) | |
447 return; // We're in the destructor. | |
448 | |
449 service_manager_->OnInstanceError(this); | |
450 } | |
451 | |
452 shell::ServiceManager* const service_manager_; | |
453 | |
454 // An id that identifies this instance. Distinct from pid, as a single process | |
455 // may vend multiple application instances, and this object may exist before a | |
456 // process is launched. | |
457 const uint32_t id_; | |
458 const Identity identity_; | |
459 const CapabilitySpec capability_spec_; | |
460 const bool allow_any_application_; | |
461 std::unique_ptr<NativeRunner> runner_; | |
462 mojom::ServicePtr service_; | |
463 mojo::Binding<mojom::PIDReceiver> pid_receiver_binding_; | |
464 mojo::BindingSet<mojom::Connector> connectors_; | |
465 mojo::BindingSet<mojom::ServiceManager> service_manager_bindings_; | |
466 base::ProcessId pid_ = base::kNullProcessId; | |
467 Instance* parent_ = nullptr; | |
468 InstanceMap children_; | |
469 base::WeakPtrFactory<Instance> weak_factory_; | |
470 | |
471 DISALLOW_COPY_AND_ASSIGN(Instance); | |
472 }; | |
473 | |
474 // static | |
475 ServiceManager::TestAPI::TestAPI(ServiceManager* service_manager) | |
476 : service_manager_(service_manager) {} | |
477 ServiceManager::TestAPI::~TestAPI() {} | |
478 | |
479 bool ServiceManager::TestAPI::HasRunningInstanceForName( | |
480 const std::string& name) const { | |
481 for (const auto& entry : service_manager_->identity_to_instance_) { | |
482 if (entry.first.name() == name) | |
483 return true; | |
484 } | |
485 return false; | |
486 } | |
487 | |
488 //////////////////////////////////////////////////////////////////////////////// | |
489 // ServiceManager, public: | |
490 | |
491 ServiceManager::ServiceManager( | |
492 std::unique_ptr<NativeRunnerFactory> native_runner_factory, | |
493 mojom::ServicePtr catalog) | |
494 : native_runner_factory_(std::move(native_runner_factory)), | |
495 weak_ptr_factory_(this) { | |
496 mojom::ServicePtr service; | |
497 mojom::ServiceRequest request = mojo::GetProxy(&service); | |
498 | |
499 CapabilitySpec spec; | |
500 spec.provided[kCapabilityClass_ServiceManager].insert( | |
501 "shell::mojom::ServiceManager"); | |
502 spec.required["*"].insert("shell:service_factory"); | |
503 spec.required["service:catalog"].insert("shell:resolver"); | |
504 | |
505 service_manager_instance_ = | |
506 CreateInstance(Identity(), CreateServiceManagerIdentity(), spec); | |
507 service_manager_instance_->StartWithService(std::move(service)); | |
508 singletons_.insert(kServiceManagerName); | |
509 service_context_.reset(new ServiceContext(this, std::move(request))); | |
510 | |
511 if (catalog) | |
512 InitCatalog(std::move(catalog)); | |
513 } | |
514 | |
515 ServiceManager::~ServiceManager() { | |
516 // Ensure we tear down the ServiceManager instance last. This is to avoid | |
517 // hitting bindings DCHECKs, since the ServiceManager or Catalog may at any | |
518 // given time own in-flight responders for Instances' Connector requests. | |
519 std::unique_ptr<Instance> service_manager_instance; | |
520 auto iter = root_instances_.find(service_manager_instance_); | |
521 DCHECK(iter != root_instances_.end()); | |
522 service_manager_instance = std::move(iter->second); | |
523 | |
524 root_instances_.clear(); | |
525 } | |
526 | |
527 void ServiceManager::SetServiceOverrides( | |
528 std::unique_ptr<ServiceOverrides> overrides) { | |
529 service_overrides_ = std::move(overrides); | |
530 } | |
531 | |
532 void ServiceManager::SetInstanceQuitCallback( | |
533 base::Callback<void(const Identity&)> callback) { | |
534 instance_quit_callback_ = callback; | |
535 } | |
536 | |
537 void ServiceManager::Connect(std::unique_ptr<ConnectParams> params) { | |
538 Connect(std::move(params), nullptr, nullptr); | |
539 } | |
540 | |
541 mojom::ServiceRequest ServiceManager::StartEmbedderService( | |
542 const std::string& name) { | |
543 std::unique_ptr<ConnectParams> params(new ConnectParams); | |
544 | |
545 Identity embedder_identity(name, mojom::kRootUserID); | |
546 params->set_source(embedder_identity); | |
547 params->set_target(embedder_identity); | |
548 | |
549 mojom::ServicePtr service; | |
550 mojom::ServiceRequest request = mojo::GetProxy(&service); | |
551 Connect(std::move(params), std::move(service), nullptr); | |
552 | |
553 return request; | |
554 } | |
555 | |
556 //////////////////////////////////////////////////////////////////////////////// | |
557 // ServiceManager, Service implementation: | |
558 | |
559 bool ServiceManager::OnConnect(const Identity& remote_identity, | |
560 InterfaceRegistry* registry) { | |
561 // The only interface we expose is mojom::ServiceManager, and access to this | |
562 // interface is brokered by a policy specific to each caller, managed by the | |
563 // caller's instance. Here we look to see who's calling, and forward to the | |
564 // caller's instance to continue. | |
565 Instance* instance = nullptr; | |
566 for (const auto& entry : identity_to_instance_) { | |
567 if (entry.first == remote_identity) { | |
568 instance = entry.second; | |
569 break; | |
570 } | |
571 } | |
572 DCHECK(instance); | |
573 return instance->OnConnect(remote_identity, registry); | |
574 } | |
575 | |
576 //////////////////////////////////////////////////////////////////////////////// | |
577 // ServiceManager, private: | |
578 | |
579 void ServiceManager::InitCatalog(mojom::ServicePtr catalog) { | |
580 // TODO(beng): It'd be great to build this from the manifest, however there's | |
581 // a bit of a chicken-and-egg problem. | |
582 CapabilitySpec spec; | |
583 spec.provided["app"].insert("filesystem::mojom::Directory"); | |
584 spec.provided["catalog:catalog"].insert("catalog::mojom::Catalog"); | |
585 spec.provided["shell:resolver"].insert("shell::mojom::Resolver"); | |
586 spec.provided["control"].insert("catalog::mojom::CatalogControl"); | |
587 Instance* instance = CreateInstance( | |
588 CreateServiceManagerIdentity(), CreateCatalogIdentity(), spec); | |
589 singletons_.insert(kCatalogName); | |
590 instance->StartWithService(std::move(catalog)); | |
591 } | |
592 | |
593 mojom::Resolver* ServiceManager::GetResolver(const Identity& identity) { | |
594 auto iter = identity_to_resolver_.find(identity); | |
595 if (iter != identity_to_resolver_.end()) | |
596 return iter->second.get(); | |
597 | |
598 mojom::ResolverPtr resolver_ptr; | |
599 ConnectToInterface(this, identity, CreateCatalogIdentity(), &resolver_ptr); | |
600 mojom::Resolver* resolver = resolver_ptr.get(); | |
601 identity_to_resolver_[identity] = std::move(resolver_ptr); | |
602 return resolver; | |
603 } | |
604 | |
605 void ServiceManager::OnInstanceError(Instance* instance) { | |
606 // We never clean up the ServiceManager's own instance. | |
607 if (instance == service_manager_instance_) | |
608 return; | |
609 | |
610 const Identity identity = instance->identity(); | |
611 identity_to_instance_.erase(identity); | |
612 | |
613 if (instance->parent()) { | |
614 // Deletes |instance|. | |
615 instance->parent()->RemoveChild(instance); | |
616 } else { | |
617 auto it = root_instances_.find(instance); | |
618 DCHECK(it != root_instances_.end()); | |
619 | |
620 // Deletes |instance|. | |
621 root_instances_.erase(it); | |
622 } | |
623 } | |
624 | |
625 void ServiceManager::OnInstanceUnreachable(Instance* instance) { | |
626 // If an Instance becomes unreachable, new connection requests for this | |
627 // identity will elicit a new Instance instantiation. The unreachable instance | |
628 // remains alive. | |
629 identity_to_instance_.erase(instance->identity()); | |
630 } | |
631 | |
632 void ServiceManager::OnInstanceStopped(const Identity& identity) { | |
633 listeners_.ForAllPtrs([identity](mojom::ServiceManagerListener* listener) { | |
634 listener->OnServiceStopped(identity); | |
635 }); | |
636 if (!instance_quit_callback_.is_null()) | |
637 instance_quit_callback_.Run(identity); | |
638 } | |
639 | |
640 void ServiceManager::Connect(std::unique_ptr<ConnectParams> params, | |
641 mojom::ServicePtr service, | |
642 base::WeakPtr<Instance> source_instance) { | |
643 TRACE_EVENT_INSTANT1("mojo_shell", "ServiceManager::Connect", | |
644 TRACE_EVENT_SCOPE_THREAD, "original_name", | |
645 params->target().name()); | |
646 DCHECK(IsValidName(params->target().name())); | |
647 DCHECK(base::IsValidGUID(params->target().user_id())); | |
648 DCHECK_NE(mojom::kInheritUserID, params->target().user_id()); | |
649 DCHECK(!service.is_bound() || !identity_to_instance_.count(params->target())); | |
650 | |
651 // Connect to an existing matching instance, if possible. | |
652 if (!service.is_bound() && ConnectToExistingInstance(¶ms)) | |
653 return; | |
654 | |
655 // The catalog needs to see the source identity as that of the originating | |
656 // app so it loads the correct store. Since the catalog is itself run as root | |
657 // when this re-enters Connect() it'll be handled by | |
658 // ConnectToExistingInstance(). | |
659 mojom::Resolver* resolver = | |
660 GetResolver(Identity(kServiceManagerName, params->target().user_id())); | |
661 | |
662 std::string name = params->target().name(); | |
663 resolver->ResolveMojoName( | |
664 name, base::Bind(&shell::ServiceManager::OnGotResolvedName, | |
665 weak_ptr_factory_.GetWeakPtr(), base::Passed(¶ms), | |
666 base::Passed(&service), !!source_instance, | |
667 source_instance)); | |
668 } | |
669 | |
670 ServiceManager::Instance* ServiceManager::GetExistingInstance( | |
671 const Identity& identity) const { | |
672 const auto& it = identity_to_instance_.find(identity); | |
673 Instance* instance = it != identity_to_instance_.end() ? it->second : nullptr; | |
674 if (instance) | |
675 return instance; | |
676 | |
677 if (singletons_.find(identity.name()) != singletons_.end()) { | |
678 for (auto entry : identity_to_instance_) { | |
679 if (entry.first.name() == identity.name() && | |
680 entry.first.instance() == identity.instance()) { | |
681 return entry.second; | |
682 } | |
683 } | |
684 } | |
685 return nullptr; | |
686 } | |
687 | |
688 void ServiceManager::NotifyPIDAvailable(const Identity& identity, | |
689 base::ProcessId pid) { | |
690 listeners_.ForAllPtrs( | |
691 [identity, pid](mojom::ServiceManagerListener* listener) { | |
692 listener->OnServiceStarted(identity, pid); | |
693 }); | |
694 } | |
695 | |
696 bool ServiceManager::ConnectToExistingInstance( | |
697 std::unique_ptr<ConnectParams>* params) { | |
698 Instance* instance = GetExistingInstance((*params)->target()); | |
699 return instance && instance->ConnectToService(params); | |
700 } | |
701 | |
702 ServiceManager::Instance* ServiceManager::CreateInstance( | |
703 const Identity& source, | |
704 const Identity& target, | |
705 const CapabilitySpec& spec) { | |
706 CHECK(target.user_id() != mojom::kInheritUserID); | |
707 | |
708 std::unique_ptr<Instance> instance(new Instance(this, target, spec)); | |
709 Instance* raw_instance = instance.get(); | |
710 | |
711 Instance* source_instance = GetExistingInstance(source); | |
712 if (source_instance) | |
713 source_instance->AddChild(std::move(instance)); | |
714 else | |
715 root_instances_.insert(std::make_pair(raw_instance, std::move(instance))); | |
716 | |
717 // NOTE: |instance| has been passed elsewhere. Use |raw_instance| from this | |
718 // point forward. It's safe for the extent of this method. | |
719 | |
720 auto result = | |
721 identity_to_instance_.insert(std::make_pair(target, raw_instance)); | |
722 DCHECK(result.second); | |
723 | |
724 mojom::ServiceInfoPtr info = raw_instance->CreateServiceInfo(); | |
725 listeners_.ForAllPtrs([&info](mojom::ServiceManagerListener* listener) { | |
726 listener->OnServiceCreated(info.Clone()); | |
727 }); | |
728 | |
729 return raw_instance; | |
730 } | |
731 | |
732 void ServiceManager::AddListener(mojom::ServiceManagerListenerPtr listener) { | |
733 // TODO(beng): filter instances provided by those visible to this service. | |
734 std::vector<mojom::ServiceInfoPtr> instances; | |
735 instances.reserve(identity_to_instance_.size()); | |
736 for (auto& instance : identity_to_instance_) | |
737 instances.push_back(instance.second->CreateServiceInfo()); | |
738 listener->OnInit(std::move(instances)); | |
739 | |
740 listeners_.AddPtr(std::move(listener)); | |
741 } | |
742 | |
743 void ServiceManager::CreateServiceWithFactory(const Identity& service_factory, | |
744 const std::string& name, | |
745 mojom::ServiceRequest request) { | |
746 mojom::ServiceFactory* factory = GetServiceFactory(service_factory); | |
747 factory->CreateService(std::move(request), name); | |
748 } | |
749 | |
750 mojom::ServiceFactory* ServiceManager::GetServiceFactory( | |
751 const Identity& service_factory_identity) { | |
752 auto it = service_factories_.find(service_factory_identity); | |
753 if (it != service_factories_.end()) | |
754 return it->second.get(); | |
755 | |
756 Identity source_identity(kServiceManagerName, mojom::kInheritUserID); | |
757 mojom::ServiceFactoryPtr factory; | |
758 ConnectToInterface(this, source_identity, service_factory_identity, | |
759 &factory); | |
760 mojom::ServiceFactory* factory_interface = factory.get(); | |
761 factory.set_connection_error_handler(base::Bind( | |
762 &shell::ServiceManager::OnServiceFactoryLost, | |
763 weak_ptr_factory_.GetWeakPtr(), service_factory_identity)); | |
764 service_factories_[service_factory_identity] = std::move(factory); | |
765 return factory_interface; | |
766 } | |
767 | |
768 void ServiceManager::OnServiceFactoryLost(const Identity& which) { | |
769 // Remove the mapping. | |
770 auto it = service_factories_.find(which); | |
771 DCHECK(it != service_factories_.end()); | |
772 service_factories_.erase(it); | |
773 } | |
774 | |
775 void ServiceManager::OnGotResolvedName(std::unique_ptr<ConnectParams> params, | |
776 mojom::ServicePtr service, | |
777 bool has_source_instance, | |
778 base::WeakPtr<Instance> source_instance, | |
779 mojom::ResolveResultPtr result) { | |
780 // If this request was originated by a specific Instance and that Instance is | |
781 // no longer around, we ignore this response. | |
782 if (has_source_instance && !source_instance) | |
783 return; | |
784 | |
785 std::string instance_name = params->target().instance(); | |
786 if (instance_name == GetNamePath(params->target().name()) && | |
787 result->qualifier != GetNamePath(result->resolved_name)) { | |
788 instance_name = result->qualifier; | |
789 } | |
790 // |result->capabilities| can be null when there is no manifest, e.g. for URL | |
791 // types not resolvable by the resolver. | |
792 CapabilitySpec capabilities = GetPermissiveCapabilities(); | |
793 if (result->capabilities.has_value()) | |
794 capabilities = result->capabilities.value(); | |
795 | |
796 const std::string user_id = HasClass(capabilities, kCapabilityClass_AllUsers) | |
797 ? base::GenerateGUID() | |
798 : params->target().user_id(); | |
799 const Identity target(params->target().name(), user_id, instance_name); | |
800 params->set_target(target); | |
801 | |
802 // It's possible that when this manifest request was issued, another one was | |
803 // already in-progress and completed by the time this one did, and so the | |
804 // requested application may already be running. | |
805 if (ConnectToExistingInstance(¶ms)) | |
806 return; | |
807 | |
808 Identity source = params->source(); | |
809 | |
810 // Services that request "all_users" class from the Service Manager are | |
811 // allowed to field connection requests from any user. They also run with a | |
812 // synthetic user id generated here. The user id provided via Connect() is | |
813 // ignored. Additionally services with the "all_users" class are not tied to | |
814 // the lifetime of the service that started them, instead they are owned by | |
815 // the Service Manager. | |
816 Identity source_identity_for_creation; | |
817 if (HasClass(capabilities, kCapabilityClass_AllUsers)) { | |
818 singletons_.insert(target.name()); | |
819 source_identity_for_creation = CreateServiceManagerIdentity(); | |
820 } else { | |
821 source_identity_for_creation = params->source(); | |
822 } | |
823 | |
824 mojom::ClientProcessConnectionPtr client_process_connection = | |
825 params->TakeClientProcessConnection(); | |
826 Instance* instance = CreateInstance(source_identity_for_creation, | |
827 target, capabilities); | |
828 | |
829 // Below are various paths through which a new Instance can be bound to a | |
830 // Service proxy. | |
831 if (service.is_bound()) { | |
832 // If a ServicePtr was provided, there's no more work to do: someone | |
833 // is already holding a corresponding ServiceRequest. | |
834 instance->StartWithService(std::move(service)); | |
835 } else if (!client_process_connection.is_null()) { | |
836 // Likewise if a ClientProcessConnection was given via Connect(), it | |
837 // provides the Service proxy to use. | |
838 instance->StartWithClientProcessConnection( | |
839 std::move(client_process_connection)); | |
840 } else { | |
841 // Otherwise we create a new Service pipe. | |
842 mojom::ServiceRequest request = GetProxy(&service); | |
843 CHECK(!result->package_path.empty() && result->capabilities.has_value()); | |
844 | |
845 if (target.name() != result->resolved_name) { | |
846 instance->StartWithService(std::move(service)); | |
847 Identity factory(result->resolved_name, target.user_id(), | |
848 instance_name); | |
849 CreateServiceWithFactory(factory, target.name(), std::move(request)); | |
850 } else { | |
851 base::FilePath package_path; | |
852 if (!service_overrides_ || !service_overrides_->GetExecutablePathOverride( | |
853 target.name(), &package_path)) { | |
854 package_path = result->package_path; | |
855 } | |
856 | |
857 Identity source_instance_identity; | |
858 base::debug::Alias(&has_source_instance); | |
859 base::debug::Alias(&package_path); | |
860 base::debug::Alias(&source); | |
861 base::debug::Alias(&target); | |
862 if (source_instance) | |
863 source_instance_identity = source_instance->identity(); | |
864 base::debug::Alias(&source_instance_identity); | |
865 #if defined(GOOGLE_CHROME_BUILD) | |
866 // We do not currently want to hit this code path in production, but it's | |
867 // happening somehow. https://crbug.com/649673. | |
868 CHECK(false); | |
869 #endif | |
870 instance->StartWithFilePath(package_path); | |
871 } | |
872 } | |
873 | |
874 // Now that the instance has a Service, we can connect to it. | |
875 bool connected = instance->ConnectToService(¶ms); | |
876 DCHECK(connected); | |
877 } | |
878 | |
879 base::WeakPtr<ServiceManager> ServiceManager::GetWeakPtr() { | |
880 return weak_ptr_factory_.GetWeakPtr(); | |
881 } | |
882 | |
883 } // namespace shell | |
OLD | NEW |