| 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 |