blob: 0c68568ecf3a513f7d7601c13c8aa33670a2ec5f [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_stream.h"
#include <stddef.h>
#include <stdint.h>
#include "base/bind.h"
#include "chromeos/binder/binder_driver_api.h"
#include "chromeos/binder/buffer_reader.h"
#include "chromeos/binder/driver.h"
#include "chromeos/binder/transaction_data.h"
#include "chromeos/binder/transaction_data_from_driver.h"
#include "chromeos/binder/util.h"
namespace binder {
CommandStream::CommandStream(Driver* driver,
IncomingCommandHandler* incoming_command_handler)
: driver_(driver),
incoming_command_handler_(incoming_command_handler),
weak_ptr_factory_(this) {}
CommandStream::~CommandStream() {
DCHECK(thread_checker_.CalledOnValidThread());
}
bool CommandStream::Fetch() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!CanProcessIncomingCommand());
// Use the same value as libbinder's IPCThreadState.
const size_t kIncomingDataSize = 256;
incoming_data_.resize(kIncomingDataSize);
size_t written_bytes = 0, read_bytes = 0;
if (!driver_->WriteRead(nullptr, 0, incoming_data_.data(),
incoming_data_.size(), &written_bytes, &read_bytes)) {
LOG(ERROR) << "WriteRead() failed.";
return false;
}
incoming_data_.resize(read_bytes);
incoming_data_reader_.reset(
new BufferReader(incoming_data_.data(), incoming_data_.size()));
return true;
}
bool CommandStream::FetchBlocking() {
DCHECK(thread_checker_.CalledOnValidThread());
return driver_->Poll() && Fetch();
}
bool CommandStream::CanProcessIncomingCommand() {
DCHECK(thread_checker_.CalledOnValidThread());
return incoming_data_reader_ && incoming_data_reader_->HasMoreData();
}
bool CommandStream::ProcessIncomingCommand() {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(CanProcessIncomingCommand());
uint32_t command = 0;
if (!incoming_data_reader_->Read(&command, sizeof(command)) ||
!OnIncomingCommand(command, incoming_data_reader_.get())) {
LOG(ERROR) << "Error while handling command: " << command;
return false;
}
return true;
}
void CommandStream::AppendOutgoingCommand(uint32_t command,
const void* data,
size_t size) {
DCHECK(thread_checker_.CalledOnValidThread());
VLOG(1) << "Appending " << CommandToString(command) << ", this = " << this;
DCHECK_EQ(0u, size % 4); // Must be 4-byte aligned.
outgoing_data_.insert(
outgoing_data_.end(), reinterpret_cast<const char*>(&command),
reinterpret_cast<const char*>(&command) + sizeof(command));
outgoing_data_.insert(outgoing_data_.end(),
reinterpret_cast<const char*>(data),
reinterpret_cast<const char*>(data) + size);
}
bool CommandStream::Flush() {
DCHECK(thread_checker_.CalledOnValidThread());
for (size_t pos = 0; pos < outgoing_data_.size();) {
size_t written_bytes = 0, read_bytes = 0;
if (!driver_->WriteRead(outgoing_data_.data() + pos,
outgoing_data_.size() - pos, nullptr, 0,
&written_bytes, &read_bytes)) {
LOG(ERROR) << "WriteRead() failed: pos = " << pos
<< ", size = " << outgoing_data_.size();
return false;
}
pos += written_bytes;
}
outgoing_data_.clear();
return true;
}
bool CommandStream::OnIncomingCommand(uint32_t command, BufferReader* reader) {
DCHECK(thread_checker_.CalledOnValidThread());
// TODO(hashimoto): Replace all NOTIMPLEMENTED with logic to handle incoming
// commands.
VLOG(1) << "Processing " << CommandToString(command) << ", this = " << this;
switch (command) {
case BR_ERROR: {
int32_t error = 0;
if (!reader->Read(&error, sizeof(error))) {
LOG(ERROR) << "Failed to read error code.";
return false;
}
break;
}
case BR_OK:
break;
case BR_TRANSACTION: {
TransactionDataFromDriver data(
base::Bind(&CommandStream::FreeTransactionBuffer,
weak_ptr_factory_.GetWeakPtr()));
if (!reader->Read(data.mutable_data(), sizeof(*data.mutable_data()))) {
LOG(ERROR) << "Failed to read transaction data.";
return false;
}
if (!incoming_command_handler_->OnTransaction(data)) {
LOG(ERROR) << "Failed to handle transaction.";
return false;
}
break;
}
case BR_REPLY: {
std::unique_ptr<TransactionDataFromDriver> data(
new TransactionDataFromDriver(
base::Bind(&CommandStream::FreeTransactionBuffer,
weak_ptr_factory_.GetWeakPtr())));
binder_transaction_data* data_struct = data->mutable_data();
if (!reader->Read(data_struct, sizeof(*data_struct))) {
LOG(ERROR) << "Failed to read transaction data.";
return false;
}
incoming_command_handler_->OnReply(std::move(data));
break;
}
case BR_ACQUIRE_RESULT:
// Kernel's binder.h says this is not currently supported.
NOTREACHED();
break;
case BR_DEAD_REPLY:
incoming_command_handler_->OnDeadReply();
break;
case BR_TRANSACTION_COMPLETE:
incoming_command_handler_->OnTransactionComplete();
break;
case BR_INCREFS:
case BR_ACQUIRE:
case BR_RELEASE:
case BR_DECREFS:
case BR_ATTEMPT_ACQUIRE: {
binder_ptr_cookie data = {};
if (!reader->Read(&data, sizeof(data))) {
LOG(ERROR) << "Failed to read arguments.";
return false;
}
void* ptr = reinterpret_cast<void*>(data.ptr);
void* cookie = reinterpret_cast<void*>(data.cookie);
switch (command) {
case BR_INCREFS:
incoming_command_handler_->OnIncrementWeakReference(ptr, cookie);
AppendOutgoingCommand(BC_INCREFS_DONE, &data, sizeof(data));
break;
case BR_ACQUIRE:
incoming_command_handler_->OnIncrementStrongReference(ptr, cookie);
AppendOutgoingCommand(BC_ACQUIRE_DONE, &data, sizeof(data));
break;
case BR_RELEASE:
incoming_command_handler_->OnDecrementStrongReference(ptr, cookie);
break;
case BR_DECREFS:
incoming_command_handler_->OnDecrementWeakReference(ptr, cookie);
break;
case BR_ATTEMPT_ACQUIRE:
// Kernel's binder.h says this is not currently supported.
NOTREACHED();
break;
}
break;
}
case BR_NOOP:
break;
case BR_SPAWN_LOOPER:
NOTIMPLEMENTED();
break;
case BR_FINISHED:
// Kernel's binder.h says this is not currently supported.
NOTREACHED();
break;
case BR_DEAD_BINDER:
NOTIMPLEMENTED();
break;
case BR_CLEAR_DEATH_NOTIFICATION_DONE:
NOTIMPLEMENTED();
break;
case BR_FAILED_REPLY:
incoming_command_handler_->OnFailedReply();
break;
default:
LOG(ERROR) << "Unexpected command: " << command;
return false;
}
return true;
}
void CommandStream::FreeTransactionBuffer(const void* ptr) {
DCHECK(thread_checker_.CalledOnValidThread());
binder_uintptr_t p = reinterpret_cast<binder_uintptr_t>(ptr);
AppendOutgoingCommand(BC_FREE_BUFFER, &p, sizeof(p));
}
} // namespace binder