blob: ad87e65d73b1844f8932e759942233a3cbf70480 [file] [log] [blame]
// 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 "chromeos/binder/command_broker.h"
#include <stddef.h>
#include <stdint.h>
#include "base/bind.h"
#include "base/logging.h"
#include "chromeos/binder/binder_driver_api.h"
#include "chromeos/binder/driver.h"
#include "chromeos/binder/local_object.h"
#include "chromeos/binder/transaction_data.h"
#include "chromeos/binder/transaction_status.h"
namespace binder {
namespace {
// Converts TransactionData to binder_transaction_data struct.
binder_transaction_data ConvertTransactionDataToStruct(
const TransactionData& data) {
binder_transaction_data result = {};
result.code = data.GetCode();
result.flags = TF_ACCEPT_FDS;
if (data.IsOneWay()) {
result.flags |= TF_ONE_WAY;
}
if (data.HasStatus()) {
result.flags |= TF_STATUS_CODE;
}
result.data_size = data.GetDataSize();
result.data.ptr.buffer = reinterpret_cast<binder_uintptr_t>(data.GetData());
result.offsets_size = data.GetNumObjectOffsets() * sizeof(binder_size_t);
result.data.ptr.offsets =
reinterpret_cast<binder_uintptr_t>(data.GetObjectOffsets());
return result;
}
} // namespace
CommandBroker::CommandBroker(Driver* driver)
: command_stream_(driver, this), weak_ptr_factory_(this) {}
CommandBroker::~CommandBroker() {
DCHECK(thread_checker_.CalledOnValidThread());
}
bool CommandBroker::EnterLooper() {
command_stream_.AppendOutgoingCommand(BC_ENTER_LOOPER, nullptr, 0);
return command_stream_.Flush();
}
bool CommandBroker::RegisterLooper() {
command_stream_.AppendOutgoingCommand(BC_REGISTER_LOOPER, nullptr, 0);
return command_stream_.Flush();
}
bool CommandBroker::ExitLooper() {
command_stream_.AppendOutgoingCommand(BC_EXIT_LOOPER, nullptr, 0);
return command_stream_.Flush();
}
bool CommandBroker::PollCommands() {
// Fetch and process commands.
if (!command_stream_.Fetch()) {
LOG(ERROR) << "Failed to fetch commands.";
return false;
}
while (command_stream_.CanProcessIncomingCommand()) {
if (!command_stream_.ProcessIncomingCommand()) {
LOG(ERROR) << "Failed to process command.";
return false;
}
}
// Flush outgoing commands.
if (!command_stream_.Flush()) {
LOG(ERROR) << "Failed to flush commands.";
return false;
}
return true;
}
bool CommandBroker::Transact(int32_t handle,
const TransactionData& request,
std::unique_ptr<TransactionData>* reply) {
DCHECK(thread_checker_.CalledOnValidThread());
// Send transaction.
binder_transaction_data tr = ConvertTransactionDataToStruct(request);
tr.target.handle = handle;
command_stream_.AppendOutgoingCommand(BC_TRANSACTION, &tr, sizeof(tr));
if (!command_stream_.Flush()) {
LOG(ERROR) << "Failed to write";
return false;
}
// Wait for response.
std::unique_ptr<TransactionData> response_data;
ResponseType response_type = WaitForResponse(&response_data);
if (response_type != RESPONSE_TYPE_TRANSACTION_COMPLETE) {
LOG(ERROR) << "Failed to wait for response: response_type = "
<< response_type;
return false;
}
if (!request.IsOneWay()) {
// Wait for reply.
std::unique_ptr<TransactionData> response_data;
ResponseType response_type = WaitForResponse(&response_data);
if (response_type != RESPONSE_TYPE_TRANSACTION_REPLY) {
LOG(ERROR) << "Failed to wait for response: response_type = "
<< response_type;
return false;
}
*reply = std::move(response_data);
}
return true;
}
void CommandBroker::AddReference(int32_t handle) {
// Increment weak reference count.
command_stream_.AppendOutgoingCommand(BC_INCREFS, &handle, sizeof(handle));
// Increment strong reference count.
command_stream_.AppendOutgoingCommand(BC_ACQUIRE, &handle, sizeof(handle));
}
void CommandBroker::ReleaseReference(int32_t handle) {
// Decrement strong reference count.
command_stream_.AppendOutgoingCommand(BC_RELEASE, &handle, sizeof(handle));
// Decrement weak reference count.
command_stream_.AppendOutgoingCommand(BC_DECREFS, &handle, sizeof(handle));
}
base::Closure CommandBroker::GetReleaseReferenceClosure(int32_t handle) {
return base::Bind(&CommandBroker::ReleaseReference,
weak_ptr_factory_.GetWeakPtr(), handle);
}
bool CommandBroker::OnTransaction(const TransactionData& data) {
LocalObject* object = reinterpret_cast<LocalObject*>(data.GetCookie());
std::unique_ptr<TransactionData> reply;
if (!object->Transact(this, data, &reply)) {
LOG(ERROR) << "Failed to transact.";
return false;
}
if (!data.IsOneWay()) {
// Send reply.
if (!reply) {
reply.reset(new TransactionStatus(Status::FAILED_TRANSACTION));
}
binder_transaction_data tr = ConvertTransactionDataToStruct(*reply);
tr.target.handle = -1; // This value will be ignored. Set invalid handle.
command_stream_.AppendOutgoingCommand(BC_REPLY, &tr, sizeof(tr));
if (!command_stream_.Flush()) {
LOG(ERROR) << "Failed to write";
return false;
}
std::unique_ptr<TransactionData> response_data;
ResponseType response_type = WaitForResponse(&response_data);
// Not returning false for errors here, as doing it can result in letting
// another process abort the loop in PollCommands() (e.g. any process can
// cause a "dead binder" error with crash). We should return false only for
// fundamental errors like binder protocol errors.
LOG_IF(ERROR, response_type != RESPONSE_TYPE_TRANSACTION_COMPLETE)
<< "Error on the other end when sending reply: " << response_type;
}
return true;
}
void CommandBroker::OnReply(std::unique_ptr<TransactionData> data) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(response_type_, RESPONSE_TYPE_NONE);
DCHECK(!response_data_);
response_type_ = RESPONSE_TYPE_TRANSACTION_REPLY;
response_data_ = std::move(data);
}
void CommandBroker::OnDeadReply() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(response_type_, RESPONSE_TYPE_NONE);
response_type_ = RESPONSE_TYPE_DEAD;
}
void CommandBroker::OnTransactionComplete() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(response_type_, RESPONSE_TYPE_NONE);
response_type_ = RESPONSE_TYPE_TRANSACTION_COMPLETE;
}
void CommandBroker::OnIncrementWeakReference(void* ptr, void* cookie) {
// Do nothing.
}
void CommandBroker::OnIncrementStrongReference(void* ptr, void* cookie) {
reinterpret_cast<LocalObject*>(cookie)->AddRef();
}
void CommandBroker::OnDecrementStrongReference(void* ptr, void* cookie) {
reinterpret_cast<LocalObject*>(cookie)->Release();
}
void CommandBroker::OnDecrementWeakReference(void* ptr, void* cookie) {
// Do nothing.
}
void CommandBroker::OnFailedReply() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(response_type_, RESPONSE_TYPE_NONE);
response_type_ = RESPONSE_TYPE_FAILED;
}
CommandBroker::ResponseType CommandBroker::WaitForResponse(
std::unique_ptr<TransactionData>* data) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK_EQ(response_type_, RESPONSE_TYPE_NONE);
DCHECK(!response_data_);
while (response_type_ == RESPONSE_TYPE_NONE) {
if (command_stream_.CanProcessIncomingCommand()) {
if (!command_stream_.ProcessIncomingCommand()) {
LOG(ERROR) << "Failed to process command.";
return RESPONSE_TYPE_NONE;
}
} else {
// Block until response is received.
if (!command_stream_.FetchBlocking()) {
LOG(ERROR) << "Failed to fetch.";
return RESPONSE_TYPE_NONE;
}
}
}
ResponseType response_type = response_type_;
response_type_ = RESPONSE_TYPE_NONE;
*data = std::move(response_data_);
return response_type;
}
} // namespace binder