| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "services/service_manager/service_manager.h" |
| |
| #include <stdint.h> |
| |
| #include <algorithm> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include "base/base_paths.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| #include "base/no_destructor.h" |
| #include "base/optional.h" |
| #include "base/path_service.h" |
| #include "base/process/process.h" |
| #include "base/process/process_handle.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/token.h" |
| #include "base/trace_event/trace_event.h" |
| #include "build/build_config.h" |
| #include "mojo/public/cpp/bindings/associated_binding.h" |
| #include "mojo/public/cpp/bindings/binding.h" |
| #include "mojo/public/cpp/bindings/binding_set.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/service_manager/public/cpp/constants.h" |
| #include "services/service_manager/public/cpp/manifest_builder.h" |
| #include "services/service_manager/public/cpp/service.h" |
| #include "services/service_manager/public/mojom/connector.mojom.h" |
| #include "services/service_manager/public/mojom/service.mojom.h" |
| #include "services/service_manager/public/mojom/service_control.mojom.h" |
| #include "services/service_manager/public/mojom/service_manager.mojom.h" |
| #include "services/service_manager/sandbox/sandbox_type.h" |
| |
| #if !defined(OS_IOS) |
| #include "services/service_manager/service_process_launcher.h" |
| #endif |
| |
| namespace service_manager { |
| |
| namespace { |
| |
| const char kCapability_ServiceManager[] = "service_manager:service_manager"; |
| |
| #if defined(OS_WIN) |
| const char kServiceExecutableExtension[] = ".service.exe"; |
| #else |
| const char kServiceExecutableExtension[] = ".service"; |
| #endif |
| |
| // Returns the set of capabilities required from the target by the source. |
| std::set<std::string> GetRequiredCapabilities( |
| const Manifest::RequiredCapabilityMap& source_requirements, |
| const std::string& target_service) { |
| std::set<std::string> capabilities; |
| |
| // Start by looking for requirements specific to the target identity. |
| auto it = source_requirements.find(target_service); |
| if (it != source_requirements.end()) { |
| std::copy(it->second.begin(), it->second.end(), |
| std::inserter(capabilities, capabilities.begin())); |
| } |
| |
| // Apply wild card rules too. |
| it = source_requirements.find("*"); |
| if (it != source_requirements.end()) { |
| std::copy(it->second.begin(), it->second.end(), |
| std::inserter(capabilities, capabilities.begin())); |
| } |
| return capabilities; |
| } |
| |
| base::ProcessId GetCurrentPid() { |
| #if defined(OS_IOS) |
| // iOS does not support base::Process. |
| return 0; |
| #else |
| return base::Process::Current().Pid(); |
| #endif |
| } |
| |
| void ReportBlockedInterface(const Manifest::ServiceName& source_service_name, |
| const Manifest::ServiceName& target_service_name, |
| const std::string& target_interface_name) { |
| #if DCHECK_IS_ON() |
| // While it would not be correct to assert that this never happens (e.g. a |
| // compromised process may request invalid interfaces), we do want to |
| // effectively treat all occurrences of this branch in production code as |
| // bugs that must be fixed. This crash allows such bugs to be caught in |
| // testing rather than relying on easily overlooked log messages. |
| NOTREACHED() |
| #else |
| LOG(ERROR) |
| #endif |
| << "The Service Manager prevented service \"" << source_service_name |
| << "\" from binding interface \"" << target_interface_name << "\"" |
| << " in target service \"" << target_service_name << "\". You probably " |
| << "need to update one or more service manifests to ensure that \"" |
| << target_service_name << "\" exposes \"" << target_interface_name |
| << "\" through a capability and that \"" << source_service_name |
| << "\" requires that capability from the \"" << target_service_name |
| << "\" service."; |
| } |
| |
| void ReportBlockedStartService(const std::string& source_service_name, |
| const std::string& target_service_name) { |
| #if DCHECK_IS_ON() |
| // See the note in ReportBlockedInterface above. |
| NOTREACHED() |
| #else |
| LOG(ERROR) |
| #endif |
| << "Service \"" << source_service_name << "\" has attempted to manually " |
| << "start service \"" << target_service_name << "\", but it is not " |
| << "sufficiently privileged to do so. You probably need to update one or " |
| << "services' manifests in order to remedy this situation."; |
| } |
| |
| bool AllowsInterface(const Manifest::RequiredCapabilityMap& source_requirements, |
| const std::string& target_name, |
| const Manifest::ExposedCapabilityMap& target_capabilities, |
| const std::string& interface_name) { |
| std::set<std::string> allowed_interfaces; |
| std::set<std::string> required_capabilities = |
| GetRequiredCapabilities(source_requirements, target_name); |
| for (const auto& capability : required_capabilities) { |
| auto it = target_capabilities.find(capability); |
| if (it != target_capabilities.end()) { |
| for (const auto& interface_name : it->second) |
| allowed_interfaces.insert(interface_name); |
| } |
| } |
| |
| bool allowed = |
| allowed_interfaces.count("*") || allowed_interfaces.count(interface_name); |
| return allowed; |
| } |
| |
| const Identity& GetServiceManagerInstanceIdentity() { |
| static base::NoDestructor<Identity> id{service_manager::mojom::kServiceName, |
| kSystemInstanceGroup, base::Token{}, |
| base::Token::CreateRandom()}; |
| return *id; |
| } |
| |
| } // namespace |
| |
| // Encapsulates a connection to an instance of a service, tracked by the |
| // Service Manager. |
| class ServiceManager::Instance |
| : public mojom::Connector, |
| public mojom::PIDReceiver, |
| public mojom::ServiceManager, |
| public mojom::ServiceControl { |
| public: |
| Instance(service_manager::ServiceManager* service_manager, |
| const Identity& identity, |
| const Manifest& manifest) |
| : service_manager_(service_manager), |
| identity_(identity), |
| manifest_(manifest), |
| can_contact_all_services_(manifest_.required_capabilities.count("*") == |
| 1), |
| pid_receiver_binding_(this), |
| control_binding_(this), |
| state_(mojom::InstanceState::kCreated), |
| weak_factory_(this) { |
| if (identity_.name() == service_manager::mojom::kServiceName) |
| pid_ = GetCurrentPid(); |
| DCHECK(identity_.IsValid()); |
| } |
| |
| void Stop() { |
| DCHECK(!stopped_); |
| |
| // Shutdown all bindings. This way the process should see the pipes closed |
| // and exit, as well as waking up any potential |
| // sync/WaitForIncomingResponse(). |
| service_.reset(); |
| if (pid_receiver_binding_.is_bound()) |
| pid_receiver_binding_.Close(); |
| connectors_.CloseAllBindings(); |
| service_manager_bindings_.CloseAllBindings(); |
| MarkUnreachable(); |
| |
| if (state_ == mojom::InstanceState::kCreated) { |
| service_manager_->NotifyServiceFailedToStart(identity_); |
| } else { |
| // Notify the ServiceManager that this Instance is really going away. |
| service_manager_->OnInstanceStopped(identity_); |
| } |
| |
| stopped_ = true; |
| } |
| |
| ~Instance() override { |
| // The instance may have already been stopped prior to destruction if the |
| // ServiceManager itself is being torn down. |
| if (!stopped_) |
| Stop(); |
| } |
| |
| bool CallOnBindInterface(std::unique_ptr<ConnectParams>* in_params) { |
| if (!service_.is_bound()) { |
| (*in_params) |
| ->set_response_data(mojom::ConnectResult::ACCESS_DENIED, identity_); |
| return false; |
| } |
| |
| std::unique_ptr<ConnectParams> params(std::move(*in_params)); |
| Instance* source = |
| service_manager_->GetExistingInstance(params->source()); |
| if (!source) |
| return false; |
| |
| const Manifest& source_manifest = source->manifest(); |
| bool bindable_on_any_service = |
| source->manifest_.interfaces_bindable_on_any_service.count( |
| params->interface_name()) > 0; |
| bool allowed_by_capabilities = AllowsInterface( |
| source_manifest.required_capabilities, identity_.name(), |
| manifest_.exposed_capabilities, params->interface_name()); |
| if (!bindable_on_any_service && !allowed_by_capabilities) { |
| ReportBlockedInterface(params->source().name(), identity_.name(), |
| params->interface_name()); |
| params->set_response_data(mojom::ConnectResult::ACCESS_DENIED, identity_); |
| return false; |
| } |
| |
| params->set_response_data(mojom::ConnectResult::SUCCEEDED, identity_); |
| |
| base::OnceClosure on_bind_interface_complete; |
| if (params->priority() == mojom::BindInterfacePriority::kImportant) { |
| pending_service_connections_++; |
| on_bind_interface_complete = |
| base::BindOnce(&Instance::OnConnectComplete, base::Unretained(this)); |
| } |
| |
| service_->OnBindInterface( |
| BindSourceInfo( |
| params->source(), |
| GetRequiredCapabilities(source_manifest.required_capabilities, |
| identity_.name())), |
| params->interface_name(), params->TakeInterfaceRequestPipe(), |
| std::move(on_bind_interface_complete)); |
| return true; |
| } |
| |
| void OnConnectComplete() { |
| DCHECK_GT(pending_service_connections_, 0); |
| pending_service_connections_--; |
| } |
| |
| void StartWithService(mojom::ServicePtr service) { |
| service_manager_->NotifyServiceCreated(this); |
| |
| CHECK(!service_); |
| service_ = std::move(service); |
| service_.set_connection_error_handler( |
| base::BindOnce(&Instance::OnServiceLost, base::Unretained(this), |
| service_manager_->GetWeakPtr())); |
| service_->OnStart(identity_, base::BindOnce(&Instance::OnStartComplete, |
| base::Unretained(this))); |
| } |
| |
| bool StartWithFilePath(const base::FilePath& path, SandboxType sandbox_type) { |
| #if defined(OS_IOS) |
| // iOS does not support launching services in their own processes. |
| NOTREACHED(); |
| return false; |
| #else |
| DCHECK(!service_); |
| DCHECK(!path.empty()); |
| runner_ = service_manager_->service_process_launcher_factory_->Create(path); |
| if (!runner_) |
| return false; |
| // TODO(tsepez): use actual sandbox type. https://crbug.com/788778 |
| mojom::ServicePtr service = runner_->Start( |
| identity_, SANDBOX_TYPE_NO_SANDBOX, |
| base::BindOnce(&Instance::PIDAvailable, weak_factory_.GetWeakPtr())); |
| StartWithService(std::move(service)); |
| return true; |
| #endif |
| } |
| |
| void BindPIDReceiver(mojom::PIDReceiverRequest request) { |
| pid_receiver_binding_.Bind(std::move(request)); |
| } |
| |
| mojom::RunningServiceInfoPtr CreateRunningServiceInfo() const { |
| mojom::RunningServiceInfoPtr info(mojom::RunningServiceInfo::New()); |
| info->identity = identity_; |
| info->pid = pid_; |
| info->state = state_; |
| return info; |
| } |
| |
| const Manifest& manifest() const { return manifest_; } |
| |
| const Identity& identity() const { return identity_; } |
| |
| void set_identity(const Identity& identity) { |
| DCHECK(identity.IsValid()); |
| identity_ = identity; |
| } |
| |
| void BindServiceManager(mojom::ServiceManagerRequest request) { |
| service_manager_bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| private: |
| class InterfaceProviderImpl : public mojom::InterfaceProvider { |
| public: |
| InterfaceProviderImpl(Instance* instance, |
| const std::string& filter_name, |
| const Identity& source_identity, |
| const Identity& target_identity, |
| service_manager::ServiceManager* service_manager, |
| mojom::InterfaceProviderPtr target, |
| mojom::InterfaceProviderRequest source_request) |
| : instance_(instance), |
| filter_name_(filter_name), |
| source_identity_(source_identity), |
| target_identity_(target_identity), |
| service_manager_(service_manager), |
| target_(std::move(target)), |
| source_binding_(this, std::move(source_request)) { |
| DCHECK(source_identity_.IsValid()); |
| DCHECK(target_identity_.IsValid()); |
| target_.set_connection_error_handler(base::BindOnce( |
| &InterfaceProviderImpl::OnConnectionError, base::Unretained(this))); |
| source_binding_.set_connection_error_handler(base::BindOnce( |
| &InterfaceProviderImpl::OnConnectionError, base::Unretained(this))); |
| } |
| |
| ~InterfaceProviderImpl() override = default; |
| |
| private: |
| // mojom::InterfaceProvider: |
| void GetInterface(const std::string& interface_name, |
| mojo::ScopedMessagePipeHandle interface_pipe) override { |
| Instance* source = |
| service_manager_->GetExistingInstance(source_identity_); |
| Instance* target = |
| service_manager_->GetExistingInstance(target_identity_); |
| if (!source || !target) |
| return; |
| |
| const auto& source_filters = |
| source->manifest().required_interface_filter_capabilities; |
| const auto& target_filters = |
| target->manifest().exposed_interface_filter_capabilities; |
| |
| auto source_iter = source_filters.find(filter_name_); |
| if (source_iter == source_filters.end()) { |
| DLOG(ERROR) << source_identity_.name() << " does not specify any " |
| << "requirements for a filter named '" << filter_name_ |
| << "'"; |
| return; |
| } |
| |
| auto target_iter = target_filters.find(filter_name_); |
| if (target_iter == target_filters.end()) { |
| DLOG(ERROR) << target_identity_.name() << " does not expose any " |
| << "capabilities for a filter named '" << filter_name_ |
| << "'"; |
| return; |
| } |
| |
| if (AllowsInterface(source_iter->second, target_identity_.name(), |
| target_iter->second, interface_name)) { |
| target_->GetInterface(interface_name, std::move(interface_pipe)); |
| } else { |
| ReportBlockedInterface(source_identity_.name(), target_identity_.name(), |
| interface_name); |
| } |
| } |
| |
| void OnConnectionError() { |
| // Deletes |this|. |
| instance_->filters_.erase(this); |
| } |
| |
| Instance* const instance_; |
| const std::string filter_name_; |
| const Identity source_identity_; |
| const Identity target_identity_; |
| const service_manager::ServiceManager* service_manager_; |
| |
| mojom::InterfaceProviderPtr target_; |
| mojo::Binding<mojom::InterfaceProvider> source_binding_; |
| |
| DISALLOW_COPY_AND_ASSIGN(InterfaceProviderImpl); |
| }; |
| |
| void MarkUnreachable() { |
| state_ = mojom::InstanceState::kUnreachable; |
| service_manager_->OnInstanceUnreachable(this); |
| } |
| |
| void MaybeNotifyPidAvailable() { |
| // Ensure that we only notify listeners of the PID after notifying them of |
| // instance start to ensure consistent ordering of ServiceManagerListener |
| // messages pertaining to this instance. |
| if (state_ == mojom::InstanceState::kStarted && |
| pid_ != base::kNullProcessId) { |
| service_manager_->NotifyServicePIDReceived(identity_, pid_); |
| } |
| } |
| |
| // mojom::Connector implementation: |
| void BindInterface(const service_manager::ServiceFilter& target_filter, |
| const std::string& interface_name, |
| mojo::ScopedMessagePipeHandle interface_pipe, |
| mojom::BindInterfacePriority priority, |
| BindInterfaceCallback callback) override { |
| mojom::ConnectResult result = |
| ValidateConnectParams(target_filter, interface_name); |
| if (result != mojom::ConnectResult::SUCCEEDED) { |
| std::move(callback).Run(result, base::nullopt); |
| return; |
| } |
| |
| std::unique_ptr<ConnectParams> params(new ConnectParams); |
| params->set_source(identity_); |
| params->set_target(target_filter); |
| params->set_interface_request_info(interface_name, |
| std::move(interface_pipe)); |
| params->set_priority(priority); |
| params->set_connection_callback(std::move(callback)); |
| service_manager_->Connect(std::move(params)); |
| } |
| |
| void QueryService(const std::string& service_name, |
| QueryServiceCallback callback) override { |
| std::string sandbox_type; |
| bool success = service_manager_->QueryCatalog( |
| service_name, identity_.instance_group(), &sandbox_type); |
| if (success) { |
| std::move(callback).Run(mojom::ServiceInfo::New(sandbox_type)); |
| } else { |
| std::move(callback).Run(nullptr); |
| } |
| } |
| |
| void WarmService(const ServiceFilter& target_filter, |
| WarmServiceCallback callback) override { |
| mojom::ConnectResult result = ValidateConnectParams( |
| target_filter, base::nullopt /* interface_name */); |
| |
| if (result != mojom::ConnectResult::SUCCEEDED) { |
| std::move(callback).Run(result, base::nullopt); |
| return; |
| } |
| |
| std::unique_ptr<ConnectParams> params(new ConnectParams); |
| params->set_source(identity_); |
| params->set_target(target_filter); |
| params->set_connection_callback(std::move(callback)); |
| service_manager_->Connect(std::move(params)); |
| } |
| |
| void RegisterServiceInstance( |
| const Identity& identity, |
| mojo::ScopedMessagePipeHandle service_handle, |
| mojom::PIDReceiverRequest pid_receiver_request, |
| RegisterServiceInstanceCallback callback) override { |
| mojom::ServicePtr service( |
| mojom::ServicePtrInfo(std::move(service_handle), 0)); |
| |
| if (!manifest_.options.can_register_other_service_instances) { |
| LOG(ERROR) << "Instance: " << identity_.name() << " attempting " |
| << "to register an instance for a process it created for " |
| << "target: " << identity.name() << " without " |
| << "the 'can_create_other_service_instances' option."; |
| std::move(callback).Run(mojom::ConnectResult::ACCESS_DENIED); |
| return; |
| } |
| |
| if (service_manager_->GetExistingInstance(identity)) { |
| LOG(ERROR) << "Instance already exists: " << identity.ToString(); |
| std::move(callback).Run(mojom::ConnectResult::INVALID_ARGUMENT); |
| return; |
| } |
| |
| auto target_filter = ServiceFilter::ForExactIdentity(identity); |
| mojom::ConnectResult result = ValidateConnectParams( |
| target_filter, base::nullopt /* interface_name */); |
| if (result != mojom::ConnectResult::SUCCEEDED) { |
| std::move(callback).Run(result); |
| return; |
| } |
| |
| auto params = std::make_unique<ConnectParams>(); |
| params->set_source(identity_); |
| params->set_target(target_filter); |
| params->set_client_process_info(std::move(service), |
| std::move(pid_receiver_request)); |
| params->set_connection_callback(base::BindOnce( |
| [](RegisterServiceInstanceCallback callback, |
| mojom::ConnectResult result, |
| const base::Optional<Identity>& identity) { |
| std::move(callback).Run(result); |
| }, |
| std::move(callback))); |
| service_manager_->Connect(std::move(params)); |
| } |
| |
| void Clone(mojom::ConnectorRequest request) override { |
| connectors_.AddBinding(this, std::move(request)); |
| } |
| |
| void FilterInterfaces(const std::string& filter_name, |
| const Identity& source, |
| mojom::InterfaceProviderRequest source_request, |
| mojom::InterfaceProviderPtr target) override { |
| auto* filter = new InterfaceProviderImpl( |
| this, filter_name, source, identity_, service_manager_, |
| std::move(target), std::move(source_request)); |
| filters_[filter] = base::WrapUnique(filter); |
| } |
| |
| // mojom::PIDReceiver: |
| void SetPID(uint32_t pid) override { |
| PIDAvailable(pid); |
| } |
| |
| // mojom::ServiceManager implementation: |
| void AddListener(mojom::ServiceManagerListenerPtr listener) override { |
| // TODO(beng): this should only track the instances matching this instance |
| // group, or the root instance group. |
| service_manager_->AddListener(std::move(listener)); |
| } |
| |
| mojom::ConnectResult ValidateConnectParams( |
| const ServiceFilter& target_filter, |
| const base::Optional<std::string>& target_interface_name) { |
| if (target_filter.service_name().empty()) { |
| DLOG(ERROR) << "ServiceFilter has no service name."; |
| return mojom::ConnectResult::INVALID_ARGUMENT; |
| } |
| |
| bool skip_instance_group_check = |
| manifest_.options.instance_sharing_policy == |
| Manifest::InstanceSharingPolicy::kSingleton || |
| manifest_.options.instance_sharing_policy == |
| Manifest::InstanceSharingPolicy::kSharedAcrossGroups || |
| manifest_.options.can_connect_to_instances_in_any_group; |
| |
| if (!skip_instance_group_check && target_filter.instance_group() && |
| target_filter.instance_group() != identity_.instance_group() && |
| target_filter.instance_group() != kSystemInstanceGroup) { |
| LOG(ERROR) << "Instance " << identity_.ToString() << " attempting to " |
| << "connect to " << target_filter.service_name() << " in " |
| << "group " << target_filter.instance_group()->ToString() |
| << " without |can_connect_to_instances_in_any_group| set to " |
| << "|true|."; |
| return mojom::ConnectResult::ACCESS_DENIED; |
| } |
| if (target_filter.instance_id() && |
| !target_filter.instance_id()->is_zero() && |
| !manifest_.options.can_connect_to_instances_with_any_id) { |
| LOG(ERROR) << "Instance " << identity_.ToString() |
| << " attempting to connect to " << target_filter.service_name() |
| << " with instance ID " |
| << target_filter.instance_id()->ToString() << " without " |
| << "|can_connect_to_instances_with_any_id| set to |true|."; |
| return mojom::ConnectResult::ACCESS_DENIED; |
| } |
| |
| if (can_contact_all_services_ || |
| !manifest_.interfaces_bindable_on_any_service.empty() || |
| manifest_.required_capabilities.find(target_filter.service_name()) != |
| manifest_.required_capabilities.end()) { |
| return mojom::ConnectResult::SUCCEEDED; |
| } |
| |
| if (target_interface_name) { |
| ReportBlockedInterface(identity_.name(), target_filter.service_name(), |
| *target_interface_name); |
| } else { |
| ReportBlockedStartService(identity_.name(), target_filter.service_name()); |
| } |
| |
| return mojom::ConnectResult::ACCESS_DENIED; |
| } |
| |
| uint32_t GenerateUniqueID() const { |
| static uint32_t id = 0; |
| ++id; |
| CHECK_NE(0u, id); |
| return id; |
| } |
| |
| void PIDAvailable(base::ProcessId pid) { |
| #if !defined(OS_IOS) |
| // iOS does not support base::Process and simply passes 0 here, so elide |
| // this check on that platform. |
| if (pid == base::kNullProcessId) { |
| service_manager_->OnInstanceError(this); |
| return; |
| } |
| #endif |
| pid_ = pid; |
| MaybeNotifyPidAvailable(); |
| } |
| |
| void OnServiceLost( |
| base::WeakPtr<service_manager::ServiceManager> service_manager) { |
| service_.reset(); |
| OnConnectionLost(service_manager); |
| } |
| |
| void OnConnectionLost( |
| base::WeakPtr<service_manager::ServiceManager> service_manager) { |
| // Any time a Connector is lost or we lose the Service connection, it |
| // may have been the last pipe using this Instance. If so, clean up. |
| if (service_manager && !service_) { |
| if (connectors_.empty()) |
| service_manager->OnInstanceError(this); |
| else |
| MarkUnreachable(); |
| } |
| } |
| |
| void OnStartComplete(mojom::ConnectorRequest connector_request, |
| mojom::ServiceControlAssociatedRequest control_request) { |
| state_ = mojom::InstanceState::kStarted; |
| if (connector_request.is_pending()) { |
| connectors_.AddBinding(this, std::move(connector_request)); |
| connectors_.set_connection_error_handler(base::BindRepeating( |
| &Instance::OnConnectionLost, base::Unretained(this), |
| service_manager_->GetWeakPtr())); |
| } |
| if (control_request.is_pending()) |
| control_binding_.Bind(std::move(control_request)); |
| service_manager_->NotifyServiceStarted(identity_, pid_); |
| MaybeNotifyPidAvailable(); |
| } |
| |
| // mojom::ServiceControl: |
| void RequestQuit() override { |
| // If quit is requested, oblige when there are no pending OnConnects. |
| if (!pending_service_connections_) |
| OnServiceLost(service_manager_->GetWeakPtr()); |
| } |
| |
| service_manager::ServiceManager* const service_manager_; |
| |
| // An id that identifies this instance. Distinct from PID, as a single process |
| // may host multiple service instances. Globally unique across time and space. |
| Identity identity_; |
| |
| // The static service manifest provided for this service at system |
| // initialization. |
| const Manifest manifest_; |
| |
| // Indicates if this instance requires at least one capability from the |
| // wildcard '*' service. |
| const bool can_contact_all_services_; |
| |
| #if !defined(OS_IOS) |
| std::unique_ptr<ServiceProcessLauncher> runner_; |
| #endif |
| mojom::ServicePtr service_; |
| mojo::Binding<mojom::PIDReceiver> pid_receiver_binding_; |
| mojo::BindingSet<mojom::Connector> connectors_; |
| mojo::BindingSet<mojom::ServiceManager> service_manager_bindings_; |
| mojo::AssociatedBinding<mojom::ServiceControl> control_binding_; |
| base::ProcessId pid_ = base::kNullProcessId; |
| mojom::InstanceState state_; |
| bool stopped_ = false; |
| |
| std::map<InterfaceProviderImpl*, std::unique_ptr<InterfaceProviderImpl>> |
| filters_; |
| |
| // The number of outstanding OnBindInterface requests which are in flight. |
| int pending_service_connections_ = 0; |
| |
| base::WeakPtrFactory<Instance> weak_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Instance); |
| }; |
| |
| // A container of Instances that stores them with an Identity and an |
| // InstanceType so they can be resolved based on part of that Identity. See |
| // comments on the InstanceType definition for more on how different types of |
| // service identities are resolved. |
| class ServiceManager::IdentityToInstanceMap { |
| public: |
| // An entry in any of the mappings owned by this object. |
| struct Entry { |
| Entry(const base::Token& guid, Instance* instance) |
| : guid(guid), instance(instance) { |
| DCHECK(!guid.is_zero()); |
| DCHECK(instance); |
| } |
| Entry(const Entry&) = default; |
| ~Entry() = default; |
| |
| base::Token guid; |
| Instance* instance = nullptr; |
| }; |
| |
| struct RegularInstanceKey { |
| RegularInstanceKey(const std::string& service_name, |
| const base::Token& instance_group, |
| const base::Token& instance_id) |
| : service_name(service_name), |
| instance_group(instance_group), |
| instance_id(instance_id) {} |
| RegularInstanceKey(const RegularInstanceKey&) = default; |
| ~RegularInstanceKey() = default; |
| |
| bool operator==(const RegularInstanceKey& other) const { |
| return service_name == other.service_name && |
| instance_group == other.instance_group && |
| instance_id == other.instance_id; |
| } |
| |
| bool operator<(const RegularInstanceKey& other) const { |
| return std::tie(service_name, instance_group, instance_id) < |
| std::tie(other.service_name, other.instance_group, |
| other.instance_id); |
| } |
| |
| const std::string service_name; |
| const base::Token instance_group; |
| const base::Token instance_id; |
| }; |
| |
| struct SharedInstanceKey { |
| SharedInstanceKey(const std::string& service_name, |
| const base::Token& instance_id) |
| : service_name(service_name), instance_id(instance_id) {} |
| SharedInstanceKey(const SharedInstanceKey&) = default; |
| ~SharedInstanceKey() = default; |
| |
| bool operator==(const SharedInstanceKey& other) const { |
| return service_name == other.service_name && |
| instance_id == other.instance_id; |
| } |
| |
| bool operator<(const SharedInstanceKey& other) const { |
| if (service_name != other.service_name) |
| return service_name < other.service_name; |
| return instance_id < other.instance_id; |
| } |
| |
| const std::string service_name; |
| const base::Token instance_id; |
| }; |
| |
| IdentityToInstanceMap() = default; |
| ~IdentityToInstanceMap() = default; |
| |
| void Insert(const Identity& identity, InstanceType type, Instance* instance) { |
| DCHECK(identity.IsValid()); |
| DCHECK_NE(instance, nullptr); |
| DCHECK_EQ(Get(identity), nullptr); |
| switch (type) { |
| case InstanceType::kRegular: { |
| const RegularInstanceKey key{identity.name(), identity.instance_group(), |
| identity.instance_id()}; |
| regular_instances_[key].emplace_back(identity.globally_unique_id(), |
| instance); |
| break; |
| } |
| |
| case InstanceType::kSharedAcrossInstanceGroups: { |
| const SharedInstanceKey key{identity.name(), identity.instance_id()}; |
| shared_instances_[key].emplace_back(identity.globally_unique_id(), |
| instance); |
| break; |
| } |
| |
| case InstanceType::kSingleton: |
| singleton_instances_[identity.name()].emplace_back( |
| identity.globally_unique_id(), instance); |
| break; |
| |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| Instance* Get(const ServiceFilter& filter) { |
| DCHECK(filter.instance_group()); |
| DCHECK(filter.instance_id()); |
| DCHECK(!filter.globally_unique_id() || |
| !filter.globally_unique_id()->is_zero()); |
| |
| const RegularInstanceKey regular_key{ |
| filter.service_name(), *filter.instance_group(), *filter.instance_id()}; |
| auto regular_iter = regular_instances_.find(regular_key); |
| if (regular_iter != regular_instances_.end()) { |
| return FindMatchingInstance(regular_iter->second, |
| filter.globally_unique_id()); |
| } |
| |
| const SharedInstanceKey shared_key{filter.service_name(), |
| *filter.instance_id()}; |
| auto shared_iter = shared_instances_.find( |
| SharedInstanceKey(filter.service_name(), *filter.instance_id())); |
| if (shared_iter != shared_instances_.end()) { |
| return FindMatchingInstance(shared_iter->second, |
| filter.globally_unique_id()); |
| } |
| |
| auto singleton_iter = singleton_instances_.find(filter.service_name()); |
| if (singleton_iter != singleton_instances_.end()) { |
| return FindMatchingInstance(singleton_iter->second, |
| filter.globally_unique_id()); |
| } |
| |
| return nullptr; |
| } |
| |
| bool Erase(const Identity& identity) { |
| DCHECK(identity.IsValid()); |
| |
| const RegularInstanceKey regular_key{ |
| identity.name(), identity.instance_group(), identity.instance_id()}; |
| auto regular_iter = regular_instances_.find(regular_key); |
| if (regular_iter != regular_instances_.end()) { |
| auto& entries = regular_iter->second; |
| if (EraseEntry(identity.globally_unique_id(), &entries)) { |
| if (entries.empty()) |
| regular_instances_.erase(regular_iter); |
| return true; |
| } |
| } |
| |
| const SharedInstanceKey shared_key{identity.name(), identity.instance_id()}; |
| auto shared_iter = shared_instances_.find(shared_key); |
| if (shared_iter != shared_instances_.end()) { |
| auto& entries = shared_iter->second; |
| if (EraseEntry(identity.globally_unique_id(), &entries)) { |
| if (entries.empty()) |
| shared_instances_.erase(shared_iter); |
| return true; |
| } |
| } |
| |
| auto singleton_iter = singleton_instances_.find(identity.name()); |
| if (singleton_iter != singleton_instances_.end()) { |
| auto& entries = singleton_iter->second; |
| if (EraseEntry(identity.globally_unique_id(), &entries)) { |
| if (entries.empty()) |
| singleton_instances_.erase(singleton_iter); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| void PopulateRunningServiceInfo( |
| std::vector<mojom::RunningServiceInfoPtr>* running_service_info) { |
| running_service_info->reserve(regular_instances_.size() + |
| shared_instances_.size() + |
| singleton_instances_.size()); |
| for (auto& iter : regular_instances_) |
| CreateServiceInfos(iter.second, running_service_info); |
| for (auto& iter : shared_instances_) |
| CreateServiceInfos(iter.second, running_service_info); |
| for (auto& iter : singleton_instances_) |
| CreateServiceInfos(iter.second, running_service_info); |
| } |
| |
| private: |
| // Maps a 3-tuple of (service name, instance group, instance ID) to a list of |
| // service instances and their GUIDs. |
| using RegularInstanceMap = std::map<RegularInstanceKey, std::vector<Entry>>; |
| |
| // Maps a 2-tuple of (service name, instance ID) to a list of shared service |
| // instances and their GUIDs. |
| using SharedInstanceMap = std::map<SharedInstanceKey, std::vector<Entry>>; |
| |
| // Maps a service name to a list of singleton instances and their GUIDs. |
| using SingletonInstanceMap = std::map<std::string, std::vector<Entry>>; |
| |
| Instance* FindMatchingInstance(const std::vector<Entry>& entries, |
| const base::Optional<base::Token>& guid) { |
| DCHECK(!entries.empty()); |
| if (!guid.has_value()) |
| return entries.front().instance; |
| |
| for (const auto& entry : entries) { |
| if (entry.guid == *guid) |
| return entry.instance; |
| } |
| |
| return nullptr; |
| } |
| |
| bool EraseEntry(const base::Token& guid, std::vector<Entry>* entries) { |
| auto it = std::find_if( |
| entries->begin(), entries->end(), |
| [&guid](const Entry& entry) { return entry.guid == guid; }); |
| if (it == entries->end()) |
| return false; |
| |
| entries->erase(it); |
| return true; |
| } |
| |
| void CreateServiceInfos(const std::vector<Entry>& entries, |
| std::vector<mojom::RunningServiceInfoPtr>* infos) { |
| for (const auto& entry : entries) |
| infos->push_back(entry.instance->CreateRunningServiceInfo()); |
| } |
| |
| RegularInstanceMap regular_instances_; |
| SharedInstanceMap shared_instances_; |
| SingletonInstanceMap singleton_instances_; |
| |
| DISALLOW_COPY_AND_ASSIGN(IdentityToInstanceMap); |
| }; |
| |
| ServiceManager::ServiceManager(std::unique_ptr<ServiceProcessLauncherFactory> |
| service_process_launcher_factory, |
| const std::vector<Manifest>& manifests) |
| : catalog_(manifests), |
| identity_to_instance_(std::make_unique<IdentityToInstanceMap>()), |
| service_process_launcher_factory_( |
| std::move(service_process_launcher_factory)) { |
| Manifest service_manager_manifest = |
| ManifestBuilder() |
| .ExposeCapability(kCapability_ServiceManager, |
| Manifest::InterfaceList<mojom::ServiceManager>()) |
| .RequireCapability("*", "service_manager:service_factory") |
| .Build(); |
| service_manager_instance_ = |
| CreateInstance(GetServiceManagerInstanceIdentity(), |
| InstanceType::kSingleton, service_manager_manifest); |
| |
| mojom::ServicePtr service; |
| service_binding_.Bind(mojo::MakeRequest(&service)); |
| service_manager_instance_->StartWithService(std::move(service)); |
| } |
| |
| ServiceManager::~ServiceManager() { |
| // Ensure we tear down the ServiceManager instance last. This is to avoid |
| // hitting bindings DCHECKs, since the ServiceManager or Catalog may at any |
| // given time own in-flight responders for Instances' Connector requests. |
| std::unique_ptr<Instance> service_manager_instance; |
| auto iter = instances_.find(service_manager_instance_); |
| DCHECK(iter != instances_.end()); |
| service_manager_instance = std::move(iter->second); |
| |
| // Stop all of the instances before destroying any of them. This ensures that |
| // all Services will receive connection errors and avoids possible deadlock in |
| // the case where one Instance's destructor blocks waiting for its Runner to |
| // quit, while that Runner's corresponding Service blocks its shutdown on a |
| // distinct Service receiving a connection error. |
| for (const auto& instance : instances_) { |
| if (instance.first != service_manager_instance_) |
| instance.first->Stop(); |
| } |
| |
| service_manager_instance->Stop(); |
| instances_.clear(); |
| } |
| |
| void ServiceManager::SetInstanceQuitCallback( |
| base::Callback<void(const Identity&)> callback) { |
| instance_quit_callback_ = std::move(callback); |
| } |
| |
| void ServiceManager::Connect(std::unique_ptr<ConnectParams> params) { |
| TRACE_EVENT_INSTANT1("service_manager", "ServiceManager::Connect", |
| TRACE_EVENT_SCOPE_THREAD, "original_name", |
| params->target().service_name()); |
| const Identity& source = params->source(); |
| DCHECK(source.IsValid()); |
| |
| ServiceFilter target_filter = params->target(); |
| |
| // If the target filter does not specify an instance group, we assume the |
| // source's own. |
| if (!target_filter.instance_group()) |
| target_filter.set_instance_group(source.instance_group()); |
| |
| // If the target filter does not specify an instance ID, we assume zero. |
| if (!target_filter.instance_id()) |
| target_filter.set_instance_id(base::Token{}); |
| |
| if (!params->HasClientProcessInfo()) { |
| // Connect to an existing matching instance, if possible. |
| Instance* instance = identity_to_instance_->Get(target_filter); |
| if (instance) { |
| if (params->HasInterfaceRequestInfo()) { |
| instance->CallOnBindInterface(¶ms); |
| } else { |
| // This is a StartService request and the instance is already running. |
| // Make sure the response identity is properly resolved. |
| params->set_response_data(mojom::ConnectResult::SUCCEEDED, |
| instance->identity()); |
| } |
| return; |
| } |
| |
| // If there was no existing instance but the request specified a specific |
| // globally unique ID for the target, ignore the request. That instance is |
| // obviously no longer running. |
| if (target_filter.globally_unique_id()) |
| return; |
| } |
| |
| // Beyond this point, in order to fulfill the connection request we need to |
| // start a new instance of the target service. |
| |
| const service_manager::Manifest* manifest = |
| catalog_.GetManifest(target_filter.service_name()); |
| if (!manifest) { |
| LOG(ERROR) << "Failed to resolve service name: " |
| << target_filter.service_name(); |
| params->set_response_data(mojom::ConnectResult::INVALID_ARGUMENT, |
| base::nullopt); |
| return; |
| } |
| |
| bool all_user_instance = manifest->options.instance_sharing_policy == |
| Manifest::InstanceSharingPolicy::kSharedAcrossGroups; |
| bool singleton_instance = manifest->options.instance_sharing_policy == |
| Manifest::InstanceSharingPolicy::kSingleton; |
| |
| // Services that have "shared_across_instance_groups" value of |
| // "instance_sharing" option are allowed to field connection requests from |
| // instances in any instance group. They also run with a synthetic instance |
| // group generated here. The instance group provided via |Connect()| is |
| // ignored. |
| |
| InstanceType instance_type; |
| if (singleton_instance) |
| instance_type = InstanceType::kSingleton; |
| else if (all_user_instance) |
| instance_type = InstanceType::kSharedAcrossInstanceGroups; |
| else |
| instance_type = InstanceType::kRegular; |
| |
| Identity new_instance_identity; |
| if (params->HasClientProcessInfo()) { |
| // This is a service instance registration, so we should use the exact |
| // Identity given by the caller. |
| if (!target_filter.globally_unique_id() || |
| target_filter.globally_unique_id()->is_zero()) { |
| params->set_response_data(mojom::ConnectResult::INVALID_ARGUMENT, |
| base::nullopt); |
| return; |
| } |
| new_instance_identity = Identity( |
| target_filter.service_name(), *target_filter.instance_group(), |
| *target_filter.instance_id(), *target_filter.globally_unique_id()); |
| } else if (instance_type == InstanceType::kSingleton) { |
| // For singleton instances, we generate a random group ID along with the |
| // random GUID. |
| new_instance_identity = |
| Identity(target_filter.service_name(), base::Token::CreateRandom(), |
| base::Token{}, base::Token::CreateRandom()); |
| } else if (instance_type == InstanceType::kSharedAcrossInstanceGroups) { |
| // For services shared across instance groups, we respect the target |
| // instance ID but still generate a random group ID along with the random |
| // GUID. |
| new_instance_identity = |
| Identity(target_filter.service_name(), base::Token::CreateRandom(), |
| *target_filter.instance_id(), base::Token::CreateRandom()); |
| } else { |
| DCHECK_EQ(instance_type, InstanceType::kRegular); |
| new_instance_identity = |
| Identity(target_filter.service_name(), *target_filter.instance_group(), |
| *target_filter.instance_id(), base::Token::CreateRandom()); |
| } |
| |
| Instance* instance = |
| CreateInstance(new_instance_identity, instance_type, *manifest); |
| |
| // Below are various paths through which a new Instance can be bound to a |
| // Service proxy. |
| if (params->HasClientProcessInfo()) { |
| // If this is a service registration via |RegisterService()| or |
| // |Connector.RegisterServiceInstance()|, we're done. |
| instance->BindPIDReceiver(params->TakePIDReceiverRequest()); |
| instance->StartWithService(params->TakeService()); |
| return; |
| } |
| |
| const service_manager::Manifest* parent_manifest = |
| catalog_.GetParentManifest(manifest->service_name); |
| if (parent_manifest) { |
| // This service is provided by another service via a ServiceFactory. |
| // |
| // We normally ignore the target instance group and generate a unique |
| // instance group identifier when starting shared instances, but when those |
| // instances need to be started by a service factory, it's conceivable that |
| // the factory itself may be part of the originally targeted instance group. |
| // In that case we use the originally targeted instance group to identify |
| // the factory service. |
| // |
| // TODO(https://904240): This is super weird and hard to rationalize. Maybe |
| // it's the wrong thing to do. |
| instance->set_identity( |
| Identity(new_instance_identity.name(), *target_filter.instance_group(), |
| new_instance_identity.instance_id(), |
| new_instance_identity.globally_unique_id())); |
| |
| auto factory_filter = ServiceFilter::ByNameWithIdInGroup( |
| parent_manifest->service_name, *target_filter.instance_id(), |
| *target_filter.instance_group()); |
| |
| mojom::PIDReceiverPtr pid_receiver; |
| instance->BindPIDReceiver(mojo::MakeRequest(&pid_receiver)); |
| |
| mojom::ServicePtr service; |
| CreateServiceWithFactory(factory_filter, target_filter.service_name(), |
| mojo::MakeRequest(&service), |
| std::move(pid_receiver)); |
| instance->StartWithService(std::move(service)); |
| } else { |
| base::FilePath service_exe_root; |
| CHECK(base::PathService::Get(base::DIR_ASSETS, &service_exe_root)); |
| if (!instance->StartWithFilePath( |
| service_exe_root.AppendASCII(manifest->service_name + |
| kServiceExecutableExtension), |
| UtilitySandboxTypeFromString(manifest->options.sandbox_type))) { |
| OnInstanceError(instance); |
| params->set_response_data(mojom::ConnectResult::INVALID_ARGUMENT, |
| base::nullopt); |
| return; |
| } |
| } |
| |
| params->set_response_data(mojom::ConnectResult::SUCCEEDED, |
| instance->identity()); |
| if (params->HasInterfaceRequestInfo()) |
| instance->CallOnBindInterface(¶ms); |
| } |
| |
| void ServiceManager::StartService(const std::string& service_name) { |
| auto params = std::make_unique<ConnectParams>(); |
| params->set_source(GetServiceManagerInstanceIdentity()); |
| params->set_target( |
| ServiceFilter::ByNameInGroup(service_name, kSystemInstanceGroup)); |
| Connect(std::move(params)); |
| } |
| |
| bool ServiceManager::QueryCatalog(const std::string& service_name, |
| const base::Token& instance_group, |
| std::string* sandbox_type) { |
| const Manifest* manifest = catalog_.GetManifest(service_name); |
| if (!manifest) |
| return false; |
| *sandbox_type = manifest->options.sandbox_type; |
| return true; |
| } |
| |
| void ServiceManager::RegisterService( |
| const Identity& identity, |
| mojom::ServicePtr service, |
| mojom::PIDReceiverRequest pid_receiver_request) { |
| auto params = std::make_unique<ConnectParams>(); |
| |
| if (!pid_receiver_request.is_pending()) { |
| mojom::PIDReceiverPtr pid_receiver; |
| pid_receiver_request = mojo::MakeRequest(&pid_receiver); |
| pid_receiver->SetPID(GetCurrentPid()); |
| } |
| |
| DCHECK(identity.IsValid()); |
| params->set_source(identity); |
| params->set_target(ServiceFilter::ForExactIdentity(identity)); |
| params->set_client_process_info( |
| std::move(service), std::move(pid_receiver_request)); |
| Connect(std::move(params)); |
| } |
| |
| void ServiceManager::OnInstanceError(Instance* instance) { |
| // We never clean up the ServiceManager's own instance. |
| if (instance == service_manager_instance_) |
| return; |
| |
| EraseInstanceIdentity(instance); |
| auto it = instances_.find(instance); |
| DCHECK(it != instances_.end()); |
| |
| // Deletes |instance|. |
| instances_.erase(it); |
| } |
| |
| void ServiceManager::OnInstanceUnreachable(Instance* instance) { |
| // If an Instance becomes unreachable, new connection requests for this |
| // identity will elicit a new Instance instantiation. The unreachable instance |
| // remains alive. |
| EraseInstanceIdentity(instance); |
| } |
| |
| void ServiceManager::OnInstanceStopped(const Identity& identity) { |
| listeners_.ForAllPtrs([&identity](mojom::ServiceManagerListener* listener) { |
| listener->OnServiceStopped(identity); |
| }); |
| if (!instance_quit_callback_.is_null()) |
| instance_quit_callback_.Run(identity); |
| } |
| |
| ServiceManager::Instance* ServiceManager::GetExistingInstance( |
| const Identity& identity) const { |
| return identity_to_instance_->Get(ServiceFilter::ForExactIdentity(identity)); |
| } |
| |
| void ServiceManager::EraseInstanceIdentity(Instance* instance) { |
| identity_to_instance_->Erase(instance->identity()); |
| } |
| |
| void ServiceManager::NotifyServiceCreated(Instance* instance) { |
| mojom::RunningServiceInfoPtr info = instance->CreateRunningServiceInfo(); |
| listeners_.ForAllPtrs([&info](mojom::ServiceManagerListener* listener) { |
| listener->OnServiceCreated(info.Clone()); |
| }); |
| } |
| |
| void ServiceManager::NotifyServiceStarted(const Identity& identity, |
| base::ProcessId pid) { |
| listeners_.ForAllPtrs( |
| [&identity, pid](mojom::ServiceManagerListener* listener) { |
| listener->OnServiceStarted(identity, pid); |
| }); |
| } |
| |
| void ServiceManager::NotifyServiceFailedToStart(const Identity& identity) { |
| listeners_.ForAllPtrs([&identity](mojom::ServiceManagerListener* listener) { |
| listener->OnServiceFailedToStart(identity); |
| }); |
| } |
| |
| void ServiceManager::NotifyServicePIDReceived(const Identity& identity, |
| base::ProcessId pid) { |
| listeners_.ForAllPtrs( |
| [&identity, pid](mojom::ServiceManagerListener* listener) { |
| listener->OnServicePIDReceived(identity, pid); |
| }); |
| } |
| |
| ServiceManager::Instance* ServiceManager::CreateInstance( |
| const Identity& identity, |
| InstanceType instance_type, |
| const Manifest& manifest) { |
| DCHECK(identity.IsValid()); |
| |
| auto instance = std::make_unique<Instance>(this, identity, manifest); |
| Instance* raw_instance = instance.get(); |
| |
| instances_.insert(std::make_pair(raw_instance, std::move(instance))); |
| |
| // NOTE: |instance| has been passed elsewhere. Use |raw_instance| from this |
| // point forward. It's safe for the extent of this method. |
| |
| identity_to_instance_->Insert(identity, instance_type, raw_instance); |
| |
| return raw_instance; |
| } |
| |
| void ServiceManager::AddListener(mojom::ServiceManagerListenerPtr listener) { |
| // TODO(beng): filter instances provided by those visible to this service. |
| std::vector<mojom::RunningServiceInfoPtr> instances; |
| identity_to_instance_->PopulateRunningServiceInfo(&instances); |
| listener->OnInit(std::move(instances)); |
| listeners_.AddPtr(std::move(listener)); |
| } |
| |
| void ServiceManager::CreateServiceWithFactory( |
| const ServiceFilter& service_factory_filter, |
| const std::string& name, |
| mojom::ServiceRequest request, |
| mojom::PIDReceiverPtr pid_receiver) { |
| mojom::ServiceFactory* factory = GetServiceFactory(service_factory_filter); |
| factory->CreateService(std::move(request), name, std::move(pid_receiver)); |
| } |
| |
| mojom::ServiceFactory* ServiceManager::GetServiceFactory( |
| const ServiceFilter& filter) { |
| auto it = service_factories_.find(filter); |
| if (it != service_factories_.end()) |
| return it->second.get(); |
| |
| mojom::ServiceFactoryPtr factory; |
| auto params = std::make_unique<ConnectParams>(); |
| params->set_source(GetServiceManagerInstanceIdentity()); |
| params->set_target(filter); |
| params->set_interface_request_info( |
| service_manager::mojom::ServiceFactory::Name_, |
| mojo::MakeRequest(&factory).PassMessagePipe()); |
| Connect(std::move(params)); |
| |
| mojom::ServiceFactory* factory_interface = factory.get(); |
| factory.set_connection_error_handler( |
| base::BindOnce(&service_manager::ServiceManager::OnServiceFactoryLost, |
| weak_ptr_factory_.GetWeakPtr(), filter)); |
| service_factories_[filter] = std::move(factory); |
| return factory_interface; |
| } |
| |
| void ServiceManager::OnServiceFactoryLost(const ServiceFilter& which) { |
| // Remove the mapping. |
| auto it = service_factories_.find(which); |
| DCHECK(it != service_factories_.end()); |
| service_factories_.erase(it); |
| } |
| |
| base::WeakPtr<ServiceManager> ServiceManager::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| void ServiceManager::OnBindInterface( |
| const BindSourceInfo& source_info, |
| const std::string& interface_name, |
| mojo::ScopedMessagePipeHandle interface_pipe) { |
| Instance* instance = GetExistingInstance(source_info.identity); |
| DCHECK(instance); |
| if (interface_name == mojom::ServiceManager::Name_) { |
| instance->BindServiceManager( |
| mojom::ServiceManagerRequest(std::move(interface_pipe))); |
| } |
| } |
| |
| } // namespace service_manager |