libprotobinder: Add driver stub and unit tests
Added BinderDriverInterface to allow hooking of
driver calls.
BinderDriver provides access to the real driver.
BinderDriverStub is a driver simulator that can
be used for testing
Also added unit tests for BinderManager, BinderProxy
and BinderHost
BUG=brillo:582
TEST=new unit tests, ran psyche_demo on gizmo
Change-Id: I986f5c61e028f1ef7fd55c9d89f6c45c677ad7e0
Reviewed-on: https://chromium-review.googlesource.com/266460
Reviewed-by: Lee Campbell <leecam@chromium.org>
Commit-Queue: Lee Campbell <leecam@chromium.org>
Trybot-Ready: Lee Campbell <leecam@chromium.org>
Tested-by: Lee Campbell <leecam@chromium.org>
diff --git a/libprotobinder/binder_driver.cc b/libprotobinder/binder_driver.cc
new file mode 100644
index 0000000..188c4f4
--- /dev/null
+++ b/libprotobinder/binder_driver.cc
@@ -0,0 +1,65 @@
+// Copyright 2015 The Chromium OS 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 "libprotobinder/binder_driver.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+// Out of order due to this bad header requiring sys/types.h
+#include <linux/android/binder.h>
+
+#include <base/logging.h>
+
+namespace protobinder {
+
+namespace {
+const size_t kBinderMappedSize = (1 * 1024 * 1024) - (4096 * 2);
+}
+
+BinderDriver::BinderDriver() {
+}
+
+BinderDriver::~BinderDriver() {
+}
+
+void BinderDriver::Init() {
+ binder_fd_ = open("/dev/binder", O_RDWR | O_CLOEXEC);
+ if (binder_fd_ < 0) {
+ PLOG(FATAL) << "Failed to open binder";
+ }
+
+ // Check the version.
+ struct binder_version vers;
+ if ((ioctl(binder_fd_, BINDER_VERSION, &vers) < 0) ||
+ (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
+ LOG(FATAL) << "Binder driver mismatch";
+ }
+
+ // mmap the user buffer.
+ binder_mapped_address_ = mmap(NULL, kBinderMappedSize, PROT_READ,
+ MAP_PRIVATE | MAP_NORESERVE, binder_fd_, 0);
+ if (binder_mapped_address_ == MAP_FAILED) {
+ LOG(FATAL) << "Failed to mmap binder";
+ }
+}
+
+int BinderDriver::GetFdForPolling() {
+ return binder_fd_;
+}
+
+int BinderDriver::ReadWrite(struct binder_write_read* buffers) {
+ return ioctl(binder_fd_, BINDER_WRITE_READ, buffers);
+}
+
+void BinderDriver::SetMaxThreads(int max_threads) {
+ if (ioctl(binder_fd_, BINDER_SET_MAX_THREADS, &max_threads) < 0)
+ PLOG(FATAL) << "ioctl(binder_fd, BINDER_SET_MAX_THREADS) failed";
+}
+
+} // namespace protobinder
diff --git a/libprotobinder/binder_driver.h b/libprotobinder/binder_driver.h
new file mode 100644
index 0000000..bbee07d
--- /dev/null
+++ b/libprotobinder/binder_driver.h
@@ -0,0 +1,55 @@
+// Copyright 2015 The Chromium OS 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 LIBPROTOBINDER_BINDER_DRIVER_H_
+#define LIBPROTOBINDER_BINDER_DRIVER_H_
+
+#include <stdint.h>
+
+#include <base/macros.h>
+
+#include "binder_export.h" // NOLINT(build/include)
+
+struct binder_transaction_data;
+struct binder_write_read;
+
+namespace protobinder {
+
+class Parcel;
+
+class BINDER_EXPORT BinderDriverInterface {
+ public:
+ virtual ~BinderDriverInterface() = default;
+
+ // Get a file descriptor that can used with epoll.
+ virtual int GetFdForPolling() = 0;
+
+ // Do binder BINDER_WRITE_READ command.
+ virtual int ReadWrite(binder_write_read* buffers) = 0;
+
+ // Do binder BINDER_SET_MAX_THREADS command.
+ virtual void SetMaxThreads(int max_threads) = 0;
+};
+
+class BINDER_EXPORT BinderDriver : public BinderDriverInterface {
+ public:
+ BinderDriver();
+ ~BinderDriver() override;
+
+ int GetFdForPolling() override;
+ int ReadWrite(binder_write_read* buffers) override;
+ void SetMaxThreads(int max_threads) override;
+
+ void Init();
+
+ private:
+ int binder_fd_;
+ void* binder_mapped_address_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinderDriver);
+};
+
+} // namespace protobinder
+
+#endif // LIBPROTOBINDER_BINDER_DRIVER_H_
diff --git a/libprotobinder/binder_driver_stub.cc b/libprotobinder/binder_driver_stub.cc
new file mode 100644
index 0000000..c91434e
--- /dev/null
+++ b/libprotobinder/binder_driver_stub.cc
@@ -0,0 +1,239 @@
+// Copyright 2015 The Chromium OS 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 "libprotobinder/binder_driver_stub.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <base/logging.h>
+
+namespace protobinder {
+
+const int BinderDriverStub::kReplyVal = 0xDEAD;
+const char BinderDriverStub::kReplyString[] = "TEST";
+
+BinderDriverStub::BinderDriverStub() : max_threads_(0) {
+}
+
+BinderDriverStub::~BinderDriverStub() {
+ if (!user_buffers_.empty())
+ LOG(FATAL) << "Not all binder buffers were released";
+ for (auto it : handle_refs_) {
+ if (it.second != 0)
+ LOG(FATAL) << "Not all binder refs were released";
+ }
+ if (!death_notifications_.empty())
+ LOG(FATAL) << "Not all binder death notifications were released";
+}
+
+int BinderDriverStub::GetFdForPolling() {
+ return 0;
+}
+
+int BinderDriverStub::ReadWrite(binder_write_read* buffers) {
+ if (buffers->write_size > 0) {
+ CHECK(buffers->write_buffer) << "Bad binder write buffer";
+ char* buffer = reinterpret_cast<char*>(buffers->write_buffer);
+
+ char* ptr = buffer + buffers->write_consumed;
+ char* end = buffer + buffers->write_size;
+
+ while (ptr < end) {
+ if ((size_t)(end - ptr) < sizeof(uint32_t))
+ LOG(FATAL) << "Not enough data in command buffer";
+ uint32_t cmd = *(reinterpret_cast<uint32_t*>(ptr));
+ ptr += sizeof(cmd);
+ switch (cmd) {
+ case BC_TRANSACTION: {
+ struct binder_transaction_data* tr = nullptr;
+ if ((ptr < end) && (size_t)(end - ptr) < sizeof(*tr))
+ LOG(FATAL)
+ << "Not enough data in command buffer for BC_TRANSACTION";
+ tr = reinterpret_cast<struct binder_transaction_data*>(ptr);
+ ptr += sizeof(*tr);
+ ProcessTransaction(tr);
+ break;
+ }
+ case BC_REPLY: {
+ struct binder_transaction_data* tr = nullptr;
+ if ((ptr < end) && (size_t)(end - ptr) < sizeof(*tr))
+ LOG(FATAL) << "Not enough data in command buffer for BC_REPLY";
+ tr = (struct binder_transaction_data*)ptr;
+ ptr += sizeof(*tr);
+ memcpy(&last_transaction_data_, tr, sizeof(last_transaction_data_));
+ return_cmds_.WriteInt32(BR_TRANSACTION_COMPLETE);
+ break;
+ }
+ case BC_FREE_BUFFER: {
+ if ((ptr < end) && (size_t)(end - ptr) < sizeof(void*)) {
+ LOG(FATAL)
+ << "Not enough data in command buffer for BC_FREE_BUFFER";
+ }
+ void** buf = reinterpret_cast<void**>(ptr);
+ ptr += sizeof(*buf);
+ auto it = user_buffers_.find(*buf);
+ if (it == user_buffers_.end())
+ LOG(FATAL) << "Freeing invalid buffer";
+ user_buffers_.erase(it);
+ break;
+ }
+ case BC_INCREFS: {
+ if ((ptr < end) && (size_t)(end - ptr) < sizeof(uint32_t))
+ LOG(FATAL) << "Not enough data in command buffer for BC_INCREFS";
+ uint32_t handle = *(reinterpret_cast<uint32_t*>(ptr));
+ ptr += sizeof(handle);
+ handle_refs_[handle]++;
+ break;
+ }
+ case BC_DECREFS: {
+ if ((ptr < end) && (size_t)(end - ptr) < sizeof(uint32_t))
+ LOG(FATAL) << "Not enough data in command buffer for BC_DECREFS";
+ uint32_t handle = *(reinterpret_cast<uint32_t*>(ptr));
+ ptr += sizeof(uint32_t);
+ if (handle_refs_[handle] == 0)
+ LOG(FATAL) << "Calling BC_DECREFS with zero refs";
+ handle_refs_[handle]--;
+ break;
+ }
+ case BC_REQUEST_DEATH_NOTIFICATION: {
+ if ((ptr < end) &&
+ (size_t)(end - ptr) < (sizeof(uint32_t) + sizeof(uintptr_t))) {
+ LOG(FATAL) << "Not enough data in command buffer for "
+ "BC_REQUEST_DEATH_NOTIFICATION";
+ }
+ uint32_t handle = *(reinterpret_cast<uint32_t*>(ptr));
+ ptr += sizeof(handle);
+ uintptr_t cookie = *(reinterpret_cast<uintptr_t*>(ptr));
+ ptr += sizeof(cookie);
+ death_notifications_[cookie] = handle;
+ break;
+ }
+ case BC_CLEAR_DEATH_NOTIFICATION: {
+ if ((ptr < end) &&
+ (size_t)(end - ptr) < (sizeof(uint32_t) + sizeof(uintptr_t)))
+ LOG(FATAL) << "Not enough data in command buffer for "
+ "BC_CLEAR_DEATH_NOTIFICATION";
+ uint32_t handle = *(reinterpret_cast<uint32_t*>(ptr));
+ ptr += sizeof(handle);
+ uintptr_t cookie = *(reinterpret_cast<uintptr_t*>(ptr));
+ ptr += sizeof(cookie);
+ auto it = death_notifications_.find(cookie);
+ if (it == death_notifications_.end())
+ LOG(FATAL) << "BC_CLEAR_DEATH_NOTIFICATION without registering";
+ if (it->second != handle)
+ LOG(FATAL) << "BC_CLEAR_DEATH_NOTIFICATION bad cookie";
+ death_notifications_.erase(it);
+ break;
+ }
+ default:
+ LOG(FATAL) << "protobinder sent unknown command " << cmd;
+ }
+ }
+
+ buffers->write_consumed = ptr - buffer;
+ }
+
+ if (buffers->read_size > 0) {
+ CHECK(buffers->read_buffer) << "Bad binder read buffer";
+
+ size_t len = buffers->read_size;
+ if (return_cmds_.Len() <= buffers->read_size)
+ len = return_cmds_.Len();
+ else
+ LOG(FATAL) << "Return commands did not fit in user buffer";
+
+ memcpy(reinterpret_cast<void*>(buffers->read_buffer), return_cmds_.Data(),
+ len);
+ buffers->read_consumed = len;
+ return_cmds_.SetLen(0);
+ return_cmds_.SetPos(0);
+ }
+
+ return 0;
+}
+void BinderDriverStub::SetMaxThreads(int max_threads) {
+ max_threads_ = max_threads;
+}
+
+struct binder_transaction_data* BinderDriverStub::LastTransactionData() {
+ return &last_transaction_data_;
+}
+
+int BinderDriverStub::GetRefCount(uint32_t handle) {
+ return handle_refs_[handle];
+}
+
+bool BinderDriverStub::IsDeathRegistered(uintptr_t cookie, uint32_t handle) {
+ auto it = death_notifications_.find(cookie);
+ if (it == death_notifications_.end())
+ return false;
+ if (it->second != handle)
+ return false;
+ return true;
+}
+
+void BinderDriverStub::InjectDeathNotification(uintptr_t cookie) {
+ return_cmds_.WriteInt32(BR_DEAD_BINDER);
+ return_cmds_.WritePointer(cookie);
+}
+
+void BinderDriverStub::InjectTransaction(uintptr_t cookie,
+ uint32_t code,
+ const Parcel& data,
+ bool one_way) {
+ // Need to take a copy of the Parcel and add it to list so it can be freed.
+ std::unique_ptr<Parcel> transact_parcel(new Parcel);
+ transact_parcel->Write(data.Data(), data.Len());
+
+ struct binder_transaction_data transact_data;
+ memset(&transact_data, 0, sizeof(transact_data));
+
+ transact_data.data.ptr.buffer = (binder_uintptr_t)transact_parcel->Data();
+ transact_data.data_size = transact_parcel->Len();
+
+ transact_data.target.ptr = cookie;
+ transact_data.cookie = cookie;
+ transact_data.code = code;
+
+ transact_data.flags |= one_way ? TF_ONE_WAY : 0;
+
+ user_buffers_[transact_parcel->Data()] = std::move(transact_parcel);
+ return_cmds_.WriteInt32(BR_TRANSACTION);
+ return_cmds_.Write(&transact_data, sizeof(transact_data));
+}
+
+void BinderDriverStub::ProcessTransaction(struct binder_transaction_data* tr) {
+ memcpy(&last_transaction_data_, tr, sizeof(last_transaction_data_));
+
+ if (tr->target.handle == BAD_ENDPOINT) {
+ return_cmds_.WriteInt32(BR_DEAD_REPLY);
+ return;
+ }
+
+ return_cmds_.WriteInt32(BR_TRANSACTION_COMPLETE);
+
+ if (tr->target.handle == GOOD_ENDPOINT && ((tr->flags & TF_ONE_WAY) == 0)) {
+ std::unique_ptr<Parcel> reply_parcel(new Parcel);
+ reply_parcel->WriteInt32(kReplyVal);
+ reply_parcel->WriteString(kReplyString);
+
+ struct binder_transaction_data reply_data;
+
+ memset(&reply_data, 0, sizeof(reply_data));
+ reply_data.data.ptr.buffer = (binder_uintptr_t)reply_parcel->Data();
+ reply_data.data_size = reply_parcel->Len();
+
+ user_buffers_[reply_parcel->Data()] = std::move(reply_parcel);
+
+ return_cmds_.WriteInt32(BR_REPLY);
+ return_cmds_.Write(&reply_data, sizeof(reply_data));
+ }
+}
+
+} // namespace protobinder
diff --git a/libprotobinder/binder_driver_stub.h b/libprotobinder/binder_driver_stub.h
new file mode 100644
index 0000000..5456332
--- /dev/null
+++ b/libprotobinder/binder_driver_stub.h
@@ -0,0 +1,70 @@
+// Copyright 2015 The Chromium OS 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 LIBPROTOBINDER_BINDER_DRIVER_STUB_H_
+#define LIBPROTOBINDER_BINDER_DRIVER_STUB_H_
+
+#include <base/macros.h>
+#include <sys/types.h>
+// Out of order due to this bad header requiring sys/types.h
+#include <linux/android/binder.h>
+
+#include <map>
+#include <memory>
+
+#include "binder_driver.h" // NOLINT(build/include)
+#include "binder_export.h" // NOLINT(build/include)
+#include "parcel.h" // NOLINT(build/include)
+
+namespace protobinder {
+
+// Stub class that emulates the binder driver. This is used
+// when unit testing libprotobinder.
+class BINDER_EXPORT BinderDriverStub : public BinderDriverInterface {
+ public:
+ enum EndPoints {
+ // Provide a valid reply.
+ GOOD_ENDPOINT = 1,
+ // Provides a dead end point.
+ BAD_ENDPOINT = 2
+ };
+ BinderDriverStub();
+ ~BinderDriverStub() override;
+
+ int GetFdForPolling() override;
+ int ReadWrite(binder_write_read* buffers) override;
+ void SetMaxThreads(int max_threads) override;
+
+ // The following methods are used by unit tests.
+
+ // Provides access to the raw transaction data from
+ // the last Transaction on the driver.
+ struct binder_transaction_data* LastTransactionData();
+ int GetRefCount(uint32_t handle);
+ bool IsDeathRegistered(uintptr_t cookie, uint32_t handle);
+ void InjectDeathNotification(uintptr_t cookie);
+ void InjectTransaction(uintptr_t cookie,
+ uint32_t code,
+ const Parcel& data,
+ bool one_way);
+
+ static const int kReplyVal;
+ static const char kReplyString[];
+
+ private:
+ void ProcessTransaction(struct binder_transaction_data* tr);
+
+ struct binder_transaction_data last_transaction_data_;
+ Parcel return_cmds_;
+ std::map<void*, std::unique_ptr<Parcel>> user_buffers_;
+ std::map<uint32_t, int> handle_refs_;
+ std::map<uintptr_t, uint32_t> death_notifications_;
+ int max_threads_;
+
+ DISALLOW_COPY_AND_ASSIGN(BinderDriverStub);
+};
+
+} // namespace protobinder
+
+#endif // LIBPROTOBINDER_BINDER_DRIVER_STUB_H_
diff --git a/libprotobinder/binder_manager.cc b/libprotobinder/binder_manager.cc
index 3d3fd08..ee58b24 100644
--- a/libprotobinder/binder_manager.cc
+++ b/libprotobinder/binder_manager.cc
@@ -4,19 +4,10 @@
#include "libprotobinder/binder_manager.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <sys/ioctl.h>
-#include <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-// Out of order due to this bad header requiring sys/types.h
-#include <linux/android/binder.h>
-
#include <base/logging.h>
#include <base/strings/stringprintf.h>
+#include "libprotobinder/binder_driver.h"
#include "libprotobinder/binder_host.h"
#include "libprotobinder/binder_proxy.h"
#include "libprotobinder/iinterface.h"
@@ -26,16 +17,17 @@
namespace {
-const size_t kBinderMappedSize = (1 * 1024 * 1024) - (4096 * 2);
-
BinderManagerInterface* g_binder_manager = nullptr;
} // namespace
// static
BinderManagerInterface* BinderManagerInterface::Get() {
- if (!g_binder_manager)
- g_binder_manager = new BinderManager();
+ if (!g_binder_manager) {
+ std::unique_ptr<BinderDriver> driver(new BinderDriver());
+ driver->Init();
+ g_binder_manager = new BinderManager(std::move(driver));
+ }
return g_binder_manager;
}
@@ -55,20 +47,24 @@
void* cookie) {
VLOG(1) << "Binder free";
// TODO(leecam): Close FDs in Parcel
- BinderManager* manager = static_cast<BinderManager*>(
- BinderManagerInterface::Get());
+ BinderManager* manager =
+ static_cast<BinderManager*>(BinderManagerInterface::Get());
manager->out_commands_.WriteInt32(BC_FREE_BUFFER);
manager->out_commands_.WritePointer((uintptr_t)data);
}
+// TODO(leecam): Remove the DoBinderReadWriteIoctl
+// to reduce number of calls to driver
void BinderManager::IncWeakHandle(uint32_t handle) {
out_commands_.WriteInt32(BC_INCREFS);
out_commands_.WriteInt32(handle);
+ DoBinderReadWriteIoctl(false);
}
void BinderManager::DecWeakHandle(uint32_t handle) {
out_commands_.WriteInt32(BC_DECREFS);
out_commands_.WriteInt32(handle);
+ DoBinderReadWriteIoctl(false);
}
void BinderManager::RequestDeathNotification(BinderProxy* proxy) {
@@ -288,8 +284,6 @@
}
bool BinderManager::DoBinderReadWriteIoctl(bool do_read) {
- if (binder_fd_ < 0)
- return false;
// Create a binder_write_read command and send it to binder
struct binder_write_read bwr;
@@ -322,8 +316,8 @@
return true;
}
VLOG(1) << "Doing ioctl";
- if (ioctl(binder_fd_, BINDER_WRITE_READ, &bwr) < 0) {
- PLOG(ERROR) << "ioctl(binder_fd, BINDER_WRITE_READ) failed";
+ if (driver_->ReadWrite(&bwr) < 0) {
+ LOG(ERROR) << "driver ReadWrite failed";
return false;
}
VLOG(1) << base::StringPrintf("Binder data R:%lld/%lld W:%lld/%lld",
@@ -350,15 +344,9 @@
}
bool BinderManager::GetFdForPolling(int* fd) {
- if (binder_fd_ < 0)
- return false;
- int num_threads = 0;
- if (ioctl(binder_fd_, BINDER_SET_MAX_THREADS, &num_threads) < 0) {
- PLOG(ERROR) << "ioctl(binder_fd, BINDER_SET_MAX_THREADS, 0) failed";
- return false;
- }
+ driver_->SetMaxThreads(0);
out_commands_.WriteInt32(BC_ENTER_LOOPER);
- *fd = binder_fd_;
+ *fd = driver_->GetFdForPolling();
return true;
}
@@ -379,33 +367,17 @@
return DoBinderReadWriteIoctl(false);
}
-BinderManager::BinderManager() {
+BinderManager::BinderManager(std::unique_ptr<BinderDriverInterface> driver) {
VLOG(1) << "BinderManager created";
- binder_fd_ = open("/dev/binder", O_RDWR | O_CLOEXEC);
- if (binder_fd_ < 0) {
- PLOG(FATAL) << "Failed to open binder";
- }
+ driver_ = std::move(driver);
- // Check the version.
- struct binder_version vers;
- if ((ioctl(binder_fd_, BINDER_VERSION, &vers) < 0) ||
- (vers.protocol_version != BINDER_CURRENT_PROTOCOL_VERSION)) {
- LOG(FATAL) << "Binder driver mismatch";
- }
-
- // mmap the user buffer.
- binder_mapped_address_ = mmap(NULL, kBinderMappedSize, PROT_READ,
- MAP_PRIVATE | MAP_NORESERVE, binder_fd_, 0);
- if (binder_mapped_address_ == MAP_FAILED) {
- LOG(FATAL) << "Failed to mmap binder";
- }
-
- // TODO(leecam): Check these and set binder_fd_ to -1 on failure.
in_commands_.SetCapacity(256);
out_commands_.SetCapacity(256);
}
-BinderManager::~BinderManager() {}
+BinderManager::~BinderManager() {
+ DoBinderReadWriteIoctl(false);
+}
} // namespace protobinder
diff --git a/libprotobinder/binder_manager.h b/libprotobinder/binder_manager.h
index 40a1d30..aa918d0 100644
--- a/libprotobinder/binder_manager.h
+++ b/libprotobinder/binder_manager.h
@@ -6,12 +6,14 @@
#define LIBPROTOBINDER_BINDER_MANAGER_H_
#include <cstdint>
+#include <memory>
#include <base/macros.h>
#include <base/memory/scoped_ptr.h>
+#include "binder_driver.h" // NOLINT(build/include)
#include "binder_export.h" // NOLINT(build/include)
-#include "parcel.h" // NOLINT(build/include)
+#include "parcel.h" // NOLINT(build/include)
namespace protobinder {
@@ -56,7 +58,7 @@
// kernel via /dev/binder.
class BINDER_EXPORT BinderManager : public BinderManagerInterface {
public:
- BinderManager();
+ explicit BinderManager(std::unique_ptr<BinderDriverInterface> driver);
~BinderManager() override;
// BinderManagerInterface:
@@ -93,15 +95,14 @@
bool GetNextCommandAndProcess();
int SendReply(const Parcel& reply, int error_code);
- int binder_fd_;
- void* binder_mapped_address_;
-
// These parcels are used to pass binder ioctl commands to binder.
// They carry binder command buffers, not to be confused with Parcels
// used in Transactions which carry user data.
Parcel out_commands_;
Parcel in_commands_;
+ std::unique_ptr<BinderDriverInterface> driver_;
+
DISALLOW_COPY_AND_ASSIGN(BinderManager);
};
diff --git a/libprotobinder/binder_unittest.cc b/libprotobinder/binder_unittest.cc
new file mode 100644
index 0000000..0c4a938
--- /dev/null
+++ b/libprotobinder/binder_unittest.cc
@@ -0,0 +1,291 @@
+// Copyright 2015 The Chromium OS 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 "libprotobinder/binder_manager.h"
+
+#include <string.h>
+
+#include <base/bind.h>
+#include <base/macros.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/memory/weak_ptr.h>
+#include <gtest/gtest.h>
+
+#include "libprotobinder/binder_driver_stub.h"
+#include "libprotobinder/binder_host.h"
+#include "libprotobinder/binder_proxy.h"
+#include "libprotobinder/protobinder.h"
+
+namespace protobinder {
+
+class BinderTest : public testing::Test {
+ public:
+ BinderTest()
+ : got_death_callback_(false),
+ got_transact_callback_(false),
+ weak_ptr_factory_(this) {
+ // Configure BinderManager to use the stub driver.
+ // BinderManager takes ownership of BinderDriverStub.
+ driver_ = new BinderDriverStub();
+ scoped_ptr<BinderManager> manager(
+ new BinderManager(std::unique_ptr<BinderDriverInterface>(driver_)));
+
+ BinderManagerInterface::SetForTesting(manager.Pass());
+ }
+ ~BinderTest() override {
+ BinderManagerInterface::SetForTesting(scoped_ptr<BinderManagerInterface>());
+ }
+
+ protected:
+ // Checks binder transaction data presented to the driver
+ // matches what is provided in a transaction.
+ void CheckTransaction(struct binder_transaction_data* data,
+ uint32_t handle,
+ uint32_t code,
+ bool one_way) {
+ ASSERT_NE(nullptr, data);
+ EXPECT_EQ(handle, data->target.handle);
+ EXPECT_EQ(code, data->code);
+ EXPECT_EQ(code, data->code);
+
+ unsigned int flags = TF_ACCEPT_FDS;
+ flags |= one_way ? TF_ONE_WAY : 0;
+ EXPECT_EQ(flags, data->flags);
+ }
+
+ void HandleBinderDeath() { got_death_callback_ = true; }
+ void HandleTransaction() { got_transact_callback_ = true; }
+
+ BinderDriverStub* driver_; // Not owned.
+ bool got_death_callback_;
+ bool got_transact_callback_;
+ base::WeakPtrFactory<BinderTest> weak_ptr_factory_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BinderTest);
+};
+
+class HostTest : public BinderHost {
+ public:
+ explicit HostTest(const base::Closure& closure)
+ : transact_callback_(closure) {}
+ ~HostTest() override {}
+ int OnTransact(uint32_t code,
+ Parcel* data,
+ Parcel* reply,
+ bool one_way) override {
+ EXPECT_EQ(10, code);
+ int val = -1;
+ EXPECT_TRUE(data->ReadInt32(&val));
+ EXPECT_EQ(0xDEAD, val);
+ reply->WriteInt32(0xC0DE);
+ transact_callback_.Run();
+ return SUCCESS;
+ }
+
+ private:
+ base::Closure transact_callback_;
+ DISALLOW_COPY_AND_ASSIGN(HostTest);
+};
+
+TEST_F(BinderTest, BasicTransaction) {
+ Parcel data;
+ uint32_t handle = 0x1111;
+ uint32_t code = 0x2222;
+ bool one_way = true;
+ int ret;
+
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, nullptr,
+ one_way);
+ EXPECT_EQ(SUCCESS, ret);
+ CheckTransaction(driver_->LastTransactionData(), handle, code, one_way);
+
+ handle = 0x3333;
+ code = 0x4444;
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, nullptr,
+ one_way);
+ EXPECT_EQ(SUCCESS, ret);
+ CheckTransaction(driver_->LastTransactionData(), handle, code, one_way);
+
+ handle = 0x5555;
+ code = 0x6666;
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, nullptr,
+ one_way);
+ EXPECT_EQ(SUCCESS, ret);
+ CheckTransaction(driver_->LastTransactionData(), handle, code, one_way);
+
+ EXPECT_EQ(0, driver_->LastTransactionData()->data_size);
+ EXPECT_EQ(0, driver_->LastTransactionData()->offsets_size);
+}
+
+TEST_F(BinderTest, DeadEndpointTransaction) {
+ Parcel data;
+ uint32_t handle = BinderDriverStub::BAD_ENDPOINT;
+ uint32_t code = 0;
+ bool one_way = true;
+ int ret;
+
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, nullptr,
+ one_way);
+ EXPECT_EQ(ERROR_DEAD_ENDPOINT, ret);
+ CheckTransaction(driver_->LastTransactionData(), handle, code, one_way);
+}
+
+TEST_F(BinderTest, OneWayDataTransaction) {
+ Parcel data;
+ uint32_t handle = 0x1111;
+ uint32_t code = 0x2222;
+ bool one_way = true;
+ int ret;
+
+ data.WriteInt32(100);
+ data.WriteString("Yet Another IPC...");
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, nullptr,
+ one_way);
+ EXPECT_EQ(SUCCESS, ret);
+ CheckTransaction(driver_->LastTransactionData(), handle, code, one_way);
+
+ // Check the data sent to the driver is correct.
+ EXPECT_EQ(data.Len(), driver_->LastTransactionData()->data_size);
+ EXPECT_EQ(0, driver_->LastTransactionData()->offsets_size);
+ ASSERT_NE(0, driver_->LastTransactionData()->data.ptr.buffer);
+ EXPECT_EQ(0, memcmp(data.Data(),
+ reinterpret_cast<void*>(
+ driver_->LastTransactionData()->data.ptr.buffer),
+ data.Len()));
+}
+
+TEST_F(BinderTest, OneWayDataAndObjectsTransaction) {
+ Parcel data;
+ uint32_t handle = 0x1111;
+ uint32_t code = 0x2222;
+ bool one_way = true;
+ int ret;
+
+ data.WriteInt32(100);
+ data.WriteString("Yet Another IPC...");
+ data.WriteFd(10);
+ data.WriteFd(20);
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, nullptr,
+ one_way);
+ EXPECT_EQ(SUCCESS, ret);
+ CheckTransaction(driver_->LastTransactionData(), handle, code, one_way);
+
+ // Check that the data and offset buffers sent to the driver are correct.
+ EXPECT_EQ(data.Len(), driver_->LastTransactionData()->data_size);
+ EXPECT_EQ(data.ObjectCount() * sizeof(binder_size_t),
+ driver_->LastTransactionData()->offsets_size);
+ ASSERT_NE(0, driver_->LastTransactionData()->data.ptr.buffer);
+ EXPECT_EQ(0, memcmp(data.Data(),
+ reinterpret_cast<void*>(
+ driver_->LastTransactionData()->data.ptr.buffer),
+ data.Len()));
+ ASSERT_NE(0, driver_->LastTransactionData()->data.ptr.offsets);
+ EXPECT_EQ(0, memcmp(data.ObjectData(),
+ reinterpret_cast<void*>(
+ driver_->LastTransactionData()->data.ptr.offsets),
+ data.ObjectCount() * sizeof(binder_size_t)));
+}
+
+TEST_F(BinderTest, TwoWayTransaction) {
+ Parcel data;
+ Parcel reply;
+ uint32_t handle = BinderDriverStub::GOOD_ENDPOINT;
+ uint32_t code = 0;
+ bool one_way = false;
+ int ret;
+
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, &reply,
+ one_way);
+ EXPECT_EQ(SUCCESS, ret);
+ CheckTransaction(driver_->LastTransactionData(), handle, code, one_way);
+
+ int val;
+ EXPECT_TRUE(reply.ReadInt32(&val));
+ EXPECT_EQ(BinderDriverStub::kReplyVal, val);
+
+ std::string str;
+ EXPECT_TRUE(reply.ReadString(&str));
+ EXPECT_EQ(BinderDriverStub::kReplyString, str);
+
+ EXPECT_TRUE(reply.IsEmpty());
+
+ // Check one-way works on a call that replies with data.
+ ret = BinderManagerInterface::Get()->Transact(handle, code, data, nullptr,
+ true);
+ EXPECT_EQ(SUCCESS, ret);
+}
+
+TEST_F(BinderTest, Proxy) {
+ EXPECT_EQ(0, driver_->GetRefCount(BinderDriverStub::GOOD_ENDPOINT));
+ {
+ BinderProxy proxy(BinderDriverStub::GOOD_ENDPOINT);
+ EXPECT_EQ(BinderDriverStub::GOOD_ENDPOINT, proxy.handle());
+ EXPECT_EQ(1, driver_->GetRefCount(BinderDriverStub::GOOD_ENDPOINT));
+ EXPECT_TRUE(driver_->IsDeathRegistered(reinterpret_cast<uintptr_t>(&proxy),
+ BinderDriverStub::GOOD_ENDPOINT));
+
+ BinderProxy proxy2(BinderDriverStub::GOOD_ENDPOINT);
+ EXPECT_EQ(2, driver_->GetRefCount(BinderDriverStub::GOOD_ENDPOINT));
+ EXPECT_TRUE(driver_->IsDeathRegistered(reinterpret_cast<uintptr_t>(&proxy2),
+ BinderDriverStub::GOOD_ENDPOINT));
+ }
+ EXPECT_EQ(0, driver_->GetRefCount(BinderDriverStub::GOOD_ENDPOINT));
+}
+
+TEST_F(BinderTest, ProxyTransaction) {
+ BinderProxy proxy(BinderDriverStub::GOOD_ENDPOINT);
+
+ Parcel data;
+ uint32_t code = 0x10;
+
+ int ret = proxy.Transact(code, &data, nullptr, true);
+ EXPECT_EQ(SUCCESS, ret);
+ CheckTransaction(driver_->LastTransactionData(),
+ BinderDriverStub::GOOD_ENDPOINT, code, true);
+}
+
+TEST_F(BinderTest, ProxyDeathNotifcation) {
+ BinderProxy proxy(BinderDriverStub::GOOD_ENDPOINT);
+ EXPECT_FALSE(got_death_callback_);
+
+ proxy.SetDeathCallback(
+ base::Bind(&BinderTest_ProxyDeathNotifcation_Test::HandleBinderDeath,
+ weak_ptr_factory_.GetWeakPtr()));
+
+ driver_->InjectDeathNotification((uintptr_t)&proxy);
+ BinderManagerInterface::Get()->HandleEvent();
+
+ EXPECT_TRUE(got_death_callback_);
+}
+
+TEST_F(BinderTest, HostOneWay) {
+ EXPECT_FALSE(got_transact_callback_);
+
+ HostTest host(base::Bind(&BinderTest_HostOneWay_Test::HandleTransaction,
+ weak_ptr_factory_.GetWeakPtr()));
+ Parcel data;
+ data.WriteInt32(0xDEAD);
+ driver_->InjectTransaction((uintptr_t)&host, 10, data, true);
+ BinderManagerInterface::Get()->HandleEvent();
+ EXPECT_TRUE(got_transact_callback_);
+}
+
+TEST_F(BinderTest, HostTwoWay) {
+ EXPECT_FALSE(got_transact_callback_);
+
+ HostTest host(base::Bind(&BinderTest_HostTwoWay_Test::HandleTransaction,
+ weak_ptr_factory_.GetWeakPtr()));
+ Parcel data;
+ data.WriteInt32(0xDEAD);
+ driver_->InjectTransaction((uintptr_t)&host, 10, data, false);
+ BinderManagerInterface::Get()->HandleEvent();
+ EXPECT_TRUE(got_transact_callback_);
+
+ // Unfortunately the reply data has now gone out of scope,
+ // but the size can still be validated.
+ EXPECT_EQ(sizeof(uint32_t), driver_->LastTransactionData()->data_size);
+}
+
+} // namespace protobinder
diff --git a/libprotobinder/libprotobinder.gyp b/libprotobinder/libprotobinder.gyp
index 53e71f9..c74170e 100644
--- a/libprotobinder/libprotobinder.gyp
+++ b/libprotobinder/libprotobinder.gyp
@@ -23,6 +23,7 @@
# TODO(derat): If the amount of testing-specific code, e.g. stubs, ever
# becomes substantial, move it to a separate shared library.
'<(proto_in_dir)/binder.proto',
+ 'binder_driver.cc',
'binder_host.cc',
'binder_manager.cc',
'binder_manager_stub.cc',
@@ -46,6 +47,8 @@
'includes': ['../common-mk/common_test.gypi'],
'dependencies': ['libprotobinder'],
'sources': [
+ 'binder_driver_stub.cc',
+ 'binder_unittest.cc',
'libprotobinder_testrunner.cc',
'parcel_unittest.cc',
],