Merge changes from topic "java_native_handles"

* changes:
  Testing native handles with non-zero FDs
  Augmenting hidl-gen to support native handles in Java
diff --git a/CompoundType.cpp b/CompoundType.cpp
index 54baa84..c2a0f34 100644
--- a/CompoundType.cpp
+++ b/CompoundType.cpp
@@ -1829,18 +1829,7 @@
 
     out.indent(2);
 
-    bool useName = false;
-    for (const auto &field : *mFields) {
-        if (field->type().useNameInEmitReaderWriterEmbedded(isReader)) {
-            useName = true;
-            break;
-        }
-    }
-    std::string name = useName ? "obj" : "/* obj */";
-    // if not useName, then obj  should not be used at all,
-    // then the #error should not be emitted.
-    std::string error = useName ? "" : "\n#error\n";
-
+    const std::string name = "obj";
     if (isReader) {
         out << "const " << space << localName() << " &" << name << ",\n";
         out << "const ::android::hardware::Parcel &parcel,\n";
@@ -1876,8 +1865,8 @@
         }
 
         const std::string fieldName = (mStyle == STYLE_SAFE_UNION)
-                                        ? (name + "." + field->name() + "()" + error)
-                                        : (name + "." + field->name() + error);
+                                        ? (name + "." + field->name() + "()")
+                                        : (name + "." + field->name());
 
         const std::string fieldOffset = (mStyle == STYLE_SAFE_UNION)
                                         ? (name + ".hidl_getUnionOffset() " +
diff --git a/HandleType.cpp b/HandleType.cpp
index 7b6545d..56660a6 100644
--- a/HandleType.cpp
+++ b/HandleType.cpp
@@ -51,6 +51,14 @@
     }
 }
 
+std::string HandleType::getJavaType(bool /* forInitializer */) const {
+    return "android.os.NativeHandle";
+}
+
+std::string HandleType::getJavaSuffix() const {
+    return "NativeHandle";
+}
+
 std::string HandleType::getVtsType() const {
     return "TYPE_HANDLE";
 }
@@ -89,15 +97,11 @@
     }
 }
 
-bool HandleType::useNameInEmitReaderWriterEmbedded(bool isReader) const {
-    return !isReader;
-}
-
 void HandleType::emitReaderWriterEmbedded(
         Formatter &out,
         size_t /* depth */,
         const std::string &name,
-        const std::string &sanitizedName,
+        const std::string & /* sanitizedName */,
         bool nameIsPointer,
         const std::string &parcelObj,
         bool parcelObjIsPointer,
@@ -105,52 +109,65 @@
         ErrorMode mode,
         const std::string &parentName,
         const std::string &offsetText) const {
+    emitReaderWriterEmbeddedForTypeName(
+            out,
+            name,
+            nameIsPointer,
+            parcelObj,
+            parcelObjIsPointer,
+            isReader,
+            mode,
+            parentName,
+            offsetText,
+            "::android::hardware::hidl_handle",
+            "" /* childName */,
+            "::android::hardware");
+}
+
+void HandleType::emitJavaFieldInitializer(
+        Formatter &out, const std::string &fieldName) const {
+    const std::string fieldDeclaration = getJavaType(false) + " " + fieldName;
+    emitJavaFieldDefaultInitialValue(out, fieldDeclaration);
+}
+
+void HandleType::emitJavaFieldDefaultInitialValue(
+        Formatter &out, const std::string &declaredFieldName) const {
+    out << declaredFieldName
+        << " = new "
+        << getJavaType(true)
+        << "();\n";
+}
+
+void HandleType::emitJavaFieldReaderWriter(
+        Formatter &out,
+        size_t /* depth */,
+        const std::string &parcelName,
+        const std::string &blobName,
+        const std::string &fieldName,
+        const std::string &offset,
+        bool isReader) const {
     if (isReader) {
-        const std::string ptrName = "_hidl_" + sanitizedName  + "_ptr";
+        out << fieldName
+            << " = "
+            << parcelName
+            << ".readEmbeddedNativeHandle(\n";
 
-        out << "const native_handle_t *"
-            << ptrName << ";\n"
-            << "_hidl_err = "
-            << parcelObj
-            << (parcelObjIsPointer ? "->" : ".")
-            << "readNullableEmbeddedNativeHandle(\n";
+        out.indent(2, [&] {
+            out << blobName
+                << ".handle(),\n"
+                << offset
+                << " + 0 /* offsetof(hidl_handle, mHandle) */);\n\n";
+        });
 
-        out.indent();
-        out.indent();
-
-        out << parentName
-            << ",\n"
-            << offsetText
-            << ",\n"
-            << "&" << ptrName
-            << "\n"
-            << ");\n\n";
-
-        out.unindent();
-        out.unindent();
-
-        handleError(out, mode);
-    } else {
-        out << "_hidl_err = "
-            << parcelObj
-            << (parcelObjIsPointer ? "->" : ".")
-            << "writeEmbeddedNativeHandle(\n";
-
-        out.indent();
-        out.indent();
-
-        out << (nameIsPointer ? ("*" + name) : name)
-            << ",\n"
-            << parentName
-            << ",\n"
-            << offsetText
-            << ");\n\n";
-
-        out.unindent();
-        out.unindent();
-
-        handleError(out, mode);
+        return;
     }
+
+    out << blobName
+        << ".putNativeHandle("
+        << offset
+        << ", "
+        << fieldName
+        << ");\n";
 }
 
 bool HandleType::needsEmbeddedReadWrite() const {
@@ -158,7 +175,7 @@
 }
 
 bool HandleType::deepIsJavaCompatible(std::unordered_set<const Type*>* /* visited */) const {
-    return false;
+    return true;
 }
 
 static HidlTypeAssertion assertion("hidl_handle", 16 /* size */);
diff --git a/HandleType.h b/HandleType.h
index feaddb0..8d3a984 100644
--- a/HandleType.h
+++ b/HandleType.h
@@ -33,6 +33,10 @@
             StorageMode mode,
             bool specifyNamespaces) const override;
 
+    std::string getJavaType(bool forInitializer) const override;
+
+    std::string getJavaSuffix() const override;
+
     std::string getVtsType() const override;
 
     void emitReaderWriter(
@@ -56,12 +60,25 @@
             const std::string &parentName,
             const std::string &offsetText) const override;
 
+    void emitJavaFieldInitializer(
+            Formatter &out, const std::string &fieldName) const override;
+
+    void emitJavaFieldDefaultInitialValue(
+            Formatter &out, const std::string &declaredFieldName) const override;
+
+    void emitJavaFieldReaderWriter(
+            Formatter &out,
+            size_t depth,
+            const std::string &parcelName,
+            const std::string &blobName,
+            const std::string &fieldName,
+            const std::string &offset,
+            bool isReader) const override;
+
     bool needsEmbeddedReadWrite() const override;
 
     bool deepIsJavaCompatible(std::unordered_set<const Type*>* visited) const override;
 
-    bool useNameInEmitReaderWriterEmbedded(bool isReader) const override;
-
     void getAlignmentAndSize(size_t *align, size_t *size) const override;
 
     void emitVtsTypeDeclarations(Formatter& out) const override;
diff --git a/Type.cpp b/Type.cpp
index c0e8fa8..6dba5c9 100644
--- a/Type.cpp
+++ b/Type.cpp
@@ -472,10 +472,6 @@
     return needsResolveReferences();
 }
 
-bool Type::useNameInEmitReaderWriterEmbedded(bool) const {
-    return needsEmbeddedReadWrite();
-}
-
 void Type::emitReaderWriterEmbedded(
         Formatter &,
         size_t,
diff --git a/Type.h b/Type.h
index 2d79915..0cac877 100644
--- a/Type.h
+++ b/Type.h
@@ -239,8 +239,6 @@
 
     virtual bool useParentInEmitResolveReferencesEmbedded() const;
 
-    virtual bool useNameInEmitReaderWriterEmbedded(bool isReader) const;
-
     virtual void emitJavaReaderWriter(
             Formatter &out,
             const std::string &parcelObj,
diff --git a/test/hidl_test/hidl_test_client.cpp b/test/hidl_test/hidl_test_client.cpp
index a58ef8e..525f5f9 100644
--- a/test/hidl_test/hidl_test_client.cpp
+++ b/test/hidl_test/hidl_test_client.cpp
@@ -3,6 +3,7 @@
 #include "FooCallback.h"
 #include "hidl_test.h"
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 
 #include <android/hidl/manager/1.0/IServiceNotification.h>
@@ -58,6 +59,7 @@
 #include <random>
 #include <set>
 #include <sstream>
+#include <sys/stat.h>
 #include <thread>
 #include <type_traits>
 #include <unordered_set>
@@ -107,6 +109,7 @@
 using ::android::wp;
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_memory;
 using ::android::hardware::hidl_string;
 using ::android::hardware::hidl_vec;
@@ -144,6 +147,7 @@
 using ::android::hidl::token::V1_0::ITokenManager;
 using std::to_string;
 
+using HandleTypeSafeUnion = ISafeUnion::HandleTypeSafeUnion;
 using InterfaceTypeSafeUnion = ISafeUnion::InterfaceTypeSafeUnion;
 using LargeSafeUnion = ISafeUnion::LargeSafeUnion;
 using SmallSafeUnion = ISafeUnion::SmallSafeUnion;
@@ -2063,6 +2067,196 @@
         }));
 }
 
+// does not check for fd equality
+static void checkNativeHandlesDataEquality(const native_handle_t* reference,
+                                           const native_handle_t* result) {
+    if (reference == nullptr || result == nullptr) {
+        EXPECT_EQ(reference == nullptr, result == nullptr);
+        return;
+    }
+
+    ASSERT_NE(reference, result);
+    ASSERT_EQ(reference->version, result->version);
+    EXPECT_EQ(reference->numFds, result->numFds);
+    EXPECT_EQ(reference->numInts, result->numInts);
+
+    int offset = reference->numFds;
+    int numInts = reference->numInts;
+    EXPECT_ARRAYEQ(&(reference->data[offset]), &(result->data[offset]), numInts);
+}
+
+TEST_F(HidlTest, SafeUnionNullHandleTest) {
+    HandleTypeSafeUnion safeUnion;
+
+    EXPECT_OK(safeunionInterface->setHandleA(
+        safeUnion, hidl_handle(nullptr), [&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::a,
+                      safeUnion.getDiscriminator());
+
+            checkNativeHandlesDataEquality(nullptr, safeUnion.a().getNativeHandle());
+        }));
+}
+
+TEST_F(HidlTest, SafeUnionSimpleHandleTest) {
+    const std::array<int, 6> testData{2, -32, 10, -4329454, 11, 24};
+    native_handle_t* h = native_handle_create(0, testData.size());
+    ASSERT_EQ(sizeof(testData), testData.size() * sizeof(int));
+    std::memcpy(h->data, testData.data(), sizeof(testData));
+
+    std::array<hidl_handle, 5> testArray;
+    for (size_t i = 0; i < testArray.size(); i++) {
+        testArray[i].setTo(native_handle_clone(h), true /* shouldOwn */);
+    }
+
+    std::vector<hidl_handle> testVector(256);
+    for (size_t i = 0; i < testVector.size(); i++) {
+        testVector[i].setTo(native_handle_clone(h), true /* shouldOwn */);
+    }
+
+    EXPECT_OK(
+        safeunionInterface->newHandleTypeSafeUnion([&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_OK(safeunionInterface->setHandleA(
+                safeUnion, hidl_handle(h), [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::a,
+                              safeUnion.getDiscriminator());
+
+                    checkNativeHandlesDataEquality(h, safeUnion.a().getNativeHandle());
+                }));
+
+            EXPECT_OK(safeunionInterface->setHandleB(
+                safeUnion, testArray, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::b,
+                              safeUnion.getDiscriminator());
+
+                    for (size_t i = 0; i < testArray.size(); i++) {
+                        checkNativeHandlesDataEquality(h, safeUnion.b()[i].getNativeHandle());
+                    }
+                }));
+
+            EXPECT_OK(safeunionInterface->setHandleC(
+                safeUnion, testVector, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::c,
+                              safeUnion.getDiscriminator());
+
+                    for (size_t i = 0; i < testVector.size(); i++) {
+                        checkNativeHandlesDataEquality(h, safeUnion.c()[i].getNativeHandle());
+                    }
+                }));
+        }));
+
+    native_handle_delete(h);
+}
+
+TEST_F(HidlTest, SafeUnionVecOfHandlesWithOneFdTest) {
+    const std::vector<std::string> testStrings{"This ", "is ", "so ", "much ", "data!\n"};
+    const std::string testFileName = "/data/local/tmp/SafeUnionVecOfHandlesWithOneFdTest";
+    const std::array<int, 6> testData{2, -32, 10, -4329454, 11, 24};
+    ASSERT_EQ(sizeof(testData), testData.size() * sizeof(int));
+
+    const std::string goldenResult = std::accumulate(testStrings.begin(),
+                                                     testStrings.end(),
+                                                     std::string());
+
+    int fd = open(testFileName.c_str(), (O_RDWR | O_TRUNC | O_CREAT), (S_IRUSR | S_IWUSR));
+    ASSERT_TRUE(fd >= 0);
+
+    native_handle* h = native_handle_create(1 /* numFds */, testData.size() /* numInts */);
+    std::memcpy(&(h->data[1]), testData.data(), sizeof(testData));
+    h->data[0] = fd;
+
+    hidl_vec<hidl_handle> testHandles(testStrings.size());
+    for (size_t i = 0; i < testHandles.size(); i++) {
+        testHandles[i].setTo(native_handle_clone(h), true /* shouldOwn */);
+    }
+
+    EXPECT_OK(
+        safeunionInterface->newHandleTypeSafeUnion([&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_OK(safeunionInterface->setHandleC(
+                safeUnion, testHandles, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::c,
+                              safeUnion.getDiscriminator());
+
+                    for (size_t i = 0; i < safeUnion.c().size(); i++) {
+                        const native_handle_t* reference = testHandles[i].getNativeHandle();
+                        const native_handle_t* result = safeUnion.c()[i].getNativeHandle();
+                        checkNativeHandlesDataEquality(reference, result);
+
+                        // Original FDs should be dup'd
+                        int resultFd = result->data[0];
+                        EXPECT_NE(reference->data[0], resultFd);
+
+                        EXPECT_TRUE(android::base::WriteStringToFd(testStrings[i], resultFd));
+                        EXPECT_EQ(0, fsync(resultFd));
+                    }
+                }));
+        }));
+
+    std::string result;
+    lseek(fd, 0, SEEK_SET);
+
+    EXPECT_TRUE(android::base::ReadFdToString(fd, &result));
+    EXPECT_EQ(goldenResult, result);
+
+    native_handle_delete(h);
+    EXPECT_EQ(0, close(fd));
+    EXPECT_EQ(0, remove(testFileName.c_str()));
+}
+
+TEST_F(HidlTest, SafeUnionHandleWithMultipleFdsTest) {
+    const std::vector<std::string> testStrings{"This ", "is ", "so ", "much ", "data!\n"};
+    const std::string testFileName = "/data/local/tmp/SafeUnionHandleWithMultipleFdsTest";
+    const std::array<int, 6> testData{2, -32, 10, -4329454, 11, 24};
+    ASSERT_EQ(sizeof(testData), testData.size() * sizeof(int));
+
+    const std::string goldenResult = std::accumulate(testStrings.begin(),
+                                                     testStrings.end(),
+                                                     std::string());
+
+    int fd = open(testFileName.c_str(), (O_RDWR | O_TRUNC | O_CREAT), (S_IRUSR | S_IWUSR));
+    ASSERT_TRUE(fd >= 0);
+
+    const int numFds = testStrings.size();
+    native_handle* h = native_handle_create(numFds, testData.size() /* numInts */);
+    std::memcpy(&(h->data[numFds]), testData.data(), sizeof(testData));
+    for (size_t i = 0; i < numFds; i++) {
+        h->data[i] = fd;
+    }
+
+    hidl_handle testHandle;
+    testHandle.setTo(h, false /* shouldOwn */);
+
+    EXPECT_OK(
+        safeunionInterface->newHandleTypeSafeUnion([&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_OK(safeunionInterface->setHandleA(
+                safeUnion, testHandle, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::a,
+                              safeUnion.getDiscriminator());
+
+                    const native_handle_t* result = safeUnion.a().getNativeHandle();
+                    checkNativeHandlesDataEquality(h, result);
+
+                    for (size_t i = 0; i < result->numFds; i++) {
+                        // Original FDs should be dup'd
+                        int resultFd = result->data[i];
+                        EXPECT_NE(h->data[i], resultFd);
+
+                        EXPECT_TRUE(android::base::WriteStringToFd(testStrings[i], resultFd));
+                        EXPECT_EQ(0, fsync(resultFd));
+                    }
+                }));
+        }));
+
+    std::string result;
+    lseek(fd, 0, SEEK_SET);
+
+    EXPECT_TRUE(android::base::ReadFdToString(fd, &result));
+    EXPECT_EQ(goldenResult, result);
+
+    native_handle_delete(h);
+    EXPECT_EQ(0, close(fd));
+    EXPECT_EQ(0, remove(testFileName.c_str()));
+}
+
 TEST_F(HidlTest, SafeUnionEqualityTest) {
     EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& one) {
         EXPECT_OK(safeunionInterface->newLargeSafeUnion([&](const LargeSafeUnion& two) {
diff --git a/test/java_test/hidl_test_java_native.cpp b/test/java_test/hidl_test_java_native.cpp
index bccefd4..04b6a87 100644
--- a/test/java_test/hidl_test_java_native.cpp
+++ b/test/java_test/hidl_test_java_native.cpp
@@ -17,6 +17,7 @@
 //#define LOG_NDEBUG 0
 #define LOG_TAG "hidl_test_java_native"
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 
 #include <android/hardware/tests/baz/1.0/IBaz.h>
@@ -29,6 +30,10 @@
 
 #include <hidl/HidlTransportSupport.h>
 #include <hidl/Status.h>
+
+#include <numeric>
+#include <sys/stat.h>
+
 using ::android::sp;
 using ::android::hardware::tests::baz::V1_0::IBase;
 using ::android::hardware::tests::baz::V1_0::IBaz;
@@ -38,11 +43,13 @@
 
 using ::android::hardware::hidl_array;
 using ::android::hardware::hidl_vec;
+using ::android::hardware::hidl_handle;
 using ::android::hardware::hidl_string;
 using ::android::hardware::defaultPassthroughServiceImplementation;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
 
+using HandleTypeSafeUnion = ISafeUnion::HandleTypeSafeUnion;
 using InterfaceTypeSafeUnion = ISafeUnion::InterfaceTypeSafeUnion;
 using LargeSafeUnion = ISafeUnion::LargeSafeUnion;
 using SmallSafeUnion = ISafeUnion::SmallSafeUnion;
@@ -122,6 +129,14 @@
     EXPECT_TRUE(ret.isOk());
 }
 
+template<typename T, typename S>
+static inline bool isArrayEqual(const T arr1, const S arr2, size_t size) {
+    for(size_t i = 0; i < size; i++)
+        if(arr1[i] != arr2[i])
+            return false;
+    return true;
+}
+
 TEST_F(HidlTest, GetDescriptorTest) {
     EXPECT_OK(baz->interfaceDescriptor([&] (const auto &desc) {
         EXPECT_EQ(desc, IBaz::descriptor);
@@ -673,12 +688,52 @@
     }));
 }
 
+// does not check for fd equality
+static void checkNativeHandlesDataEquality(const native_handle_t* reference,
+                                       const native_handle_t* result) {
+    if (reference == nullptr || result == nullptr) {
+        EXPECT_EQ(reference == nullptr, result == nullptr);
+        return;
+    }
+
+    ASSERT_NE(reference, result);
+    ASSERT_EQ(reference->version, result->version);
+    EXPECT_EQ(reference->numFds, result->numFds);
+    EXPECT_EQ(reference->numInts, result->numInts);
+
+    int offset = reference->numFds;
+    int numInts = reference->numInts;
+    EXPECT_TRUE(isArrayEqual(&(reference->data[offset]), &(result->data[offset]), numInts));
+}
+
+TEST_F(HidlTest, SafeUnionInterfaceNullHandleTest) {
+    InterfaceTypeSafeUnion safeUnion;
+
+    EXPECT_OK(safeunionInterface->setInterfaceF(
+        safeUnion, hidl_handle(nullptr), [&](const InterfaceTypeSafeUnion& safeUnion) {
+            EXPECT_EQ(InterfaceTypeSafeUnion::hidl_discriminator::f,
+                      safeUnion.getDiscriminator());
+
+            checkNativeHandlesDataEquality(nullptr, safeUnion.f().getNativeHandle());
+        }));
+}
+
 TEST_F(HidlTest, SafeUnionInterfaceTest) {
     const std::array<int8_t, 7> testArray{-1, -2, -3, 0, 1, 2, 3};
     const hidl_vec<hidl_string> testVector{"So", "Many", "Words"};
     const std::string testStringA = "Hello";
     const std::string testStringB = "World";
 
+    const std::array<int, 6> testHandleData{2, -32, 10, -4329454, 11, 24};
+    native_handle_t* h = native_handle_create(0, testHandleData.size());
+    CHECK(sizeof(testHandleData) == testHandleData.size() * sizeof(int));
+    std::memcpy(h->data, testHandleData.data(), sizeof(testHandleData));
+
+    std::vector<hidl_handle> testHandlesVector(256);
+    for (size_t i = 0; i < testHandlesVector.size(); i++) {
+        testHandlesVector[i].setTo(native_handle_clone(h), true /* shouldOwn */);
+    }
+
     EXPECT_OK(
         safeunionInterface->newInterfaceTypeSafeUnion([&](const InterfaceTypeSafeUnion& safeUnion) {
             EXPECT_EQ(InterfaceTypeSafeUnion::hidl_discriminator::hidl_no_init,
@@ -707,6 +762,25 @@
                               safeUnion.getDiscriminator());
                     EXPECT_EQ(testVector, safeUnion.e());
                 }));
+
+            EXPECT_OK(safeunionInterface->setInterfaceF(
+                safeUnion, hidl_handle(h), [&](const InterfaceTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(InterfaceTypeSafeUnion::hidl_discriminator::f,
+                              safeUnion.getDiscriminator());
+
+                    const native_handle_t* result = safeUnion.f().getNativeHandle();
+                    checkNativeHandlesDataEquality(h, result);
+                }));
+
+            EXPECT_OK(safeunionInterface->setInterfaceG(
+                safeUnion, testHandlesVector, [&](const InterfaceTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(InterfaceTypeSafeUnion::hidl_discriminator::g,
+                              safeUnion.getDiscriminator());
+
+                    for (size_t i = 0; i < testHandlesVector.size(); i++) {
+                        checkNativeHandlesDataEquality(h, safeUnion.g()[i].getNativeHandle());
+                    }
+                }));
         }));
 
     // Same-process interface calls are not supported in Java, so we use
@@ -720,6 +794,180 @@
         hidl_string(testStringA), hidl_string(testStringB), [&](const hidl_string& result) {
             EXPECT_EQ(testStringA + testStringB, std::string(result));
         }));
+
+    native_handle_delete(h);
+}
+
+TEST_F(HidlTest, SafeUnionNullHandleTest) {
+    HandleTypeSafeUnion safeUnion;
+
+    EXPECT_OK(safeunionInterface->setHandleA(
+        safeUnion, hidl_handle(nullptr), [&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::a,
+                      safeUnion.getDiscriminator());
+
+            checkNativeHandlesDataEquality(nullptr, safeUnion.a().getNativeHandle());
+        }));
+}
+
+TEST_F(HidlTest, SafeUnionSimpleHandleTest) {
+    const std::array<int, 6> testData{2, -32, 10, -4329454, 11, 24};
+    native_handle_t* h = native_handle_create(0, testData.size());
+    ASSERT_EQ(sizeof(testData), testData.size() * sizeof(int));
+    std::memcpy(h->data, testData.data(), sizeof(testData));
+
+    std::array<hidl_handle, 5> testArray;
+    for (size_t i = 0; i < testArray.size(); i++) {
+        testArray[i].setTo(native_handle_clone(h), true /* shouldOwn */);
+    }
+
+    std::vector<hidl_handle> testVector(256);
+    for (size_t i = 0; i < testVector.size(); i++) {
+        testVector[i].setTo(native_handle_clone(h), true /* shouldOwn */);
+    }
+
+    EXPECT_OK(
+        safeunionInterface->newHandleTypeSafeUnion([&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_OK(safeunionInterface->setHandleA(
+                safeUnion, hidl_handle(h), [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::a,
+                              safeUnion.getDiscriminator());
+
+                    checkNativeHandlesDataEquality(h, safeUnion.a().getNativeHandle());
+                }));
+
+            EXPECT_OK(safeunionInterface->setHandleB(
+                safeUnion, testArray, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::b,
+                              safeUnion.getDiscriminator());
+
+                    for (size_t i = 0; i < testArray.size(); i++) {
+                        checkNativeHandlesDataEquality(h, safeUnion.b()[i].getNativeHandle());
+                    }
+                }));
+
+            EXPECT_OK(safeunionInterface->setHandleC(
+                safeUnion, testVector, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::c,
+                              safeUnion.getDiscriminator());
+
+                    for (size_t i = 0; i < testVector.size(); i++) {
+                        checkNativeHandlesDataEquality(h, safeUnion.c()[i].getNativeHandle());
+                    }
+                }));
+        }));
+
+    native_handle_delete(h);
+}
+
+TEST_F(HidlTest, SafeUnionVecOfHandlesWithOneFdTest) {
+    const std::vector<std::string> testStrings{"This ", "is ", "so ", "much ", "data!\n"};
+    const std::string testFileName = "/data/local/tmp/SafeUnionVecOfHandlesWithOneFdTest";
+    const std::array<int, 6> testData{2, -32, 10, -4329454, 11, 24};
+    ASSERT_EQ(sizeof(testData), testData.size() * sizeof(int));
+
+    const std::string goldenResult = std::accumulate(testStrings.begin(),
+                                                     testStrings.end(),
+                                                     std::string());
+
+    int fd = open(testFileName.c_str(), (O_RDWR | O_TRUNC | O_CREAT), (S_IRUSR | S_IWUSR));
+    ASSERT_TRUE(fd >= 0);
+
+    native_handle* h = native_handle_create(1 /* numFds */, testData.size() /* numInts */);
+    std::memcpy(&(h->data[1]), testData.data(), sizeof(testData));
+    h->data[0] = fd;
+
+    hidl_vec<hidl_handle> testHandles(testStrings.size());
+    for (size_t i = 0; i < testHandles.size(); i++) {
+        testHandles[i].setTo(native_handle_clone(h), true /* shouldOwn */);
+    }
+
+    EXPECT_OK(
+        safeunionInterface->newHandleTypeSafeUnion([&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_OK(safeunionInterface->setHandleC(
+                safeUnion, testHandles, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::c,
+                              safeUnion.getDiscriminator());
+
+                    for (size_t i = 0; i < safeUnion.c().size(); i++) {
+                        const native_handle_t* reference = testHandles[i].getNativeHandle();
+                        const native_handle_t* result = safeUnion.c()[i].getNativeHandle();
+                        checkNativeHandlesDataEquality(reference, result);
+
+                        // Original FDs should be dup'd
+                        int resultFd = result->data[0];
+                        EXPECT_NE(reference->data[0], resultFd);
+
+                        EXPECT_TRUE(android::base::WriteStringToFd(testStrings[i], resultFd));
+                        EXPECT_EQ(0, fsync(resultFd));
+                    }
+                }));
+        }));
+
+    std::string result;
+    lseek(fd, 0, SEEK_SET);
+
+    EXPECT_TRUE(android::base::ReadFdToString(fd, &result));
+    EXPECT_EQ(goldenResult, result);
+
+    native_handle_delete(h);
+    EXPECT_EQ(0, close(fd));
+    EXPECT_EQ(0, remove(testFileName.c_str()));
+}
+
+TEST_F(HidlTest, SafeUnionHandleWithMultipleFdsTest) {
+    const std::vector<std::string> testStrings{"This ", "is ", "so ", "much ", "data!\n"};
+    const std::string testFileName = "/data/local/tmp/SafeUnionHandleWithMultipleFdsTest";
+    const std::array<int, 6> testData{2, -32, 10, -4329454, 11, 24};
+    ASSERT_EQ(sizeof(testData), testData.size() * sizeof(int));
+
+    const std::string goldenResult = std::accumulate(testStrings.begin(),
+                                                     testStrings.end(),
+                                                     std::string());
+
+    int fd = open(testFileName.c_str(), (O_RDWR | O_TRUNC | O_CREAT), (S_IRUSR | S_IWUSR));
+    ASSERT_TRUE(fd >= 0);
+
+    const int numFds = testStrings.size();
+    native_handle* h = native_handle_create(numFds, testData.size() /* numInts */);
+    std::memcpy(&(h->data[numFds]), testData.data(), sizeof(testData));
+    for (size_t i = 0; i < numFds; i++) {
+        h->data[i] = fd;
+    }
+
+    hidl_handle testHandle;
+    testHandle.setTo(h, false /* shouldOwn */);
+
+    EXPECT_OK(
+        safeunionInterface->newHandleTypeSafeUnion([&](const HandleTypeSafeUnion& safeUnion) {
+            EXPECT_OK(safeunionInterface->setHandleA(
+                safeUnion, testHandle, [&](const HandleTypeSafeUnion& safeUnion) {
+                    EXPECT_EQ(HandleTypeSafeUnion::hidl_discriminator::a,
+                              safeUnion.getDiscriminator());
+
+                    const native_handle_t* result = safeUnion.a().getNativeHandle();
+                    checkNativeHandlesDataEquality(h, result);
+
+                    for (size_t i = 0; i < result->numFds; i++) {
+                        // Original FDs should be dup'd
+                        int resultFd = result->data[i];
+                        EXPECT_NE(h->data[i], resultFd);
+
+                        EXPECT_TRUE(android::base::WriteStringToFd(testStrings[i], resultFd));
+                        EXPECT_EQ(0, fsync(resultFd));
+                    }
+                }));
+        }));
+
+    std::string result;
+    lseek(fd, 0, SEEK_SET);
+
+    EXPECT_TRUE(android::base::ReadFdToString(fd, &result));
+    EXPECT_EQ(goldenResult, result);
+
+    native_handle_delete(h);
+    EXPECT_EQ(0, close(fd));
+    EXPECT_EQ(0, remove(testFileName.c_str()));
 }
 
 TEST_F(HidlTest, SafeUnionEqualityTest) {
diff --git a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
index 8999bc3..222d9e2 100644
--- a/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
+++ b/test/java_test/src/com/android/commands/hidl_test_java/HidlTestJava.java
@@ -20,18 +20,26 @@
 import android.hardware.tests.baz.V1_0.IBase;
 import android.hardware.tests.baz.V1_0.IBaz;
 import android.hardware.tests.baz.V1_0.IQuux;
+import android.hardware.tests.baz.V1_0.IBaz.MyHandle;
 import android.hardware.tests.baz.V1_0.IBaz.NestedStruct;
 import android.hardware.tests.baz.V1_0.IBazCallback;
 import android.hardware.tests.safeunion.V1_0.IOtherInterface;
 import android.hardware.tests.safeunion.V1_0.ISafeUnion;
+import android.hardware.tests.safeunion.V1_0.ISafeUnion.HandleTypeSafeUnion;
 import android.hardware.tests.safeunion.V1_0.ISafeUnion.InterfaceTypeSafeUnion;
 import android.hardware.tests.safeunion.V1_0.ISafeUnion.LargeSafeUnion;
 import android.hardware.tests.safeunion.V1_0.ISafeUnion.SmallSafeUnion;
 import android.os.HwBinder;
+import android.os.NativeHandle;
 import android.os.RemoteException;
 import android.os.HidlSupport;
 import android.util.Log;
 
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.NoSuchElementException;
@@ -51,7 +59,7 @@
         System.exit(exitCode);
     }
 
-    public int run(String[] args) throws RemoteException {
+    public int run(String[] args) throws RemoteException, IOException {
         if (args[0].equals("-c")) {
             client();
         } else if (args[0].equals("-s")) {
@@ -219,7 +227,7 @@
         ExpectTrue(!HidlSupport.deepEquals(l, r));
     }
 
-    private void runClientSafeUnionTests() throws RemoteException {
+    private void runClientSafeUnionTests() throws RemoteException, IOException {
         ISafeUnion safeunionInterface = ISafeUnion.getService();
 
         {
@@ -276,6 +284,14 @@
             ExpectTrue(safeUnion.l().a() == (byte) 1);
         }
         {
+            // SafeUnionInterfaceNullNativeHandleTest
+            InterfaceTypeSafeUnion safeUnion = new InterfaceTypeSafeUnion();
+
+            safeUnion = safeunionInterface.setInterfaceF(safeUnion, null);
+            ExpectTrue(safeUnion.getDiscriminator() == InterfaceTypeSafeUnion.hidl_discriminator.f);
+            ExpectTrue(safeUnion.f() == null);
+        }
+        {
             // SafeUnionInterfaceTest
             byte[] testArray = new byte[] {-1, -2, -3, 0, 1, 2, 3};
             ArrayList<String> testVector = new ArrayList(Arrays.asList("So", "Many", "Words"));
@@ -284,6 +300,11 @@
 
             IOtherInterface otherInterface = IOtherInterface.getService();
 
+            ArrayList<NativeHandle> testHandlesVector = new ArrayList<>();
+            for (int i = 0; i < 128; i++) {
+                testHandlesVector.add(new NativeHandle());
+            }
+
             InterfaceTypeSafeUnion safeUnion = safeunionInterface.newInterfaceTypeSafeUnion();
             safeUnion = safeunionInterface.setInterfaceB(safeUnion, testArray);
             ExpectTrue(safeUnion.getDiscriminator() == InterfaceTypeSafeUnion.hidl_discriminator.b);
@@ -302,6 +323,108 @@
             safeUnion = safeunionInterface.setInterfaceE(safeUnion, testVector);
             ExpectTrue(safeUnion.getDiscriminator() == InterfaceTypeSafeUnion.hidl_discriminator.e);
             ExpectDeepEq(testVector, safeUnion.e());
+
+            safeUnion = safeunionInterface.setInterfaceG(safeUnion, testHandlesVector);
+            ExpectTrue(safeUnion.getDiscriminator() == InterfaceTypeSafeUnion.hidl_discriminator.g);
+            ExpectTrue(safeUnion.g().size() == testHandlesVector.size());
+
+            for (int i = 0; i < testHandlesVector.size(); i++) {
+                ExpectFalse(safeUnion.g().get(i).hasSingleFileDescriptor());
+            }
+        }
+        {
+            // SafeUnionNullNativeHandleTest
+            HandleTypeSafeUnion safeUnion = new HandleTypeSafeUnion();
+
+            safeUnion = safeunionInterface.setHandleA(safeUnion, null);
+            ExpectTrue(safeUnion.getDiscriminator() == HandleTypeSafeUnion.hidl_discriminator.a);
+            ExpectTrue(safeUnion.a() == null);
+        }
+        {
+            // SafeUnionDefaultNativeHandleTest
+            NativeHandle[] testHandlesArray = new NativeHandle[5];
+            for (int i = 0; i < testHandlesArray.length; i++) {
+                testHandlesArray[i] = new NativeHandle();
+            }
+
+            ArrayList<NativeHandle> testHandlesList = new ArrayList<NativeHandle>(
+                Arrays.asList(testHandlesArray));
+
+            HandleTypeSafeUnion safeUnion = safeunionInterface.newHandleTypeSafeUnion();
+            safeUnion = safeunionInterface.setHandleA(safeUnion, new NativeHandle());
+            ExpectTrue(safeUnion.getDiscriminator() == HandleTypeSafeUnion.hidl_discriminator.a);
+            ExpectFalse(safeUnion.a().hasSingleFileDescriptor());
+
+            safeUnion = safeunionInterface.setHandleB(safeUnion, testHandlesArray);
+            ExpectTrue(safeUnion.getDiscriminator() == HandleTypeSafeUnion.hidl_discriminator.b);
+            ExpectTrue(safeUnion.b().length == testHandlesArray.length);
+
+            for (int i = 0; i < testHandlesArray.length; i++) {
+                ExpectFalse(safeUnion.b()[i].hasSingleFileDescriptor());
+            }
+
+            safeUnion = safeunionInterface.setHandleC(safeUnion, testHandlesList);
+            ExpectTrue(safeUnion.getDiscriminator() == HandleTypeSafeUnion.hidl_discriminator.c);
+            ExpectTrue(safeUnion.c().size() == testHandlesList.size());
+
+            for (int i = 0; i < testHandlesList.size(); i++) {
+                ExpectFalse(safeUnion.c().get(i).hasSingleFileDescriptor());
+            }
+        }
+        {
+            // SafeUnionNativeHandleWithFdTest
+            final String testFileName = "/data/local/tmp/SafeUnionNativeHandleWithFdTest";
+            final String[] testStrings = {"This ", "is ", "so ", "much ", "data!\n"};
+            File file = new File(testFileName);
+
+            if (file.exists()) { ExpectTrue(file.delete()); }
+            ExpectTrue(file.createNewFile());
+
+            StringBuilder builder = new StringBuilder();
+            for (String testString : testStrings) {
+                builder.append(testString);
+            }
+            final String goldenResult = builder.toString();
+
+            ArrayList<NativeHandle> testHandlesList = new ArrayList<NativeHandle>();
+            FileOutputStream fos = new FileOutputStream(file);
+            for (int i = 0; i < testStrings.length; i++) {
+                testHandlesList.add(new NativeHandle(fos.getFD(), false /*own*/));
+            }
+
+            HandleTypeSafeUnion safeUnion = safeunionInterface.newHandleTypeSafeUnion();
+            safeUnion = safeunionInterface.setHandleC(safeUnion, testHandlesList);
+            for (int i = 0; i < safeUnion.c().size(); i++) {
+                ExpectTrue(safeUnion.c().get(i).hasSingleFileDescriptor());
+
+                // If you want to copy it out of the binder buffer or save it, it needs to be duped.
+                // This isn't necessary for the test since it is kept open for the binder window.
+                NativeHandle handle = safeUnion.c().get(i);
+                if (i%2 == 0) handle = handle.dup();
+
+                // Original fd is duped if not dup'd above
+                FileDescriptor resultFd = handle.getFileDescriptor();
+                ExpectTrue(resultFd.getInt$() != fos.getFD().getInt$());
+
+                FileOutputStream otherFos = new FileOutputStream(resultFd);
+                otherFos.write(testStrings[i].getBytes());
+                otherFos.flush();
+
+                otherFos.close();
+
+                if (i%2 == 0) handle.close();
+            }
+
+            byte[] resultData = new byte[(int) file.length()];
+            FileInputStream fis = new FileInputStream(file);
+            fis.read(resultData);
+
+            String result = new String(resultData);
+            Expect(result, goldenResult);
+
+            fis.close();
+            fos.close();
+            ExpectTrue(file.delete());
         }
         {
             // SafeUnionEqualityTest
@@ -353,7 +476,7 @@
         }
     }
 
-    private void client() throws RemoteException {
+    private void client() throws RemoteException, IOException {
 
         ExpectDeepEq(null, null);
         ExpectDeepNe(null, new String());
@@ -891,7 +1014,6 @@
             ArrayList<double[]> out = proxy.testDoubleVecs(in);
             ExpectTrue(in.equals(out));
         }
-
         {
             // testProxyEquals
             // TODO(b/68727931): test passthrough services as well.
@@ -1384,6 +1506,55 @@
 
             return safeUnion;
         }
+
+        @Override
+        public InterfaceTypeSafeUnion setInterfaceF(
+            InterfaceTypeSafeUnion safeUnion, NativeHandle f) {
+            Log.d(TAG, "SERVER: setInterfaceF(" + f + ")");
+            safeUnion.f(f);
+
+            return safeUnion;
+        }
+
+        @Override
+        public InterfaceTypeSafeUnion setInterfaceG(
+            InterfaceTypeSafeUnion safeUnion, ArrayList<NativeHandle> g) {
+            Log.d(TAG, "SERVER: setInterfaceG(" + g + ")");
+            safeUnion.g(g);
+
+            return safeUnion;
+        }
+
+        @Override
+        public HandleTypeSafeUnion newHandleTypeSafeUnion() {
+            Log.d(TAG, "SERVER: newHandleTypeSafeUnion");
+            return new HandleTypeSafeUnion();
+        }
+
+        @Override
+        public HandleTypeSafeUnion setHandleA(HandleTypeSafeUnion safeUnion, NativeHandle a) {
+            Log.d(TAG, "SERVER: setHandleA(" + a + ")");
+            safeUnion.a(a);
+
+            return safeUnion;
+        }
+
+        @Override
+        public HandleTypeSafeUnion setHandleB(HandleTypeSafeUnion safeUnion, NativeHandle[] b) {
+            Log.d(TAG, "SERVER: setHandleB(" + b + ")");
+            safeUnion.b(b);
+
+            return safeUnion;
+        }
+
+        @Override
+        public HandleTypeSafeUnion setHandleC(HandleTypeSafeUnion safeUnion,
+                                              ArrayList<NativeHandle> c) {
+            Log.d(TAG, "SERVER: setHandleC(" + c + ")");
+            safeUnion.c(c);
+
+            return safeUnion;
+        }
     }
 
     class OtherInterface extends IOtherInterface.Stub {