Implement duplicate buffer

Allow BufferClient to generate a opaque token in hidl_handle (i.e.
native_handle_t) format that could be used for IBufferHub::import.

The service will keep a mapping from token to client. Token comparison
is handled in BufferHubService.cpp, as it's opaque to the users.

Test: BufferHubBuffer_test (passed)
Bug: 118614157
Change-Id: Ie2b0b650dba90fe2ed2f8091dd88acb9768f261f
diff --git a/services/bufferhub/Android.bp b/services/bufferhub/Android.bp
index 871561f..8b43333 100644
--- a/services/bufferhub/Android.bp
+++ b/services/bufferhub/Android.bp
@@ -34,6 +34,7 @@
     ],
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
+        "libcutils",
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
@@ -61,6 +62,7 @@
     shared_libs: [
         "android.frameworks.bufferhub@1.0",
         "libbufferhubservice",
+        "libcutils",
         "libhidltransport",
         "libhwbinder",
         "liblog",
diff --git a/services/bufferhub/BufferClient.cpp b/services/bufferhub/BufferClient.cpp
index b3662b2..37fd75f 100644
--- a/services/bufferhub/BufferClient.cpp
+++ b/services/bufferhub/BufferClient.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <bufferhub/BufferClient.h>
+#include <bufferhub/BufferHubService.h>
 #include <hidl/HidlSupport.h>
 
 namespace android {
@@ -26,9 +27,34 @@
 using hardware::hidl_handle;
 using hardware::Void;
 
+BufferClient* BufferClient::create(BufferHubService* service,
+                                   const std::shared_ptr<BufferNode>& node) {
+    if (!service) {
+        ALOGE("%s: service cannot be nullptr.", __FUNCTION__);
+        return nullptr;
+    } else if (!node) {
+        ALOGE("%s: node cannot be nullptr.", __FUNCTION__);
+        return nullptr;
+    }
+    return new BufferClient(service, node);
+}
+
 Return<void> BufferClient::duplicate(duplicate_cb _hidl_cb) {
-    // TODO(b/118614157): implement token generation and registration
-    _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::NO_ERROR);
+    if (!mBufferNode) {
+        // Should never happen
+        ALOGE("%s: node is missing.", __FUNCTION__);
+        _hidl_cb(/*token=*/hidl_handle(), /*status=*/BufferHubStatus::BUFFER_FREED);
+        return Void();
+    }
+
+    sp<BufferHubService> service = mService.promote();
+    if (service == nullptr) {
+        // Should never happen. Kill the process.
+        LOG_FATAL("%s: service died.", __FUNCTION__);
+    }
+
+    const hidl_handle token = service->registerToken(this);
+    _hidl_cb(/*token=*/token, /*status=*/BufferHubStatus::NO_ERROR);
     return Void();
 }
 
diff --git a/services/bufferhub/BufferHubService.cpp b/services/bufferhub/BufferHubService.cpp
index b72b556..3bfd9cb 100644
--- a/services/bufferhub/BufferHubService.cpp
+++ b/services/bufferhub/BufferHubService.cpp
@@ -16,6 +16,7 @@
 
 #include <android/hardware_buffer.h>
 #include <bufferhub/BufferHubService.h>
+#include <cutils/native_handle.h>
 #include <log/log.h>
 
 namespace android {
@@ -41,7 +42,7 @@
         return Void();
     }
 
-    sp<BufferClient> client = new BufferClient(node);
+    sp<BufferClient> client = BufferClient::create(this, node);
     // Add it to list for bookkeeping and dumpsys.
     std::lock_guard<std::mutex> lock(mClientListMutex);
     mClientList.push_back(client);
@@ -57,6 +58,25 @@
     return Void();
 }
 
+hidl_handle BufferHubService::registerToken(const BufferClient* client) {
+    uint32_t token;
+    std::lock_guard<std::mutex> lock(mTokenMapMutex);
+    do {
+        token = mTokenEngine();
+    } while (mTokenMap.find(token) != mTokenMap.end());
+
+    // native_handle_t use int[], so here need one slots to fit in uint32_t
+    native_handle_t* handle = native_handle_create(/*numFds=*/0, /*numInts=*/1);
+    handle->data[0] = token;
+
+    // returnToken owns the native_handle_t* thus doing lifecycle management
+    hidl_handle returnToken;
+    returnToken.setTo(handle, /*shoudOwn=*/true);
+
+    mTokenMap.emplace(token, client);
+    return returnToken;
+}
+
 } // namespace implementation
 } // namespace V1_0
 } // namespace bufferhub
diff --git a/services/bufferhub/include/bufferhub/BufferClient.h b/services/bufferhub/include/bufferhub/BufferClient.h
index c6eb791..5456ec3 100644
--- a/services/bufferhub/include/bufferhub/BufferClient.h
+++ b/services/bufferhub/include/bufferhub/BufferClient.h
@@ -17,6 +17,8 @@
 #ifndef ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
 #define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_CLIENT_H
 
+#include <mutex>
+
 #include <android/frameworks/bufferhub/1.0/IBufferClient.h>
 #include <bufferhub/BufferNode.h>
 
@@ -26,6 +28,7 @@
 namespace V1_0 {
 namespace implementation {
 
+using hardware::hidl_handle;
 using hardware::Return;
 
 // Forward declaration to avoid circular dependency
@@ -35,11 +38,16 @@
 public:
     // Creates a server-side buffer client from an existing BufferNode. Note that
     // this funciton takes ownership of the shared_ptr.
-    explicit BufferClient(const std::shared_ptr<BufferNode>& node) : mBufferNode(node){};
+    // Returns a raw pointer to the BufferClient on success, nullptr on failure.
+    static BufferClient* create(BufferHubService* service, const std::shared_ptr<BufferNode>& node);
 
     Return<void> duplicate(duplicate_cb _hidl_cb) override;
 
 private:
+    BufferClient(wp<BufferHubService> service, const std::shared_ptr<BufferNode>& node)
+          : mService(service), mBufferNode(node){};
+
+    wp<BufferHubService> mService;
     std::shared_ptr<BufferNode> mBufferNode;
 };
 
diff --git a/services/bufferhub/include/bufferhub/BufferHubService.h b/services/bufferhub/include/bufferhub/BufferHubService.h
index 1bbd807..dbbee8f 100644
--- a/services/bufferhub/include/bufferhub/BufferHubService.h
+++ b/services/bufferhub/include/bufferhub/BufferHubService.h
@@ -18,6 +18,7 @@
 #define ANDROID_FRAMEWORKS_BUFFERHUB_V1_0_BUFFER_HUB_SERVICE_H
 
 #include <mutex>
+#include <random>
 
 #include <android/frameworks/bufferhub/1.0/IBufferHub.h>
 #include <bufferhub/BufferClient.h>
@@ -29,9 +30,9 @@
 namespace V1_0 {
 namespace implementation {
 
-using ::android::hardware::hidl_handle;
-using ::android::hardware::Return;
-using ::android::hardware::graphics::common::V1_2::HardwareBufferDescription;
+using hardware::hidl_handle;
+using hardware::Return;
+using hardware::graphics::common::V1_2::HardwareBufferDescription;
 
 class BufferHubService : public IBufferHub {
 public:
@@ -40,10 +41,20 @@
                                 allocateBuffer_cb _hidl_cb) override;
     Return<void> importBuffer(const hidl_handle& nativeHandle, importBuffer_cb _hidl_cb) override;
 
+    // Non-binder functions
+    // Internal help function for IBufferClient::duplicate.
+    hidl_handle registerToken(const BufferClient* client);
+
 private:
     // List of active BufferClient for bookkeeping.
     std::mutex mClientListMutex;
     std::vector<sp<BufferClient>> mClientList GUARDED_BY(mClientListMutex);
+
+    // TODO(b/118180214): use a more secure implementation
+    std::mt19937 mTokenEngine;
+    // The mapping from token to the client creates it.
+    std::mutex mTokenMapMutex;
+    std::map<uint32_t, const BufferClient*> mTokenMap GUARDED_BY(mTokenMapMutex);
 };
 
 } // namespace implementation