Make BufferHubQueue binder parcelable

The goal of this CL is to enable sending BufferHubQueue clients over
binder, so that ANativeWindow/Surface backed by BufferHub can be send
over binder just like the current binder BufferQueue based
implementation.

To enable PDX clients to be binder parcelable, this CL introduced new
DPX interface: pdx::ChannelParcelable with UDS-backed
implementation. Note that the ChannelParcelable interface is only
exposed to each implementation of PDX client (BufferHubQueue clients in
this CL) through protected pdx::Client interface. The reason for doing
that is each client implementation may require different custom clean up
logic between the client can be safely exported to another
process. Thus, we don't want to expose this export-to-binder becomes a
pdx-Client level feature, but let each client implement their own. In
addition, there is no immediate need for all types of pdx::Client
implementation being binder parcelable, thus we only implemented the
logic for BufferHubQueue.

This is the first of a seriers of CLs to eventually enable binder
parcelable ANativeSurface backed by BufferHub.

Bug: 37517761
Bug: 63909629
Test: buffer_hub_queue-test
Change-Id: I41c8a710af3095312db823ff01bc5aa526775edd
diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp
index e3cb494..a550ba5 100644
--- a/libs/vr/libbufferhub/Android.bp
+++ b/libs/vr/libbufferhub/Android.bp
@@ -29,6 +29,7 @@
 
 sharedLibraries = [
     "libbase",
+    "libbinder",
     "libcutils",
     "libhardware",
     "liblog",
diff --git a/libs/vr/libbufferhubqueue/Android.bp b/libs/vr/libbufferhubqueue/Android.bp
index 93ccd0f..feefe74 100644
--- a/libs/vr/libbufferhubqueue/Android.bp
+++ b/libs/vr/libbufferhubqueue/Android.bp
@@ -14,6 +14,7 @@
 
 sourceFiles = [
     "buffer_hub_queue_client.cpp",
+    "buffer_hub_queue_parcelable.cpp",
     "buffer_hub_queue_producer.cpp",
 ]
 
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
index 8bea0cd..a5cefd9 100644
--- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp
@@ -137,6 +137,28 @@
   return status;
 }
 
+pdx::Status<ConsumerQueueParcelable>
+BufferHubQueue::CreateConsumerQueueParcelable(bool silent) {
+  auto status = CreateConsumerQueueHandle(silent);
+  if (!status)
+    return status.error_status();
+
+  // A temporary consumer queue client to pull its channel parcelable.
+  auto consumer_queue =
+      std::unique_ptr<ConsumerQueue>(new ConsumerQueue(status.take()));
+  ConsumerQueueParcelable queue_parcelable(
+      consumer_queue->GetChannel()->TakeChannelParcelable());
+
+  if (!queue_parcelable.IsValid()) {
+    ALOGE(
+        "BufferHubQueue::CreateConsumerQueueParcelable: Failed to create "
+        "consumer queue parcelable.");
+    return ErrorStatus(EINVAL);
+  }
+
+  return {std::move(queue_parcelable)};
+}
+
 bool BufferHubQueue::WaitForBuffers(int timeout) {
   ATRACE_NAME("BufferHubQueue::WaitForBuffers");
   std::array<epoll_event, kMaxEvents> events;
@@ -555,6 +577,25 @@
   return {std::move(buffer)};
 }
 
+pdx::Status<ProducerQueueParcelable> ProducerQueue::TakeAsParcelable() {
+  if (capacity() != 0) {
+    ALOGE(
+        "ProducerQueue::TakeAsParcelable: producer queue can only be taken out"
+        " as a parcelable when empty. Current queue capacity: %zu",
+        capacity());
+    return ErrorStatus(EINVAL);
+  }
+
+  std::unique_ptr<pdx::ClientChannel> channel = TakeChannel();
+  ProducerQueueParcelable queue_parcelable(channel->TakeChannelParcelable());
+
+  // Here the queue parcelable is returned and holds the underlying system
+  // resources backing the queue; while the original client channel of this
+  // producer queue is destroyed in place so that this client can no longer
+  // provide producer operations.
+  return {std::move(queue_parcelable)};
+}
+
 ConsumerQueue::ConsumerQueue(LocalChannelHandle handle)
     : BufferHubQueue(std::move(handle)) {
   auto status = ImportQueue();
diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
new file mode 100644
index 0000000..2cd7c45
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_parcelable.cpp
@@ -0,0 +1,82 @@
+#include "include/private/dvr/buffer_hub_queue_parcelable.h"
+
+#include <binder/Parcel.h>
+#include <pdx/default_transport/channel_parcelable.h>
+
+namespace android {
+namespace dvr {
+
+template <BufferHubQueueParcelableMagic Magic>
+bool BufferHubQueueParcelable<Magic>::IsValid() const {
+  return !!channel_parcelable_ && channel_parcelable_->IsValid();
+}
+
+template <BufferHubQueueParcelableMagic Magic>
+pdx::LocalChannelHandle BufferHubQueueParcelable<Magic>::TakeChannelHandle() {
+  if (!IsValid()) {
+    ALOGE(
+        "BufferHubQueueParcelable::TakeChannelHandle: Invalid channel parcel.");
+    return {};  // Returns an empty channel handle.
+  }
+
+  // Take channel handle out of the parcelable and reset the parcelable.
+  pdx::LocalChannelHandle handle = channel_parcelable_->TakeChannelHandle();
+  // Now channel_parcelable_ should already be invalid, but reset it to release
+  // the invalid parcelable object from unique_ptr.
+  channel_parcelable_ = nullptr;
+  return handle;
+}
+
+template <BufferHubQueueParcelableMagic Magic>
+status_t BufferHubQueueParcelable<Magic>::writeToParcel(Parcel* parcel) const {
+  if (!IsValid()) {
+    ALOGE("BufferHubQueueParcelable::writeToParcel: Invalid channel.");
+    return -EINVAL;
+  }
+
+  status_t res = parcel->writeUint32(Magic);
+  if (res != NO_ERROR) {
+    ALOGE("BufferHubQueueParcelable::writeToParcel: Cannot write magic.");
+    return res;
+  }
+
+  return channel_parcelable_->writeToParcel(parcel);
+}
+
+template <BufferHubQueueParcelableMagic Magic>
+status_t BufferHubQueueParcelable<Magic>::readFromParcel(const Parcel* parcel) {
+  if (IsValid()) {
+    ALOGE(
+        "BufferHubQueueParcelable::readFromParcel: This parcelable object has "
+        "been initialized already.");
+    return -EINVAL;
+  }
+
+  uint32_t out_magic = 0;
+  status_t res = NO_ERROR;
+
+  res = parcel->readUint32(&out_magic);
+  if (res != NO_ERROR)
+    return res;
+
+  if (out_magic != Magic) {
+    ALOGE(
+        "BufferHubQueueParcelable::readFromParcel: Unexpected magic: 0x%x, "
+        "epxected: 0x%x",
+        out_magic, Magic);
+    return -EINVAL;
+  }
+
+  // (Re)Alocate channel parcelable object.
+  channel_parcelable_ =
+      std::make_unique<pdx::default_transport::ChannelParcelable>();
+  return channel_parcelable_->readFromParcel(parcel);
+}
+
+template class BufferHubQueueParcelable<
+    BufferHubQueueParcelableMagic::Producer>;
+template class BufferHubQueueParcelable<
+    BufferHubQueueParcelableMagic::Consumer>;
+
+}  // namespace dvr
+}  // namespace android
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
index 6962d6c..c8e31c7 100644
--- a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_client.h
@@ -6,6 +6,7 @@
 #include <pdx/client.h>
 #include <pdx/status.h>
 #include <private/dvr/buffer_hub_client.h>
+#include <private/dvr/buffer_hub_queue_parcelable.h>
 #include <private/dvr/bufferhub_rpc.h>
 #include <private/dvr/epoll_file_descriptor.h>
 #include <private/dvr/ring_buffer.h>
@@ -54,6 +55,11 @@
   pdx::Status<pdx::LocalChannelHandle> CreateConsumerQueueHandle(
       bool silent = false);
 
+  // Creates a new consumer in parcelable form for immediate transport over
+  // Binder.
+  pdx::Status<ConsumerQueueParcelable> CreateConsumerQueueParcelable(
+      bool silent = false);
+
   // Returns the number of buffers avaiable for dequeue.
   size_t count() const { return available_buffers_.size(); }
 
@@ -337,6 +343,11 @@
     return BufferHubQueue::Enqueue({buffer, slot, index});
   }
 
+  // Takes out the current producer queue as a binder parcelable object. Note
+  // that the queue must be empty to be exportable. After successful export, the
+  // producer queue client should no longer be used.
+  pdx::Status<ProducerQueueParcelable> TakeAsParcelable();
+
  private:
   friend BASE;
 
diff --git a/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
new file mode 100644
index 0000000..89baf92
--- /dev/null
+++ b/libs/vr/libbufferhubqueue/include/private/dvr/buffer_hub_queue_parcelable.h
@@ -0,0 +1,60 @@
+#ifndef ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
+#define ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
+
+#include <pdx/channel_parcelable.h>
+
+namespace android {
+namespace dvr {
+
+enum BufferHubQueueParcelableMagic : uint32_t {
+  Producer = 0x62687170,  // 'bhqp'
+  Consumer = 0x62687163,  // 'bhqc'
+};
+
+template <BufferHubQueueParcelableMagic Magic>
+class BufferHubQueueParcelable : public Parcelable {
+ public:
+  BufferHubQueueParcelable() = default;
+
+  BufferHubQueueParcelable(BufferHubQueueParcelable&& other) = default;
+  BufferHubQueueParcelable& operator=(BufferHubQueueParcelable&& other) =
+      default;
+
+  // Constructs an parcelable contains the channel parcelable.
+  BufferHubQueueParcelable(
+      std::unique_ptr<pdx::ChannelParcelable> channel_parcelable)
+      : channel_parcelable_(std::move(channel_parcelable)) {}
+
+  BufferHubQueueParcelable(const BufferHubQueueParcelable&) = delete;
+  void operator=(const BufferHubQueueParcelable&) = delete;
+
+  bool IsValid() const;
+
+  // Returns a channel handle constructed from this parcelable object and takes
+  // the ownership of all resources from the parcelable object.
+  pdx::LocalChannelHandle TakeChannelHandle();
+
+  // Serializes the queue parcelable into the given parcel. Note that no system
+  // resources are getting duplicated, nor did the parcel takes ownership of the
+  // queue parcelable. Thus, the parcelable object must remain valid for the
+  // lifetime of the parcel.
+  status_t writeToParcel(Parcel* parcel) const override;
+
+  // Deserialize the queue parcelable from the given parcel. Note that system
+  // resources are duplicated from the parcel into the queue parcelable. Returns
+  // error if the targeting parcelable object is already valid.
+  status_t readFromParcel(const Parcel* parcel) override;
+
+ private:
+  std::unique_ptr<pdx::ChannelParcelable> channel_parcelable_;
+};
+
+using ProducerQueueParcelable =
+    BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Producer>;
+using ConsumerQueueParcelable =
+    BufferHubQueueParcelable<BufferHubQueueParcelableMagic::Consumer>;
+
+}  // namespace dvr
+}  // namespace android
+
+#endif  // ANDROID_DVR_BUFFER_HUB_QUEUE_PARCELABLE_H_
diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
index 8a72531..f67ef37 100644
--- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
+++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue-test.cpp
@@ -1,4 +1,5 @@
 #include <base/logging.h>
+#include <binder/Parcel.h>
 #include <private/dvr/buffer_hub_client.h>
 #include <private/dvr/buffer_hub_queue_client.h>
 
@@ -14,6 +15,7 @@
 namespace android {
 namespace dvr {
 
+using pdx::LocalChannelHandle;
 using pdx::LocalHandle;
 
 namespace {
@@ -680,6 +682,156 @@
 #undef CHECK_NO_BUFFER_THEN_ALLOCATE
 }
 
+TEST_F(BufferHubQueueTest, TestProducerToParcelableNotEmpty) {
+  ASSERT_TRUE(CreateQueues(config_builder_.SetMetadata<uint64_t>().Build(),
+                           UsagePolicy{}));
+
+  // Allocate only one buffer.
+  AllocateBuffer();
+
+  // Export should fail as the queue is not empty.
+  auto status = producer_queue_->TakeAsParcelable();
+  EXPECT_FALSE(status.ok());
+}
+
+TEST_F(BufferHubQueueTest, TestProducerExportToParcelable) {
+  ASSERT_TRUE(CreateQueues(config_builder_.Build(), UsagePolicy{}));
+
+  auto s1 = producer_queue_->TakeAsParcelable();
+  EXPECT_TRUE(s1.ok());
+
+  ProducerQueueParcelable output_parcelable = s1.take();
+  EXPECT_TRUE(output_parcelable.IsValid());
+
+  Parcel parcel;
+  status_t res;
+  res = output_parcelable.writeToParcel(&parcel);
+  EXPECT_EQ(res, NO_ERROR);
+
+  // After written into parcelable, the output_parcelable is still valid has
+  // keeps the producer channel alive.
+  EXPECT_TRUE(output_parcelable.IsValid());
+
+  // Creating producer buffer should fail.
+  auto s2 = producer_queue_->AllocateBuffer(kBufferWidth, kBufferHeight,
+                                            kBufferLayerCount, kBufferFormat,
+                                            kBufferUsage);
+  ASSERT_FALSE(s2.ok());
+
+  // Reset the data position so that we can read back from the same parcel
+  // without doing actually Binder IPC.
+  parcel.setDataPosition(0);
+  producer_queue_ = nullptr;
+
+  // Recreate the producer queue from the parcel.
+  ProducerQueueParcelable input_parcelable;
+  EXPECT_FALSE(input_parcelable.IsValid());
+
+  res = input_parcelable.readFromParcel(&parcel);
+  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_TRUE(input_parcelable.IsValid());
+
+  EXPECT_EQ(producer_queue_, nullptr);
+  producer_queue_ = ProducerQueue::Import(input_parcelable.TakeChannelHandle());
+  EXPECT_FALSE(input_parcelable.IsValid());
+  ASSERT_NE(producer_queue_, nullptr);
+
+  // Newly created queue from the parcel can allocate buffer, post buffer to
+  // consumer.
+  EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
+  EXPECT_EQ(producer_queue_->count(), 1U);
+  EXPECT_EQ(producer_queue_->capacity(), 1U);
+
+  size_t slot;
+  DvrNativeBufferMetadata producer_meta;
+  DvrNativeBufferMetadata consumer_meta;
+  LocalHandle fence;
+  auto s3 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
+  EXPECT_TRUE(s3.ok());
+
+  std::shared_ptr<BufferProducer> p1 = s3.take();
+  EXPECT_NE(p1, nullptr);
+
+  producer_meta.timestamp = 42;
+  EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
+
+  // Make sure the buffer can be dequeued from consumer side.
+  auto s4 = consumer_queue_->Dequeue(100, &slot, &consumer_meta, &fence);
+  EXPECT_TRUE(s4.ok());
+  EXPECT_EQ(consumer_queue_->capacity(), 1U);
+
+  auto consumer = s4.take();
+  EXPECT_NE(consumer, nullptr);
+  EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
+}
+
+TEST_F(BufferHubQueueTest, TestCreateConsumerParcelable) {
+  ASSERT_TRUE(CreateProducerQueue(config_builder_.Build(), UsagePolicy{}));
+
+  auto s1 = producer_queue_->CreateConsumerQueueParcelable();
+  EXPECT_TRUE(s1.ok());
+  ConsumerQueueParcelable output_parcelable = s1.take();
+  EXPECT_TRUE(output_parcelable.IsValid());
+
+  // Write to a Parcel new object.
+  Parcel parcel;
+  status_t res;
+  res = output_parcelable.writeToParcel(&parcel);
+
+  // Reset the data position so that we can read back from the same parcel
+  // without doing actually Binder IPC.
+  parcel.setDataPosition(0);
+
+  // No consumer queue created yet.
+  EXPECT_EQ(consumer_queue_, nullptr);
+
+  // If the parcel contains a consumer queue, read into a
+  // ProducerQueueParcelable should fail.
+  ProducerQueueParcelable wrongly_typed_parcelable;
+  EXPECT_FALSE(wrongly_typed_parcelable.IsValid());
+  res = wrongly_typed_parcelable.readFromParcel(&parcel);
+  EXPECT_EQ(res, -EINVAL);
+  parcel.setDataPosition(0);
+
+  // Create the consumer queue from the parcel.
+  ConsumerQueueParcelable input_parcelable;
+  EXPECT_FALSE(input_parcelable.IsValid());
+
+  res = input_parcelable.readFromParcel(&parcel);
+  EXPECT_EQ(res, NO_ERROR);
+  EXPECT_TRUE(input_parcelable.IsValid());
+
+  consumer_queue_ = ConsumerQueue::Import(input_parcelable.TakeChannelHandle());
+  EXPECT_FALSE(input_parcelable.IsValid());
+  ASSERT_NE(consumer_queue_, nullptr);
+
+  EXPECT_NO_FATAL_FAILURE(AllocateBuffer());
+  EXPECT_EQ(producer_queue_->count(), 1U);
+  EXPECT_EQ(producer_queue_->capacity(), 1U);
+
+  size_t slot;
+  DvrNativeBufferMetadata producer_meta;
+  DvrNativeBufferMetadata consumer_meta;
+  LocalHandle fence;
+  auto s2 = producer_queue_->Dequeue(0, &slot, &producer_meta, &fence);
+  EXPECT_TRUE(s2.ok());
+
+  std::shared_ptr<BufferProducer> p1 = s2.take();
+  EXPECT_NE(p1, nullptr);
+
+  producer_meta.timestamp = 42;
+  EXPECT_EQ(p1->PostAsync(&producer_meta, LocalHandle()), 0);
+
+  // Make sure the buffer can be dequeued from consumer side.
+  auto s3 = consumer_queue_->Dequeue(100, &slot, &consumer_meta, &fence);
+  EXPECT_TRUE(s3.ok());
+  EXPECT_EQ(consumer_queue_->capacity(), 1U);
+
+  auto consumer = s3.take();
+  EXPECT_NE(consumer, nullptr);
+  EXPECT_EQ(producer_meta.timestamp, consumer_meta.timestamp);
+}
+
 }  // namespace
 
 }  // namespace dvr
diff --git a/libs/vr/libdisplay/Android.bp b/libs/vr/libdisplay/Android.bp
index e3ab7fa..684dff1 100644
--- a/libs/vr/libdisplay/Android.bp
+++ b/libs/vr/libdisplay/Android.bp
@@ -26,6 +26,7 @@
 
 sharedLibraries = [
     "libbase",
+    "libbinder",
     "libcutils",
     "liblog",
     "libutils",
diff --git a/libs/vr/libpdx/private/pdx/channel_parcelable.h b/libs/vr/libpdx/private/pdx/channel_parcelable.h
new file mode 100644
index 0000000..59ef9d3
--- /dev/null
+++ b/libs/vr/libpdx/private/pdx/channel_parcelable.h
@@ -0,0 +1,31 @@
+#ifndef ANDROID_PDX_CHANNEL_PARCELABLE_H_
+#define ANDROID_PDX_CHANNEL_PARCELABLE_H_
+
+#include <binder/Parcelable.h>
+#include <pdx/channel_handle.h>
+
+namespace android {
+namespace pdx {
+
+/**
+ * A parcelable object holds all necessary objects to recreate a ClientChannel.
+ * In addition to the android::Parcelable interface, this interface exposees
+ * more PDX-related interface.
+ */
+class ChannelParcelable : public Parcelable {
+ public:
+  virtual ~ChannelParcelable() = default;
+
+  // Returns whether the parcelable object holds a valid client channel.
+  virtual bool IsValid() const = 0;
+
+  // Returns a channel handle constructed from this parcelable object and takes
+  // the ownership of all resources from the parcelable object. In another word,
+  // the parcelable object will become invalid after TakeChannelHandle returns.
+  virtual LocalChannelHandle TakeChannelHandle() = 0;
+};
+
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_CHANNEL_PARCELABLE_H_
diff --git a/libs/vr/libpdx/private/pdx/client_channel.h b/libs/vr/libpdx/private/pdx/client_channel.h
index 10a49bb..8f5fdfe 100644
--- a/libs/vr/libpdx/private/pdx/client_channel.h
+++ b/libs/vr/libpdx/private/pdx/client_channel.h
@@ -4,6 +4,7 @@
 #include <vector>
 
 #include <pdx/channel_handle.h>
+#include <pdx/channel_parcelable.h>
 #include <pdx/file_handle.h>
 #include <pdx/status.h>
 
@@ -61,6 +62,11 @@
                              LocalHandle* handle) const = 0;
   virtual bool GetChannelHandle(void* transaction_state, ChannelReference ref,
                                 LocalChannelHandle* handle) const = 0;
+
+  // Returns the internal state of the channel as a parcelable object. The
+  // ClientChannel is invalidated however, the channel is kept alive by the
+  // parcelable object and may be transferred to another process.
+  virtual std::unique_ptr<ChannelParcelable> TakeChannelParcelable() = 0;
 };
 
 }  // namespace pdx
diff --git a/libs/vr/libpdx/private/pdx/mock_client_channel.h b/libs/vr/libpdx/private/pdx/mock_client_channel.h
index 49e0682..ecc20b3 100644
--- a/libs/vr/libpdx/private/pdx/mock_client_channel.h
+++ b/libs/vr/libpdx/private/pdx/mock_client_channel.h
@@ -49,6 +49,7 @@
   MOCK_CONST_METHOD3(GetChannelHandle,
                      bool(void* transaction_state, ChannelReference ref,
                           LocalChannelHandle* handle));
+  MOCK_METHOD0(TakeChannelParcelable, std::unique_ptr<ChannelParcelable>());
 };
 
 }  // namespace pdx
diff --git a/libs/vr/libpdx_default_transport/Android.bp b/libs/vr/libpdx_default_transport/Android.bp
index f891c59..cda3c95 100644
--- a/libs/vr/libpdx_default_transport/Android.bp
+++ b/libs/vr/libpdx_default_transport/Android.bp
@@ -42,6 +42,7 @@
         "pdx_tool.cpp",
     ],
     shared_libs: [
+        "libbinder",
         "libcutils",
         "liblog",
     ],
@@ -59,6 +60,7 @@
     ],
     shared_libs: [
         "libbase",
+        "libbinder",
         "libchrome",
         "libcutils",
         "liblog",
diff --git a/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_parcelable.h b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_parcelable.h
new file mode 100644
index 0000000..a8623b2
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/servicefs/pdx/default_transport/channel_parcelable.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_PARCELABLE_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_PARCELABLE_H_
+
+#include <servicefs/channel_parcelable.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelParcelable = ::android::pdx::servicefs::ChannelParcelable;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_SERVICEFS_CHANNEL_PARCELABLE_H_
diff --git a/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_parcelable.h b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_parcelable.h
new file mode 100644
index 0000000..bcd74e6
--- /dev/null
+++ b/libs/vr/libpdx_default_transport/private/uds/pdx/default_transport/channel_parcelable.h
@@ -0,0 +1,17 @@
+#ifndef ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_PARCELABLE_H_
+#define ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_PARCELABLE_H_
+
+#include <uds/channel_parcelable.h>
+
+namespace android {
+namespace pdx {
+namespace default_transport {
+
+using ChannelParcelable = ::android::pdx::uds::ChannelParcelable;
+
+}  // namespace default_transport
+}  // namespace pdx
+}  // namespace android
+
+
+#endif  // ANDROID_PDX_DEFAULT_TRANSPORT_UDS_CHANNEL_PARCELABLE_H_
diff --git a/libs/vr/libpdx_uds/Android.bp b/libs/vr/libpdx_uds/Android.bp
index d0b7cab..d640950 100644
--- a/libs/vr/libpdx_uds/Android.bp
+++ b/libs/vr/libpdx_uds/Android.bp
@@ -13,6 +13,7 @@
     srcs: [
         "channel_event_set.cpp",
         "channel_manager.cpp",
+        "channel_parcelable.cpp",
         "client_channel_factory.cpp",
         "client_channel.cpp",
         "ipc_helper.cpp",
@@ -23,6 +24,9 @@
         "libbase",
         "libpdx",
     ],
+    shared_libs: [
+        "libbinder",
+    ],
     whole_static_libs: [
         "libselinux",
     ],
@@ -52,5 +56,6 @@
         "libcutils",
         "liblog",
         "libutils",
+        "libbinder",
     ],
 }
diff --git a/libs/vr/libpdx_uds/channel_parcelable.cpp b/libs/vr/libpdx_uds/channel_parcelable.cpp
new file mode 100644
index 0000000..e7bce27
--- /dev/null
+++ b/libs/vr/libpdx_uds/channel_parcelable.cpp
@@ -0,0 +1,125 @@
+#include "uds/channel_parcelable.h"
+
+#include <binder/Parcel.h>
+#include <uds/channel_manager.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+namespace {
+
+static constexpr uint32_t kUdsMagicParcelHeader = 0x7564736d;  // 'udsm'.
+
+}  // namespace
+
+ChannelParcelable::ChannelParcelable(LocalHandle data_fd,
+                                     LocalHandle pollin_event_fd,
+                                     LocalHandle pollhup_event_fd)
+    : data_fd_{std::move(data_fd)},
+      pollin_event_fd_{std::move(pollin_event_fd)},
+      pollhup_event_fd_{std::move(pollhup_event_fd)} {}
+
+bool ChannelParcelable::IsValid() const {
+  return !!data_fd_ && !!pollin_event_fd_ && !!pollhup_event_fd_;
+}
+
+LocalChannelHandle ChannelParcelable::TakeChannelHandle() {
+  if (!IsValid()) {
+    ALOGE("ChannelParcelable::TakeChannelHandle: Invalid channel parcel.");
+    return {};  // Returns an empty channel handle.
+  }
+
+  return ChannelManager::Get().CreateHandle(std::move(data_fd_),
+                                            std::move(pollin_event_fd_),
+                                            std::move(pollhup_event_fd_));
+}
+
+status_t ChannelParcelable::writeToParcel(Parcel* parcel) const {
+  status_t res = NO_ERROR;
+
+  if (!IsValid()) {
+    ALOGE("ChannelParcelable::writeToParcel: Invalid channel parcel.");
+    return BAD_VALUE;
+  }
+
+  res = parcel->writeUint32(kUdsMagicParcelHeader);
+  if (res != NO_ERROR) {
+    ALOGE("ChannelParcelable::writeToParcel: Cannot write magic: res=%d.", res);
+    return res;
+  }
+
+  res = parcel->writeFileDescriptor(data_fd_.Get());
+  if (res != NO_ERROR) {
+    ALOGE("ChannelParcelable::writeToParcel: Cannot write data fd: res=%d.",
+          res);
+    return res;
+  }
+
+  res = parcel->writeFileDescriptor(pollin_event_fd_.Get());
+  if (res != NO_ERROR) {
+    ALOGE(
+        "ChannelParcelable::writeToParcel: Cannot write pollin event fd: "
+        "res=%d.",
+        res);
+    return res;
+  }
+
+  res = parcel->writeFileDescriptor(pollhup_event_fd_.Get());
+  if (res != NO_ERROR) {
+    ALOGE(
+        "ChannelParcelable::writeToParcel: Cannot write pollhup event fd: "
+        "res=%d.",
+        res);
+    return res;
+  }
+
+  return res;
+}
+
+status_t ChannelParcelable::readFromParcel(const Parcel* parcel) {
+  uint32_t magic = 0;
+  status_t res = NO_ERROR;
+
+  if (IsValid()) {
+    ALOGE(
+        "ChannelParcelable::readFromParcel: This channel parcel is already "
+        "initailzied.");
+    return ALREADY_EXISTS;
+  }
+
+  res = parcel->readUint32(&magic);
+  if (res != NO_ERROR) {
+    ALOGE("ChannelParcelable::readFromParcel: Failed to read magic: res=%d.",
+          res);
+    return res;
+  }
+
+  if (magic != kUdsMagicParcelHeader) {
+    ALOGE(
+        "ChannelParcelable::readFromParcel: Unknown magic: 0x%x, epxected: "
+        "0x%x",
+        magic, kUdsMagicParcelHeader);
+    return BAD_VALUE;
+  }
+
+  // TODO(b/69010509): We have to dup() the FD from android::Parcel as it
+  // doesn't support taking out the FD's ownership. We can remove the dup() here
+  // once android::Parcel support such operation.
+  data_fd_.Reset(dup(parcel->readFileDescriptor()));
+  pollin_event_fd_.Reset(dup(parcel->readFileDescriptor()));
+  pollhup_event_fd_.Reset(dup(parcel->readFileDescriptor()));
+  if (!IsValid()) {
+    ALOGE(
+        "ChannelParcelable::readFromParcel: Cannot read fd from parcel: "
+        "data_fd=%d, pollin_event_fd=%d, pollhup_event_fd=%d.",
+        data_fd_.Get(), pollin_event_fd_.Get(), pollhup_event_fd_.Get());
+    return DEAD_OBJECT;
+  }
+
+  return res;
+}
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
diff --git a/libs/vr/libpdx_uds/client_channel.cpp b/libs/vr/libpdx_uds/client_channel.cpp
index 2e9c1de..6073c3c 100644
--- a/libs/vr/libpdx_uds/client_channel.cpp
+++ b/libs/vr/libpdx_uds/client_channel.cpp
@@ -1,3 +1,4 @@
+#include "uds/channel_parcelable.h"
 #include "uds/client_channel.h"
 
 #include <errno.h>
@@ -292,6 +293,29 @@
   return state->GetLocalChannelHandle(ref, handle);
 }
 
+std::unique_ptr<pdx::ChannelParcelable> ClientChannel::TakeChannelParcelable()
+    {
+  if (!channel_handle_)
+    return nullptr;
+
+  if (auto* channel_data =
+          ChannelManager::Get().GetChannelData(channel_handle_.value())) {
+    auto fds = channel_data->TakeFds();
+    auto parcelable = std::make_unique<ChannelParcelable>(
+        std::move(std::get<0>(fds)), std::move(std::get<1>(fds)),
+        std::move(std::get<2>(fds)));
+
+    // Here we need to explicitly close the channel handle so that the channel
+    // won't get shutdown in the destructor, while the FDs in ChannelParcelable
+    // can keep the channel alive so that new client can be created from it
+    // later.
+    channel_handle_.Close();
+    return parcelable;
+  } else {
+    return nullptr;
+  }
+}
+
 }  // namespace uds
 }  // namespace pdx
 }  // namespace android
diff --git a/libs/vr/libpdx_uds/private/uds/channel_parcelable.h b/libs/vr/libpdx_uds/private/uds/channel_parcelable.h
new file mode 100644
index 0000000..1c3fae9
--- /dev/null
+++ b/libs/vr/libpdx_uds/private/uds/channel_parcelable.h
@@ -0,0 +1,35 @@
+#ifndef ANDROID_PDX_UDS_CHANNEL_PARCELABLE_H_
+#define ANDROID_PDX_UDS_CHANNEL_PARCELABLE_H_
+
+#include <pdx/channel_parcelable.h>
+#include <pdx/file_handle.h>
+
+namespace android {
+namespace pdx {
+namespace uds {
+
+class ChannelParcelable : public pdx::ChannelParcelable {
+ public:
+  ChannelParcelable() = default;
+  ChannelParcelable(LocalHandle data_fd, LocalHandle pollin_event_fd,
+                    LocalHandle pollhup_event_fd);
+
+  // Implements pdx::ChannelParcelable interface.
+  bool IsValid() const override;
+  LocalChannelHandle TakeChannelHandle() override;
+
+  // Implements android::Parcelable interface.
+  status_t writeToParcel(Parcel* parcel) const override;
+  status_t readFromParcel(const Parcel* parcel) override;
+
+ private:
+  LocalHandle data_fd_;
+  LocalHandle pollin_event_fd_;
+  LocalHandle pollhup_event_fd_;
+};
+
+}  // namespace uds
+}  // namespace pdx
+}  // namespace android
+
+#endif  // ANDROID_PDX_UDS_CHANNEL_PARCELABLE_H_
diff --git a/libs/vr/libpdx_uds/private/uds/client_channel.h b/libs/vr/libpdx_uds/private/uds/client_channel.h
index 7a5ddf4..b5524d8 100644
--- a/libs/vr/libpdx_uds/private/uds/client_channel.h
+++ b/libs/vr/libpdx_uds/private/uds/client_channel.h
@@ -74,6 +74,8 @@
   bool GetChannelHandle(void* transaction_state, ChannelReference ref,
                         LocalChannelHandle* handle) const override;
 
+  std::unique_ptr<pdx::ChannelParcelable> TakeChannelParcelable() override;
+
  private:
   explicit ClientChannel(LocalChannelHandle channel_handle);
 
diff --git a/libs/vr/libperformance/Android.bp b/libs/vr/libperformance/Android.bp
index 364873d..278a425 100644
--- a/libs/vr/libperformance/Android.bp
+++ b/libs/vr/libperformance/Android.bp
@@ -23,6 +23,7 @@
 
 sharedLibraries = [
     "libbase",
+    "libbinder",
     "libcutils",
     "liblog",
     "libutils",
diff --git a/libs/vr/libvrsensor/Android.bp b/libs/vr/libvrsensor/Android.bp
index d022adf..cc1803b 100644
--- a/libs/vr/libvrsensor/Android.bp
+++ b/libs/vr/libvrsensor/Android.bp
@@ -33,6 +33,7 @@
 
 sharedLibraries = [
     "libbase",
+    "libbinder",
     "libcutils",
     "libhardware",
     "liblog",