Add function for converting from HIDL to AIDL MQDescriptor

Given a HIDL MQDescriptor with a payload that is compatible with AIDL,
we can create an AIDL MQDescriptor to send over an AIDL interface.
The receiving side can use the AIDL MQDescriptor to create their own
instance of the Queue and use it to communicate with the other process.
The types need to be compatible between the HIDL side and AIDL side. If
they have different memory layout, the user will encounter problems when
trying to read the types.
The function statically checks the two types sizes, but not the offset
for all of the fields in the case of parcelables and HIDL data types.
The user must validate that themselves.

Test: atest fmq_unit_tests
Bug: 170748510
Change-Id: I6fe4be2a9f947ebb13e28bceff7db9c020972002
diff --git a/include/fmq/AidlMessageQueue.h b/include/fmq/AidlMessageQueue.h
index f1dd7e2..b2e8bab 100644
--- a/include/fmq/AidlMessageQueue.h
+++ b/include/fmq/AidlMessageQueue.h
@@ -59,13 +59,16 @@
 template <typename T>
 struct has_typedef_fixed_size<T, std::void_t<typename T::fixed_size>> : T::fixed_size {};
 
+#define STATIC_AIDL_TYPE_CHECK(T)                                                                  \
+    static_assert(has_typedef_fixed_size<T>::value == true || std::is_fundamental<T>::value ||     \
+                          std::is_enum<T>::value,                                                  \
+                  "Only fundamental types, enums, and AIDL parcelables annotated with @FixedSize " \
+                  "and built for the NDK backend are supported as payload types(T).");
+
 template <typename T, typename U>
 struct AidlMessageQueue final
     : public MessageQueueBase<AidlMQDescriptorShim, T, FlavorTypeToValue<U>::value> {
-    static_assert(has_typedef_fixed_size<T>::value == true || std::is_fundamental<T>::value ||
-                          std::is_enum<T>::value,
-                  "Only fundamental types, enums, and AIDL parcelables annotated with @FixedSize "
-                  "and built for the NDK backend are supported as payload types(T).");
+    STATIC_AIDL_TYPE_CHECK(T);
     typedef AidlMQDescriptorShim<T, FlavorTypeToValue<U>::value> Descriptor;
     /**
      * This constructor uses the external descriptor used with AIDL interfaces.
diff --git a/include/fmq/ConvertMQDescriptors.h b/include/fmq/ConvertMQDescriptors.h
new file mode 100644
index 0000000..e3956c9
--- /dev/null
+++ b/include/fmq/ConvertMQDescriptors.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/common/fmq/MQDescriptor.h>
+#include <aidl/android/hardware/common/fmq/SynchronizedReadWrite.h>
+#include <aidl/android/hardware/common/fmq/UnsynchronizedWrite.h>
+#include <cutils/native_handle.h>
+#include <fmq/AidlMessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+using aidl::android::hardware::common::fmq::GrantorDescriptor;
+using aidl::android::hardware::common::fmq::MQDescriptor;
+using hardware::details::logError;
+
+/**
+ * This function converts a HIDL hardware::MQDescriptor to an AIDL
+ * aidl::android::hardware::common::fmq::MQDescriptor for Fast
+ * Message Queue.
+ *
+ * This is considered UNSAFE because it is not checking the offsets of each of the
+ * paylod types' fields. In order for these objects to be passed through shared memory safely,
+ * they must have the exact same memory layout. Same size, same alignment, and same
+ * offsets for each field. Make sure this is the case before using this!
+ * Same sized C++ fundamental types and enums with same sized backing types are OK.
+ * Ex 1: uint64_t is compatible with int64_t
+ * Ex 2:
+ * @FixedSize parcelable Foo {
+ *   int a;
+ *   long b;
+ *   MyEnum c; // backed by int32_t
+ * }
+ * struct Bar {
+ *   int a;
+ *   long b;
+ *   YourEnum c; // backed by uint32_t
+ * }
+ * The two types above are compatible with each other as long as the fields have
+ * the same offsets.
+ *
+ * Template params:
+ *    HidlPayload - the type of the payload used for the HIDL MessageQueue
+ *    AidlPayload - the type of the payload used for the AIDL AidlMessageQueue
+ *    AidlFlavor - the flavor of the queues. Either SynchronizedReadWrite,
+ *                 or UnsynchronizedWrite
+ * Function params:
+ *    hidlDesc - reference to the HIDL MQDescriptor to be copied from
+ *    aidlDesc - pointer to the AIDL MQDescriptor to be copied to
+ */
+template <typename HidlPayload, typename AidlPayload, typename AidlFlavor>
+bool unsafeHidlToAidlMQDescriptor(
+        const hardware::MQDescriptor<HidlPayload, FlavorTypeToValue<AidlFlavor>::value>& hidlDesc,
+        MQDescriptor<AidlPayload, AidlFlavor>* aidlDesc) {
+    static_assert(sizeof(HidlPayload) == sizeof(AidlPayload),
+                  "Payload types are definitely incompatible");
+    static_assert(alignof(HidlPayload) == alignof(AidlPayload),
+                  "Payload types are definitely incompatible");
+    STATIC_AIDL_TYPE_CHECK(AidlPayload);
+    if (!aidlDesc->grantors.empty()) {
+        logError("Destination AIDL MQDescriptor should be empty, but already contains grantors.");
+        return false;
+    }
+    uint32_t fdIndex = 0;
+
+    for (const auto& grantor : hidlDesc.grantors()) {
+        if (static_cast<int32_t>(grantor.offset) < 0 || static_cast<int64_t>(grantor.extent) < 0) {
+            logError(
+                    "Unsafe static_cast of grantor fields. Either the hardware::MQDescriptor is "
+                    "invalid, or the MessageQueue is too large to be described by AIDL.");
+            return false;
+        }
+        if (!aidlDesc->grantors.empty() && fdIndex != grantor.fdIndex) {
+            logError("AIDL MQDescriptor does not support multiple FDs. " + std::to_string(fdIndex) +
+                     " and " + std::to_string(grantor.fdIndex));
+            return false;
+        }
+        fdIndex = grantor.fdIndex;
+        aidlDesc->grantors.push_back(
+                GrantorDescriptor{.offset = static_cast<int32_t>(grantor.offset),
+                                  .extent = static_cast<int64_t>(grantor.extent)});
+    }
+
+    aidlDesc->fileDescriptor = ndk::ScopedFileDescriptor(dup(hidlDesc.handle()->data[fdIndex]));
+    if (static_cast<int32_t>(hidlDesc.getQuantum()) < 0 ||
+        static_cast<int32_t>(hidlDesc.getFlags()) < 0) {
+        logError(
+                "Unsafe static_cast of quantum or flags. Either the hardware::MQDescriptor is "
+                "invalid, or the MessageQueue is too large to be described by AIDL.");
+        return false;
+    }
+    if (hidlDesc.getFlags() != FlavorTypeToValue<AidlFlavor>::value) {
+        logError("hardware::MQDescriptor hidlDesc is invalid. Unexpected getFlags() value: " +
+                 std::to_string(hidlDesc.getFlags()) +
+                 ". Expected value: " + std::to_string(FlavorTypeToValue<AidlFlavor>::value));
+        return false;
+    }
+    aidlDesc->quantum = static_cast<int32_t>(hidlDesc.getQuantum());
+    aidlDesc->flags = static_cast<int32_t>(hidlDesc.getFlags());
+    return true;
+}
+
+}  // namespace android
diff --git a/tests/fmq_unit_tests.cpp b/tests/fmq_unit_tests.cpp
index 1f16189..580e2a9 100644
--- a/tests/fmq_unit_tests.cpp
+++ b/tests/fmq_unit_tests.cpp
@@ -16,6 +16,7 @@
 
 #include <asm-generic/mman.h>
 #include <fmq/AidlMessageQueue.h>
+#include <fmq/ConvertMQDescriptors.h>
 #include <fmq/EventFlag.h>
 #include <fmq/MessageQueue.h>
 #include <gtest/gtest.h>
@@ -41,6 +42,17 @@
 typedef android::AidlMessageQueue<uint16_t, SynchronizedReadWrite> AidlMessageQueueSync16;
 typedef android::hardware::MessageQueue<uint16_t, kSynchronizedReadWrite> MessageQueueSync16;
 
+typedef android::hardware::MessageQueue<uint8_t, kSynchronizedReadWrite> MessageQueueSync8;
+typedef android::hardware::MQDescriptor<uint8_t, kSynchronizedReadWrite> HidlMQDescSync8;
+typedef android::AidlMessageQueue<int8_t, SynchronizedReadWrite> AidlMessageQueueSync8;
+typedef aidl::android::hardware::common::fmq::MQDescriptor<int8_t, SynchronizedReadWrite>
+        AidlMQDescSync8;
+typedef android::hardware::MessageQueue<uint8_t, kUnsynchronizedWrite> MessageQueueUnsync8;
+typedef android::hardware::MQDescriptor<uint8_t, kUnsynchronizedWrite> HidlMQDescUnsync8;
+typedef android::AidlMessageQueue<int8_t, UnsynchronizedWrite> AidlMessageQueueUnsync8;
+typedef aidl::android::hardware::common::fmq::MQDescriptor<int8_t, UnsynchronizedWrite>
+        AidlMQDescUnsync8;
+
 // Run everything on both the AIDL and HIDL versions with sync and unsync flavors
 typedef ::testing::Types<AidlMessageQueueSync, MessageQueueSync> SyncTypes;
 typedef ::testing::Types<AidlMessageQueueUnsync, MessageQueueUnsync> UnsyncTypes;
@@ -154,6 +166,7 @@
 class BadQueueConfig : public TestBase<T> {};
 
 class AidlOnlyBadQueueConfig : public ::testing::Test {};
+class Hidl2AidlOperation : public ::testing::Test {};
 
 /*
  * Utility function to initialize data to be written to the FMQ
@@ -231,6 +244,130 @@
     ASSERT_FALSE(fmq->isValid());
 }
 
+TEST_F(Hidl2AidlOperation, ConvertDescriptorsSync) {
+    size_t numElementsInQueue = 64;
+
+    // Create HIDL side and get MQDescriptor
+    MessageQueueSync8* fmq = new (std::nothrow) MessageQueueSync8(numElementsInQueue);
+    ASSERT_NE(nullptr, fmq);
+    ASSERT_TRUE(fmq->isValid());
+    const HidlMQDescSync8* hidlDesc = fmq->getDesc();
+    ASSERT_NE(nullptr, hidlDesc);
+
+    // Create AIDL MQDescriptor to send to another process based off the HIDL MQDescriptor
+    AidlMQDescSync8 aidlDesc;
+    android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, SynchronizedReadWrite>(*hidlDesc,
+                                                                                  &aidlDesc);
+
+    // Other process will create the other side of the queue using the AIDL MQDescriptor
+    AidlMessageQueueSync8* aidlFmq = new (std::nothrow) AidlMessageQueueSync8(aidlDesc);
+    ASSERT_NE(nullptr, aidlFmq);
+    ASSERT_TRUE(aidlFmq->isValid());
+
+    // Make sure a write to the HIDL side, will show up for the AIDL side
+    constexpr size_t dataLen = 4;
+    uint8_t data[dataLen] = {12, 11, 10, 9};
+    fmq->write(data, dataLen);
+
+    int8_t readData[dataLen];
+    ASSERT_TRUE(aidlFmq->read(readData, dataLen));
+
+    ASSERT_EQ(data[0], readData[0]);
+    ASSERT_EQ(data[1], readData[1]);
+    ASSERT_EQ(data[2], readData[2]);
+    ASSERT_EQ(data[3], readData[3]);
+}
+
+TEST_F(Hidl2AidlOperation, ConvertDescriptorsUnsync) {
+    size_t numElementsInQueue = 64;
+
+    // Create HIDL side and get MQDescriptor
+    MessageQueueUnsync8* fmq = new (std::nothrow) MessageQueueUnsync8(numElementsInQueue);
+    ASSERT_NE(nullptr, fmq);
+    ASSERT_TRUE(fmq->isValid());
+    const HidlMQDescUnsync8* hidlDesc = fmq->getDesc();
+    ASSERT_NE(nullptr, hidlDesc);
+
+    // Create AIDL MQDescriptor to send to another process based off the HIDL MQDescriptor
+    AidlMQDescUnsync8 aidlDesc;
+    android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, UnsynchronizedWrite>(*hidlDesc,
+                                                                                &aidlDesc);
+
+    // Other process will create the other side of the queue using the AIDL MQDescriptor
+    AidlMessageQueueUnsync8* aidlFmq = new (std::nothrow) AidlMessageQueueUnsync8(aidlDesc);
+    ASSERT_NE(nullptr, aidlFmq);
+    ASSERT_TRUE(aidlFmq->isValid());
+
+    // Can have multiple readers with unsync flavor
+    AidlMessageQueueUnsync8* aidlFmq2 = new (std::nothrow) AidlMessageQueueUnsync8(aidlDesc);
+    ASSERT_NE(nullptr, aidlFmq2);
+    ASSERT_TRUE(aidlFmq2->isValid());
+
+    // Make sure a write to the HIDL side, will show up for the AIDL side
+    constexpr size_t dataLen = 4;
+    uint8_t data[dataLen] = {12, 11, 10, 9};
+    fmq->write(data, dataLen);
+
+    int8_t readData[dataLen];
+    ASSERT_TRUE(aidlFmq->read(readData, dataLen));
+    int8_t readData2[dataLen];
+    ASSERT_TRUE(aidlFmq2->read(readData2, dataLen));
+
+    ASSERT_EQ(data[0], readData[0]);
+    ASSERT_EQ(data[1], readData[1]);
+    ASSERT_EQ(data[2], readData[2]);
+    ASSERT_EQ(data[3], readData[3]);
+    ASSERT_EQ(data[0], readData2[0]);
+    ASSERT_EQ(data[1], readData2[1]);
+    ASSERT_EQ(data[2], readData2[2]);
+    ASSERT_EQ(data[3], readData2[3]);
+}
+
+TEST_F(Hidl2AidlOperation, ConvertFdIndex1) {
+    native_handle_t* mqHandle = native_handle_create(2 /* numFds */, 0 /* numInts */);
+    if (mqHandle == nullptr) {
+        return;
+    }
+    mqHandle->data[0] = 12;
+    mqHandle->data[1] = 5;
+    ::android::hardware::hidl_vec<android::hardware::GrantorDescriptor> grantors;
+    grantors.resize(3);
+    grantors[0] = {0, 1 /* fdIndex */, 16, 16};
+    grantors[1] = {0, 1 /* fdIndex */, 16, 16};
+    grantors[2] = {0, 1 /* fdIndex */, 16, 16};
+
+    HidlMQDescUnsync8* hidlDesc = new (std::nothrow) HidlMQDescUnsync8(grantors, mqHandle, 10);
+    ASSERT_TRUE(hidlDesc->isHandleValid());
+
+    AidlMQDescUnsync8 aidlDesc;
+    bool ret = android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, UnsynchronizedWrite>(
+            *hidlDesc, &aidlDesc);
+    ASSERT_TRUE(ret);
+}
+
+TEST_F(Hidl2AidlOperation, ConvertMultipleFdIndexFail) {
+    native_handle_t* mqHandle = native_handle_create(2 /* numFds */, 0 /* numInts */);
+    if (mqHandle == nullptr) {
+        return;
+    }
+    mqHandle->data[0] = 12;
+    mqHandle->data[1] = 5;
+    ::android::hardware::hidl_vec<android::hardware::GrantorDescriptor> grantors;
+    grantors.resize(3);
+    grantors[0] = {0, 1 /* fdIndex */, 16, 16};
+    grantors[1] = {0, 1 /* fdIndex */, 16, 16};
+    // Different fdIndex. Conversion should fail.
+    grantors[2] = {0, 0 /* fdIndex */, 16, 16};
+
+    HidlMQDescUnsync8* hidlDesc = new (std::nothrow) HidlMQDescUnsync8(grantors, mqHandle, 10);
+    ASSERT_TRUE(hidlDesc->isHandleValid());
+
+    AidlMQDescUnsync8 aidlDesc;
+    bool ret = android::unsafeHidlToAidlMQDescriptor<uint8_t, int8_t, UnsynchronizedWrite>(
+            *hidlDesc, &aidlDesc);
+    ASSERT_FALSE(ret);
+}
+
 // TODO(b/165674950) Since AIDL does not support unsigned integers, it can only support
 // 1/2 the queue size of HIDL. Once support is added to AIDL, this restriction can be
 // lifted. Until then, check against SSIZE_MAX instead of SIZE_MAX.