Add importBuffer to BufferHubBinderService

Now you can create a new IBufferClient using token generated by
duplicate(). The new client will be linked to the same node in the
backend.

Import an unexisting token will return PERMISSION_DENIED, while import a
gone buffer will get DEAD_OBJECT.

Test: atest buffer_hub_binder_service-test
Bug: 116681016
Change-Id: Ib07fd160dad98bcaed017b3efa648863b70aa142
diff --git a/services/vr/bufferhubd/IBufferHub.cpp b/services/vr/bufferhubd/IBufferHub.cpp
index a2c0a4a..022a9cc 100644
--- a/services/vr/bufferhubd/IBufferHub.cpp
+++ b/services/vr/bufferhubd/IBufferHub.cpp
@@ -13,6 +13,8 @@
                                  uint32_t layer_count, uint32_t format,
                                  uint64_t usage,
                                  uint64_t user_metadata_size) override;
+
+  status_t importBuffer(uint64_t token, sp<IBufferClient>* outClient) override;
 };
 
 IMPLEMENT_META_INTERFACE(BufferHub, "android.dvr.IBufferHub");
@@ -20,6 +22,7 @@
 // Transaction code
 enum {
   CREATE_BUFFER = IBinder::FIRST_CALL_TRANSACTION,
+  IMPORT_BUFFER,
 };
 
 sp<IBufferClient> BpBufferHub::createBuffer(uint32_t width, uint32_t height,
@@ -50,6 +53,27 @@
   }
 }
 
+status_t BpBufferHub::importBuffer(uint64_t token,
+                                   sp<IBufferClient>* outClient) {
+  Parcel data, reply;
+  status_t ret = NO_ERROR;
+  ret |= data.writeInterfaceToken(IBufferHub::getInterfaceDescriptor());
+  ret |= data.writeUint64(token);
+  if (ret != NO_ERROR) {
+    ALOGE("BpBufferHub::importBuffer: failed to write into parcel");
+    return ret;
+  }
+
+  ret = remote()->transact(IMPORT_BUFFER, data, &reply);
+  if (ret == NO_ERROR) {
+    *outClient = interface_cast<IBufferClient>(reply.readStrongBinder());
+    return NO_ERROR;
+  } else {
+    ALOGE("BpBufferHub::importBuffer: failed to transact; errno=%d", ret);
+    return ret;
+  }
+}
+
 status_t BnBufferHub::onTransact(uint32_t code, const Parcel& data,
                                  Parcel* reply, uint32_t flags) {
   switch (code) {
@@ -65,6 +89,17 @@
                                            usage, user_metadata_size);
       return reply->writeStrongBinder(IInterface::asBinder(ret));
     }
+    case IMPORT_BUFFER: {
+      CHECK_INTERFACE(IBufferHub, data, reply);
+      uint64_t token = data.readUint64();
+      sp<IBufferClient> client;
+      status_t ret = importBuffer(token, &client);
+      if (ret == NO_ERROR) {
+        return reply->writeStrongBinder(IInterface::asBinder(client));
+      } else {
+        return ret;
+      }
+    }
     default:
       // Should not reach except binder defined transactions such as dumpsys
       return BBinder::onTransact(code, data, reply, flags);
diff --git a/services/vr/bufferhubd/buffer_hub_binder.cpp b/services/vr/bufferhubd/buffer_hub_binder.cpp
index 7b0dc9a..afd34aa 100644
--- a/services/vr/bufferhubd/buffer_hub_binder.cpp
+++ b/services/vr/bufferhubd/buffer_hub_binder.cpp
@@ -88,5 +88,42 @@
   return client;
 }
 
+status_t BufferHubBinderService::importBuffer(uint64_t token,
+                                              sp<IBufferClient>* outClient) {
+  auto iter = token_map_.find(token);
+
+  if (iter == token_map_.end()) {  // Not found
+    ALOGE("BufferHubBinderService::importBuffer: token %" PRIu64
+          "does not exist.",
+          token);
+    return PERMISSION_DENIED;
+  }
+
+  if (iter->second.expired()) {  // Gone
+    ALOGW(
+        "BufferHubBinderService::importBuffer: the original node of token "
+        "%" PRIu64 "has gone.",
+        token);
+    token_map_.erase(iter);
+    return DEAD_OBJECT;
+  }
+
+  // Promote the weak_ptr
+  std::shared_ptr<BufferNode> node(iter->second);
+  if (!node) {
+    ALOGE("BufferHubBinderService::importBuffer: promote weak_ptr failed.");
+    token_map_.erase(iter);
+    return DEAD_OBJECT;
+  }
+
+  sp<BufferClient> client = new BufferClient(node, this);
+  *outClient = client;
+
+  token_map_.erase(iter);
+  client_list_.push_back(client);
+
+  return NO_ERROR;
+}
+
 }  // namespace dvr
 }  // namespace android
diff --git a/services/vr/bufferhubd/include/private/dvr/IBufferHub.h b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
index bd5f9cf..502c6d6 100644
--- a/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
+++ b/services/vr/bufferhubd/include/private/dvr/IBufferHub.h
@@ -17,6 +17,9 @@
                                          uint32_t layer_count, uint32_t format,
                                          uint64_t usage,
                                          uint64_t user_metadata_size) = 0;
+
+  virtual status_t importBuffer(uint64_t token,
+                                sp<IBufferClient>* outClient) = 0;
 };
 
 class BnBufferHub : public BnInterface<IBufferHub> {
diff --git a/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
index 6c74cc4..cf6124b 100644
--- a/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
+++ b/services/vr/bufferhubd/include/private/dvr/buffer_hub_binder.h
@@ -33,6 +33,8 @@
                                  uint64_t usage,
                                  uint64_t user_metadata_size) override;
 
+  status_t importBuffer(uint64_t token, sp<IBufferClient>* outClient) override;
+
  private:
   std::shared_ptr<BufferHubService> pdx_service_;
 
diff --git a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
index 393f13e..7c00fa6 100644
--- a/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
+++ b/services/vr/bufferhubd/tests/buffer_hub_binder_service-test.cpp
@@ -10,6 +10,7 @@
 
 namespace {
 
+using testing::IsNull;
 using testing::NotNull;
 
 const int kWidth = 640;
@@ -38,21 +39,44 @@
   EXPECT_TRUE(bufferClient->isValid());
 }
 
-TEST_F(BufferHubBinderServiceTest, TestDuplicateBuffer) {
+TEST_F(BufferHubBinderServiceTest, TestDuplicateAndImportBuffer) {
   sp<IBufferClient> bufferClient = service->createBuffer(
       kWidth, kHeight, kLayerCount, kFormat, kUsage, kUserMetadataSize);
-  EXPECT_THAT(bufferClient, NotNull());
+  ASSERT_THAT(bufferClient, NotNull());
   EXPECT_TRUE(bufferClient->isValid());
 
   uint64_t token1 = 0ULL;
   status_t ret = bufferClient->duplicate(&token1);
   EXPECT_EQ(ret, NO_ERROR);
 
-  // Should be different
+  // Tokens should be unique even corresponding to the same buffer
   uint64_t token2 = 0ULL;
   ret = bufferClient->duplicate(&token2);
   EXPECT_EQ(ret, NO_ERROR);
   EXPECT_NE(token2, token1);
+
+  sp<IBufferClient> bufferClient1;
+  ret = service->importBuffer(token1, &bufferClient1);
+  EXPECT_EQ(ret, NO_ERROR);
+  ASSERT_THAT(bufferClient1, NotNull());
+  EXPECT_TRUE(bufferClient1->isValid());
+
+  // Consumes the token to keep the service clean
+  sp<IBufferClient> bufferClient2;
+  ret = service->importBuffer(token2, &bufferClient2);
+  EXPECT_EQ(ret, NO_ERROR);
+  ASSERT_THAT(bufferClient2, NotNull());
+  EXPECT_TRUE(bufferClient2->isValid());
+}
+
+TEST_F(BufferHubBinderServiceTest, TestImportUnexistingToken) {
+  // There is very little chance that this test fails if there is a token = 0
+  // in the service.
+  uint64_t unexistingToken = 0ULL;
+  sp<IBufferClient> bufferClient;
+  status_t ret = service->importBuffer(unexistingToken, &bufferClient);
+  EXPECT_EQ(ret, PERMISSION_DENIED);
+  EXPECT_THAT(bufferClient, IsNull());
 }
 
 }  // namespace