[Lut NDK] Define ASurfaceTransaction_setLuts API

- And also ADisplayLuts, ADisplayLutsEntry struct and correspsonding NDK
  APIs.

Bug: 377329333
Test: android.view.surfacecontrol.cts.ASurfaceControlTest#testSurfaceTransaction_setLuts_*
Flag: EXEMPT NDK
Change-Id: I23eaef36725a0d63ceba557811812b82b157f83e
diff --git a/native/android/Android.bp b/native/android/Android.bp
index da29c49..cd6de5a 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -55,6 +55,7 @@
         "surface_control_input_receiver.cpp",
         "choreographer.cpp",
         "configuration.cpp",
+        "display_luts.cpp",
         "dynamic_instrumentation_manager.cpp",
         "hardware_buffer_jni.cpp",
         "input.cpp",
diff --git a/native/android/display_luts.cpp b/native/android/display_luts.cpp
new file mode 100644
index 0000000..179a32b
--- /dev/null
+++ b/native/android/display_luts.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2024 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 "DisplayLuts"
+
+#include <android/display_luts.h>
+#include <display_luts_private.h>
+#include <utils/Log.h>
+
+#include <cmath>
+
+#define ADISPLAYLUTS_BUFFER_LENGTH_LIMIT (100000)
+
+#define CHECK_NOT_NULL(name) \
+    LOG_ALWAYS_FATAL_IF(name == nullptr, "nullptr passed as " #name " argument");
+
+ADisplayLutsEntry* ADisplayLutsEntry_createEntry(float* buffer, int32_t length, int32_t dimension,
+                                                 int32_t key) {
+    CHECK_NOT_NULL(buffer);
+    LOG_ALWAYS_FATAL_IF(length >= ADISPLAYLUTS_BUFFER_LENGTH_LIMIT,
+                        "the lut raw buffer length is too big to handle");
+    if (dimension != ADISPLAYLUTS_ONE_DIMENSION && dimension != ADISPLAYLUTS_THREE_DIMENSION) {
+        LOG_ALWAYS_FATAL("the lut dimension is be either 1 or 3");
+    }
+    int32_t size = 0;
+    if (dimension == ADISPLAYLUTS_THREE_DIMENSION) {
+        LOG_ALWAYS_FATAL_IF(length % 3 != 0, "the 3d lut raw buffer is not divisible by 3");
+        int32_t lengthPerChannel = length / 3;
+        float sizeForDim = std::cbrt(static_cast<float>(lengthPerChannel));
+        LOG_ALWAYS_FATAL_IF(sizeForDim != (int)(sizeForDim),
+                            "the 3d lut buffer length is incorrect");
+        size = (int)sizeForDim;
+    } else {
+        size = length;
+    }
+    LOG_ALWAYS_FATAL_IF(size < 2, "the lut size for each dimension is too small");
+
+    ADisplayLutsEntry* entry = new ADisplayLutsEntry();
+    entry->buffer.data.resize(length);
+    std::copy(buffer, buffer + length, entry->buffer.data.begin());
+    entry->properties = {dimension, size, key};
+
+    entry->incStrong((void*)ADisplayLutsEntry_createEntry);
+    return static_cast<ADisplayLutsEntry*>(entry);
+}
+
+void ADisplayLutsEntry_destroy(ADisplayLutsEntry* entry) {
+    if (entry != NULL) {
+        entry->decStrong((void*)ADisplayLutsEntry_createEntry);
+    }
+}
+
+ADisplayLuts_Dimension ADisplayLutsEntry_getDimension(const ADisplayLutsEntry* entry) {
+    CHECK_NOT_NULL(entry);
+    return static_cast<ADisplayLuts_Dimension>(entry->properties.dimension);
+}
+
+int32_t ADisplayLutsEntry_getSize(const ADisplayLutsEntry* entry) {
+    CHECK_NOT_NULL(entry);
+    return entry->properties.size;
+}
+
+ADisplayLuts_SamplingKey ADisplayLutsEntry_getSamplingKey(const ADisplayLutsEntry* entry) {
+    CHECK_NOT_NULL(entry);
+    return static_cast<ADisplayLuts_SamplingKey>(entry->properties.samplingKey);
+}
+
+const float* ADisplayLutsEntry_getBuffer(const ADisplayLutsEntry* _Nonnull entry) {
+    CHECK_NOT_NULL(entry);
+    return entry->buffer.data.data();
+}
+
+ADisplayLuts* ADisplayLuts_create() {
+    ADisplayLuts* luts = new ADisplayLuts();
+    if (luts == NULL) {
+        delete luts;
+        return NULL;
+    }
+    luts->incStrong((void*)ADisplayLuts_create);
+    return static_cast<ADisplayLuts*>(luts);
+}
+
+void ADisplayLuts_clearLuts(ADisplayLuts* luts) {
+    for (auto& entry : luts->entries) {
+        entry->decStrong((void*)ADisplayLuts_setEntries); // Decrement ref count
+    }
+    luts->entries.clear();
+    luts->offsets.clear();
+    luts->totalBufferSize = 0;
+}
+
+void ADisplayLuts_destroy(ADisplayLuts* luts) {
+    if (luts != NULL) {
+        ADisplayLuts_clearLuts(luts);
+        luts->decStrong((void*)ADisplayLuts_create);
+    }
+}
+
+void ADisplayLuts_setEntries(ADisplayLuts* luts, ADisplayLutsEntry** entries, int32_t numEntries) {
+    CHECK_NOT_NULL(luts);
+    // always clear the previously set lut(s)
+    ADisplayLuts_clearLuts(luts);
+
+    // do nothing
+    if (!entries || numEntries == 0) {
+        return;
+    }
+
+    LOG_ALWAYS_FATAL_IF(numEntries > 2, "The number of entries should be not over 2!");
+    if (numEntries == 2 && entries[0]->properties.dimension != ADISPLAYLUTS_ONE_DIMENSION &&
+        entries[1]->properties.dimension != ADISPLAYLUTS_THREE_DIMENSION) {
+        LOG_ALWAYS_FATAL("The entries should be 1D and 3D in order!");
+    }
+
+    luts->offsets.reserve(numEntries);
+    luts->entries.reserve(numEntries);
+    for (int32_t i = 0; i < numEntries; i++) {
+        luts->offsets.emplace_back(luts->totalBufferSize);
+        luts->totalBufferSize += entries[i]->buffer.data.size();
+        luts->entries.emplace_back(entries[i]);
+        luts->entries.back()->incStrong((void*)ADisplayLuts_setEntries);
+    }
+}
\ No newline at end of file
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 2d1fbf9..5dd4542 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -95,6 +95,15 @@
     AConfiguration_setTouchscreen;
     AConfiguration_setUiModeNight;
     AConfiguration_setUiModeType;
+    ADisplayLuts_create; # introduced=36
+    ADisplayLuts_setEntries; # introduced=36
+    ADisplayLuts_destroy; # introduced=36
+    ADisplayLutsEntry_createEntry; # introduced=36
+    ADisplayLutsEntry_getDimension; # introduced=36
+    ADisplayLutsEntry_getSize; # introduced=36
+    ADisplayLutsEntry_getSamplingKey; # introduced=36
+    ADisplayLutsEntry_getBuffer; # introduced=36
+    ADisplayLutsEntry_destroy; # introduced=36
     AInputEvent_getDeviceId;
     AInputEvent_getSource;
     AInputEvent_getType;
@@ -300,6 +309,7 @@
     ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
     ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake
     ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream
+    ASurfaceTransaction_setLuts; # introduced=36
     ASurfaceTransaction_setOnComplete; # introduced=29
     ASurfaceTransaction_setOnCommit; # introduced=31
     ASurfaceTransaction_setPosition; # introduced=31
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 698bc84..fc64e9b 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
+#include <android/gui/LutProperties.h>
 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
 #include <android/native_window.h>
 #include <android/surface_control.h>
 #include <android/surface_control_jni.h>
 #include <android_runtime/android_view_SurfaceControl.h>
 #include <configstore/Utils.h>
+#include <cutils/ashmem.h>
+#include <display_luts_private.h>
 #include <gui/HdrMetadata.h>
 #include <gui/ISurfaceComposer.h>
 #include <gui/Surface.h>
@@ -53,6 +56,14 @@
 static_assert(static_cast<int>(ADATASPACE_DISPLAY_P3) ==
               static_cast<int>(HAL_DATASPACE_DISPLAY_P3));
 static_assert(static_cast<int>(ADATASPACE_BT2020_PQ) == static_cast<int>(HAL_DATASPACE_BT2020_PQ));
+static_assert(static_cast<int>(ADISPLAYLUTS_ONE_DIMENSION) ==
+              static_cast<int>(android::gui::LutProperties::Dimension::ONE_D));
+static_assert(static_cast<int>(ADISPLAYLUTS_THREE_DIMENSION) ==
+              static_cast<int>(android::gui::LutProperties::Dimension::THREE_D));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_RGB) ==
+              static_cast<int>(android::gui::LutProperties::SamplingKey::RGB));
+static_assert(static_cast<int>(ADISPLAYLUTS_SAMPLINGKEY_MAX_RGB) ==
+              static_cast<int>(android::gui::LutProperties::SamplingKey::MAX_RGB));
 
 Transaction* ASurfaceTransaction_to_Transaction(ASurfaceTransaction* aSurfaceTransaction) {
     return reinterpret_cast<Transaction*>(aSurfaceTransaction);
@@ -693,6 +704,58 @@
     transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio);
 }
 
+void ASurfaceTransaction_setLuts(ASurfaceTransaction* aSurfaceTransaction,
+                                 ASurfaceControl* aSurfaceControl,
+                                 const struct ADisplayLuts* luts) {
+    CHECK_NOT_NULL(aSurfaceTransaction);
+    CHECK_NOT_NULL(aSurfaceControl);
+
+    sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+    Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+    int fd = -1;
+    std::vector<int32_t> offsets;
+    std::vector<int32_t> dimensions;
+    std::vector<int32_t> sizes;
+    std::vector<int32_t> samplingKeys;
+
+    if (luts) {
+        std::vector<float> buffer(luts->totalBufferSize);
+        int32_t count = luts->offsets.size();
+        offsets = luts->offsets;
+
+        dimensions.reserve(count);
+        sizes.reserve(count);
+        samplingKeys.reserve(count);
+        for (int32_t i = 0; i < count; i++) {
+            dimensions.emplace_back(luts->entries[i]->properties.dimension);
+            sizes.emplace_back(luts->entries[i]->properties.size);
+            samplingKeys.emplace_back(luts->entries[i]->properties.samplingKey);
+            std::copy(luts->entries[i]->buffer.data.begin(), luts->entries[i]->buffer.data.end(),
+                      buffer.begin() + offsets[i]);
+        }
+
+        // mmap
+        fd = ashmem_create_region("lut_shared_mem", luts->totalBufferSize * sizeof(float));
+        if (fd < 0) {
+            LOG_ALWAYS_FATAL("setLuts, ashmem_create_region() failed");
+            return;
+        }
+        void* ptr = mmap(nullptr, luts->totalBufferSize * sizeof(float), PROT_READ | PROT_WRITE,
+                         MAP_SHARED, fd, 0);
+        if (ptr == MAP_FAILED) {
+            LOG_ALWAYS_FATAL("setLuts, Failed to map the shared memory");
+            return;
+        }
+
+        memcpy(ptr, buffer.data(), luts->totalBufferSize * sizeof(float));
+        munmap(ptr, luts->totalBufferSize * sizeof(float));
+    }
+
+    transaction->setLuts(surfaceControl, base::unique_fd(fd), offsets, dimensions, sizes,
+                         samplingKeys);
+}
+
 void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
                                   ASurfaceControl* aSurfaceControl,
                                   float r, float g, float b, float alpha,