|  | // Copyright 2018 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 "base/fuchsia/service_directory.h" | 
|  |  | 
|  | #include <lib/async/default.h> | 
|  | #include <lib/svc/dir.h> | 
|  | #include <lib/zx/channel.h> | 
|  | #include <zircon/process.h> | 
|  | #include <zircon/processargs.h> | 
|  |  | 
|  | #include "base/fuchsia/fuchsia_logging.h" | 
|  | #include "base/message_loop/message_loop_current.h" | 
|  | #include "base/no_destructor.h" | 
|  | #include "starboard/types.h" | 
|  |  | 
|  | namespace base { | 
|  | namespace fuchsia { | 
|  |  | 
|  | ServiceDirectory::ServiceDirectory(zx::channel directory_request) { | 
|  | zx_status_t status = svc_dir_create(async_get_default_dispatcher(), | 
|  | directory_request.release(), &svc_dir_); | 
|  | ZX_CHECK(status == ZX_OK, status); | 
|  | } | 
|  |  | 
|  | ServiceDirectory::~ServiceDirectory() { | 
|  | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
|  | DCHECK(services_.empty()); | 
|  |  | 
|  | zx_status_t status = svc_dir_destroy(svc_dir_); | 
|  | ZX_DCHECK(status == ZX_OK, status); | 
|  | } | 
|  |  | 
|  | // static | 
|  | ServiceDirectory* ServiceDirectory::GetDefault() { | 
|  | static base::NoDestructor<ServiceDirectory> directory( | 
|  | zx::channel(zx_take_startup_handle(PA_DIRECTORY_REQUEST))); | 
|  | return directory.get(); | 
|  | } | 
|  |  | 
|  | void ServiceDirectory::AddService(StringPiece name, | 
|  | ConnectServiceCallback connect_callback) { | 
|  | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
|  | DCHECK(services_.find(name) == services_.end()); | 
|  |  | 
|  | std::string name_str = name.as_string(); | 
|  | services_[name_str] = connect_callback; | 
|  |  | 
|  | zx_status_t status = | 
|  | svc_dir_add_service(svc_dir_, "public", name_str.c_str(), this, | 
|  | &ServiceDirectory::HandleConnectRequest); | 
|  | ZX_DCHECK(status == ZX_OK, status); | 
|  |  | 
|  | // Publish to the legacy "flat" namespace, which is required by some clients. | 
|  | status = svc_dir_add_service(svc_dir_, nullptr, name_str.c_str(), this, | 
|  | &ServiceDirectory::HandleConnectRequest); | 
|  | ZX_DCHECK(status == ZX_OK, status); | 
|  | } | 
|  |  | 
|  | void ServiceDirectory::RemoveService(StringPiece name) { | 
|  | DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); | 
|  |  | 
|  | std::string name_str = name.as_string(); | 
|  |  | 
|  | auto it = services_.find(name_str); | 
|  | DCHECK(it != services_.end()); | 
|  | services_.erase(it); | 
|  |  | 
|  | zx_status_t status = | 
|  | svc_dir_remove_service(svc_dir_, "public", name_str.c_str()); | 
|  | ZX_DCHECK(status == ZX_OK, status); | 
|  |  | 
|  | // Unregister from the legacy "flat" namespace. | 
|  | status = svc_dir_remove_service(svc_dir_, nullptr, name_str.c_str()); | 
|  | ZX_DCHECK(status == ZX_OK, status); | 
|  | } | 
|  |  | 
|  | void ServiceDirectory::RemoveAllServices() { | 
|  | while (!services_.empty()) { | 
|  | RemoveService(services_.begin()->first); | 
|  | } | 
|  | } | 
|  |  | 
|  | // static | 
|  | void ServiceDirectory::HandleConnectRequest(void* context, | 
|  | const char* service_name, | 
|  | zx_handle_t service_request) { | 
|  | auto* directory = reinterpret_cast<ServiceDirectory*>(context); | 
|  | DCHECK_CALLED_ON_VALID_THREAD(directory->thread_checker_); | 
|  |  | 
|  | auto it = directory->services_.find(service_name); | 
|  |  | 
|  | // HandleConnectRequest() is expected to be called only for registered | 
|  | // services. | 
|  | DCHECK(it != directory->services_.end()); | 
|  |  | 
|  | it->second.Run(zx::channel(service_request)); | 
|  | } | 
|  |  | 
|  | }  // namespace fuchsia | 
|  | }  // namespace base |