Merge "Native Side of Binder Proxy Tracking by Uid"
diff --git a/include/layerproto b/include/layerproto
new file mode 120000
index 0000000..ef21a4e
--- /dev/null
+++ b/include/layerproto
@@ -0,0 +1 @@
+../services/surfaceflinger/layerproto/include/layerproto/
\ No newline at end of file
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index a610a51..102964f 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -118,4 +118,7 @@
     vendor_available: true,
 }
 
-subdirs = ["tests"]
+subdirs = [
+    "tests",
+    "tools",
+]
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index f327200..e3cb494 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -61,10 +61,10 @@
 
 cc_test {
     tags: ["optional"],
-    srcs: ["bufferhub_tests.cpp"],
+    srcs: ["buffer_hub-test.cpp"],
     static_libs: ["libbufferhub"] + staticLibraries,
     shared_libs: sharedLibraries,
     header_libs: headerLibraries,
-    name: "bufferhub_tests",
+    name: "buffer_hub-test",
 }
 
diff --git a/libs/vr/libbufferhub/bufferhub_tests.cpp b/libs/vr/libbufferhub/buffer_hub-test.cpp
similarity index 68%
rename from libs/vr/libbufferhub/bufferhub_tests.cpp
rename to libs/vr/libbufferhub/buffer_hub-test.cpp
index c4b9a8c..3c99f99 100644
--- a/libs/vr/libbufferhub/bufferhub_tests.cpp
+++ b/libs/vr/libbufferhub/buffer_hub-test.cpp
@@ -20,6 +20,10 @@
 using android::dvr::BufferConsumer;
 using android::dvr::BufferHubDefs::kConsumerStateMask;
 using android::dvr::BufferHubDefs::kProducerStateBit;
+using android::dvr::BufferHubDefs::IsBufferGained;
+using android::dvr::BufferHubDefs::IsBufferPosted;
+using android::dvr::BufferHubDefs::IsBufferAcquired;
+using android::dvr::BufferHubDefs::IsBufferReleased;
 using android::dvr::BufferProducer;
 using android::pdx::LocalHandle;
 
@@ -28,6 +32,7 @@
 const int kFormat = HAL_PIXEL_FORMAT_RGBA_8888;
 const int kUsage = 0;
 const uint64_t kContext = 42;
+const size_t kMaxConsumerCount = 63;
 
 using LibBufferHubTest = ::testing::Test;
 
@@ -159,10 +164,10 @@
       kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
   ASSERT_TRUE(p.get() != nullptr);
 
-  // It's ok to create up to 63 consumer buffers.
+  // It's ok to create up to kMaxConsumerCount consumer buffers.
   uint64_t buffer_state_bits = p->buffer_state_bit();
-  std::array<std::unique_ptr<BufferConsumer>, 63> cs;
-  for (size_t i = 0; i < 63; i++) {
+  std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+  for (size_t i = 0; i < kMaxConsumerCount; i++) {
     cs[i] = BufferConsumer::Import(p->CreateConsumer());
     ASSERT_TRUE(cs[i].get() != nullptr);
     // Expect all buffers have unique state mask.
@@ -176,7 +181,7 @@
   EXPECT_EQ(state.error(), E2BIG);
 
   // Release any consumer should allow us to re-create.
-  for (size_t i = 0; i < 63; i++) {
+  for (size_t i = 0; i < kMaxConsumerCount; i++) {
     buffer_state_bits &= ~cs[i]->buffer_state_bit();
     cs[i] = nullptr;
     cs[i] = BufferConsumer::Import(p->CreateConsumer());
@@ -240,6 +245,217 @@
   EXPECT_EQ(-EALREADY, p->Gain(&fence));
 }
 
+TEST_F(LibBufferHubTest, TestAsyncStateTransitions) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // The producer buffer starts in gained state.
+
+  // Acquire, release, and gain in gained state should fail.
+  EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+  EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+
+  // Post in gained state should succeed.
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_EQ(p->buffer_state(), c->buffer_state());
+  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+
+  // Post, release, and gain in posted state should fail.
+  EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+  EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+
+  // Acquire in posted state should succeed.
+  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+  EXPECT_EQ(p->buffer_state(), c->buffer_state());
+  EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+
+  // Acquire, post, and gain in acquired state should fail.
+  EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+  EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_EQ(-EBUSY, p->GainAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+
+  // Release in acquired state should succeed.
+  EXPECT_EQ(0, c->ReleaseAsync(&metadata, invalid_fence));
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+  EXPECT_EQ(p->buffer_state(), c->buffer_state());
+  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+
+  // Release, acquire, and post in released state should fail.
+  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+  EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+  EXPECT_EQ(-EBUSY, p->PostAsync(&metadata, invalid_fence));
+
+  // Gain in released state should succeed.
+  EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+  EXPECT_EQ(p->buffer_state(), c->buffer_state());
+  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+  // Acquire, release, and gain in gained state should fail.
+  EXPECT_EQ(-EBUSY, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+  EXPECT_EQ(-EBUSY, c->ReleaseAsync(&metadata, invalid_fence));
+  EXPECT_EQ(-EALREADY, p->GainAsync(&metadata, &invalid_fence));
+  EXPECT_FALSE(invalid_fence.IsValid());
+}
+
+TEST_F(LibBufferHubTest, TestZeroConsumer) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // Newly created.
+  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+
+  // The buffer should stay in posted stay until a consumer picks it up.
+  EXPECT_GE(0, RETRY_EINTR(p->Poll(100)));
+
+  // A new consumer should still be able to acquire the buffer immediately.
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestMaxConsumers) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+
+  std::array<std::unique_ptr<BufferConsumer>, kMaxConsumerCount> cs;
+  for (size_t i = 0; i < kMaxConsumerCount; i++) {
+    cs[i] = BufferConsumer::Import(p->CreateConsumer());
+    ASSERT_TRUE(cs[i].get() != nullptr);
+    EXPECT_TRUE(IsBufferGained(cs[i]->buffer_state()));
+  }
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // Post the producer should trigger all consumers to be available.
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  for (size_t i = 0; i < kMaxConsumerCount; i++) {
+    EXPECT_TRUE(IsBufferPosted(cs[i]->buffer_state(),
+                               cs[i]->buffer_state_bit()));
+    EXPECT_LT(0, RETRY_EINTR(cs[i]->Poll(10)));
+    EXPECT_EQ(0, cs[i]->AcquireAsync(&metadata, &invalid_fence));
+    EXPECT_TRUE(IsBufferAcquired(p->buffer_state()));
+  }
+
+  // All consumers have to release before the buffer is considered to be
+  // released.
+  for (size_t i = 0; i < kMaxConsumerCount; i++) {
+    EXPECT_FALSE(IsBufferReleased(p->buffer_state()));
+    EXPECT_EQ(0, cs[i]->ReleaseAsync(&metadata, invalid_fence));
+  }
+
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+
+  // Buffer state cross all clients must be consistent.
+  for (size_t i = 0; i < kMaxConsumerCount; i++) {
+    EXPECT_EQ(p->buffer_state(), cs[i]->buffer_state());
+  }
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferGained) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_TRUE(IsBufferGained(c->buffer_state()));
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // Post the gained buffer should signal already created consumer.
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+  EXPECT_LT(0, RETRY_EINTR(c->Poll(10)));
+  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferPosted) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // Post the gained buffer before any consumer gets created.
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_TRUE(IsBufferPosted(p->buffer_state()));
+
+  // Newly created consumer should be automatically sigalled.
+  std::unique_ptr<BufferConsumer> c =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c.get() != nullptr);
+  EXPECT_TRUE(IsBufferPosted(c->buffer_state()));
+  EXPECT_EQ(0, c->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_TRUE(IsBufferAcquired(c->buffer_state()));
+}
+
+TEST_F(LibBufferHubTest, TestCreateConsumerWhenBufferReleased) {
+  std::unique_ptr<BufferProducer> p = BufferProducer::Create(
+      kWidth, kHeight, kFormat, kUsage, sizeof(uint64_t));
+  ASSERT_TRUE(p.get() != nullptr);
+
+  std::unique_ptr<BufferConsumer> c1 =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c1.get() != nullptr);
+
+  DvrNativeBufferMetadata metadata;
+  LocalHandle invalid_fence;
+
+  // Post, acquire, and release the buffer..
+  EXPECT_EQ(0, p->PostAsync(&metadata, invalid_fence));
+  EXPECT_LT(0, RETRY_EINTR(c1->Poll(10)));
+  EXPECT_EQ(0, c1->AcquireAsync(&metadata, &invalid_fence));
+  EXPECT_EQ(0, c1->ReleaseAsync(&metadata, invalid_fence));
+
+  // Create another consumer immediately after the release, should not make the
+  // buffer un-released. This is guaranteed by IPC execution order in bufferhubd.
+  std::unique_ptr<BufferConsumer> c2 =
+      BufferConsumer::Import(p->CreateConsumer());
+  ASSERT_TRUE(c2.get() != nullptr);
+
+  EXPECT_LT(0, RETRY_EINTR(p->Poll(10)));
+  EXPECT_TRUE(IsBufferReleased(p->buffer_state()));
+  EXPECT_EQ(0, p->GainAsync(&metadata, &invalid_fence));
+  EXPECT_TRUE(IsBufferGained(p->buffer_state()));
+}
+
 TEST_F(LibBufferHubTest, TestWithCustomMetadata) {
   struct Metadata {
     int64_t field1;
diff --git a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
index 1186f93..a356959 100644
--- a/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
+++ b/libs/vr/libbufferhub/include/private/dvr/buffer_hub_client.h
@@ -94,6 +94,9 @@
 
   int id() const { return id_; }
 
+  // Returns the buffer buffer state.
+  uint64_t buffer_state() { return buffer_state_->load(); };
+
   // A state mask which is unique to a buffer hub client among all its siblings
   // sharing the same concrete graphic buffer.
   uint64_t buffer_state_bit() const { return buffer_state_bit_; }
diff --git a/libs/vr/libbufferhubqueue/tests/Android.bp b/libs/vr/libbufferhubqueue/tests/Android.bp
index 8bd1ef1..c4ffb41 100644
--- a/libs/vr/libbufferhubqueue/tests/Android.bp
+++ b/libs/vr/libbufferhubqueue/tests/Android.bp
@@ -12,6 +12,7 @@
     "libhardware",
     "libui",
     "libutils",
+    "libnativewindow",
 ]
 
 static_libraries = [
@@ -20,6 +21,7 @@
     "libchrome",
     "libdvrcommon",
     "libpdx_default_transport",
+    "libperformance",
 ]
 
 cc_test {
@@ -51,3 +53,17 @@
     name: "buffer_hub_queue_producer-test",
     tags: ["optional"],
 }
+
+cc_test {
+    srcs: ["buffer_transport_benchmark.cpp"],
+    static_libs: static_libraries,
+    shared_libs: shared_libraries,
+    header_libs: header_libraries,
+    cflags: [
+        "-DLOG_TAG=\"buffer_transport_benchmark\"",
+        "-DTRACE=0",
+        "-O2",
+    ],
+    name: "buffer_transport_benchmark",
+    tags: ["optional"],
+}
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
new file mode 100644
index 0000000..5b580df
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/tests/buffer_transport_benchmark.cpp
@@ -0,0 +1,619 @@
+#include <android/native_window.h>
+#include <base/logging.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <dvr/dvr_api.h>
+#include <dvr/performance_client_api.h>
+#include <gtest/gtest.h>
+#include <gui/BufferItem.h>
+#include <gui/BufferItemConsumer.h>
+#include <gui/Surface.h>
+#include <private/dvr/buffer_hub_queue_producer.h>
+#include <utils/Trace.h>
+
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+
+#include <poll.h>
+#include <sys/wait.h>
+#include <unistd.h>  // for pipe
+
+// Use ALWAYS at the tag level. Control is performed manually during command
+// line processing.
+#ifdef ATRACE_TAG
+#undef ATRACE_TAG
+#endif
+#define ATRACE_TAG ATRACE_TAG_ALWAYS
+
+using namespace android;
+using namespace android::dvr;
+
+static const String16 kBinderService = String16("bufferTransport");
+static const uint32_t kBufferWidth = 100;
+static const uint32_t kBufferHeight = 1;
+static const uint32_t kBufferLayerCount = 1;
+static const uint32_t kBufferFormat = HAL_PIXEL_FORMAT_BLOB;
+static const uint64_t kBufferUsage =
+    GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN;
+static const int kMaxAcquiredImages = 1;
+static const size_t kMaxQueueCounts = 128;
+
+static int gConcurrency = 1;  // 1 writer at a time
+static int gIterations = 1000;  // 1K times
+static int gSleepIntervalUs = 16 * 1000;  // 16ms
+
+enum BufferTransportServiceCode {
+  CREATE_BUFFER_QUEUE = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+// A mininal cross process helper class based on a bidirectional pipe pair. This
+// is used to signal that Binder-based BufferTransportService has finished
+// initialization.
+class Pipe {
+ public:
+  static std::tuple<Pipe, Pipe> CreatePipePair() {
+    int a[2] = {-1, -1};
+    int b[2] = {-1, -1};
+
+    pipe(a);
+    pipe(b);
+
+    return std::make_tuple(Pipe(a[0], b[1]), Pipe(b[0], a[1]));
+  }
+
+  Pipe() = default;
+
+  Pipe(Pipe&& other) {
+    read_fd_ = other.read_fd_;
+    write_fd_ = other.write_fd_;
+    other.read_fd_ = 0;
+    other.write_fd_ = 0;
+  }
+
+  Pipe& operator=(Pipe&& other) {
+    Reset();
+    read_fd_ = other.read_fd_;
+    write_fd_ = other.write_fd_;
+    other.read_fd_ = 0;
+    other.write_fd_ = 0;
+    return *this;
+  }
+
+  ~Pipe() { Reset(); }
+
+  Pipe(const Pipe&) = delete;
+  Pipe& operator=(const Pipe&) = delete;
+  Pipe& operator=(const Pipe&&) = delete;
+
+  bool IsValid() { return read_fd_ > 0 && write_fd_ > 0; }
+
+  void Signal() {
+    bool val = true;
+    int error = write(write_fd_, &val, sizeof(val));
+    ASSERT_GE(error, 0);
+  };
+
+  void Wait() {
+    bool val = false;
+    int error = read(read_fd_, &val, sizeof(val));
+    ASSERT_GE(error, 0);
+  }
+
+  void Reset() {
+    if (read_fd_)
+      close(read_fd_);
+    if (write_fd_)
+      close(write_fd_);
+  }
+
+ private:
+  int read_fd_ = -1;
+  int write_fd_ = -1;
+  Pipe(int read_fd, int write_fd) : read_fd_{read_fd}, write_fd_{write_fd} {}
+};
+
+// A binder services that minics a compositor that consumes buffers. It provides
+// one Binder interface to create a new Surface for buffer producer to write
+// into; while itself will carry out no-op buffer consuming by acquiring then
+// releasing the buffer immediately.
+class BufferTransportService : public BBinder {
+ public:
+  BufferTransportService() = default;
+  ~BufferTransportService() = default;
+
+  virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply,
+                              uint32_t flags = 0) {
+    (void)flags;
+    (void)data;
+    switch (code) {
+      case CREATE_BUFFER_QUEUE: {
+        auto new_queue = std::make_shared<BufferQueueHolder>(this);
+        reply->writeStrongBinder(
+            IGraphicBufferProducer::asBinder(new_queue->producer_));
+        buffer_queues_.push_back(new_queue);
+        return NO_ERROR;
+      }
+      default:
+        return UNKNOWN_TRANSACTION;
+    };
+  }
+
+ private:
+  struct FrameListener : public ConsumerBase::FrameAvailableListener {
+   public:
+    FrameListener(BufferTransportService* service,
+                  sp<BufferItemConsumer> buffer_item_consumer)
+        : service_(service),
+          buffer_item_consumer_(buffer_item_consumer) {}
+
+    void onFrameAvailable(const BufferItem& /*item*/) override {
+      std::unique_lock<std::mutex> autolock(service_->reader_mutex_);
+
+      BufferItem buffer;
+      status_t ret = 0;
+      {
+        ATRACE_NAME("AcquireBuffer");
+        ret = buffer_item_consumer_->acquireBuffer(&buffer, /*presentWhen=*/0,
+                                                   /*waitForFence=*/false);
+      }
+
+      if (ret != NO_ERROR) {
+        LOG(ERROR) << "Failed to acquire next buffer.";
+        return;
+      }
+
+      {
+        ATRACE_NAME("ReleaseBuffer");
+        ret = buffer_item_consumer_->releaseBuffer(buffer);
+      }
+
+      if (ret != NO_ERROR) {
+        LOG(ERROR) << "Failed to release buffer.";
+        return;
+      }
+    }
+
+   private:
+    BufferTransportService* service_ = nullptr;
+    sp<BufferItemConsumer> buffer_item_consumer_;
+  };
+
+  struct BufferQueueHolder {
+    explicit BufferQueueHolder(BufferTransportService* service) {
+      BufferQueue::createBufferQueue(&producer_, &consumer_);
+
+      sp<BufferItemConsumer> buffer_item_consumer =
+          new BufferItemConsumer(consumer_, kBufferUsage, kMaxAcquiredImages,
+                                 /*controlledByApp=*/true);
+      buffer_item_consumer->setName(String8("BinderBufferTransport"));
+      frame_listener_ = new FrameListener(service, buffer_item_consumer);
+      buffer_item_consumer->setFrameAvailableListener(frame_listener_);
+    }
+
+    sp<IGraphicBufferProducer> producer_;
+    sp<IGraphicBufferConsumer> consumer_;
+    sp<FrameListener> frame_listener_;
+  };
+
+  std::mutex reader_mutex_;
+  std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
+};
+
+// A virtual interfaces that abstracts the common BufferQueue operations, so
+// that the test suite can use the same test case to drive different types of
+// transport backends.
+class BufferTransport {
+ public:
+  virtual ~BufferTransport() {}
+
+  virtual int Start() = 0;
+  virtual sp<Surface> CreateSurface() = 0;
+};
+
+// Binder-based buffer transport backend.
+//
+// On Start() a new process will be swapned to run a Binder server that
+// actually consumes the buffer.
+// On CreateSurface() a new Binder BufferQueue will be created, which the
+// service holds the concrete binder node of the IGraphicBufferProducer while
+// sending the binder proxy to the client. In another word, the producer side
+// operations are carried out process while the consumer side operations are
+// carried out within the BufferTransportService's own process.
+class BinderBufferTransport : public BufferTransport {
+ public:
+  BinderBufferTransport() {}
+
+  ~BinderBufferTransport() {
+    if (client_pipe_.IsValid()) {
+      client_pipe_.Signal();
+      LOG(INFO) << "Client signals service to shut down.";
+    }
+  }
+
+  int Start() override {
+    // Fork a process to run a binder server. The parent process will return
+    // a pipe here, and we use the pipe to signal the binder server to exit.
+    client_pipe_ = CreateBinderServer();
+
+    // Wait until service is ready.
+    LOG(INFO) << "Service is ready for client.";
+    client_pipe_.Wait();
+    return 0;
+  }
+
+  sp<Surface> CreateSurface() override {
+    sp<IServiceManager> sm = defaultServiceManager();
+    service_ = sm->getService(kBinderService);
+    if (service_ == nullptr) {
+      LOG(ERROR) << "Failed to set the benchmark service.";
+      return nullptr;
+    }
+
+    Parcel data;
+    Parcel reply;
+    int error = service_->transact(CREATE_BUFFER_QUEUE, data, &reply);
+    if (error != NO_ERROR) {
+      LOG(ERROR) << "Failed to get buffer queue over binder.";
+      return nullptr;
+    }
+
+    sp<IBinder> binder;
+    error = reply.readNullableStrongBinder(&binder);
+    if (error != NO_ERROR) {
+      LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
+      return nullptr;
+    }
+
+    auto producer = interface_cast<IGraphicBufferProducer>(binder);
+    if (producer == nullptr) {
+      LOG(ERROR) << "Failed to get IGraphicBufferProducer over binder.";
+      return nullptr;
+    }
+
+    sp<Surface> surface = new Surface(producer, /*controlledByApp=*/true);
+
+    // Set buffer dimension.
+    ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
+    ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight,
+                                     kBufferFormat);
+
+    return surface;
+  }
+
+ private:
+  static Pipe CreateBinderServer() {
+    std::tuple<Pipe, Pipe> pipe_pair = Pipe::CreatePipePair();
+    pid_t pid = fork();
+    if (pid) {
+      // parent, i.e. the client side.
+      ProcessState::self()->startThreadPool();
+      LOG(INFO) << "Binder server pid: " << pid;
+      return std::move(std::get<0>(pipe_pair));
+    } else {
+      // child, i.e. the service side.
+      Pipe service_pipe = std::move(std::get<1>(pipe_pair));
+
+      ProcessState::self()->startThreadPool();
+      sp<IServiceManager> sm = defaultServiceManager();
+      sp<BufferTransportService> service = new BufferTransportService;
+      sm->addService(kBinderService, service, false);
+
+      LOG(INFO) << "Binder Service Running...";
+
+      service_pipe.Signal();
+      service_pipe.Wait();
+
+      LOG(INFO) << "Service Exiting...";
+      exit(EXIT_SUCCESS);
+
+      /* never get here */
+      return {};
+    }
+  }
+
+  sp<IBinder> service_;
+  Pipe client_pipe_;
+};
+
+// BufferHub/PDX-based buffer transport.
+//
+// On Start() a new thread will be swapned to run an epoll polling thread which
+// minics the behavior of a compositor. Similar to Binder-based backend, the
+// buffer available handler is also a no-op: Buffer gets acquired and released
+// immediately.
+// On CreateSurface() a pair of dvr::ProducerQueue and dvr::ConsumerQueue will
+// be created. The epoll thread holds on the consumer queue and dequeues buffer
+// from it; while the producer queue will be wrapped in a Surface and returned
+// to test suite.
+class BufferHubTransport : public BufferTransport {
+ public:
+  virtual ~BufferHubTransport() {
+    stopped_.store(true);
+    if (reader_thread_.joinable()) {
+      reader_thread_.join();
+    }
+  }
+
+  int Start() override {
+    int ret = epoll_fd_.Create();
+    if (ret < 0) {
+      LOG(ERROR) << "Failed to create epoll fd: %s", strerror(-ret);
+      return -1;
+    }
+
+    // Create the reader thread.
+    reader_thread_ = std::thread([this]() {
+      int ret = dvrSetSchedulerClass(0, "graphics");
+      if (ret < 0) {
+        LOG(ERROR) << "Failed to set thread priority";
+        return;
+      }
+
+
+      ret = dvrSetCpuPartition(0, "/system/performance");
+      if (ret < 0) {
+        LOG(ERROR) << "Failed to set thread cpu partition";
+        return;
+      }
+
+      stopped_.store(false);
+      LOG(INFO) << "Reader Thread Running...";
+
+      while (!stopped_.load()) {
+        std::array<epoll_event, kMaxQueueCounts> events;
+
+        // Don't sleep forever so that we will have a chance to wake up.
+        const int ret = epoll_fd_.Wait(events.data(), events.size(),
+                                       /*timeout=*/100);
+        if (ret < 0) {
+          LOG(ERROR) << "Error polling consumer queues.";
+          continue;
+        }
+        if (ret == 0) {
+          continue;
+        }
+
+        const int num_events = ret;
+        for (int i = 0; i < num_events; i++) {
+          uint32_t surface_index = events[i].data.u32;
+          // LOG(INFO) << "!!! handle queue events index: " << surface_index;
+          buffer_queues_[surface_index]->consumer_queue_->HandleQueueEvents();
+        }
+      }
+
+      LOG(INFO) << "Reader Thread Exiting...";
+    });
+
+    return 0;
+  }
+
+  sp<Surface> CreateSurface() override {
+    std::lock_guard<std::mutex> autolock(queue_mutex_);
+
+    auto new_queue = std::make_shared<BufferQueueHolder>();
+    if (new_queue->producer_ == nullptr) {
+      LOG(ERROR) << "Failed to create buffer producer.";
+      return nullptr;
+    }
+
+    sp<Surface> surface =
+        new Surface(new_queue->producer_, /*controlledByApp=*/true);
+
+    // Set buffer dimension.
+    ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
+    ANativeWindow_setBuffersGeometry(window, kBufferWidth, kBufferHeight,
+                                     kBufferFormat);
+
+    // Use the next position as buffer_queue index.
+    uint32_t index = buffer_queues_.size();
+    epoll_event event = {.events = EPOLLIN | EPOLLET, .data = {.u32 = index}};
+    const int ret = epoll_fd_.Control(
+        EPOLL_CTL_ADD, new_queue->consumer_queue_->queue_fd(), &event);
+    if (ret < 0) {
+      LOG(ERROR) << "Failed to track consumer queue: " << strerror(-ret)
+                 << ", consumer queue fd: "
+                 << new_queue->consumer_queue_->queue_fd();
+      return nullptr;
+    }
+
+    new_queue->queue_index_ = index;
+    buffer_queues_.push_back(new_queue);
+    return surface;
+  }
+
+ private:
+  struct BufferQueueHolder {
+    BufferQueueHolder() {
+      ProducerQueueConfigBuilder config_builder;
+      producer_queue_ =
+          ProducerQueue::Create(config_builder.SetDefaultWidth(kBufferWidth)
+                                    .SetDefaultHeight(kBufferHeight)
+                                    .SetDefaultFormat(kBufferFormat)
+                                    .SetMetadata<DvrNativeBufferMetadata>()
+                                    .Build(),
+                                UsagePolicy{});
+      consumer_queue_ = producer_queue_->CreateConsumerQueue();
+      consumer_queue_->SetBufferAvailableCallback([this]() {
+        size_t index = 0;
+        pdx::LocalHandle fence;
+        DvrNativeBufferMetadata meta;
+        pdx::Status<std::shared_ptr<BufferConsumer>> status;
+
+        {
+          ATRACE_NAME("AcquireBuffer");
+          status = consumer_queue_->Dequeue(0, &index, &meta, &fence);
+        }
+        if (!status.ok()) {
+          LOG(ERROR) << "Failed to dequeue consumer buffer, error: "
+                     << status.GetErrorMessage().c_str();
+          return;
+        }
+
+        auto buffer = status.take();
+
+        if (buffer) {
+          ATRACE_NAME("ReleaseBuffer");
+          buffer->ReleaseAsync();
+        }
+      });
+
+      producer_ = BufferHubQueueProducer::Create(producer_queue_);
+    }
+
+    int count_ = 0;
+    int queue_index_;
+    std::shared_ptr<ProducerQueue> producer_queue_;
+    std::shared_ptr<ConsumerQueue> consumer_queue_;
+    sp<IGraphicBufferProducer> producer_;
+  };
+
+  std::atomic<bool> stopped_;
+  std::thread reader_thread_;
+
+  // Mutex to guard epoll_fd_ and buffer_queues_.
+  std::mutex queue_mutex_;
+  EpollFileDescriptor epoll_fd_;
+  std::vector<std::shared_ptr<BufferQueueHolder>> buffer_queues_;
+};
+
+enum TransportType {
+  kBinderBufferTransport,
+  kBufferHubTransport,
+};
+
+// Main test suite, which supports two transport backend: 1) BinderBufferQueue,
+// 2) BufferHubQueue. The test case drives the producer end of both transport
+// backend by queuing buffers into the buffer queue by using ANativeWindow API.
+class BufferTransportBenchmark
+    : public ::testing::TestWithParam<TransportType> {
+ public:
+  void SetUp() override {
+    switch (GetParam()) {
+      case kBinderBufferTransport:
+        transport_.reset(new BinderBufferTransport);
+        break;
+      case kBufferHubTransport:
+        transport_.reset(new BufferHubTransport);
+        break;
+      default:
+        FAIL() << "Unknown test case.";
+        break;
+    }
+  }
+
+ protected:
+  void ProduceBuffers(sp<Surface> surface, int iterations, int sleep_usec) {
+    ANativeWindow* window = static_cast<ANativeWindow*>(surface.get());
+    ANativeWindow_Buffer buffer;
+    int32_t error = 0;
+
+    for (int i = 0; i < iterations; i++) {
+      usleep(sleep_usec);
+
+      {
+        ATRACE_NAME("GainBuffer");
+        error = ANativeWindow_lock(window, &buffer,
+                                   /*inOutDirtyBounds=*/nullptr);
+      }
+      ASSERT_EQ(error, 0);
+
+      {
+        ATRACE_NAME("PostBuffer");
+        error = ANativeWindow_unlockAndPost(window);
+      }
+      ASSERT_EQ(error, 0);
+    }
+  }
+
+  std::unique_ptr<BufferTransport> transport_;
+};
+
+TEST_P(BufferTransportBenchmark, ContinuousLoad) {
+  ASSERT_NE(transport_, nullptr);
+  const int ret = transport_->Start();
+  ASSERT_EQ(ret, 0);
+
+  LOG(INFO) << "Start Running.";
+
+  std::vector<std::thread> writer_threads;
+  for (int i = 0; i < gConcurrency; i++) {
+    std::thread writer_thread = std::thread([this]() {
+      sp<Surface> surface = transport_->CreateSurface();
+      ASSERT_NE(surface, nullptr);
+
+      ASSERT_NO_FATAL_FAILURE(
+          ProduceBuffers(surface, gIterations, gSleepIntervalUs));
+
+      usleep(1000 * 100);
+    });
+
+    writer_threads.push_back(std::move(writer_thread));
+  }
+
+  for (auto& writer_thread : writer_threads) {
+    writer_thread.join();
+  }
+
+  LOG(INFO) << "All done.";
+};
+
+INSTANTIATE_TEST_CASE_P(BufferTransportBenchmarkInstance,
+                        BufferTransportBenchmark,
+                        ::testing::ValuesIn({kBinderBufferTransport,
+                                             kBufferHubTransport}));
+
+// To run binder-based benchmark, use:
+// adb shell buffer_transport_benchmark \
+//   --gtest_filter="BufferTransportBenchmark.ContinuousLoad/0"
+//
+// To run bufferhub-based benchmark, use:
+// adb shell buffer_transport_benchmark \
+//   --gtest_filter="BufferTransportBenchmark.ContinuousLoad/1"
+int main(int argc, char** argv) {
+  bool tracing_enabled = false;
+
+  // Parse arguments in addition to "--gtest_filter" paramters.
+  for (int i = 1; i < argc; i++) {
+    if (std::string(argv[i]) == "--help") {
+      std::cout << "Usage: binderThroughputTest [OPTIONS]" << std::endl;
+      std::cout << "\t-c N: Specify number of concurrent writer threads, "
+                   "(default: 1, max: 128)."
+                << std::endl;
+      std::cout << "\t-i N: Specify number of iterations, (default: 1000)."
+                << std::endl;
+      std::cout << "\t-s N: Specify sleep interval in usec, (default: 16000)."
+                << std::endl;
+      std::cout << "\t--trace: Enable systrace logging."
+                << std::endl;
+      return 0;
+    }
+    if (std::string(argv[i]) == "-c") {
+      gConcurrency = atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (std::string(argv[i]) == "-s") {
+      gSleepIntervalUs = atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (std::string(argv[i]) == "-i") {
+      gIterations = atoi(argv[i + 1]);
+      i++;
+      continue;
+    }
+    if (std::string(argv[i]) == "--trace") {
+      tracing_enabled = true;
+      continue;
+    }
+  }
+
+  // Setup ATRACE/systrace based on command line.
+  atrace_setup();
+  atrace_set_tracing_enabled(tracing_enabled);
+
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 64a2a50..bd7f0ea 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -5,4 +5,4 @@
     export_static_lib_headers = ["libserviceutils"],
 }
 
-subdirs = ["tests/fakehwc"]
\ No newline at end of file
+subdirs = ["tests/fakehwc", "layerproto"]
\ No newline at end of file
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 390263f..d8152e0 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -39,6 +39,7 @@
     RenderEngine/RenderEngine.cpp \
     RenderEngine/Texture.cpp \
     RenderEngine/GLES20RenderEngine.cpp \
+    LayerProtoHelper.cpp \
 
 LOCAL_MODULE := libsurfaceflinger
 LOCAL_C_INCLUDES := \
@@ -98,7 +99,8 @@
     libsync \
     libprotobuf-cpp-lite \
     libbase \
-    android.hardware.power@1.0
+    android.hardware.power@1.0 \
+    liblayers_proto
 
 LOCAL_EXPORT_SHARED_LIBRARY_HEADERS := \
     android.hardware.graphics.allocator@2.0 \
@@ -145,7 +147,8 @@
     libutils \
     libui \
     libgui \
-    libdl
+    libdl \
+    liblayers_proto
 
 LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
 LOCAL_STATIC_LIBRARIES := libtrace_proto \
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index 7d6d988..c707e3c 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -223,6 +223,10 @@
     mWriter.reset();
 }
 
+Error Composer::executeCommands() {
+    return execute();
+}
+
 uint32_t Composer::getMaxVirtualDisplayCount()
 {
     auto ret = mClient->getMaxVirtualDisplayCount();
@@ -750,6 +754,11 @@
         }
     }
 
+    if (commandLength == 0) {
+        mWriter.reset();
+        return Error::NONE;
+    }
+
     Error error = kDefaultError;
     auto ret = mClient->executeCommands(commandLength, commandHandles,
             [&](const auto& tmpError, const auto& tmpOutChanged,
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 31a3c1d..104ca60 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -152,6 +152,9 @@
     // skip a frame but have already queued some commands.
     void resetCommands();
 
+    // Explicitly flush all pending commands in the command buffer.
+    Error executeCommands();
+
     uint32_t getMaxVirtualDisplayCount();
     bool isUsingVrComposer() const { return mIsUsingVrComposer; }
     Error createVirtualDisplay(uint32_t width, uint32_t height,
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 78c0c85..ab4a4b2 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -221,6 +221,11 @@
     }
 }
 
+Error Device::flushCommands()
+{
+    return static_cast<Error>(mComposer->executeCommands());
+}
+
 // Display methods
 
 Display::Display(android::Hwc2::Composer& composer,
@@ -632,11 +637,6 @@
     return error;
 }
 
-void Display::discardCommands()
-{
-    mComposer.resetCommands();
-}
-
 // For use by Device
 
 void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index fbe4c7e..a15c6d9 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -106,6 +106,11 @@
 
     android::Hwc2::Composer* getComposer() { return mComposer.get(); }
 
+    // We buffer most state changes and flush them implicitly with
+    // Display::validate, Display::present, and Display::presentOrValidate.
+    // This method provides an explicit way to flush state changes to HWC.
+    Error flushCommands();
+
 private:
     // Initialization methods
 
@@ -244,12 +249,6 @@
                                                  uint32_t* outNumRequests,
                                                           android::sp<android::Fence>* outPresentFence, uint32_t* state);
 
-    // Most methods in this class write a command to a command buffer.  The
-    // command buffer is implicitly submitted in validate, present, and
-    // presentOrValidate.  This method provides a way to discard the commands,
-    // which can be used to discard stale commands.
-    void discardCommands();
-
     // Other Display methods
 
     hwc2_display_t getId() const { return mId; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index b096a3a..5328a22 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -605,8 +605,11 @@
     auto& hwcDisplay = displayData.hwcDisplay;
 
     if (displayData.validateWasSkipped) {
-        hwcDisplay->discardCommands();
-        auto error = displayData.presentError;
+        // explicitly flush all pending commands
+        auto error = mHwcDevice->flushCommands();
+        if (displayData.presentError != HWC2::Error::None) {
+            error = displayData.presentError;
+        }
         if (error != HWC2::Error::None) {
             ALOGE("skipValidate: failed for display %d: %s (%d)",
                   displayId, to_string(error).c_str(), static_cast<int32_t>(error));
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 956f7f6..27739ce 100755
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -56,6 +56,7 @@
 #include "RenderEngine/RenderEngine.h"
 
 #include <mutex>
+#include "LayerProtoHelper.h"
 
 #define DEBUG_RESIZE    0
 
@@ -2826,6 +2827,82 @@
     mDrawingParent = mCurrentParent;
 }
 
+void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) {
+    const bool useDrawing = stateSet == LayerVector::StateSet::Drawing;
+    const LayerVector& children = useDrawing ? mDrawingChildren : mCurrentChildren;
+    const State& state = useDrawing ? mDrawingState : mCurrentState;
+
+    Transform requestedTransform = state.active.transform;
+    Transform transform = getTransform();
+
+    layerInfo->set_id(sequence);
+    layerInfo->set_name(getName().c_str());
+    layerInfo->set_type(String8(getTypeId()));
+
+    for (const auto& child : children) {
+        layerInfo->add_children(child->sequence);
+    }
+
+    for (const wp<Layer>& weakRelative : state.zOrderRelatives) {
+        sp<Layer> strongRelative = weakRelative.promote();
+        if (strongRelative != nullptr) {
+            layerInfo->add_relatives(strongRelative->sequence);
+        }
+    }
+
+    LayerProtoHelper::writeToProto(state.activeTransparentRegion,
+                                   layerInfo->mutable_transparent_region());
+    LayerProtoHelper::writeToProto(visibleRegion, layerInfo->mutable_visible_region());
+    LayerProtoHelper::writeToProto(surfaceDamageRegion, layerInfo->mutable_damage_region());
+
+    layerInfo->set_layer_stack(getLayerStack());
+    layerInfo->set_z(state.z);
+
+    PositionProto* position = layerInfo->mutable_position();
+    position->set_x(transform.tx());
+    position->set_y(transform.ty());
+
+    PositionProto* requestedPosition = layerInfo->mutable_requested_position();
+    requestedPosition->set_x(requestedTransform.tx());
+    requestedPosition->set_y(requestedTransform.ty());
+
+    SizeProto* size = layerInfo->mutable_size();
+    size->set_w(state.active.w);
+    size->set_h(state.active.h);
+
+    LayerProtoHelper::writeToProto(state.crop, layerInfo->mutable_crop());
+    LayerProtoHelper::writeToProto(state.finalCrop, layerInfo->mutable_final_crop());
+
+    layerInfo->set_is_opaque(isOpaque(state));
+    layerInfo->set_invalidate(contentDirty);
+    layerInfo->set_dataspace(dataspaceDetails(getDataSpace()));
+    layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat()));
+    LayerProtoHelper::writeToProto(getColor(), layerInfo->mutable_color());
+    LayerProtoHelper::writeToProto(state.color, layerInfo->mutable_requested_color());
+    layerInfo->set_flags(state.flags);
+
+    LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform());
+    LayerProtoHelper::writeToProto(requestedTransform, layerInfo->mutable_requested_transform());
+
+    auto parent = getParent();
+    if (parent != nullptr) {
+        layerInfo->set_parent(parent->sequence);
+    }
+
+    auto zOrderRelativeOf = state.zOrderRelativeOf.promote();
+    if (zOrderRelativeOf != nullptr) {
+        layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence);
+    }
+
+    auto activeBuffer = getActiveBuffer();
+    if (activeBuffer != nullptr) {
+        LayerProtoHelper::writeToProto(activeBuffer, layerInfo->mutable_active_buffer());
+    }
+
+    layerInfo->set_queued_frames(getQueuedFrameCount());
+    layerInfo->set_refresh_pending(isBufferLatched());
+}
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 47924ae..06c4863 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -49,9 +49,12 @@
 #include "DisplayHardware/HWComposerBufferCache.h"
 #include "RenderEngine/Mesh.h"
 #include "RenderEngine/Texture.h"
+#include <layerproto/LayerProtoHeader.h>
 
 #include <math/vec4.h>
 
+using namespace android::surfaceflinger;
+
 namespace android {
 
 // ---------------------------------------------------------------------------
@@ -303,6 +306,8 @@
      */
     virtual bool isFixedSize() const;
 
+    void writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet = LayerVector::StateSet::Drawing);
+
 protected:
     /*
      * onDraw - draws the surface.
diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp
new file mode 100644
index 0000000..6a33148
--- /dev/null
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "LayerProtoHelper.h"
+
+namespace android {
+namespace surfaceflinger {
+void LayerProtoHelper::writeToProto(const Region& region, RegionProto* regionProto) {
+    Region::const_iterator head = region.begin();
+    Region::const_iterator const tail = region.end();
+    uint64_t address = reinterpret_cast<uint64_t>(&region);
+    regionProto->set_id(address);
+    while (head != tail) {
+        RectProto* rectProto = regionProto->add_rect();
+        writeToProto(*head, rectProto);
+        head++;
+    }
+}
+
+void LayerProtoHelper::writeToProto(const Rect& rect, RectProto* rectProto) {
+    rectProto->set_left(rect.left);
+    rectProto->set_top(rect.top);
+    rectProto->set_bottom(rect.bottom);
+    rectProto->set_right(rect.right);
+}
+
+void LayerProtoHelper::writeToProto(const half4 color, ColorProto* colorProto) {
+    colorProto->set_r(color.r);
+    colorProto->set_g(color.g);
+    colorProto->set_b(color.b);
+    colorProto->set_a(color.a);
+}
+
+void LayerProtoHelper::writeToProto(const Transform& transform, TransformProto* transformProto) {
+    transformProto->set_dsdx(transform[0][0]);
+    transformProto->set_dtdx(transform[0][1]);
+    transformProto->set_dsdy(transform[1][0]);
+    transformProto->set_dtdy(transform[1][1]);
+}
+
+void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer,
+                                    ActiveBufferProto* activeBufferProto) {
+    activeBufferProto->set_width(buffer->getWidth());
+    activeBufferProto->set_height(buffer->getHeight());
+    activeBufferProto->set_stride(buffer->getStride());
+    activeBufferProto->set_format(buffer->format);
+}
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h
new file mode 100644
index 0000000..45a0b5d
--- /dev/null
+++ b/services/surfaceflinger/LayerProtoHelper.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <layerproto/LayerProtoHeader.h>
+
+#include <ui/GraphicBuffer.h>
+#include <ui/Rect.h>
+#include <ui/Region.h>
+
+#include <Transform.h>
+
+#include <math/vec4.h>
+
+namespace android {
+namespace surfaceflinger {
+class LayerProtoHelper {
+public:
+    static void writeToProto(const Rect& rect, RectProto* rectProto);
+    static void writeToProto(const Region& region, RegionProto* regionProto);
+    static void writeToProto(const half4 color, ColorProto* colorProto);
+    static void writeToProto(const Transform& transform, TransformProto* transformProto);
+    static void writeToProto(const sp<GraphicBuffer>& buffer, ActiveBufferProto* activeBufferProto);
+};
+
+} // namespace surfaceflinger
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 5982422..54c040d 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -90,6 +90,8 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
 
+#include <layerproto/LayerProtoParser.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -682,6 +684,12 @@
     property_get("persist.sys.sf.color_saturation", value, "1.0");
     mSaturation = atof(value);
     ALOGV("Saturation is set to %.2f", mSaturation);
+
+    property_get("persist.sys.sf.native_mode", value, "0");
+    mForceNativeColorMode = atoi(value) == 1;
+    if (mForceNativeColorMode) {
+        ALOGV("Forcing native color mode");
+    }
 }
 
 void SurfaceFlinger::startBootAnim() {
@@ -1288,12 +1296,13 @@
                 break;
         }
     }
+    bool useWideColorMode = hasWideColorModes && hasWideColorDisplay && !mForceNativeColorMode;
     sp<DisplayDevice> hw = new DisplayDevice(this, DisplayDevice::DISPLAY_PRIMARY, type, isSecure,
                                              token, fbs, producer, mRenderEngine->getEGLConfig(),
-                                             hasWideColorModes && hasWideColorDisplay);
+                                             useWideColorMode);
     mDisplays.add(token, hw);
     android_color_mode defaultColorMode = HAL_COLOR_MODE_NATIVE;
-    if (hasWideColorModes && hasWideColorDisplay) {
+    if (useWideColorMode) {
         defaultColorMode = HAL_COLOR_MODE_SRGB;
     }
     setActiveColorModeInternal(hw, defaultColorMode);
@@ -1810,6 +1819,10 @@
 // pickColorMode translates a given dataspace into the best available color mode.
 // Currently only support sRGB and Display-P3.
 android_color_mode SurfaceFlinger::pickColorMode(android_dataspace dataSpace) const {
+    if (mForceNativeColorMode) {
+        return HAL_COLOR_MODE_NATIVE;
+    }
+
     switch (dataSpace) {
         // treat Unknown as regular SRGB buffer, since that's what the rest of the
         // system expects.
@@ -2644,8 +2657,10 @@
         ALOGV("hasClientComposition");
 
 #ifdef USE_HWC2
-        mRenderEngine->setWideColor(displayDevice->getWideColorSupport());
-        mRenderEngine->setColorMode(displayDevice->getActiveColorMode());
+        mRenderEngine->setWideColor(
+                displayDevice->getWideColorSupport() && !mForceNativeColorMode);
+        mRenderEngine->setColorMode(mForceNativeColorMode ?
+                HAL_COLOR_MODE_NATIVE : displayDevice->getActiveColorMode());
 #endif
         if (!displayDevice->makeCurrent(mEGLDisplay, mEGLContext)) {
             ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
@@ -3532,6 +3547,13 @@
                 dumpWideColorInfo(result);
                 dumpAll = false;
             }
+
+            if ((index < numArgs) && (args[index] == String16("--proto"))) {
+                index++;
+                LayersProto layersProto = dumpProtoInfo();
+                result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
+                dumpAll = false;
+            }
         }
 
         if (dumpAll) {
@@ -3704,6 +3726,7 @@
 
 void SurfaceFlinger::dumpWideColorInfo(String8& result) const {
     result.appendFormat("hasWideColorDisplay: %d\n", hasWideColorDisplay);
+    result.appendFormat("forceNativeColorMode: %d\n", mForceNativeColorMode);
 
     // TODO: print out if wide-color mode is active or not
 
@@ -3727,6 +3750,16 @@
     result.append("\n");
 }
 
+LayersProto SurfaceFlinger::dumpProtoInfo() const {
+    LayersProto layersProto;
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
+        LayerProto* layerProto = layersProto.add_layers();
+        layer->writeToProto(layerProto, LayerVector::StateSet::Current);
+    });
+
+    return layersProto;
+}
+
 void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
         String8& result) const
 {
@@ -3791,9 +3824,10 @@
     colorizer.bold(result);
     result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        result.append(to_string(layer->getLayerDebugInfo()).c_str());
-    });
+
+    LayersProto layersProto = dumpProtoInfo();
+    auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+    result.append(LayerProtoParser::layersToString(layerTree).c_str());
 
     /*
      * Dump Display state
@@ -4157,6 +4191,17 @@
                 repaintEverything();
                 return NO_ERROR;
             }
+            case 1023: { // Set native mode
+                mForceNativeColorMode = data.readInt32() == 1;
+
+                invalidateHwcGeometry();
+                repaintEverything();
+                return NO_ERROR;
+            }
+            case 1024: { // Is wide color gamut rendering/color management supported?
+                reply->writeBool(hasWideColorDisplay);
+                return NO_ERROR;
+            }
         }
     }
     return err;
@@ -4314,8 +4359,9 @@
     WindowDisconnector disconnector(window, NATIVE_WINDOW_API_EGL);
 
     ANativeWindowBuffer* buffer = nullptr;
-    result = getWindowBuffer(window, reqWidth, reqHeight, hasWideColorDisplay,
-                                      getRenderEngine().usesWideColor(), &buffer);
+    result = getWindowBuffer(window, reqWidth, reqHeight,
+            hasWideColorDisplay && !mForceNativeColorMode,
+            getRenderEngine().usesWideColor(), &buffer);
     if (result != NO_ERROR) {
         return result;
     }
@@ -4417,8 +4463,8 @@
     }
 
 #ifdef USE_HWC2
-     engine.setWideColor(hw->getWideColorSupport());
-     engine.setColorMode(hw->getActiveColorMode());
+     engine.setWideColor(hw->getWideColorSupport() && !mForceNativeColorMode);
+     engine.setColorMode(mForceNativeColorMode ? HAL_COLOR_MODE_NATIVE : hw->getActiveColorMode());
 #endif
 
     // make sure to clear all GL error flags
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 2cba500..bd98c8f 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -78,6 +78,10 @@
 #include <thread>
 #include <utility>
 
+#include <layerproto/LayerProtoHeader.h>
+
+using namespace android::surfaceflinger;
+
 namespace android {
 
 // ---------------------------------------------------------------------------
@@ -622,6 +626,7 @@
             std::vector<OccupancyTracker::Segment>&& history);
     void dumpBufferingStats(String8& result) const;
     void dumpWideColorInfo(String8& result) const;
+    LayersProto dumpProtoInfo() const;
 
     bool isLayerTripleBufferingDisabled() const {
         return this->mLayerTripleBufferingDisabled;
@@ -830,6 +835,7 @@
 #endif
 
     float mSaturation = 1.0f;
+    bool mForceNativeColorMode = false;
 };
 }; // namespace android
 
diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
index b718ec8..ed7641f 100644
--- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
+++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp
@@ -87,6 +87,8 @@
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <configstore/Utils.h>
 
+#include <layerproto/LayerProtoParser.h>
+
 #define DISPLAY_COUNT       1
 
 /*
@@ -3073,6 +3075,13 @@
                 dumpFrameEventsLocked(result);
                 dumpAll = false;
             }
+
+            if ((index < numArgs) && (args[index] == String16("--proto"))) {
+                index++;
+                LayersProto layersProto = dumpProtoInfo();
+                result.append(layersProto.SerializeAsString().c_str(), layersProto.ByteSize());
+                dumpAll = false;
+            }
         }
 
         if (dumpAll) {
@@ -3243,6 +3252,16 @@
     result.append("\n");
 }
 
+LayersProto SurfaceFlinger::dumpProtoInfo() const {
+    LayersProto layersProto;
+    mCurrentState.traverseInZOrder([&](Layer* layer) {
+        LayerProto* layerProto = layersProto.add_layers();
+        layer->writeToProto(layerProto, LayerVector::StateSet::Current);
+    });
+
+    return layersProto;
+}
+
 void SurfaceFlinger::dumpAllLocked(const Vector<String16>& args, size_t& index,
         String8& result) const
 {
@@ -3302,9 +3321,10 @@
     colorizer.bold(result);
     result.appendFormat("Visible layers (count = %zu)\n", mNumLayers);
     colorizer.reset(result);
-    mCurrentState.traverseInZOrder([&](Layer* layer) {
-        result.append(to_string(layer->getLayerDebugInfo()).c_str());
-    });
+
+    LayersProto layersProto = dumpProtoInfo();
+    auto layerTree = LayerProtoParser::generateLayerTree(layersProto);
+    result.append(LayerProtoParser::layersToString(layerTree).c_str());
 
     /*
      * Dump Display state
diff --git a/services/surfaceflinger/layerproto/Android.bp b/services/surfaceflinger/layerproto/Android.bp
new file mode 100644
index 0000000..4c52bdf
--- /dev/null
+++ b/services/surfaceflinger/layerproto/Android.bp
@@ -0,0 +1,35 @@
+cc_library_shared {
+    name: "liblayers_proto",
+    vendor_available: true,
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "LayerProtoParser.cpp",
+        "layers.proto",
+    ],
+
+    shared_libs: [
+        "libui",
+        "libprotobuf-cpp-lite",
+        "libbase",
+    ],
+
+    proto: {
+        export_proto_headers: true,
+    },
+
+    cppflags: [
+        "-Werror",
+        "-Wno-unused-parameter",
+        "-Wno-format",
+        "-Wno-c++98-compat-pedantic",
+        "-Wno-float-conversion",
+        "-Wno-disabled-macro-expansion",
+        "-Wno-float-equal",
+        "-Wno-sign-conversion",
+        "-Wno-padded",
+        "-Wno-old-style-cast",
+        "-Wno-undef",
+    ],
+
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
new file mode 100644
index 0000000..6554167
--- /dev/null
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <layerproto/LayerProtoParser.h>
+
+namespace android {
+namespace surfaceflinger {
+bool sortLayers(const LayerProtoParser::Layer* lhs, const LayerProtoParser::Layer* rhs) {
+    uint32_t ls = lhs->layerStack;
+    uint32_t rs = rhs->layerStack;
+    if (ls != rs) return ls < rs;
+
+    uint32_t lz = lhs->z;
+    uint32_t rz = rhs->z;
+    if (lz != rz) return lz < rz;
+
+    return lhs->id < rhs->id;
+}
+
+std::vector<const LayerProtoParser::Layer*> LayerProtoParser::generateLayerTree(
+        const LayersProto& layersProto) {
+    auto layerMap = generateMap(layersProto);
+
+    std::vector<const Layer*> layers;
+    std::for_each(layerMap.begin(), layerMap.end(),
+                  [&](const std::pair<const int32_t, Layer*>& ref) {
+                      if (ref.second->parent == nullptr) {
+                          // only save top level layers
+                          layers.push_back(ref.second);
+                      }
+                  });
+
+    std::sort(layers.begin(), layers.end(), sortLayers);
+    return layers;
+}
+
+std::unordered_map<int32_t, LayerProtoParser::Layer*> LayerProtoParser::generateMap(
+        const LayersProto& layersProto) {
+    std::unordered_map<int32_t, Layer*> layerMap;
+
+    for (int i = 0; i < layersProto.layers_size(); i++) {
+        const LayerProto& layerProto = layersProto.layers(i);
+        layerMap[layerProto.id()] = generateLayer(layerProto);
+    }
+
+    for (int i = 0; i < layersProto.layers_size(); i++) {
+        const LayerProto& layerProto = layersProto.layers(i);
+        updateChildrenAndRelative(layerProto, layerMap);
+    }
+
+    return layerMap;
+}
+
+LayerProtoParser::Layer* LayerProtoParser::generateLayer(const LayerProto& layerProto) {
+    Layer* layer = new Layer();
+    layer->id = layerProto.id();
+    layer->name = layerProto.name();
+    layer->type = layerProto.type();
+    layer->transparentRegion = generateRegion(layerProto.transparent_region());
+    layer->visibleRegion = generateRegion(layerProto.visible_region());
+    layer->damageRegion = generateRegion(layerProto.damage_region());
+    layer->layerStack = layerProto.layer_stack();
+    layer->z = layerProto.z();
+    layer->position = {layerProto.position().x(), layerProto.position().y()};
+    layer->requestedPosition = {layerProto.requested_position().x(),
+                                layerProto.requested_position().y()};
+    layer->size = {layerProto.size().w(), layerProto.size().h()};
+    layer->crop = generateRect(layerProto.crop());
+    layer->finalCrop = generateRect(layerProto.final_crop());
+    layer->isOpaque = layerProto.is_opaque();
+    layer->invalidate = layerProto.invalidate();
+    layer->dataspace = layerProto.dataspace();
+    layer->pixelFormat = layerProto.pixel_format();
+    layer->color = {layerProto.color().r(), layerProto.color().g(), layerProto.color().b(),
+                    layerProto.color().a()};
+    layer->requestedColor = {layerProto.requested_color().r(), layerProto.requested_color().g(),
+                             layerProto.requested_color().b(), layerProto.requested_color().a()};
+    layer->flags = layerProto.flags();
+    layer->transform = generateTransform(layerProto.transform());
+    layer->requestedTransform = generateTransform(layerProto.requested_transform());
+    layer->activeBuffer = generateActiveBuffer(layerProto.active_buffer());
+    layer->queuedFrames = layerProto.queued_frames();
+    layer->refreshPending = layerProto.refresh_pending();
+
+    return layer;
+}
+
+LayerProtoParser::Region LayerProtoParser::generateRegion(const RegionProto& regionProto) {
+    LayerProtoParser::Region region;
+    region.id = regionProto.id();
+    for (int i = 0; i < regionProto.rect_size(); i++) {
+        const RectProto& rectProto = regionProto.rect(i);
+        region.rects.push_back(generateRect(rectProto));
+    }
+
+    return region;
+}
+
+LayerProtoParser::Rect LayerProtoParser::generateRect(const RectProto& rectProto) {
+    LayerProtoParser::Rect rect;
+    rect.left = rectProto.left();
+    rect.top = rectProto.top();
+    rect.right = rectProto.right();
+    rect.bottom = rectProto.bottom();
+
+    return rect;
+}
+
+LayerProtoParser::Transform LayerProtoParser::generateTransform(
+        const TransformProto& transformProto) {
+    LayerProtoParser::Transform transform;
+    transform.dsdx = transformProto.dsdx();
+    transform.dtdx = transformProto.dtdx();
+    transform.dsdy = transformProto.dsdy();
+    transform.dtdy = transformProto.dtdy();
+
+    return transform;
+}
+
+LayerProtoParser::ActiveBuffer LayerProtoParser::generateActiveBuffer(
+        const ActiveBufferProto& activeBufferProto) {
+    LayerProtoParser::ActiveBuffer activeBuffer;
+    activeBuffer.width = activeBufferProto.width();
+    activeBuffer.height = activeBufferProto.height();
+    activeBuffer.stride = activeBufferProto.stride();
+    activeBuffer.format = activeBufferProto.format();
+
+    return activeBuffer;
+}
+
+void LayerProtoParser::updateChildrenAndRelative(const LayerProto& layerProto,
+                                                 std::unordered_map<int32_t, Layer*>& layerMap) {
+    auto currLayer = layerMap[layerProto.id()];
+
+    for (int i = 0; i < layerProto.children_size(); i++) {
+        if (layerMap.count(layerProto.children(i)) > 0) {
+            auto childLayer = layerMap[layerProto.children(i)];
+            currLayer->children.push_back(childLayer);
+        }
+    }
+
+    for (int i = 0; i < layerProto.relatives_size(); i++) {
+        if (layerMap.count(layerProto.relatives(i)) > 0) {
+            auto relativeLayer = layerMap[layerProto.relatives(i)];
+            currLayer->relatives.push_back(relativeLayer);
+        }
+    }
+
+    if (layerProto.has_parent()) {
+        if (layerMap.count(layerProto.parent()) > 0) {
+            auto parentLayer = layerMap[layerProto.parent()];
+            currLayer->parent = parentLayer;
+        }
+    }
+
+    if (layerProto.has_z_order_relative_of()) {
+        if (layerMap.count(layerProto.z_order_relative_of()) > 0) {
+            auto relativeLayer = layerMap[layerProto.z_order_relative_of()];
+            currLayer->zOrderRelativeOf = relativeLayer;
+        }
+    }
+}
+
+std::string LayerProtoParser::layersToString(
+        const std::vector<const LayerProtoParser::Layer*> layers) {
+    std::string result;
+    for (const LayerProtoParser::Layer* layer : layers) {
+        if (layer->zOrderRelativeOf != nullptr) {
+            continue;
+        }
+        result.append(layerToString(layer).c_str());
+    }
+
+    return result;
+}
+
+std::string LayerProtoParser::layerToString(const LayerProtoParser::Layer* layer) {
+    std::string result;
+
+    std::vector<const Layer*> traverse(layer->relatives);
+    for (const LayerProtoParser::Layer* child : layer->children) {
+        if (child->zOrderRelativeOf != nullptr) {
+            continue;
+        }
+
+        traverse.push_back(child);
+    }
+
+    std::sort(traverse.begin(), traverse.end(), sortLayers);
+
+    size_t i = 0;
+    for (; i < traverse.size(); i++) {
+        const auto& relative = traverse[i];
+        if (relative->z >= 0) {
+            break;
+        }
+        result.append(layerToString(relative).c_str());
+    }
+    result.append(layer->to_string().c_str());
+    result.append("\n");
+    for (; i < traverse.size(); i++) {
+        const auto& relative = traverse[i];
+        result.append(layerToString(relative).c_str());
+    }
+
+    return result;
+}
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoHeader.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoHeader.h
new file mode 100644
index 0000000..054d4f2
--- /dev/null
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoHeader.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2007 The Android Open Source Projectlayerproto/LayerProtoHeader.h
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// pragma is used here to disable the warnings emitted from the protobuf
+// headers. By adding #pragma before including layer.pb.h, it supresses
+// protobuf warnings, but allows the rest of the files to continuing using
+// the current flags.
+// This file should be included instead of directly including layer.b.h
+#pragma GCC system_header
+#include <layers.pb.h>
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
new file mode 100644
index 0000000..7b94cef
--- /dev/null
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <layerproto/LayerProtoHeader.h>
+
+#include <math/vec4.h>
+
+#include <android-base/stringprintf.h>
+#include <ui/DebugUtils.h>
+#include <unordered_map>
+#include <vector>
+
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
+namespace android {
+namespace surfaceflinger {
+
+class LayerProtoParser {
+public:
+    class ActiveBuffer {
+    public:
+        uint32_t width;
+        uint32_t height;
+        uint32_t stride;
+        int32_t format;
+
+        std::string to_string() const {
+            return StringPrintf("[%4ux%4u:%4u,%s]", width, height, stride,
+                                decodePixelFormat(format).c_str());
+        }
+    };
+
+    class Transform {
+    public:
+        float dsdx;
+        float dtdx;
+        float dsdy;
+        float dtdy;
+
+        std::string to_string() const {
+            return StringPrintf("[%.2f, %.2f][%.2f, %.2f]", static_cast<double>(dsdx),
+                                static_cast<double>(dtdx), static_cast<double>(dsdy),
+                                static_cast<double>(dtdy));
+        }
+    };
+
+    class Rect {
+    public:
+        int32_t left;
+        int32_t top;
+        int32_t right;
+        int32_t bottom;
+
+        std::string to_string() const {
+            return StringPrintf("[%3d, %3d, %3d, %3d]", left, top, right, bottom);
+        }
+    };
+
+    class Region {
+    public:
+        uint64_t id;
+        std::vector<Rect> rects;
+
+        std::string to_string(const char* what) const {
+            std::string result =
+                    StringPrintf("  Region %s (this=%lx count=%d)\n", what,
+                                 static_cast<unsigned long>(id), static_cast<int>(rects.size()));
+
+            for (auto& rect : rects) {
+                StringAppendF(&result, "    %s\n", rect.to_string().c_str());
+            }
+
+            return result;
+        }
+    };
+
+    class Layer {
+    public:
+        int32_t id;
+        std::string name;
+        std::vector<const Layer*> children;
+        std::vector<const Layer*> relatives;
+        std::string type;
+        LayerProtoParser::Region transparentRegion;
+        LayerProtoParser::Region visibleRegion;
+        LayerProtoParser::Region damageRegion;
+        uint32_t layerStack;
+        int32_t z;
+        float2 position;
+        float2 requestedPosition;
+        int2 size;
+        LayerProtoParser::Rect crop;
+        LayerProtoParser::Rect finalCrop;
+        bool isOpaque;
+        bool invalidate;
+        std::string dataspace;
+        std::string pixelFormat;
+        half4 color;
+        half4 requestedColor;
+        uint32_t flags;
+        Transform transform;
+        Transform requestedTransform;
+        Layer* parent = 0;
+        Layer* zOrderRelativeOf = 0;
+        LayerProtoParser::ActiveBuffer activeBuffer;
+        int32_t queuedFrames;
+        bool refreshPending;
+
+        std::string to_string() const {
+            std::string result;
+            StringAppendF(&result, "+ %s (%s)\n", type.c_str(), name.c_str());
+            result.append(transparentRegion.to_string("TransparentRegion").c_str());
+            result.append(visibleRegion.to_string("VisibleRegion").c_str());
+            result.append(damageRegion.to_string("SurfaceDamageRegion").c_str());
+
+            StringAppendF(&result, "      layerStack=%4d, z=%9d, pos=(%g,%g), size=(%4d,%4d), ",
+                          layerStack, z, static_cast<double>(position.x),
+                          static_cast<double>(position.y), size.x, size.y);
+
+            StringAppendF(&result, "crop=%s, finalCrop=%s, ", crop.to_string().c_str(),
+                          finalCrop.to_string().c_str());
+            StringAppendF(&result, "isOpaque=%1d, invalidate=%1d, ", isOpaque, invalidate);
+            StringAppendF(&result, "dataspace=%s, ", dataspace.c_str());
+            StringAppendF(&result, "pixelformat=%s, ", pixelFormat.c_str());
+            StringAppendF(&result, "color=(%.3f,%.3f,%.3f,%.3f), flags=0x%08x, ",
+                          static_cast<double>(color.r), static_cast<double>(color.g),
+                          static_cast<double>(color.b), static_cast<double>(color.a), flags);
+            StringAppendF(&result, "tr=%s", transform.to_string().c_str());
+            result.append("\n");
+            StringAppendF(&result, "      parent=%s\n",
+                          parent == nullptr ? "none" : parent->name.c_str());
+            StringAppendF(&result, "      zOrderRelativeOf=%s\n",
+                          zOrderRelativeOf == nullptr ? "none" : zOrderRelativeOf->name.c_str());
+            StringAppendF(&result, "      activeBuffer=%s,", activeBuffer.to_string().c_str());
+            StringAppendF(&result, " queued-frames=%d, mRefreshPending=%d", queuedFrames,
+                          refreshPending);
+
+            return result;
+        }
+    };
+
+    static std::vector<const Layer*> generateLayerTree(const LayersProto& layersProto);
+    static std::string layersToString(const std::vector<const LayerProtoParser::Layer*> layers);
+
+private:
+    static std::unordered_map<int32_t, Layer*> generateMap(const LayersProto& layersProto);
+    static LayerProtoParser::Layer* generateLayer(const LayerProto& layerProto);
+    static LayerProtoParser::Region generateRegion(const RegionProto& regionProto);
+    static LayerProtoParser::Rect generateRect(const RectProto& rectProto);
+    static LayerProtoParser::Transform generateTransform(const TransformProto& transformProto);
+    static LayerProtoParser::ActiveBuffer generateActiveBuffer(
+            const ActiveBufferProto& activeBufferProto);
+    static void updateChildrenAndRelative(const LayerProto& layerProto,
+                                          std::unordered_map<int32_t, Layer*>& layerMap);
+
+    static std::string layerToString(const LayerProtoParser::Layer* layer);
+};
+
+} // namespace surfaceflinger
+} // namespace android
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
new file mode 100644
index 0000000..d27dc9b
--- /dev/null
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -0,0 +1,110 @@
+// Definitions for SurfaceFlinger layers.
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package android.surfaceflinger;
+
+// Contains a list of all layers.
+message LayersProto {
+  repeated LayerProto layers = 1;
+}
+
+// Information about each layer.
+message LayerProto {
+  // unique id per layer.
+  optional int32 id = 1;
+  // unique name per layer.
+  optional string name = 2;
+  // list of children this layer may have. May be empty.
+  repeated int32 children = 3;
+  // list of layers that are z order relative to this layer.
+  repeated int32 relatives = 4;
+  // The type of layer, ex Color, Layer
+  optional string type = 5;
+  optional RegionProto transparent_region = 6;
+  optional RegionProto visible_region = 7;
+  optional RegionProto damage_region = 8;
+  optional uint32 layer_stack = 9;
+  // The layer's z order. Can be z order in layer stack, relative to parent,
+  // or relative to another layer specified in zOrderRelative.
+  optional int32 z = 10;
+  // The layer's position on the display.
+  optional PositionProto position = 11;
+  // The layer's requested position.
+  optional PositionProto requested_position = 12;
+  // The layer's size.
+  optional SizeProto size = 13;
+  // The layer's crop in it's own bounds.
+  optional RectProto crop = 14;
+  // The layer's crop in it's parent's bounds.
+  optional RectProto final_crop = 15;
+  optional bool is_opaque = 16;
+  optional bool invalidate = 17;
+  optional string dataspace = 18;
+  optional string pixel_format = 19;
+  // The layer's actual color.
+  optional ColorProto color = 20;
+  // The layer's requested color.
+  optional ColorProto requested_color = 21;
+  // Can be any combination of
+  //    hidden = 0x01
+  //    opaque = 0x02,
+  //    secure = 0x80,
+  optional uint32 flags = 22;
+  // The layer's actual transform
+  optional TransformProto transform = 23;
+  // The layer's requested transform.
+  optional TransformProto requested_transform = 24;
+  // The parent layer. This value can be null if there is no parent.
+  optional int32 parent = 25 [default = -1];
+  // The layer that this layer has a z order relative to. This value can be null.
+  optional int32 z_order_relative_of = 26 [default = -1];
+  // This value can be null if there's nothing to draw.
+  optional ActiveBufferProto active_buffer = 27;
+  // The number of frames available.
+  optional int32 queued_frames = 28;
+  optional bool refresh_pending = 29;
+}
+
+message PositionProto {
+  optional float x = 1;
+  optional float y = 2;
+}
+
+message SizeProto {
+  optional int32 w = 1;
+  optional int32 h = 2;
+}
+
+message TransformProto {
+  optional float dsdx = 1;
+  optional float dtdx = 2;
+  optional float dsdy = 3;
+  optional float dtdy = 4;
+}
+
+message RegionProto {
+  optional uint64 id = 1;
+  repeated RectProto rect = 2;
+}
+
+message RectProto {
+  optional int32 left   = 1;
+  optional int32 top    = 2;
+  optional int32 right  = 3;
+  optional int32 bottom = 4;
+}
+
+message ActiveBufferProto {
+  optional uint32 width = 1;
+  optional uint32 height = 2;
+  optional uint32 stride = 3;
+  optional int32 format = 4;
+}
+
+message ColorProto {
+  optional float r = 1;
+  optional float g = 2;
+  optional float b = 3;
+  optional float a = 4;
+}
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 94f3f25..212b9e7 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -22,7 +22,8 @@
         "libsync",
         "libfmq",
         "libbase",
-        "libhidltransport"
+        "libhidltransport",
+        "liblayers_proto"
     ],
     static_libs: [
         "libhwcomposer-client",