// 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 #include #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(data.GetData()); result.offsets_size = data.GetNumObjectOffsets() * sizeof(binder_size_t); result.data.ptr.offsets = reinterpret_cast(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, scoped_ptr* 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. scoped_ptr 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. scoped_ptr 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(data.GetCookie()); scoped_ptr 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; } scoped_ptr 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(scoped_ptr 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(cookie)->AddRef(); } void CommandBroker::OnDecrementStrongReference(void* ptr, void* cookie) { reinterpret_cast(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( scoped_ptr* 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