// 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 #include #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(&command), reinterpret_cast(&command) + sizeof(command)); outgoing_data_.insert(outgoing_data_.end(), reinterpret_cast(data), reinterpret_cast(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: { scoped_ptr 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(data.ptr); void* cookie = reinterpret_cast(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(ptr); AppendOutgoingCommand(BC_FREE_BUFFER, &p, sizeof(p)); } } // namespace binder