Add relative address to DeviceProductInfo.

This CL adds a field relativeAddress to DeviceProductInfo so it can
be surfaced in the framework. For HDMI connections the field is
populated with the physical address. This CL also adds helper
functions for serializing to Flattenable and enhances the
serialization security.

Bug: 147994746
Bug: 153589294
Test: atest DisplayIdentificationTest
Change-Id: I2711407aa1be079df8086e233ee3f0cf90ba1741
diff --git a/libs/ui/DeviceProductInfo.cpp b/libs/ui/DeviceProductInfo.cpp
index efd61b6..7bced9b 100644
--- a/libs/ui/DeviceProductInfo.cpp
+++ b/libs/ui/DeviceProductInfo.cpp
@@ -18,33 +18,38 @@
 
 #include <ui/FlattenableHelpers.h>
 
+#define RETURN_IF_ERROR(op) \
+    if (const status_t status = (op); status != OK) return status;
+
 namespace android {
 
 size_t DeviceProductInfo::getFlattenedSize() const {
-    return FlattenableHelpers::getFlattenedSize(name) + sizeof(manufacturerPnpId) +
-            FlattenableHelpers::getFlattenedSize(productId) + sizeof(manufactureOrModelDate);
+    return FlattenableHelpers::getFlattenedSize(name) +
+            FlattenableHelpers::getFlattenedSize(manufacturerPnpId) +
+            FlattenableHelpers::getFlattenedSize(productId) +
+            FlattenableHelpers::getFlattenedSize(manufactureOrModelDate) +
+            FlattenableHelpers::getFlattenedSize(relativeAddress);
 }
 
 status_t DeviceProductInfo::flatten(void* buffer, size_t size) const {
     if (size < getFlattenedSize()) {
         return NO_MEMORY;
     }
-    FlattenableHelpers::write(buffer, size, name);
-    FlattenableUtils::write(buffer, size, manufacturerPnpId);
-    FlattenableHelpers::write(buffer, size, productId);
-    FlattenableUtils::write(buffer, size, manufactureOrModelDate);
-    return NO_ERROR;
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, name));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufacturerPnpId));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, productId));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, manufactureOrModelDate));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, relativeAddress));
+    return OK;
 }
 
 status_t DeviceProductInfo::unflatten(void const* buffer, size_t size) {
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-    FlattenableHelpers::read(buffer, size, &name);
-    FlattenableUtils::read(buffer, size, manufacturerPnpId);
-    FlattenableHelpers::read(buffer, size, &productId);
-    FlattenableUtils::read(buffer, size, manufactureOrModelDate);
-    return NO_ERROR;
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &name));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufacturerPnpId));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &productId));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &manufactureOrModelDate));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &relativeAddress));
+    return OK;
 }
 
 } // namespace android
diff --git a/libs/ui/DisplayInfo.cpp b/libs/ui/DisplayInfo.cpp
index 6ed7e19..73a78af 100644
--- a/libs/ui/DisplayInfo.cpp
+++ b/libs/ui/DisplayInfo.cpp
@@ -20,10 +20,15 @@
 
 #include <ui/FlattenableHelpers.h>
 
+#define RETURN_IF_ERROR(op) \
+    if (const status_t status = (op); status != OK) return status;
+
 namespace android {
 
 size_t DisplayInfo::getFlattenedSize() const {
-    return sizeof(connectionType) + sizeof(density) + sizeof(secure) +
+    return FlattenableHelpers::getFlattenedSize(connectionType) +
+            FlattenableHelpers::getFlattenedSize(density) +
+            FlattenableHelpers::getFlattenedSize(secure) +
             FlattenableHelpers::getFlattenedSize(deviceProductInfo);
 }
 
@@ -31,24 +36,19 @@
     if (size < getFlattenedSize()) {
         return NO_MEMORY;
     }
-    FlattenableUtils::write(buffer, size, connectionType);
-    FlattenableUtils::write(buffer, size, density);
-    FlattenableUtils::write(buffer, size, secure);
-    FlattenableHelpers::write(buffer, size, deviceProductInfo);
-
-    return NO_ERROR;
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, connectionType));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, density));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, secure));
+    RETURN_IF_ERROR(FlattenableHelpers::flatten(&buffer, &size, deviceProductInfo));
+    return OK;
 }
 
 status_t DisplayInfo::unflatten(void const* buffer, size_t size) {
-    if (size < getFlattenedSize()) {
-        return NO_MEMORY;
-    }
-    FlattenableUtils::read(buffer, size, connectionType);
-    FlattenableUtils::read(buffer, size, density);
-    FlattenableUtils::read(buffer, size, secure);
-    FlattenableHelpers::read(buffer, size, &deviceProductInfo);
-
-    return NO_ERROR;
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &connectionType));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &density));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &secure));
+    RETURN_IF_ERROR(FlattenableHelpers::unflatten(&buffer, &size, &deviceProductInfo));
+    return OK;
 }
 
 } // namespace android
diff --git a/libs/ui/include/ui/DeviceProductInfo.h b/libs/ui/include/ui/DeviceProductInfo.h
index cc5ebe4..0bc1e5c 100644
--- a/libs/ui/include/ui/DeviceProductInfo.h
+++ b/libs/ui/include/ui/DeviceProductInfo.h
@@ -22,6 +22,7 @@
 #include <string>
 #include <type_traits>
 #include <variant>
+#include <vector>
 
 #include <utils/Flattenable.h>
 
@@ -58,6 +59,11 @@
     static_assert(std::is_trivially_copyable_v<ManufactureOrModelDate>);
     ManufactureOrModelDate manufactureOrModelDate;
 
+    // Relative address in the display network. Empty vector indicates that the
+    // address is unavailable.
+    // For example, for HDMI connected device this will be the physical address.
+    std::vector<uint8_t> relativeAddress;
+
     bool isFixedSize() const { return false; }
     size_t getFlattenedSize() const;
     status_t flatten(void* buffer, size_t size) const;
diff --git a/libs/ui/include_private/ui/FlattenableHelpers.h b/libs/ui/include_private/ui/FlattenableHelpers.h
index bdf4804..946a67f 100644
--- a/libs/ui/include_private/ui/FlattenableHelpers.h
+++ b/libs/ui/include_private/ui/FlattenableHelpers.h
@@ -16,56 +16,143 @@
 
 #pragma once
 
+#include <numeric>
 #include <optional>
 #include <type_traits>
+#include <vector>
 
 #include <utils/Flattenable.h>
 
+#define RETURN_IF_ERROR(op) \
+    if (const status_t status = (op); status != OK) return status;
+
 namespace android {
 
 struct FlattenableHelpers {
-    // Flattenable helpers for reading and writing std::string
-    static size_t getFlattenedSize(const std::string& str) { return str.length() + 1; }
-
-    static void write(void*& buffer, size_t& size, const std::string& str) {
-        strcpy(reinterpret_cast<char*>(buffer), str.c_str());
-        FlattenableUtils::advance(buffer, size, getFlattenedSize(str));
+    // Helpers for reading and writing POD structures
+    template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+    static constexpr size_t getFlattenedSize(const T&) {
+        return sizeof(T);
     }
 
-    static void read(void const*& buffer, size_t& size, std::string* str) {
-        str->assign(reinterpret_cast<const char*>(buffer));
-        FlattenableUtils::advance(buffer, size, getFlattenedSize(*str));
+    template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+    static status_t flatten(void** buffer, size_t* size, const T& value) {
+        if (*size < sizeof(T)) return NO_MEMORY;
+        FlattenableUtils::write(*buffer, *size, value);
+        return OK;
     }
 
-    // Flattenable utils for reading and writing std::optional
-    template <class T, typename = std::enable_if_t<std::is_base_of_v<LightFlattenable<T>, T>>>
+    template <class T, typename = std::enable_if_t<std::is_trivially_copyable_v<T>>>
+    static status_t unflatten(const void** buffer, size_t* size, T* value) {
+        if (*size < sizeof(T)) return NO_MEMORY;
+        FlattenableUtils::read(*buffer, *size, *value);
+        return OK;
+    }
+
+    // Helpers for reading and writing std::string
+    static size_t getFlattenedSize(const std::string& str) { return sizeof(size_t) + str.length(); }
+
+    static status_t flatten(void** buffer, size_t* size, const std::string& str) {
+        if (*size < getFlattenedSize(str)) return NO_MEMORY;
+        flatten(buffer, size, str.length());
+        memcpy(reinterpret_cast<char*>(*buffer), str.c_str(), str.length());
+        FlattenableUtils::advance(*buffer, *size, str.length());
+        return OK;
+    }
+
+    static status_t unflatten(const void** buffer, size_t* size, std::string* str) {
+        size_t length;
+        RETURN_IF_ERROR(unflatten(buffer, size, &length));
+        if (*size < length) return NO_MEMORY;
+        str->assign(reinterpret_cast<const char*>(*buffer), length);
+        FlattenableUtils::advance(*buffer, *size, length);
+        return OK;
+    }
+
+    // Helpers for reading and writing LightFlattenable
+    template <class T>
+    static size_t getFlattenedSize(const LightFlattenable<T>& value) {
+        return value.getFlattenedSize();
+    }
+
+    template <class T>
+    static status_t flatten(void** buffer, size_t* size, const LightFlattenable<T>& value) {
+        RETURN_IF_ERROR(value.flatten(*buffer, *size));
+        FlattenableUtils::advance(*buffer, *size, value.getFlattenedSize());
+        return OK;
+    }
+
+    template <class T>
+    static status_t unflatten(const void** buffer, size_t* size, LightFlattenable<T>* value) {
+        RETURN_IF_ERROR(value->unflatten(*buffer, *size));
+        FlattenableUtils::advance(*buffer, *size, value->getFlattenedSize());
+        return OK;
+    }
+
+    // Helpers for reading and writing std::optional
+    template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
     static size_t getFlattenedSize(const std::optional<T>& value) {
-        return sizeof(bool) + (value ? value->getFlattenedSize() : 0);
+        return sizeof(bool) + (value ? getFlattenedSize(*value) : 0);
     }
 
-    template <class T, typename = std::enable_if_t<std::is_base_of_v<LightFlattenable<T>, T>>>
-    static void write(void*& buffer, size_t& size, const std::optional<T>& value) {
+    template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+    static status_t flatten(void** buffer, size_t* size, const std::optional<T>& value) {
         if (value) {
-            FlattenableUtils::write(buffer, size, true);
-            value->flatten(buffer, size);
-            FlattenableUtils::advance(buffer, size, value->getFlattenedSize());
+            RETURN_IF_ERROR(flatten(buffer, size, true));
+            RETURN_IF_ERROR(flatten(buffer, size, *value));
         } else {
-            FlattenableUtils::write(buffer, size, false);
+            RETURN_IF_ERROR(flatten(buffer, size, false));
         }
+        return OK;
     }
 
-    template <class T, typename = std::enable_if_t<std::is_base_of_v<LightFlattenable<T>, T>>>
-    static void read(void const*& buffer, size_t& size, std::optional<T>* value) {
+    template <class T, typename = std::enable_if_t<std::negation_v<std::is_trivially_copyable<T>>>>
+    static status_t unflatten(const void** buffer, size_t* size, std::optional<T>* value) {
         bool isPresent;
-        FlattenableUtils::read(buffer, size, isPresent);
+        RETURN_IF_ERROR(unflatten(buffer, size, &isPresent));
         if (isPresent) {
             *value = T();
-            (*value)->unflatten(buffer, size);
-            FlattenableUtils::advance(buffer, size, (*value)->getFlattenedSize());
+            RETURN_IF_ERROR(unflatten(buffer, size, &(**value)));
         } else {
             value->reset();
         }
+        return OK;
+    }
+
+    // Helpers for reading and writing std::vector
+    template <class T>
+    static size_t getFlattenedSize(const std::vector<T>& value) {
+        return std::accumulate(value.begin(), value.end(), sizeof(size_t),
+                               [](size_t sum, const T& element) {
+                                   return sum + getFlattenedSize(element);
+                               });
+    }
+
+    template <class T>
+    static status_t flatten(void** buffer, size_t* size, const std::vector<T>& value) {
+        RETURN_IF_ERROR(flatten(buffer, size, value.size()));
+        for (const auto& element : value) {
+            RETURN_IF_ERROR(flatten(buffer, size, element));
+        }
+        return OK;
+    }
+
+    template <class T>
+    static status_t unflatten(const void** buffer, size_t* size, std::vector<T>* value) {
+        size_t numElements;
+        RETURN_IF_ERROR(unflatten(buffer, size, &numElements));
+        // We don't need an extra size check since each iteration of the loop does that
+        std::vector<T> elements;
+        for (size_t i = 0; i < numElements; i++) {
+            T element;
+            RETURN_IF_ERROR(unflatten(buffer, size, &element));
+            elements.push_back(element);
+        }
+        *value = std::move(elements);
+        return OK;
     }
 };
 
 } // namespace android
+
+#undef RETURN_IF_ERROR
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index b53342c..28ef77a 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -29,6 +29,13 @@
 }
 
 cc_test {
+    name: "FlattenableHelpers_test",
+    shared_libs: ["libui"],
+    srcs: ["FlattenableHelpers_test.cpp"],
+    cflags: ["-Wall", "-Werror"],
+}
+
+cc_test {
     name: "GraphicBufferAllocator_test",
     header_libs: [
         "libnativewindow_headers",
diff --git a/libs/ui/tests/FlattenableHelpers_test.cpp b/libs/ui/tests/FlattenableHelpers_test.cpp
new file mode 100644
index 0000000..db32bc7
--- /dev/null
+++ b/libs/ui/tests/FlattenableHelpers_test.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "FlattenableHelpersTest"
+
+#include <ui/FlattenableHelpers.h>
+
+#include <gtest/gtest.h>
+#include <utils/Flattenable.h>
+#include <cstdint>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+namespace android {
+
+namespace {
+
+struct TestLightFlattenable : LightFlattenable<TestLightFlattenable> {
+    std::unique_ptr<int32_t> ptr;
+
+    bool isFixedSize() const { return true; }
+    size_t getFlattenedSize() const { return sizeof(int32_t); }
+
+    status_t flatten(void* buffer, size_t size) const {
+        FlattenableUtils::write(buffer, size, *ptr);
+        return OK;
+    }
+
+    status_t unflatten(void const* buffer, size_t size) {
+        int value;
+        FlattenableUtils::read(buffer, size, value);
+        ptr = std::make_unique<int32_t>(value);
+        return OK;
+    }
+};
+
+class FlattenableHelpersTest : public testing::Test {
+public:
+    template <class T>
+    void testWriteThenRead(const T& value, size_t bufferSize) {
+        std::vector<int8_t> buffer(bufferSize);
+        auto rawBuffer = reinterpret_cast<void*>(buffer.data());
+        size_t size = buffer.size();
+        ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+
+        auto rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+        size = buffer.size();
+        T valueRead;
+        ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+        EXPECT_EQ(value, valueRead);
+    }
+
+    template <class T>
+    void testTriviallyCopyable(const T& value) {
+        testWriteThenRead(value, sizeof(T));
+    }
+
+    template <class T>
+    void testWriteThenRead(const T& value) {
+        testWriteThenRead(value, FlattenableHelpers::getFlattenedSize(value));
+    }
+};
+
+TEST_F(FlattenableHelpersTest, TriviallyCopyable) {
+    testTriviallyCopyable(42);
+    testTriviallyCopyable(1LL << 63);
+    testTriviallyCopyable(false);
+    testTriviallyCopyable(true);
+    testTriviallyCopyable(std::optional<int>());
+    testTriviallyCopyable(std::optional<int>(4));
+}
+
+TEST_F(FlattenableHelpersTest, String) {
+    testWriteThenRead(std::string("Android"));
+    testWriteThenRead(std::string());
+}
+
+TEST_F(FlattenableHelpersTest, Vector) {
+    testWriteThenRead(std::vector<int>({1, 2, 3}));
+    testWriteThenRead(std::vector<int>());
+}
+
+TEST_F(FlattenableHelpersTest, OptionalOfLightFlattenable) {
+    std::vector<size_t> buffer;
+    constexpr int kInternalValue = 16;
+    {
+        std::optional<TestLightFlattenable> value =
+                TestLightFlattenable{.ptr = std::make_unique<int32_t>(kInternalValue)};
+        buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+        void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+        size_t size = buffer.size();
+        ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+    }
+
+    const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+    size_t size = buffer.size();
+    std::optional<TestLightFlattenable> valueRead;
+    ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+    ASSERT_TRUE(valueRead.has_value());
+    EXPECT_EQ(kInternalValue, *valueRead->ptr);
+}
+
+TEST_F(FlattenableHelpersTest, NullOptionalOfLightFlattenable) {
+    std::vector<size_t> buffer;
+    {
+        std::optional<TestLightFlattenable> value;
+        buffer.assign(FlattenableHelpers::getFlattenedSize(value), 0);
+        void* rawBuffer = reinterpret_cast<void*>(buffer.data());
+        size_t size = buffer.size();
+        ASSERT_EQ(OK, FlattenableHelpers::flatten(&rawBuffer, &size, value));
+    }
+
+    const void* rawReadBuffer = reinterpret_cast<const void*>(buffer.data());
+    size_t size = buffer.size();
+    std::optional<TestLightFlattenable> valueRead;
+    ASSERT_EQ(OK, FlattenableHelpers::unflatten(&rawReadBuffer, &size, &valueRead));
+    ASSERT_FALSE(valueRead.has_value());
+}
+
+} // namespace
+} // namespace android