blob: 7dbbfd81dd41cfb42bdb4e41258cf3056fb15d1c [file] [log] [blame]
// Copyright 2015 The Chromium OS 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 "psyche/lib/psyche/psyche_connection.h"
#include <map>
#include <utility>
#include <base/logging.h>
#include <chromeos/make_unique_ptr.h>
#include <protobinder/binder_proxy.h>
#include <protobinder/ibinder.h>
#include <protobinder/iservice_manager.h>
#include <protobinder/status.h>
#include "psyche/common/constants.h"
#include "psyche/proto_bindings/psyche.pb.h"
#include "psyche/proto_bindings/psyche.pb.rpc.h"
using chromeos::make_unique_ptr;
using protobinder::BinderHost;
using protobinder::BinderProxy;
namespace psyche {
// This class's methods mirror PsycheConnection's.
class PsycheConnection::Impl : public IPsycheClientHostInterface {
public:
Impl() = default;
~Impl() override = default;
void SetProxyForTesting(std::unique_ptr<BinderProxy> psyched_proxy) {
CHECK(!psyched_proxy_);
psyched_proxy_ = std::move(psyched_proxy);
}
bool Init() {
if (!psyched_proxy_) {
psyched_proxy_.reset(
static_cast<BinderProxy*>(
protobinder::GetServiceManager()->GetService(
kPsychedServiceManagerName)));
if (!psyched_proxy_) {
LOG(ERROR) << "Failed to connect to psyched";
return false;
}
}
psyched_interface_ =
protobinder::CreateInterface<IPsyched>(psyched_proxy_.get());
return true;
}
bool RegisterService(const std::string& service_name,
const BinderHost* service) {
DCHECK(psyched_interface_);
RegisterServiceRequest request;
request.set_name(service_name);
service->CopyToProtocolBuffer(request.mutable_binder());
RegisterServiceResponse response;
Status status = psyched_interface_->RegisterService(&request, &response);
if (!status) {
LOG(ERROR) << "RegisterService binder call failed with " << status;
return false;
}
return true;
}
bool GetService(const std::string& service_name,
const GetServiceCallback& callback) {
DCHECK(psyched_interface_);
RequestServiceRequest request;
request.set_name(service_name);
CopyToProtocolBuffer(request.mutable_client_binder());
Status status = psyched_interface_->RequestService(&request);
if (!status) {
LOG(ERROR) << "RequestService binder call failed with " << status;
return false;
}
// Register the callback last so it doesn't stick around if the RPC failed.
// This is safe to do since we're making a one-way call, i.e. psyched's
// asynchronous ReceiveService call won't be handled until control returns
// to the message loop.
get_service_callbacks_.insert(std::make_pair(service_name, callback));
return true;
}
// IPsycheClientHostInterface:
Status ReceiveService(ReceiveServiceRequest* in) override {
const std::string service_name = in->name();
std::unique_ptr<BinderProxy> proxy;
if (in->has_binder())
proxy.reset(new BinderProxy(in->binder().proxy_handle()));
auto range = get_service_callbacks_.equal_range(service_name);
if (range.first == get_service_callbacks_.end()) {
LOG(WARNING) << "Received unknown service \"" << service_name << "\"";
return STATUS_OK();
}
for (auto it = range.first; it != range.second; ++it) {
// Create a new BinderProxy for each callback based on the original
// proxy's handle. The handle's references are incremented and decremented
// in BinderProxy's c'tor and d'tor, so this is safe to do.
it->second.Run(
make_unique_ptr(proxy ? new BinderProxy(proxy->handle()) : nullptr));
}
return STATUS_OK();
}
private:
std::unique_ptr<BinderProxy> psyched_proxy_;
std::unique_ptr<IPsyched> psyched_interface_;
// Keyed by service name.
std::multimap<std::string, GetServiceCallback> get_service_callbacks_;
DISALLOW_COPY_AND_ASSIGN(Impl);
};
PsycheConnection::PsycheConnection() : impl_(new Impl) {}
PsycheConnection::~PsycheConnection() = default;
void PsycheConnection::SetProxyForTesting(
std::unique_ptr<BinderProxy> psyched_proxy) {
impl_->SetProxyForTesting(std::move(psyched_proxy));
}
bool PsycheConnection::Init() {
return impl_->Init();
}
bool PsycheConnection::RegisterService(const std::string& service_name,
const BinderHost* service) {
return impl_->RegisterService(service_name, service);
}
bool PsycheConnection::GetService(const std::string& service_name,
const GetServiceCallback& callback) {
return impl_->GetService(service_name, callback);
}
} // namespace psyche