GraphicBuffer: add optional DetachedBufferHandle

Stores a BufferHub handle that can be used to reattach this
GraphicBuffer back into a BufferHub producer/consumer set. In terms of
GraphicBuffer's relationship with BufferHub, there are three different
modes:

1. Legacy mode: GraphicBuffer is not backed by BufferHub and
   mDetachedBufferHandle must be invalid.
2. Detached mode: GraphicBuffer is backed BufferHub, but not part of a
   producer/consumer set. In this mode, mDetachedBufferHandle must be valid.
3. Attached mode: GraphicBuffer is backed BufferHub and it's part of a
   producer/consumer set. In this mode, mDetachedBufferHandle must be
   invalid.

Note that the DetachedBufferHandle is used as a opaque handle for
GraphicBuffer and it's not exposed to VNDK.

Bug: 38137191
Bug: 69982239
Bug: 70046255
Bug: 70912269
Test: GraphicBuffer_test
Change-Id: I4c3a967cb498291aa175312fdadc84edd3b1a8be
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 6899f12..e477a83 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -83,6 +83,7 @@
         "libhidlbase",
         "libhidltransport",
         "libhwbinder",
+        "libpdx_default_transport",
         "libsync",
         "libutils",
         "libutilscallstack",
@@ -99,10 +100,9 @@
         "libbase_headers",
         "libnativebase_headers",
         "libhardware_headers",
+        "libui_headers",
     ],
 
-    export_include_dirs: ["include"],
-
     export_static_lib_headers: [
         "libarect",
         "libmath",
@@ -112,6 +112,7 @@
         "libbase_headers",
         "libnativebase_headers",
         "libhardware_headers",
+        "libui_headers",
     ],
 }
 
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 4ed2aa4..254038b 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -22,6 +22,7 @@
 
 #include <grallocusage/GrallocUsageConversion.h>
 
+#include <ui/DetachedBufferHandle.h>
 #include <ui/Gralloc2.h>
 #include <ui/GraphicBufferAllocator.h>
 #include <ui/GraphicBufferMapper.h>
@@ -486,6 +487,24 @@
     return NO_ERROR;
 }
 
+bool GraphicBuffer::isDetachedBuffer() const {
+    return mDetachedBufferHandle && mDetachedBufferHandle->isValid();
+}
+
+status_t GraphicBuffer::setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> channel) {
+    if (isDetachedBuffer()) {
+        ALOGW("setDetachedBuffer: there is already a BufferHub channel associated with this "
+              "GraphicBuffer. Replacing the old one.");
+    }
+
+    mDetachedBufferHandle = std::move(channel);
+    return NO_ERROR;
+}
+
+std::unique_ptr<DetachedBufferHandle> GraphicBuffer::takeDetachedBufferHandle() {
+    return std::move(mDetachedBufferHandle);
+}
+
 // ---------------------------------------------------------------------------
 
 }; // namespace android
diff --git a/libs/ui/include/ui/DetachedBufferHandle.h b/libs/ui/include/ui/DetachedBufferHandle.h
new file mode 100644
index 0000000..f3c328d
--- /dev/null
+++ b/libs/ui/include/ui/DetachedBufferHandle.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+#ifndef ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
+#define ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
+
+#include <pdx/channel_handle.h>
+
+#include <memory>
+
+namespace android {
+
+// A wrapper that holds a pdx::LocalChannelHandle object. From the handle, a BufferHub buffer can be
+// created. Current implementation assumes that the underlying transport is using libpdx (thus
+// holding a pdx::LocalChannelHandle object), but future implementation can change it to a Binder
+// backend if ever needed.
+class DetachedBufferHandle {
+public:
+    static std::unique_ptr<DetachedBufferHandle> Create(pdx::LocalChannelHandle handle) {
+        return std::unique_ptr<DetachedBufferHandle>(new DetachedBufferHandle(std::move(handle)));
+    }
+
+    // Accessors to get or take the internal pdx::LocalChannelHandle.
+    pdx::LocalChannelHandle& handle() { return mHandle; }
+    const pdx::LocalChannelHandle& handle() const { return mHandle; }
+
+    // Returns whether the DetachedBufferHandle holds a BufferHub channel.
+    bool isValid() const { return mHandle.valid(); }
+
+private:
+    // Constructs a DetachedBufferHandle from a pdx::LocalChannelHandle.
+    explicit DetachedBufferHandle(pdx::LocalChannelHandle handle) : mHandle(std::move(handle)) {}
+
+    pdx::LocalChannelHandle mHandle;
+};
+
+} // namespace android
+
+#endif // ANDROID_DETACHED_BUFFER_HUB_HANDLE_H
diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h
index e794462..cc38982 100644
--- a/libs/ui/include/ui/GraphicBuffer.h
+++ b/libs/ui/include/ui/GraphicBuffer.h
@@ -34,6 +34,7 @@
 
 namespace android {
 
+class DetachedBufferHandle;
 class GraphicBufferMapper;
 
 // ===========================================================================
@@ -190,6 +191,11 @@
     status_t flatten(void*& buffer, size_t& size, int*& fds, size_t& count) const;
     status_t unflatten(void const*& buffer, size_t& size, int const*& fds, size_t& count);
 
+    // Sets and takes DetachedBuffer. Should only be called from BufferHub.
+    bool isDetachedBuffer() const;
+    status_t setDetachedBufferHandle(std::unique_ptr<DetachedBufferHandle> detachedBuffer);
+    std::unique_ptr<DetachedBufferHandle> takeDetachedBufferHandle();
+
 private:
     ~GraphicBuffer();
 
@@ -240,6 +246,17 @@
     // match the BufferQueue's internal generation number (set through
     // IGBP::setGenerationNumber), attempts to attach the buffer will fail.
     uint32_t mGenerationNumber;
+
+    // Stores a BufferHub handle that can be used to re-attach this GraphicBuffer back into a
+    // BufferHub producer/consumer set. In terms of GraphicBuffer's relationship with BufferHub,
+    // there are three different modes:
+    // 1. Legacy mode: GraphicBuffer is not backed by BufferHub and mDetachedBufferHandle must be
+    //    invalid.
+    // 2. Detached mode: GraphicBuffer is backed by BufferHub, but not part of a producer/consumer
+    //    set. In this mode, mDetachedBufferHandle must be valid.
+    // 3. Attached mode: GraphicBuffer is backed by BufferHub and it's part of a producer/consumer
+    //    set. In this mode, mDetachedBufferHandle must be invalid.
+    std::unique_ptr<DetachedBufferHandle> mDetachedBufferHandle;
 };
 
 }; // namespace android
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index 08067fc..aef6428 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -27,3 +27,10 @@
     srcs: ["colorspace_test.cpp"],
     cflags: ["-Wall", "-Werror"],
 }
+
+cc_test {
+    name: "GraphicBuffer_test",
+    shared_libs: ["libpdx_default_transport", "libui", "libutils"],
+    srcs: ["GraphicBuffer_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/ui/tests/GraphicBuffer_test.cpp b/libs/ui/tests/GraphicBuffer_test.cpp
new file mode 100644
index 0000000..eb679ac
--- /dev/null
+++ b/libs/ui/tests/GraphicBuffer_test.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#define LOG_TAG "GraphicBufferTest"
+
+#include <ui/DetachedBufferHandle.h>
+#include <ui/GraphicBuffer.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+namespace {
+
+constexpr uint32_t kTestWidth = 1024;
+constexpr uint32_t kTestHeight = 1;
+constexpr uint32_t kTestFormat = HAL_PIXEL_FORMAT_BLOB;
+constexpr uint32_t kTestLayerCount = 1;
+constexpr uint64_t kTestUsage = GraphicBuffer::USAGE_SW_WRITE_OFTEN;
+
+} // namespace
+
+class GraphicBufferTest : public testing::Test {};
+
+TEST_F(GraphicBufferTest, DetachedBuffer) {
+    sp<GraphicBuffer> buffer(
+            new GraphicBuffer(kTestWidth, kTestHeight, kTestFormat, kTestLayerCount, kTestUsage));
+
+    // Currently a newly allocated GraphicBuffer is in legacy mode, i.e. not associated with
+    // BufferHub. But this may change in the future.
+    EXPECT_FALSE(buffer->isDetachedBuffer());
+
+    pdx::LocalChannelHandle channel{nullptr, 1234};
+    EXPECT_TRUE(channel.valid());
+
+    std::unique_ptr<DetachedBufferHandle> handle = DetachedBufferHandle::Create(std::move(channel));
+    EXPECT_FALSE(channel.valid());
+    EXPECT_TRUE(handle->isValid());
+    EXPECT_TRUE(handle->handle().valid());
+
+    buffer->setDetachedBufferHandle(std::move(handle));
+    EXPECT_TRUE(handle == nullptr);
+    EXPECT_TRUE(buffer->isDetachedBuffer());
+
+    handle = buffer->takeDetachedBufferHandle();
+    EXPECT_TRUE(handle != nullptr);
+    EXPECT_TRUE(handle->isValid());
+    EXPECT_FALSE(buffer->isDetachedBuffer());
+}
+
+} // namespace android