| // Copyright 2015 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/reaper/reaper_impl.h" |
| |
| #include "base/logging.h" |
| #include "crypto/random.h" |
| #include "mojo/public/cpp/application/application_connection.h" |
| #include "services/reaper/reaper_binding.h" |
| #include "services/reaper/transfer_binding.h" |
| |
| namespace reaper { |
| |
| struct ReaperImpl::NodeLocator { |
| NodeLocator() : node_id(0) {} |
| NodeLocator(const NodeLocator& other) = default; |
| NodeLocator(AppId app_id, uint32 node_id) |
| : app_id(app_id), node_id(node_id) {} |
| bool operator<(const NodeLocator& rhs) const { |
| if (app_id < rhs.app_id) |
| return true; |
| else if (app_id == rhs.app_id && node_id < rhs.node_id) |
| return true; |
| else |
| return false; |
| } |
| AppId app_id; |
| uint32 node_id; |
| }; |
| |
| struct ReaperImpl::NodeInfo { |
| NodeInfo() : is_source(false) {} |
| NodeInfo(const NodeInfo& other) = default; |
| NodeInfo(const NodeLocator& other_node) |
| : other_node(other_node), is_source(false) {} |
| NodeLocator other_node; |
| bool is_source; |
| }; |
| |
| ReaperImpl::ReaperImpl() |
| : reaper_url_("mojo:reaper"), next_app_id_(1), next_transfer_id_(1) { |
| } |
| |
| ReaperImpl::~ReaperImpl() { |
| } |
| |
| ReaperImpl::AppId ReaperImpl::GetAppId(const GURL& app_url) { |
| auto result = app_ids_[app_url]; |
| if (result == 0) { |
| result = app_ids_[app_url] = next_app_id_++; |
| } |
| return result; |
| } |
| |
| const GURL& ReaperImpl::GetAppURLSlowly(AppId app_id) const { |
| for (const auto& it : app_ids_) { |
| if (it.second == app_id) |
| return it.first; |
| } |
| return GURL::EmptyGURL(); |
| } |
| |
| bool ReaperImpl::MoveNode(const NodeLocator& source, const NodeLocator& dest) { |
| const auto& source_it = nodes_.find(source); |
| if (source_it == nodes_.end()) |
| return false; |
| |
| const auto& dest_it = nodes_.find(dest); |
| if (dest_it != nodes_.end()) |
| return false; |
| |
| nodes_[dest] = source_it->second; |
| nodes_.erase(source_it); |
| |
| nodes_[nodes_[dest].other_node].other_node = dest; |
| return true; |
| } |
| |
| void ReaperImpl::GetApplicationSecret( |
| const GURL& caller_app, |
| const mojo::Callback<void(AppSecret)>& callback) { |
| AppId app_id = GetAppId(caller_app); |
| AppSecret secret = app_id_to_secret_[app_id]; |
| if (secret == 0u) { |
| crypto::RandBytes(&secret, sizeof(AppSecret)); |
| CHECK_NE(secret, 0u); |
| app_id_to_secret_[app_id] = secret; |
| app_secret_to_id_[secret] = app_id; |
| } |
| callback.Run(secret); |
| } |
| |
| void ReaperImpl::CreateReference(const GURL& caller_app, |
| uint32 source_node_id, |
| uint32 target_node_id) { |
| NodeLocator source_locator(GetAppId(caller_app), source_node_id); |
| NodeLocator target_locator(GetAppId(caller_app), target_node_id); |
| |
| if (nodes_.find(source_locator) != nodes_.end()) { |
| LOG(ERROR) << "Duplicate source node: " << source_node_id; |
| return; |
| } |
| |
| if (nodes_.find(target_locator) != nodes_.end()) { |
| LOG(ERROR) << "Duplicate target node: " << target_node_id; |
| return; |
| } |
| |
| NodeInfo source_node(target_locator); |
| source_node.is_source = true; |
| nodes_[source_locator] = source_node; |
| |
| NodeInfo target_node(source_locator); |
| nodes_[target_locator] = target_node; |
| } |
| |
| void ReaperImpl::DropNode(const GURL& caller_app, uint32 node_id) { |
| const auto& it = nodes_.find(NodeLocator(GetAppId(caller_app), node_id)); |
| if (it == nodes_.end()) { |
| LOG(ERROR) << "Specified node does not exist: " << node_id; |
| return; |
| } |
| |
| const auto& other_it = nodes_.find( |
| NodeLocator(it->second.other_node.app_id, it->second.other_node.node_id)); |
| DCHECK(other_it != nodes_.end()); |
| nodes_.erase(other_it->first); |
| |
| nodes_.erase(it); |
| } |
| |
| void ReaperImpl::StartTransfer(const GURL& caller_app, |
| uint32 node_id, |
| mojo::InterfaceRequest<Transfer> request) { |
| AppId transfer_id = next_transfer_id_++; |
| NodeLocator source(GetAppId(caller_app), node_id); |
| if (!MoveNode(source, NodeLocator(GetAppId(reaper_url_), transfer_id))) { |
| LOG(ERROR) << "Could not start node transfer because move failed from: (" |
| << caller_app.spec() << "," << node_id << ") to: (" |
| << reaper_url_.spec() << "," << transfer_id << ")"; |
| return; |
| } |
| new TransferBinding(transfer_id, this, request.Pass()); |
| } |
| |
| void ReaperImpl::CompleteTransfer(uint32 source_node_id, |
| uint64 dest_app_secret, |
| uint32 dest_node_id) { |
| AppId dest_app_id = app_secret_to_id_[dest_app_secret]; |
| if (dest_app_id == 0u) { |
| LOG(ERROR) << "Specified destination app secret does not exist: " |
| << dest_app_secret; |
| return; |
| } |
| |
| NodeLocator source(GetAppId(reaper_url_), source_node_id); |
| NodeLocator dest(dest_app_id, dest_node_id); |
| if (!MoveNode(source, dest)) { |
| LOG(ERROR) << "Could not complete transfer because move failed from: (" |
| << reaper_url_.spec() << "," << source_node_id << ") to: (" |
| << GetAppURLSlowly(dest_app_id).spec() << "," << dest_node_id |
| << ")"; |
| } |
| } |
| |
| bool ReaperImpl::ConfigureIncomingConnection( |
| mojo::ApplicationConnection* connection) { |
| connection->AddService<Reaper>(this); |
| connection->AddService<Diagnostics>(this); |
| return true; |
| } |
| |
| void ReaperImpl::Create(mojo::ApplicationConnection* connection, |
| mojo::InterfaceRequest<Reaper> request) { |
| GetReaperForApp(connection->GetRemoteApplicationURL(), request.Pass()); |
| } |
| |
| void ReaperImpl::Create(mojo::ApplicationConnection* connection, |
| mojo::InterfaceRequest<Diagnostics> request) { |
| // TODO(aa): Enforce that only testing code can connect to this interface. |
| diagnostics_bindings_.AddBinding(this, request.Pass()); |
| } |
| |
| void ReaperImpl::DumpNodes( |
| const mojo::Callback<void(mojo::Array<NodePtr>)>& callback) { |
| std::map<AppId, GURL> app_urls; |
| for (const auto& it : app_ids_) |
| app_urls[it.second] = it.first; |
| mojo::Array<NodePtr> result(0u); |
| for (const auto& entry : nodes_) { |
| NodePtr node(Node::New()); |
| node->app_url = app_urls[entry.first.app_id].spec(); |
| node->node_id = entry.first.node_id; |
| node->other_app_url = app_urls[entry.second.other_node.app_id].spec(); |
| node->other_id = entry.second.other_node.node_id; |
| node->is_source = entry.second.is_source; |
| result.push_back(node.Pass()); |
| } |
| callback.Run(result.Pass()); |
| } |
| |
| void ReaperImpl::Reset() { |
| nodes_.clear(); |
| app_id_to_secret_.clear(); |
| app_secret_to_id_.clear(); |
| } |
| |
| void ReaperImpl::GetReaperForApp(const mojo::String& app_url, |
| mojo::InterfaceRequest<Reaper> request) { |
| new ReaperBinding(GURL(app_url), this, request.Pass()); |
| } |
| |
| void ReaperImpl::Ping(const mojo::Closure& closure) { |
| closure.Run(); |
| } |
| |
| } // namespace reaper |