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',
           ],