Add C++ implementation of ParcelFileDescriptor

Bug: 80377815
Test: make binderLibTest && adb sync && adb shell /data/nativetest64/binderLibTest/binderLibTest
Change-Id: I1c54a80b7bf1cf1a51987502e4d844765c20531d
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index d4db8c8..715c1c1 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -61,6 +61,7 @@
         "MemoryDealer.cpp",
         "MemoryHeapBase.cpp",
         "Parcel.cpp",
+        "ParcelFileDescriptor.cpp",
         "PermissionCache.cpp",
         "PersistableBundle.cpp",
         "ProcessInfoService.cpp",
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index f739f07..e221c6d 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -1179,6 +1179,19 @@
     return writeFileDescriptor(fd, takeOwnership);
 }
 
+status_t Parcel::writeDupParcelFileDescriptor(int fd)
+{
+    int dupFd = fcntl(fd, F_DUPFD_CLOEXEC, 0);
+    if (dupFd < 0) {
+        return -errno;
+    }
+    status_t err = writeParcelFileDescriptor(dupFd, true /*takeOwnership*/);
+    if (err != OK) {
+        close(dupFd);
+    }
+    return err;
+}
+
 status_t Parcel::writeUniqueFileDescriptor(const base::unique_fd& fd) {
     return writeDupFileDescriptor(fd.get());
 }
@@ -2167,6 +2180,22 @@
     return OK;
 }
 
+status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const
+{
+    int got = readParcelFileDescriptor();
+
+    if (got == BAD_TYPE) {
+        return BAD_TYPE;
+    }
+
+    val->reset(fcntl(got, F_DUPFD_CLOEXEC, 0));
+
+    if (val->get() < 0) {
+        return BAD_VALUE;
+    }
+
+    return OK;
+}
 
 status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const {
     return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor);
diff --git a/libs/binder/ParcelFileDescriptor.cpp b/libs/binder/ParcelFileDescriptor.cpp
new file mode 100644
index 0000000..4f8b76f
--- /dev/null
+++ b/libs/binder/ParcelFileDescriptor.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#include <binder/ParcelFileDescriptor.h>
+
+namespace android {
+namespace os {
+
+ParcelFileDescriptor::ParcelFileDescriptor() = default;
+
+ParcelFileDescriptor::ParcelFileDescriptor(android::base::unique_fd fd) : mFd(std::move(fd)) {}
+
+ParcelFileDescriptor::~ParcelFileDescriptor() = default;
+
+status_t ParcelFileDescriptor::writeToParcel(Parcel* parcel) const {
+    return parcel->writeDupParcelFileDescriptor(mFd.get());
+}
+
+status_t ParcelFileDescriptor::readFromParcel(const Parcel* parcel) {
+    return parcel->readUniqueParcelFileDescriptor(&mFd);
+}
+
+} // namespace os
+} // namespace android
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index 5d36526..b9a3ae6 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -205,6 +205,10 @@
     // The Parcel does not take ownership of the given fd unless you ask it to.
     status_t            writeParcelFileDescriptor(int fd, bool takeOwnership = false);
 
+    // Place a Java "parcel file descriptor" into the parcel.  A dup of the fd is made, which will
+    // be closed once the parcel is destroyed.
+    status_t            writeDupParcelFileDescriptor(int fd);
+
     // Place a file descriptor into the parcel.  This will not affect the
     // semantics of the smart file descriptor. A new descriptor will be
     // created, and will be closed when the parcel is destroyed.
@@ -364,6 +368,9 @@
     status_t            readUniqueFileDescriptor(
                             base::unique_fd* val) const;
 
+    // Retrieve a Java "parcel file descriptor" from the parcel.
+    status_t            readUniqueParcelFileDescriptor(base::unique_fd* val) const;
+
 
     // Retrieve a vector of smart file descriptors from the parcel.
     status_t            readUniqueFileDescriptorVector(
diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h
new file mode 100644
index 0000000..455462b
--- /dev/null
+++ b/libs/binder/include/binder/ParcelFileDescriptor.h
@@ -0,0 +1,51 @@
+/*
+ * 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_PARCEL_FILE_DESCRIPTOR_H_
+#define ANDROID_PARCEL_FILE_DESCRIPTOR_H_
+
+#include <android-base/unique_fd.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+namespace android {
+namespace os {
+
+/*
+ * C++ implementation of the Java class android.os.ParcelFileDescriptor
+ */
+class ParcelFileDescriptor : public android::Parcelable {
+public:
+    ParcelFileDescriptor();
+    explicit ParcelFileDescriptor(android::base::unique_fd fd);
+    ~ParcelFileDescriptor() override;
+
+    int get() const { return mFd.get(); }
+    android::base::unique_fd release() { return std::move(mFd); }
+    void reset(android::base::unique_fd fd = android::base::unique_fd()) { mFd = std::move(fd); }
+
+    // android::Parcelable override:
+    android::status_t writeToParcel(android::Parcel* parcel) const override;
+    android::status_t readFromParcel(const android::Parcel* parcel) override;
+
+private:
+    android::base::unique_fd mFd;
+};
+
+} // namespace os
+} // namespace android
+
+#endif // ANDROID_OS_PARCEL_FILE_DESCRIPTOR_H_
diff --git a/libs/binder/tests/binderLibTest.cpp b/libs/binder/tests/binderLibTest.cpp
index 1611e11..fb9b3fe 100644
--- a/libs/binder/tests/binderLibTest.cpp
+++ b/libs/binder/tests/binderLibTest.cpp
@@ -65,6 +65,7 @@
     BINDER_LIB_TEST_ADD_STRONG_REF_TRANSACTION,
     BINDER_LIB_TEST_LINK_DEATH_TRANSACTION,
     BINDER_LIB_TEST_WRITE_FILE_TRANSACTION,
+    BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION,
     BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION,
     BINDER_LIB_TEST_EXIT_TRANSACTION,
     BINDER_LIB_TEST_DELAYED_EXIT_TRANSACTION,
@@ -763,6 +764,41 @@
     close(pipefd[0]);
 }
 
+TEST_F(BinderLibTest, PassParcelFileDescriptor) {
+    const int datasize = 123;
+    std::vector<uint8_t> writebuf(datasize);
+    for (size_t i = 0; i < writebuf.size(); ++i) {
+        writebuf[i] = i;
+    }
+
+    android::base::unique_fd read_end, write_end;
+    {
+        int pipefd[2];
+        ASSERT_EQ(0, pipe2(pipefd, O_NONBLOCK));
+        read_end.reset(pipefd[0]);
+        write_end.reset(pipefd[1]);
+    }
+    {
+        Parcel data;
+        EXPECT_EQ(NO_ERROR, data.writeDupParcelFileDescriptor(write_end.get()));
+        write_end.reset();
+        EXPECT_EQ(NO_ERROR, data.writeInt32(datasize));
+        EXPECT_EQ(NO_ERROR, data.write(writebuf.data(), datasize));
+
+        Parcel reply;
+        EXPECT_EQ(NO_ERROR,
+                  m_server->transact(BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION, data,
+                                     &reply));
+    }
+    std::vector<uint8_t> readbuf(datasize);
+    EXPECT_EQ(datasize, read(read_end.get(), readbuf.data(), datasize));
+    EXPECT_EQ(writebuf, readbuf);
+
+    waitForReadData(read_end.get(), 5000); /* wait for other proccess to close pipe */
+
+    EXPECT_EQ(0, read(read_end.get(), readbuf.data(), datasize));
+}
+
 TEST_F(BinderLibTest, PromoteLocal) {
     sp<IBinder> strong = new BBinder();
     wp<IBinder> weak = strong;
@@ -1138,6 +1174,28 @@
                     return UNKNOWN_ERROR;
                 return NO_ERROR;
             }
+            case BINDER_LIB_TEST_WRITE_PARCEL_FILE_DESCRIPTOR_TRANSACTION: {
+                int ret;
+                int32_t size;
+                const void *buf;
+                android::base::unique_fd fd;
+
+                ret = data.readUniqueParcelFileDescriptor(&fd);
+                if (ret != NO_ERROR) {
+                    return ret;
+                }
+                ret = data.readInt32(&size);
+                if (ret != NO_ERROR) {
+                    return ret;
+                }
+                buf = data.readInplace(size);
+                if (buf == NULL) {
+                    return BAD_VALUE;
+                }
+                ret = write(fd.get(), buf, size);
+                if (ret != size) return UNKNOWN_ERROR;
+                return NO_ERROR;
+            }
             case BINDER_LIB_TEST_PROMOTE_WEAK_REF_TRANSACTION: {
                 int ret;
                 wp<IBinder> weak;
@@ -1300,4 +1358,3 @@
     ProcessState::self()->startThreadPool();
     return RUN_ALL_TESTS();
 }
-