From be6a0e8b264918239e84da3b9204dae348bf6eb5 Mon Sep 17 00:00:00 2001 From: Robert Sesek Date: Wed, 21 Jan 2015 20:15:01 -0500 Subject: [PATCH] First cut at minibind classes. This does not yet receive transactions correctly. --- .gitignore | 1 + jni/Android.mk | 3 + jni/Application.mk | 2 + jni/minibind/Android.mk | 9 ++ jni/minibind/channel.cc | 96 +++++++++++++++++++++ jni/minibind/channel.h | 44 ++++++++++ jni/minibind/command.cc | 39 +++++++++ jni/minibind/command.h | 55 ++++++++++++ jni/minibind/common.h | 20 +++++ jni/minibind/minibind.cc | 181 +++++++++++++++++++++++++++++++++++++++ jni/minibind/minibind.h | 18 ++++ jni/minibind/parcel.cc | 50 +++++++++++ jni/minibind/parcel.h | 40 +++++++++ 13 files changed, 558 insertions(+) create mode 100644 jni/Android.mk create mode 100644 jni/Application.mk create mode 100644 jni/minibind/Android.mk create mode 100644 jni/minibind/channel.cc create mode 100644 jni/minibind/channel.h create mode 100644 jni/minibind/command.cc create mode 100644 jni/minibind/command.h create mode 100644 jni/minibind/common.h create mode 100644 jni/minibind/minibind.cc create mode 100644 jni/minibind/minibind.h create mode 100644 jni/minibind/parcel.cc create mode 100644 jni/minibind/parcel.h diff --git a/.gitignore b/.gitignore index 639cdd5..15cd4ea 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ local.properties +obj/ diff --git a/jni/Android.mk b/jni/Android.mk new file mode 100644 index 0000000..9494502 --- /dev/null +++ b/jni/Android.mk @@ -0,0 +1,3 @@ +TOP_DIR := $(call my-dir) + +include $(TOP_DIR)/minibind/Android.mk diff --git a/jni/Application.mk b/jni/Application.mk new file mode 100644 index 0000000..637619b --- /dev/null +++ b/jni/Application.mk @@ -0,0 +1,2 @@ +APP_STL := stlport_static +APP_CPPFLAGS := -std=gnu++11 diff --git a/jni/minibind/Android.mk b/jni/minibind/Android.mk new file mode 100644 index 0000000..83ab355 --- /dev/null +++ b/jni/minibind/Android.mk @@ -0,0 +1,9 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := minibind +LOCAL_SRC_FILES := minibind.cc parcel.cc channel.cc command.cc +LOCAL_CFLAGS := -DBINDER_IPC_32BIT=1 + +include $(BUILD_STATIC_LIBRARY) diff --git a/jni/minibind/channel.cc b/jni/minibind/channel.cc new file mode 100644 index 0000000..97f7b67 --- /dev/null +++ b/jni/minibind/channel.cc @@ -0,0 +1,96 @@ +// 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 "channel.h" + +#include +#include +#include + +#include + +#include "command.h" +#include "common.h" + +namespace minibind { + +namespace { + +int ConnectDriver() { + int fd = open("/dev/binder", O_RDONLY); + if (fd < 0) { + AERR("open driver"); + abort(); + } + + if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { + AERR("fcntl FD_CLOEXEC"); + } + + struct binder_version version; + int rv = ioctl(fd, BINDER_VERSION, &version); + if (rv) { + AERR("ioctl BINDER_VERSION"); + abort(); + } + ALOG("Binder version: %d", version.protocol_version); + if (version.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION) { + ALOG("Version is not compatible with current protocol: %d", BINDER_CURRENT_PROTOCOL_VERSION); + abort(); + } + + void* vm = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0); + if (vm == MAP_FAILED) { + AERR("mmap BINDER_VM_SIZE"); + abort(); + } + + return fd; +} + +void CopyCommandsToBuffer(const std::vector& commands, + std::vector* buffer) { + size_t last_command_size = 0; + for (const Command* command : commands) { + memcpy(&(*buffer)[last_command_size], reinterpret_cast(command->GetData()), command->GetSize()); + last_command_size += command->GetSize(); + } +} + +} // namespace + +Channel::Channel(uint32_t handle) + : handle_(handle), + driver_(ConnectDriver()), + write_commands_size_(0), + write_commands_(), + reader_() { + reader_.SetDataSize(256); +} + +Channel::~Channel() { +} + +void Channel::QueueCommand(Command* cmd) { + write_commands_size_ += cmd->GetSize(); + write_commands_.push_back(cmd); +} + +int Channel::TransactCommands() { + struct binder_write_read bwr = {}; + + std::vector write_data(write_commands_size_, 0); + CopyCommandsToBuffer(write_commands_, &write_data); + + bwr.write_size = write_commands_size_; + bwr.write_buffer = reinterpret_cast(&write_data[0]); + bwr.read_size = reader_.DataSize(); + bwr.read_buffer = reader_.DataPointer(); + + int rv = ioctl(driver_, BINDER_WRITE_READ, &bwr); + ALOG("BINDER_WRITE_READ %d", rv); + return rv; +} + +} // namespace minibind diff --git a/jni/minibind/channel.h b/jni/minibind/channel.h new file mode 100644 index 0000000..bbd8004 --- /dev/null +++ b/jni/minibind/channel.h @@ -0,0 +1,44 @@ +// 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. + +#ifndef MINIBIND_CHANNEL_H_ +#define MINIBIND_CHANNEL_H_ + +#include + +#include + +#include "parcel.h" + +namespace minibind { + +class Command; + +class Channel { + public: + explicit Channel(uint32_t handle); + ~Channel(); + + void QueueCommand(Command* cmd); + + int TransactCommands(); + + Parcel* reader() { return &reader_; } + + uint32_t handle() const { return handle_; } + const std::vector& write_commands() const { return write_commands_; } + + private: + uint32_t handle_; + int driver_; + + Parcel reader_; + + size_t write_commands_size_; + std::vector write_commands_; +}; + +} // namespace minibind + +#endif // MINIBIND_CHANNEL_H_ diff --git a/jni/minibind/command.cc b/jni/minibind/command.cc new file mode 100644 index 0000000..d6f060d --- /dev/null +++ b/jni/minibind/command.cc @@ -0,0 +1,39 @@ +// 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 "command.h" + +#include "common.h" +#include "parcel.h" + +namespace minibind { + +TransactionCommand::TransactionCommand(TransactionCommand::Type type) + : command_(type == TWO_WAY ? BC_TRANSACTION : BC_REPLY) { +} + +TransactionCommand::~TransactionCommand() {} + +void TransactionCommand::SetCode(uint32_t code) { + transaction_.code = code; +} + +void TransactionCommand::SetHandle(uint32_t handle) { + transaction_.target.handle = handle; +} + +void TransactionCommand::SetParcel(const Parcel& parcel) { + transaction_.data_size = parcel.DataSize(); + transaction_.data.ptr.buffer = parcel.DataPointer(); +} + +size_t TransactionCommand::GetSize() const { + return sizeof(command_) + sizeof(transaction_); +} + +binder_uintptr_t TransactionCommand::GetData() const { + return reinterpret_cast(&command_); +} + +} // namespace minibind diff --git a/jni/minibind/command.h b/jni/minibind/command.h new file mode 100644 index 0000000..1f2019f --- /dev/null +++ b/jni/minibind/command.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef MINIBIND_COMMAND_H_ +#define MINIBIND_COMAMND_H_ + +#include +#include + +#include + +#include "common.h" + +namespace minibind { + +class Parcel; + +class Command { + public: + virtual size_t GetSize() const = 0; + virtual binder_uintptr_t GetData() const = 0; + + protected: + ~Command() {} +}; + +class TransactionCommand : public Command { + public: + enum Type { + TWO_WAY, + REPLY_ASYNC, + }; + + TransactionCommand(Type type); + ~TransactionCommand(); + + void SetHandle(uint32_t handle); + void SetCode(uint32_t code); + void SetParcel(const Parcel& parcel); + + // Command: + size_t GetSize() const override; + binder_uintptr_t GetData() const override; + + private: + const binder_driver_command_protocol command_; + struct binder_transaction_data transaction_; + + DISALLOW_COPY_AND_ASSIGN(TransactionCommand); +}; + +} // namespace minibind + +#endif // MINIBIND_COMAMND_H_ diff --git a/jni/minibind/common.h b/jni/minibind/common.h new file mode 100644 index 0000000..2384af9 --- /dev/null +++ b/jni/minibind/common.h @@ -0,0 +1,20 @@ +// 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. + +#ifndef MINIBIND_COMMON_H_ + +#include +#include + +#define ALOG(...) __android_log_print(ANDROID_LOG_WARN, "minibind", __VA_ARGS__) + +#define AERR(msg) ALOG(msg ": %s", strerror(errno)) + +#define CHECK(cond) if (!(cond)) { ALOG("ASSERTION FAILURE: %s @ %s:%d", #cond, __FILE__, __LINE__ ); abort(); } + +#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) + +#define DISALLOW_COPY_AND_ASSIGN(cls) cls(const cls&) = delete + +#endif // MINIBIND_COMMON_H_ diff --git a/jni/minibind/minibind.cc b/jni/minibind/minibind.cc new file mode 100644 index 0000000..0feb8e8 --- /dev/null +++ b/jni/minibind/minibind.cc @@ -0,0 +1,181 @@ +// 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 "minibind.h" + +#include +#include +#include +#include +#include + +#include + +#include "channel.h" +#include "command.h" +#include "common.h" +#include "parcel.h" + +namespace minibind { + +int ConnectDriver() { + int fd = open("/dev/binder", O_RDONLY); + if (fd < 0) { + AERR("open driver"); + abort(); + } + + if (fcntl(fd, F_SETFD, FD_CLOEXEC)) { + AERR("fcntl FD_CLOEXEC"); + } + + struct binder_version version; + int rv = ioctl(fd, BINDER_VERSION, &version); + if (rv) { + AERR("ioctl BINDER_VERSION"); + abort(); + } + ALOG("Binder version: %d", version.protocol_version); + if (version.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION) { + ALOG("Version is not compatible with current protocol: %d", BINDER_CURRENT_PROTOCOL_VERSION); + abort(); + } + + void* vm = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, fd, 0); + if (vm == MAP_FAILED) { + AERR("mmap BINDER_VM_SIZE"); + abort(); + } + + return fd; +} + +class Transaction { + public: + Transaction() : sealed_(false), + read_buffer_(), + write_buffer_(), + command_(), + write_tx_(), + read_tx_() { + read_buffer_.resize(64); + } + ~Transaction() {} + + void WriteString8(const std::string& str) { + CHECK(!sealed_); + for (size_t i = 0; i < str.length(); ++i) { + write_buffer_.push_back(str[i]); + } + write_buffer_.push_back('\0'); + } + + void WriteInt32(uint32_t i) { + CHECK(!sealed_); + write_buffer_.push_back((i >> 24) & 0xFF); + write_buffer_.push_back((i >> 16) & 0xFF); + write_buffer_.push_back((i >> 8) & 0xFF); + write_buffer_.push_back((i >> 0) & 0xFF); + } + + const struct binder_transaction_data* read_data() const { return &read_tx_.data; } + + private: + friend class Channel2; + + struct TransactionBuffer { + TransactionBuffer() : command(BC_TRANSACTION), data() {} + + uint32_t command; + struct binder_transaction_data data; + }; + + void Seal() { + CHECK(!sealed_); + + write_tx_.data.data_size = write_buffer_.size(); + write_tx_.data.data.ptr.buffer = reinterpret_cast(&write_buffer_[0]); + + read_tx_.command = BC_REPLY; + read_tx_.data.data_size = read_buffer_.size(); + read_tx_.data.data.ptr.buffer = reinterpret_cast(&read_buffer_[0]); + + command_.write_size = sizeof(write_tx_); + command_.write_buffer = reinterpret_cast(&write_tx_); + command_.read_size = sizeof(read_tx_); + command_.read_buffer = reinterpret_cast(&read_tx_); + + sealed_ = true; + } + + bool sealed_; + + std::vector write_buffer_; + std::vector read_buffer_; + + struct binder_write_read command_; + TransactionBuffer write_tx_; + TransactionBuffer read_tx_; +}; + +class Channel2 { + public: + Channel2(const std::string& interface) + : interface_(interface), + fd_(ConnectDriver()) { + } + ~Channel2() {} + + Transaction* NewTransaction(uint32_t code) const { + Transaction* t = new Transaction(); + t->write_tx_.data.code = code; + t->WriteString8(interface()); + return t; + } + + int ExecuteTransaction(Transaction* tx) { + tx->Seal(); + int rv = ioctl(fd_, BINDER_WRITE_READ, &tx->command_); + ALOG("BINDER_WRITE_READ: %d", rv); + if (rv) { + AERR("BINDER_WRITE_READ"); + } + + for (size_t i = 0; i < tx->read_buffer_.size(); i += 4) { + ALOG("%0x %0x %0x %0x", tx->read_buffer_[i], tx->read_buffer_[i+1], tx->read_buffer_[i+2], tx->read_buffer_[i+3]); + } + } + + const std::string& interface() const { return interface_; } + + private: + const std::string interface_; + int fd_; +}; + +Channel* LookupService(const std::string& name) { + Parcel data; + data.WriteInterfaceToken("android.os.IServiceManager"); + data.WriteUTF8(name); + + Channel channel(0); + + TransactionCommand command(TransactionCommand::TWO_WAY); + command.SetHandle(channel.handle()); + command.SetCode(2 /*CHECK_SERVICE_TRANSACTION*/); + command.SetParcel(data); + + channel.QueueCommand(&command); + channel.TransactCommands(); + channel.reader()->Print(); + +#if 0 + Channel2* ch = new Channel2("android.os.IServiceManager"); + Transaction* tx = ch->NewTransaction(2 /*CHECK_SERVICE_TRANSACTION*/); + tx->WriteString8(name); + ch->ExecuteTransaction(tx); +#endif +} + +} // namespace minibind diff --git a/jni/minibind/minibind.h b/jni/minibind/minibind.h new file mode 100644 index 0000000..019159f --- /dev/null +++ b/jni/minibind/minibind.h @@ -0,0 +1,18 @@ +// 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. + +#ifndef MINIBIND_MINIBIND_H_ +#define MINIBIND_MINIBIND_H_ + +#include + +namespace minibind { + +class Channel; + +Channel* LookupService(const std::string& name); + +} // namespace minibind + +#endif // MINIBIND_MINIBIND_H_ diff --git a/jni/minibind/parcel.cc b/jni/minibind/parcel.cc new file mode 100644 index 0000000..7cf419d --- /dev/null +++ b/jni/minibind/parcel.cc @@ -0,0 +1,50 @@ +// 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 "parcel.h" + +#include "common.h" + +namespace minibind { + +Parcel::Parcel() : data_(), objects_() {} + +Parcel::~Parcel() {} + +void Parcel::WriteInterfaceToken(const std::string& str) { + WriteUTF8(str); +} + +void Parcel::WriteUTF8(const std::string& str) { + data_.insert(data_.end(), str.begin(), str.end()); +} + +void Parcel::WriteUInt32(uint32_t ui32) { + data_.push_back((ui32 >> 24) & 0xff); + data_.push_back((ui32 >> 16) & 0xff); + data_.push_back((ui32 >> 8) & 0xff); + data_.push_back((ui32 >> 0) & 0xff); +} + +binder_uintptr_t Parcel::DataPointer() const { + return reinterpret_cast(&data_[0]); +} + +size_t Parcel::DataSize() const { + return 0; +} + +void Parcel::SetDataSize(size_t size) { + // TODO: handle object lifetimes + CHECK(size % 4 == 0); + data_.resize(size); +} + +void Parcel::Print() const { + for (size_t i = 0; i < data_.size(); i += 4) { + ALOG("%0x %0x %0x %0x", data_[i], data_[i+1], data_[i+2], data_[i+3]); + } +} + +} // namespace minibind diff --git a/jni/minibind/parcel.h b/jni/minibind/parcel.h new file mode 100644 index 0000000..f7718f5 --- /dev/null +++ b/jni/minibind/parcel.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef MINIBIND_PARCEL_H_ +#define MINIBIND_PARCEL_H_ + +#include +#include + +#include + +#include +#include + +namespace minibind { + +class Parcel { + public: + Parcel(); + ~Parcel(); + + void WriteInterfaceToken(const std::string& str); + void WriteUTF8(const std::string& str); + void WriteUInt32(uint32_t ui32); + + binder_uintptr_t DataPointer() const; + size_t DataSize() const; + void SetDataSize(size_t size); + + void Print() const; + + private: + std::vector data_; + std::vector objects_; +}; + +} // namespace minibind + +#endif // MINIBIND_PARCEL_H_ -- 2.22.5