Merge "Add stubs to libEGL cc_library"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 61e22a4..8400cdc 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -933,6 +933,31 @@
unlink(path.c_str());
}
+static void DumpVisibleWindowViews() {
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping visible views because it's not a zipped bugreport\n");
+ return;
+ }
+ DurationReporter duration_reporter("VISIBLE WINDOW VIEWS");
+ const std::string path = ds.bugreport_internal_dir_ + "/tmp_visible_window_views";
+ auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ if (fd < 0) {
+ MYLOGE("Could not open %s to dump visible views.\n", path.c_str());
+ return;
+ }
+ RunCommandToFd(fd, "", {"cmd", "window", "dump-visible-window-views"},
+ CommandOptions::WithTimeout(120).Build());
+ bool empty = 0 == lseek(fd, 0, SEEK_END);
+ if (!empty) {
+ ds.AddZipEntry("visible_windows.zip", path);
+ } else {
+ MYLOGW("Failed to dump visible windows\n");
+ }
+ unlink(path.c_str());
+}
+
static void DumpIpTablesAsRoot() {
RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
@@ -1317,6 +1342,8 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20);
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpVisibleWindowViews);
+
DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
DumpFile("SLAB INFO", "/proc/slabinfo");
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 4f47db1..bac8b66 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -280,19 +280,31 @@
std::condition_variable mCv;
};
+ // Simple RAII object to ensure a function call immediately before going out of scope
+ class Defer {
+ public:
+ Defer(std::function<void()>&& f) : mF(std::move(f)) {}
+ ~Defer() { mF(); }
+ private:
+ std::function<void()> mF;
+ };
+
const std::string name = String8(name16).c_str();
sp<IBinder> out;
if (!mTheRealServiceManager->getService(name, &out).isOk()) {
return nullptr;
}
- if(out != nullptr) return out;
+ if (out != nullptr) return out;
sp<Waiter> waiter = new Waiter;
if (!mTheRealServiceManager->registerForNotifications(
name, waiter).isOk()) {
return nullptr;
}
+ Defer unregister ([&] {
+ mTheRealServiceManager->unregisterForNotifications(name, waiter);
+ });
while(true) {
{
@@ -316,7 +328,7 @@
if (!mTheRealServiceManager->getService(name, &out).isOk()) {
return nullptr;
}
- if(out != nullptr) return out;
+ if (out != nullptr) return out;
ALOGW("Waited one second for %s", name.c_str());
}
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
new file mode 100644
index 0000000..cb5305f
--- /dev/null
+++ b/libs/gralloc/types/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2019 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.
+
+cc_library {
+ name: "libgralloctypes",
+ defaults: ["libbinder_ndk_host_user"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ ],
+ host_supported: true,
+
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+
+ srcs: [
+ "Gralloc4.cpp"
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.mapper@4.0",
+ "libhidlbase",
+ "liblog",
+ "vintf-graphics-common-ndk_platform",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.graphics.mapper@4.0",
+ "vintf-graphics-common-ndk_platform",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+}
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
new file mode 100644
index 0000000..3bded7f
--- /dev/null
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -0,0 +1,637 @@
+/*
+ * Copyright 2019 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 <cstring>
+#include <cinttypes>
+#include <limits>
+
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+
+#include "gralloctypes/Gralloc4.h"
+
+using android::hardware::hidl_vec;
+
+using ::aidl::android::hardware::graphics::common::BlendMode;
+using ::aidl::android::hardware::graphics::common::Dataspace;
+using ::aidl::android::hardware::graphics::common::PlaneLayout;
+using ::aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using ::aidl::android::hardware::graphics::common::ExtendableType;
+using ::aidl::android::hardware::graphics::common::Rect;
+using ::aidl::android::hardware::graphics::common::StandardMetadataType;
+
+using MetadataType = ::android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+namespace gralloc4 {
+
+bool isStandardMetadataType(const MetadataType& metadataType) {
+ return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
+}
+
+StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
+ return static_cast<StandardMetadataType>(metadataType.value);
+}
+
+status_t copyToHidlVec(const std::vector<uint8_t>& vec, hidl_vec<uint8_t>* hidlVec) {
+ if (!hidlVec) {
+ return BAD_VALUE;
+ }
+
+ hidlVec->setToExternal(const_cast<uint8_t*>(vec.data()), vec.size(), false /*shouldOwn*/);
+
+ return NO_ERROR;
+}
+
+template <class T>
+status_t encodeInteger(T input, std::vector<uint8_t>* output) {
+ static_assert(std::is_same<T, uint32_t>::value ||
+ std::is_same<T, int32_t>::value ||
+ std::is_same<T, uint64_t>::value ||
+ std::is_same<T, int64_t>::value);
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ size_t outputOffset = output->size();
+ size_t size = sizeof(input);
+
+ if (outputOffset > UINT_MAX - size) {
+ return BAD_VALUE;
+ }
+ output->resize(size + outputOffset);
+
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(&input);
+ std::copy(tmp, tmp + size, output->data() + outputOffset);
+
+ return NO_ERROR;
+}
+
+template <class T>
+status_t decodeInteger(const hidl_vec<uint8_t>& input, T* output, size_t* inputOffset = nullptr) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ size_t offset = (inputOffset)? *inputOffset: 0;
+ if (offset >= input.size()) {
+ return BAD_VALUE;
+ }
+ size_t inputMaxSize = input.size() - offset;
+ size_t outputSize = sizeof(*output);
+
+ if (inputMaxSize < outputSize) {
+ return BAD_VALUE;
+ }
+
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
+ const uint8_t* data = input.data() + offset;
+ std::copy(data, data + outputSize, tmp);
+
+ if (inputOffset) {
+ *inputOffset += outputSize;
+ }
+
+ return NO_ERROR;
+}
+
+status_t encodeString(const std::string& input, std::vector<uint8_t>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(input.size(), output);
+ if (err) {
+ return err;
+ }
+
+ size_t outputOffset = output->size();
+ size_t size = input.size();
+ output->resize(size + outputOffset);
+
+ std::copy(input.c_str(), input.c_str() + size, output->data() + outputOffset);
+
+ return NO_ERROR;
+}
+
+status_t decodeString(const hidl_vec<uint8_t>& input, std::string* output, size_t* inputOffset = nullptr) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size, inputOffset);
+ if (err || size < 0) {
+ return err;
+ }
+
+ size_t offset = (inputOffset)? *inputOffset + sizeof(size): sizeof(size);
+ if ((offset > UINT_MAX - size) || (offset + size > input.size())) {
+ return BAD_VALUE;
+ }
+
+ auto data = input.data() + offset;
+ output->assign(data, data + size);
+
+ if (inputOffset) {
+ *inputOffset += size;
+ }
+
+ return NO_ERROR;
+}
+
+status_t encodeExtendableType(const ExtendableType& input, std::vector<uint8_t>* output) {
+ status_t err = encodeString(input.name, output);
+ if (err) {
+ return err;
+ }
+
+ return encodeInteger<int64_t>(input.value, output);
+}
+
+status_t decodeExtendableType(const hidl_vec<uint8_t>& input, ExtendableType* output, size_t* inputOffset = nullptr) {
+ status_t err = decodeString(input, &output->name, inputOffset);
+ if (err) {
+ return err;
+ }
+
+ err = decodeInteger<int64_t>(input, &output->value, inputOffset);
+ if (err) {
+ output->name.clear();
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t encodeRect(const Rect& input, std::vector<uint8_t>* output) {
+ status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.top), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.right), output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<int32_t>(static_cast<int32_t>(input.bottom), output);
+}
+
+status_t decodeRect(const hidl_vec<uint8_t>& input, Rect* output, size_t* inputOffset = nullptr) {
+ status_t err = decodeInteger<int32_t>(input, &output->left, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, &output->top, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, &output->right, inputOffset);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<int32_t>(input, &output->bottom, inputOffset);
+}
+
+status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, std::vector<uint8_t>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeExtendableType(input.type, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.offsetInBits), output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<int64_t>(static_cast<int64_t>(input.sizeInBits), output);
+}
+
+status_t decodePlaneLayoutComponent(const hidl_vec<uint8_t>& input, PlaneLayoutComponent* output, size_t* inputOffset = nullptr) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = decodeExtendableType(input, &output->type, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->offsetInBits, inputOffset);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<int64_t>(input, &output->sizeInBits, inputOffset);
+}
+
+status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, std::vector<uint8_t>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(input.size()), output);
+ if (err) {
+ return err;
+ }
+
+ for (const auto& planeLayoutComponent: input) {
+ err = encodePlaneLayoutComponent(planeLayoutComponent, output);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodePlaneLayoutComponents(const hidl_vec<uint8_t>& input, std::vector<PlaneLayoutComponent>* output, size_t* inputOffset = nullptr) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size, inputOffset);
+ if (err || size < 0) {
+ return err;
+ }
+
+ for (int i = 0; i < size; i++) {
+ output->emplace_back();
+ err = decodePlaneLayoutComponent(input, &output->back(), inputOffset);
+ if (err) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t encodePlaneLayout(const PlaneLayout& input, std::vector<uint8_t>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodePlaneLayoutComponents(input.components, output);
+ if (err) {
+ return err;
+ }
+
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.offsetInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.sampleIncrementInBits), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.strideInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.widthInSamples), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.heightInSamples), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.totalSizeInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.horizontalSubsampling), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
+ if (err) {
+ return err;
+ }
+
+ return encodeRect(input.crop, output);
+}
+
+status_t decodePlaneLayout(const hidl_vec<uint8_t>& input, PlaneLayout* output, size_t* inputOffset = nullptr) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = decodePlaneLayoutComponents(input, &output->components, inputOffset);
+ if (err) {
+ return err;
+ }
+
+ err = decodeInteger<int64_t>(input, &output->offsetInBytes, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->strideInBytes, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->widthInSamples, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->heightInSamples, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->totalSizeInBytes, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->horizontalSubsampling, inputOffset);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->verticalSubsampling, inputOffset);
+ if (err) {
+ return err;
+ }
+
+ return decodeRect(input, &output->crop, inputOffset);
+}
+
+status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
+ if (!outPlaneLayouts) {
+ return BAD_VALUE;
+ }
+
+ std::vector<uint8_t> tmpOutPlaneLayouts;
+
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), &tmpOutPlaneLayouts);
+ if (err) {
+ return err;
+ }
+
+ for (const auto& planeLayout : planeLayouts) {
+ err = encodePlaneLayout(planeLayout, &tmpOutPlaneLayouts);
+ if (err) {
+ return err;
+ }
+ }
+
+ return copyToHidlVec(tmpOutPlaneLayouts, outPlaneLayouts);
+}
+
+status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
+ if (!outPlaneLayouts) {
+ return BAD_VALUE;
+ }
+
+ size_t offset = 0;
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(planeLayouts, &size, &offset);
+ if (err || size < 0) {
+ return err;
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ outPlaneLayouts->emplace_back();
+ err = decodePlaneLayout(planeLayouts, &outPlaneLayouts->back(), &offset);
+ if (err) {
+ outPlaneLayouts->resize(0);
+ return err;
+ }
+ }
+ if (offset < planeLayouts.size()) {
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
+ std::vector<uint8_t> tmpOutBufferId;
+ status_t err = encodeInteger<uint64_t>(bufferId, &tmpOutBufferId);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutBufferId, outBufferId);
+}
+
+status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
+ return decodeInteger<uint64_t>(bufferId, outBufferId);
+}
+
+status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
+ std::vector<uint8_t> tmpOutName;
+ status_t err = encodeString(name, &tmpOutName);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutName, outName);
+}
+
+status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
+ return decodeString(name, outName);
+}
+
+status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
+ std::vector<uint8_t> tmpOutWidth;
+ status_t err = encodeInteger<uint64_t>(width, &tmpOutWidth);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutWidth, outWidth);
+}
+
+status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
+ return decodeInteger<uint64_t>(width, outWidth);
+}
+
+status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
+ std::vector<uint8_t> tmpOutHeight;
+ status_t err = encodeInteger<uint64_t>(height, &tmpOutHeight);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutHeight, outHeight);
+}
+
+status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
+ return decodeInteger<uint64_t>(height, outHeight);
+}
+
+status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
+ std::vector<uint8_t> tmpOutLayerCount;
+ status_t err = encodeInteger<uint64_t>(layerCount, &tmpOutLayerCount);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutLayerCount, outLayerCount);
+}
+
+status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
+ return decodeInteger<uint64_t>(layerCount, outLayerCount);
+}
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested, hidl_vec<uint8_t>* outPixelFormatRequested) {
+ std::vector<uint8_t> tmpOutPixelFormatRequested;
+ status_t err = encodeInteger<int32_t>(static_cast<int32_t>(pixelFormatRequested), &tmpOutPixelFormatRequested);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutPixelFormatRequested, outPixelFormatRequested);
+}
+
+status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested, hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
+ return decodeInteger<int32_t>(pixelFormatRequested, reinterpret_cast<int32_t*>(outPixelFormatRequested));
+}
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
+ std::vector<uint8_t> tmpOutPixelFormatFourCC;
+ status_t err = encodeInteger<uint32_t>(pixelFormatFourCC, &tmpOutPixelFormatFourCC);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutPixelFormatFourCC, outPixelFormatFourCC);
+}
+
+status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
+ return decodeInteger<uint32_t>(pixelFormatFourCC, outPixelFormatFourCC);
+}
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
+ std::vector<uint8_t> tmpOutPixelFormatModifier;
+ status_t err = encodeInteger<uint64_t>(pixelFormatModifier, &tmpOutPixelFormatModifier);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutPixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
+ return decodeInteger<uint64_t>(pixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
+ std::vector<uint8_t> tmpOutUsage;
+ status_t err = encodeInteger<uint64_t>(usage, &tmpOutUsage);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutUsage, outUsage);
+}
+
+status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
+ return decodeInteger<uint64_t>(usage, outUsage);
+}
+
+status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
+ std::vector<uint8_t> tmpOutAllocationSize;
+ status_t err = encodeInteger<uint64_t>(allocationSize, &tmpOutAllocationSize);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutAllocationSize, outAllocationSize);
+}
+
+status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
+ return decodeInteger<uint64_t>(allocationSize, outAllocationSize);
+}
+
+status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
+ std::vector<uint8_t> tmpOutProtectedContent;
+ status_t err = encodeInteger<uint64_t>(protectedContent, &tmpOutProtectedContent);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutProtectedContent, outProtectedContent);
+}
+
+status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
+ return decodeInteger<uint64_t>(protectedContent, outProtectedContent);
+}
+
+status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
+ std::vector<uint8_t> tmpOutCompression;
+ status_t err = encodeExtendableType(compression, &tmpOutCompression);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutCompression, outCompression);
+}
+
+status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
+ return decodeExtendableType(compression, outCompression);
+}
+
+status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
+ std::vector<uint8_t> tmpOutInterlaced;
+ status_t err = encodeExtendableType(interlaced, &tmpOutInterlaced);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutInterlaced, outInterlaced);
+}
+
+status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
+ return decodeExtendableType(interlaced, outInterlaced);
+}
+
+status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
+ std::vector<uint8_t> tmpOutChromaSiting;
+ status_t err = encodeExtendableType(chromaSiting, &tmpOutChromaSiting);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutChromaSiting, outChromaSiting);
+}
+
+status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
+ return decodeExtendableType(chromaSiting, outChromaSiting);
+}
+
+status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
+ std::vector<uint8_t> tmpOutDataspace;
+ status_t err = encodeInteger<int32_t>(static_cast<int32_t>(dataspace), &tmpOutDataspace);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutDataspace, outDataspace);
+}
+
+status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
+ return decodeInteger<int32_t>(dataspace, reinterpret_cast<int32_t*>(outDataspace));
+}
+
+status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
+ std::vector<uint8_t> tmpOutBlendMode;
+ status_t err = encodeInteger<int32_t>(static_cast<int32_t>(blendMode), &tmpOutBlendMode);
+ if (err) {
+ return err;
+ }
+ return copyToHidlVec(tmpOutBlendMode, outBlendMode);
+}
+
+status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
+ return decodeInteger<int32_t>(blendMode, reinterpret_cast<int32_t*>(outBlendMode));
+}
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
new file mode 100644
index 0000000..e062345
--- /dev/null
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2019 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/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+
+namespace android {
+
+namespace gralloc4 {
+
+/**
+ * This library is compiled into VNDK-SP and FWK_ONLY copies. When a device is upgraded, the vendor
+ * partition may choose to use an older copy of the VNDK-SP.
+ *
+ * Prepend the version to every encode and decode so the system partition can fallback to an older
+ * version if necessary.
+ */
+#define GRALLOC4_METADATA_VERSION 1
+
+#define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
+#define GRALLOC4_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
+#define GRALLOC4_COMPRESSION "android.hardware.graphics.common.Compression"
+#define GRALLOC4_INTERLACED "android.hardware.graphics.common.Interlaced"
+#define GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE "android.hardware.graphics.common.PlaneLayoutComponentType"
+
+/*---------------------------------------------------------------------------------------------*/
+/**
+ * Definitions of the standard buffer metadata types. It is recommended that everyone uses
+ * these definitions directly for standard buffer metadata types.
+ */
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BufferId = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BUFFER_ID)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Name = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::NAME)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Width = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::WIDTH)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Height = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::HEIGHT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_LayerCount = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::LAYER_COUNT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatRequested = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_REQUESTED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatFourCC = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_FOURCC)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatModifier = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_MODIFIER)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Usage = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::USAGE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_AllocationSize = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::ALLOCATION_SIZE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ProtectedContent = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PROTECTED_CONTENT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Compression = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::COMPRESSION)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Interlaced = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::INTERLACED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ChromaSiting = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::CHROMA_SITING)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PlaneLayouts = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PLANE_LAYOUTS)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Dataspace = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::DATASPACE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BlendMode = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard compression strategies. It is recommended that everyone uses
+ * these definitions directly for standard compression strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_None = {
+ GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_DisplayStreamCompression = {
+ GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::DISPLAY_STREAM_COMPRESSION)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard interlaced strategies. It is recommended that everyone uses
+ * these definitions directly for standard interlaced strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None = {
+ GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom = {
+ GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft = {
+ GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard chroma siting. It is recommended that everyone uses
+ * these definitions directly for standard chroma siting.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_SitedInterstitial = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::SITED_INTERSTITIAL)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_CositedHorizontal = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::COSITED_HORIZONTAL)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard plane layout component types. It is recommended that everyone uses
+ * these definitions directly for standard plane layout component types
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * The functions below can be used to parse a StandardMetadataType.
+ */
+bool isStandardMetadataType(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+
+aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+
+/**
+ * The functions below encode and decode standard metadata into a byte stream. It is STRONGLY
+ * recommended that both the vendor and system partitions use these functions when getting
+ * and setting metadata through gralloc 4 (IMapper 4.0).
+ */
+status_t encodeBufferId(uint64_t bufferId, android::hardware::hidl_vec<uint8_t>* outBufferId);
+status_t decodeBufferId(const android::hardware::hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId);
+
+status_t encodeName(const std::string& name, android::hardware::hidl_vec<uint8_t>* outName);
+status_t decodeName(const android::hardware::hidl_vec<uint8_t>& name, std::string* outName);
+
+status_t encodeWidth(uint64_t width, android::hardware::hidl_vec<uint8_t>* outWidth);
+status_t decodeWidth(const android::hardware::hidl_vec<uint8_t>& width, uint64_t* outWidth);
+
+status_t encodeHeight(uint64_t height, android::hardware::hidl_vec<uint8_t>* outHeight);
+status_t decodeHeight(const android::hardware::hidl_vec<uint8_t>& height, uint64_t* outHeight);
+
+status_t encodeLayerCount(uint64_t layerCount, android::hardware::hidl_vec<uint8_t>* outLayerCount);
+status_t decodeLayerCount(const android::hardware::hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount);
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested, android::hardware::hidl_vec<uint8_t>* outPixelFormatRequested);
+status_t decodePixelFormatRequested(const android::hardware::hidl_vec<uint8_t>& pixelFormatRequested, hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested);
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, android::hardware::hidl_vec<uint8_t>* outPixelFormatFourCC);
+status_t decodePixelFormatFourCC(const android::hardware::hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC);
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, android::hardware::hidl_vec<uint8_t>* outPixelFormatModifier);
+status_t decodePixelFormatModifier(const android::hardware::hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier);
+
+status_t encodeUsage(uint64_t usage, android::hardware::hidl_vec<uint8_t>* outUsage);
+status_t decodeUsage(const android::hardware::hidl_vec<uint8_t>& usage, uint64_t* outUsage);
+
+status_t encodeAllocationSize(uint64_t allocationSize, android::hardware::hidl_vec<uint8_t>* outAllocationSize);
+status_t decodeAllocationSize(const android::hardware::hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize);
+
+status_t encodeProtectedContent(uint64_t protectedContent, android::hardware::hidl_vec<uint8_t>* outProtectedContent);
+status_t decodeProtectedContent(const android::hardware::hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent);
+
+status_t encodeCompression(const aidl::android::hardware::graphics::common::ExtendableType& compression, android::hardware::hidl_vec<uint8_t>* outCompression);
+status_t decodeCompression(const android::hardware::hidl_vec<uint8_t>& compression, aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+
+status_t encodeInterlaced(const aidl::android::hardware::graphics::common::ExtendableType& interlaced, android::hardware::hidl_vec<uint8_t>* outInterlaced);
+status_t decodeInterlaced(const android::hardware::hidl_vec<uint8_t>& interlaced, aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+
+status_t encodeChromaSiting(const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting, android::hardware::hidl_vec<uint8_t>* outChromaSiting);
+status_t decodeChromaSiting(const android::hardware::hidl_vec<uint8_t>& chromaSiting, aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+
+status_t encodePlaneLayouts(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& planeLayouts, android::hardware::hidl_vec<uint8_t>* outPlaneLayouts);
+status_t decodePlaneLayouts(const android::hardware::hidl_vec<uint8_t>& planeLayouts, std::vector<aidl::android::hardware::graphics::common::PlaneLayout>* outPlaneLayouts);
+
+status_t encodeDataspace(const aidl::android::hardware::graphics::common::Dataspace& dataspace, android::hardware::hidl_vec<uint8_t>* outDataspace);
+status_t decodeDataspace(const android::hardware::hidl_vec<uint8_t>& dataspace, aidl::android::hardware::graphics::common::Dataspace* outDataspace);
+
+status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
+status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 5805797..b9597db 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -978,6 +978,35 @@
}
return NO_ERROR;
}
+
+ virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to write interface token: %d", error);
+ return error;
+ }
+
+ std::vector<float> shadowConfig = {ambientColor.r, ambientColor.g, ambientColor.b,
+ ambientColor.a, spotColor.r, spotColor.g,
+ spotColor.b, spotColor.a, lightPosY,
+ lightPosZ, lightRadius};
+
+ error = data.writeFloatVector(shadowConfig);
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to write shadowConfig: %d", error);
+ return error;
+ }
+
+ error = remote()->transact(BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to transact: %d", error);
+ return error;
+ }
+ return NO_ERROR;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1593,6 +1622,25 @@
}
return notifyPowerHint(hintId);
}
+ case SET_GLOBAL_SHADOW_SETTINGS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ std::vector<float> shadowConfig;
+ status_t error = data.readFloatVector(&shadowConfig);
+ if (error != NO_ERROR || shadowConfig.size() != 11) {
+ ALOGE("setGlobalShadowSettings: failed to read shadowConfig: %d", error);
+ return error;
+ }
+
+ half4 ambientColor = {shadowConfig[0], shadowConfig[1], shadowConfig[2],
+ shadowConfig[3]};
+ half4 spotColor = {shadowConfig[4], shadowConfig[5], shadowConfig[6], shadowConfig[7]};
+ float lightPosY = shadowConfig[8];
+ float lightPosZ = shadowConfig[9];
+ float lightRadius = shadowConfig[10];
+ return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
+ lightRadius);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index a538e14..2ab4d8a 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1726,6 +1726,14 @@
return ComposerService::getComposerService()->notifyPowerHint(hintId);
}
+status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
+ const half4& spotColor, float lightPosY,
+ float lightPosZ, float lightRadius) {
+ return ComposerService::getComposerService()->setGlobalShadowSettings(ambientColor, spotColor,
+ lightPosY, lightPosZ,
+ lightRadius);
+}
+
// ----------------------------------------------------------------------------
status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index f2bae98..514dfe2 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -25,6 +25,8 @@
#include <gui/ITransactionCompletedListener.h>
+#include <math/vec4.h>
+
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
@@ -439,6 +441,28 @@
* Returns NO_ERROR upon success.
*/
virtual status_t notifyPowerHint(int32_t hintId) = 0;
+
+ /*
+ * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+ * material design guidelines.
+ *
+ * ambientColor
+ * Color to the ambient shadow. The alpha is premultiplied.
+ *
+ * spotColor
+ * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ * depends on the light position.
+ *
+ * lightPosY/lightPosZ
+ * Position of the light used to cast the spot shadow. The X value is always the display
+ * width / 2.
+ *
+ * lightRadius
+ * Radius of the light casting the shadow.
+ */
+ virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ,
+ float lightRadius) = 0;
};
// ----------------------------------------------------------------------------
@@ -492,6 +516,7 @@
SET_DISPLAY_BRIGHTNESS,
CAPTURE_SCREEN_BY_ID,
NOTIFY_POWER_HINT,
+ SET_GLOBAL_SHADOW_SETTINGS,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 37387ac..d218356 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -214,6 +214,27 @@
*/
static status_t notifyPowerHint(int32_t hintId);
+ /*
+ * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+ * material design guidelines.
+ *
+ * ambientColor
+ * Color to the ambient shadow. The alpha is premultiplied.
+ *
+ * spotColor
+ * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ * depends on the light position.
+ *
+ * lightPosY/lightPosZ
+ * Position of the light used to cast the spot shadow. The X value is always the display
+ * width / 2.
+ *
+ * lightRadius
+ * Radius of the light casting the shadow.
+ */
+ static status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius);
+
// ------------------------------------------------------------------------
// surface creation / destruction
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a4fdb35..c4f35ae 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -833,6 +833,12 @@
}
status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+ status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
+ float /*lightPosY*/, float /*lightPosZ*/,
+ float /*lightRadius*/) override {
+ return NO_ERROR;
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 67607af..188ac6b 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1201,6 +1201,20 @@
return Void();
}
+Return<void> HardwareComposer::ComposerCallback::onVsync_2_4(
+ Hwc2::Display /*display*/, int64_t /*timestamp*/,
+ Hwc2::VsyncPeriodNanos /*vsyncPeriodNanos*/) {
+ LOG_ALWAYS_FATAL("Unexpected onVsync_2_4 callback");
+ return Void();
+}
+
+Return<void> HardwareComposer::ComposerCallback::onVsyncPeriodTimingChanged(
+ Hwc2::Display /*display*/,
+ const Hwc2::VsyncPeriodChangeTimeline& /*updatedTimeline*/) {
+ LOG_ALWAYS_FATAL("Unexpected onVsyncPeriodTimingChanged callback");
+ return Void();
+}
+
void HardwareComposer::ComposerCallback::SetVsyncService(
const sp<VsyncService>& vsync_service) {
std::lock_guard<std::mutex> lock(mutex_);
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 989ce35..8698814 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -375,6 +375,12 @@
hardware::Return<void> onRefresh(Hwc2::Display display) override;
hardware::Return<void> onVsync(Hwc2::Display display,
int64_t timestamp) override;
+ hardware::Return<void> onVsync_2_4(
+ Hwc2::Display display, int64_t timestamp,
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override;
+ hardware::Return<void> onVsyncPeriodTimingChanged(
+ Hwc2::Display display,
+ const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
bool GotFirstHotplug() { return got_first_hotplug_; }
void SetVsyncService(const sp<VsyncService>& vsync_service);
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 930c7c7..e925f5b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -60,7 +60,7 @@
// --- EventEntry ---
-EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags)
+EventEntry::EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags)
: sequenceNum(sequenceNum),
refCount(1),
type(type),
@@ -92,7 +92,7 @@
// --- ConfigurationChangedEntry ---
ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime)
- : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {}
+ : EventEntry(sequenceNum, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
@@ -103,7 +103,7 @@
// --- DeviceResetEntry ---
DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId)
- : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
+ : EventEntry(sequenceNum, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
DeviceResetEntry::~DeviceResetEntry() {}
@@ -117,7 +117,7 @@
int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
nsecs_t downTime)
- : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+ : EventEntry(sequenceNum, Type::KEY, eventTime, policyFlags),
deviceId(deviceId),
source(source),
displayId(displayId),
@@ -165,7 +165,7 @@
float xCursorPosition, float yCursorPosition, nsecs_t downTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, float xOffset, float yOffset)
- : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
+ : EventEntry(sequenceNum, Type::MOTION, eventTime, policyFlags),
eventTime(eventTime),
deviceId(deviceId),
source(source),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 28c2799..9dcaadc 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -33,11 +33,24 @@
constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
struct EventEntry {
- enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION };
+ enum class Type { CONFIGURATION_CHANGED, DEVICE_RESET, KEY, MOTION };
+
+ static const char* typeToString(Type type) {
+ switch (type) {
+ case Type::CONFIGURATION_CHANGED:
+ return "CONFIGURATION_CHANGED";
+ case Type::DEVICE_RESET:
+ return "DEVICE_RESET";
+ case Type::KEY:
+ return "KEY";
+ case Type::MOTION:
+ return "MOTION";
+ }
+ }
uint32_t sequenceNum;
mutable int32_t refCount;
- int32_t type;
+ Type type;
nsecs_t eventTime;
uint32_t policyFlags;
InjectionState* injectionState;
@@ -66,7 +79,7 @@
virtual void appendDescription(std::string& msg) const = 0;
protected:
- EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+ EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags);
virtual ~EventEntry();
void releaseInjectionState();
};
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 58a5b3c..c219941 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -386,7 +386,7 @@
}
switch (mPendingEvent->type) {
- case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+ case EventEntry::Type::CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
@@ -394,14 +394,14 @@
break;
}
- case EventEntry::TYPE_DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET: {
DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
break;
}
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEvent(*typedEntry)) {
@@ -421,7 +421,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
@@ -435,10 +435,6 @@
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
-
- default:
- ALOG_ASSERT(false);
- break;
}
if (done) {
@@ -458,7 +454,7 @@
traceInboundQueueLengthLocked();
switch (entry->type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
@@ -480,7 +476,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
// Optimize case where the current application is unresponsive and the user
// decides to touch a window in a different application.
// If the application takes too long to catch up then we drop all events preceding
@@ -508,6 +504,11 @@
}
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ // nothing to do
+ break;
+ }
}
return needWake;
@@ -627,12 +628,12 @@
}
switch (entry.type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
@@ -643,6 +644,11 @@
}
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type));
+ break;
+ }
}
}
@@ -1174,18 +1180,19 @@
int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) {
int32_t displayId;
switch (entry.type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
displayId = keyEntry.displayId;
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
displayId = motionEntry.displayId;
break;
}
- default: {
- ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry.type);
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type));
return ADISPLAY_ID_NONE;
}
}
@@ -1849,7 +1856,7 @@
}
// Ensure that the dispatch queues aren't too far backed up for this event.
- if (eventEntry.type == EventEntry::TYPE_KEY) {
+ if (eventEntry.type == EventEntry::Type::KEY) {
// If the event is a key event, then we must wait for all previous events to
// complete before delivering it because previous events may have the
// side-effect of transferring focus to a different window and we want to
@@ -1937,7 +1944,7 @@
int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
switch (eventEntry.type) {
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
return;
@@ -1948,7 +1955,7 @@
}
break;
}
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
return;
@@ -1956,6 +1963,12 @@
eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s events are not user activity",
+ EventEntry::typeToString(eventEntry.type));
+ break;
+ }
}
std::unique_ptr<CommandEntry> commandEntry =
@@ -1996,7 +2009,7 @@
// Split a motion event if needed.
if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
- ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
+ ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) {
@@ -2080,7 +2093,7 @@
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2097,7 +2110,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
@@ -2147,6 +2160,12 @@
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s events should not go to apps",
+ EventEntry::typeToString(eventEntry->type));
+ break;
+ }
}
// Remember that we are waiting for this dispatch to complete.
@@ -2206,7 +2225,7 @@
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Publish the key event.
@@ -2221,7 +2240,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
PointerCoords scaledCoords[MAX_POINTERS];
@@ -2502,15 +2521,23 @@
for (size_t i = 0; i < cancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = cancelationEvents[i];
switch (cancelationEventEntry->type) {
- case EventEntry::TYPE_KEY:
+ case EventEntry::Type::KEY: {
logOutboundKeyDetails("cancel - ",
static_cast<const KeyEntry&>(*cancelationEventEntry));
break;
- case EventEntry::TYPE_MOTION:
+ }
+ case EventEntry::Type::MOTION: {
logOutboundMotionDetails("cancel - ",
static_cast<const MotionEntry&>(
*cancelationEventEntry));
break;
+ }
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+ EventEntry::typeToString(cancelationEventEntry->type));
+ break;
+ }
}
InputTarget target;
@@ -4237,11 +4264,11 @@
}
bool restartEvent;
- if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
+ if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
restartEvent =
afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
+ } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
handled);
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4ff941f..16095b9 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -56,8 +56,8 @@
void SwitchInputMapper::sync(nsecs_t when) {
if (mUpdatedSwitchMask) {
uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
- NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
- mUpdatedSwitchMask);
+ NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0 /*policyFlags*/,
+ updatedSwitchValues, mUpdatedSwitchMask);
getListener()->notifySwitch(&args);
mUpdatedSwitchMask = 0;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2a29e0d..8863ec2 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -365,17 +365,48 @@
class FakeInputReceiver {
public:
- void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
- int32_t expectedFlags) {
+ InputEvent* consume() {
uint32_t consumeSeq;
InputEvent* event;
- status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
- &consumeSeq, &event);
- ASSERT_EQ(OK, status)
- << mName.c_str() << ": consumer consume should return OK.";
- ASSERT_TRUE(event != nullptr)
- << mName.c_str() << ": consumer should have returned non-NULL event.";
+ std::chrono::time_point start = std::chrono::steady_clock::now();
+ status_t status = WOULD_BLOCK;
+ while (status == WOULD_BLOCK) {
+ status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, &consumeSeq,
+ &event);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+ if (elapsed > 100ms) {
+ break;
+ }
+ }
+
+ if (status == WOULD_BLOCK) {
+ // Just means there's no event available.
+ return nullptr;
+ }
+
+ if (status != OK) {
+ ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
+ return nullptr;
+ }
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
+ return nullptr;
+ }
+
+ status = mConsumer->sendFinishedSignal(consumeSeq, handled());
+ if (status != OK) {
+ ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+ }
+ return event;
+ }
+
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+ int32_t expectedFlags) {
+ InputEvent* event = consume();
+
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
ASSERT_EQ(expectedEventType, event->getType())
<< mName.c_str() << ": event type should match.";
@@ -398,10 +429,6 @@
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
}
-
- status = mConsumer->sendFinishedSignal(consumeSeq, handled());
- ASSERT_EQ(OK, status)
- << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
@@ -415,13 +442,10 @@
}
void assertNoEvents() {
- uint32_t consumeSeq;
- InputEvent* event;
- status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
- &consumeSeq, &event);
- ASSERT_NE(OK, status)
+ InputEvent* event = consume();
+ ASSERT_EQ(nullptr, event)
<< mName.c_str()
- << ": should not have received any events, so consume(..) should not return OK.";
+ << ": should not have received any events, so consume() should return NULL";
}
protected:
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 4b71bd8..963d755 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -97,6 +97,10 @@
"android.hardware.power@1.3",
"libhidlbase",
],
+ // TODO (marissaw): this library is not used by surfaceflinger. This is here so
+ // the library compiled in a way that is accessible to system partition when running
+ // IMapper's VTS.
+ required: ["libgralloctypes"]
}
cc_defaults {
diff --git a/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
new file mode 100644
index 0000000..2675dcf
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 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
+
+/**
+ * CallOrderStateMachineHelper is a helper class for setting up a compile-time
+ * checked state machine that a sequence of calls is correct for completely
+ * setting up the state for some other type.
+ *
+ * Two examples where this could be used are with setting up a "Builder" flow
+ * for initializing an instance of some type, and writing tests where the state
+ * machine sets up expectations and preconditions, calls the function under
+ * test, and then evaluations postconditions.
+ *
+ * The purpose of this helper is to offload some of the boilerplate code to
+ * simplify the actual state classes, and is also a place to document how to
+ * go about setting up the state classes.
+ *
+ * To work at compile time, the idea is that each state is a unique C++ type,
+ * and the valid transitions between states are given by member functions on
+ * those types, with those functions returning a simple value type expressing
+ * the new state to use. Illegal state transitions become a compile error because
+ * a named member function does not exist.
+ *
+ * Example usage in a test:
+ *
+ * A two step (+ terminator step) setup process can defined using:
+ *
+ * class Step1 : public CallOrderStateMachineHelper<TestFixtureType, Step1> {
+ * [[nodiscard]] auto firstMockCalledWith(int value1) {
+ * // Set up an expectation or initial state using the fixture
+ * EXPECT_CALL(getInstance->firstMock, FirstCall(value1));
+ * return nextState<Step2>();
+ * }
+ * };
+ *
+ * class Step2 : public CallOrderStateMachineHelper<TestFixtureType, Step2> {
+ * [[nodiscard]] auto secondMockCalledWith(int value2) {
+ * // Set up an expectation or initial state using the fixture
+ * EXPECT_CALL(getInstance()->secondMock, SecondCall(value2));
+ * return nextState<StepExecute>();
+ * }
+ * };
+ *
+ * class StepExecute : public CallOrderStateMachineHelper<TestFixtureType, Step3> {
+ * void execute() {
+ * invokeFunctionUnderTest();
+ * }
+ * };
+ *
+ * Note how the non-terminator steps return by value and use [[nodiscard]] to
+ * enforce the setup flow. Only the terminator step returns void.
+ *
+ * This can then be used in the tests with:
+ *
+ * Step1::make(this).firstMockCalledWith(value1)
+ * .secondMockCalledWith(value2)
+ * .execute);
+ *
+ * If the test fixture defines a `verify()` helper function which returns
+ * `Step1::make(this)`, this can be simplified to:
+ *
+ * verify().firstMockCalledWith(value1)
+ * .secondMockCalledWith(value2)
+ * .execute();
+ *
+ * This is equivalent to the following calls made by the text function:
+ *
+ * EXPECT_CALL(firstMock, FirstCall(value1));
+ * EXPECT_CALL(secondMock, SecondCall(value2));
+ * invokeFunctionUnderTest();
+ */
+template <typename InstanceType, typename CurrentStateType>
+class CallOrderStateMachineHelper {
+public:
+ CallOrderStateMachineHelper() = default;
+
+ // Disallow copying
+ CallOrderStateMachineHelper(const CallOrderStateMachineHelper&) = delete;
+ CallOrderStateMachineHelper& operator=(const CallOrderStateMachineHelper&) = delete;
+
+ // Moving is intended use case.
+ CallOrderStateMachineHelper(CallOrderStateMachineHelper&&) = default;
+ CallOrderStateMachineHelper& operator=(CallOrderStateMachineHelper&&) = default;
+
+ // Using a static "Make" function means the CurrentStateType classes do not
+ // need anything other than a default no-argument constructor.
+ static CurrentStateType make(InstanceType* instance) {
+ auto helper = CurrentStateType();
+ helper.mInstance = instance;
+ return helper;
+ }
+
+ // Each non-terminal state function
+ template <typename NextStateType>
+ auto nextState() {
+ // Note: Further operations on the current state become undefined
+ // operations as the instance pointer is moved to the next state type.
+ // But that doesn't stop someone from storing an intermediate state
+ // instance as a local and possibly calling one than one member function
+ // on it. By swapping with nullptr, we at least can try to catch this
+ // this at runtime.
+ InstanceType* instance = nullptr;
+ std::swap(instance, mInstance);
+ return NextStateType::make(instance);
+ }
+
+ InstanceType* getInstance() const { return mInstance; }
+
+private:
+ InstanceType* mInstance;
+};
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index c07dfbb..21b9aa9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -19,35 +19,6 @@
#include <compositionengine/mock/CompositionEngine.h>
#include <gtest/gtest.h>
-namespace android::hardware::graphics::common::V1_1 {
-
-// Note: These operator overloads need to be defined in the same namespace as
-// the values they print.
-
-std::ostream& operator<<(std::ostream& os, const RenderIntent& value) {
- return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
- << ")";
-}
-
-} // namespace android::hardware::graphics::common::V1_1
-
-namespace android::hardware::graphics::common::V1_2 {
-
-// Note: These operator overloads need to be defined in the same namespace as
-// the values they print.
-
-std::ostream& operator<<(std::ostream& os, const Dataspace& value) {
- return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
- << ")";
-}
-
-std::ostream& operator<<(std::ostream& os, const ColorMode& value) {
- return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
- << ")";
-}
-
-} // namespace android::hardware::graphics::common::V1_2
-
namespace android::compositionengine {
namespace {
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 5cfec77..364661b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -81,6 +81,11 @@
MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
MOCK_CONST_METHOD0(isUsingVrComposer, bool());
+ MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
+ MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
+ MOCK_METHOD4(setActiveConfigWithConstraints,
+ status_t(DisplayId, size_t, const HWC2::VsyncPeriodChangeConstraints&,
+ HWC2::VsyncPeriodChangeTimeline*));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 09ddc60..a9a735a 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -16,6 +16,7 @@
#include <cmath>
+#include <android-base/stringprintf.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -31,6 +32,8 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+#include "CallOrderStateMachineHelper.h"
+#include "MockHWC2.h"
#include "RegionMatcher.h"
#include "TransformMatcher.h"
@@ -38,10 +41,16 @@
namespace {
using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Eq;
using testing::InSequence;
+using testing::Mock;
+using testing::Property;
using testing::Ref;
using testing::Return;
using testing::ReturnRef;
+using testing::SetArgPointee;
using testing::StrictMock;
constexpr auto TR_IDENT = 0u;
@@ -51,6 +60,9 @@
const mat4 kNonIdentityHalf = mat4() * 0.5;
const mat4 kNonIdentityQuarter = mat4() * 0.25;
+constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting =
+ static_cast<OutputColorSetting>(0x100);
+
struct OutputPartialMockBase : public impl::Output {
// compositionengine::Output overrides
const OutputCompositionState& getState() const override { return mState; }
@@ -104,8 +116,70 @@
std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine);
};
+// Extension of the base test useful for checking interactions with the LayerFE
+// functions to latch composition state.
+struct OutputLatchFEStateTest : public OutputTest {
+ OutputLatchFEStateTest() {
+ EXPECT_CALL(*mOutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mLayer1));
+ EXPECT_CALL(*mOutputLayer2, getLayer()).WillRepeatedly(ReturnRef(mLayer2));
+ EXPECT_CALL(*mOutputLayer3, getLayer()).WillRepeatedly(ReturnRef(mLayer3));
+
+ EXPECT_CALL(*mOutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mLayer1FE));
+ EXPECT_CALL(*mOutputLayer2, getLayerFE()).WillRepeatedly(ReturnRef(mLayer2FE));
+ EXPECT_CALL(*mOutputLayer3, getLayerFE()).WillRepeatedly(ReturnRef(mLayer3FE));
+
+ EXPECT_CALL(mLayer1, editFEState()).WillRepeatedly(ReturnRef(mLayer1FEState));
+ EXPECT_CALL(mLayer2, editFEState()).WillRepeatedly(ReturnRef(mLayer2FEState));
+ EXPECT_CALL(mLayer3, editFEState()).WillRepeatedly(ReturnRef(mLayer3FEState));
+ }
+
+ void injectLayer(std::unique_ptr<mock::OutputLayer> layer) {
+ mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(layer.release()));
+ }
+
+ std::unique_ptr<mock::OutputLayer> mOutputLayer1{new StrictMock<mock::OutputLayer>};
+ std::unique_ptr<mock::OutputLayer> mOutputLayer2{new StrictMock<mock::OutputLayer>};
+ std::unique_ptr<mock::OutputLayer> mOutputLayer3{new StrictMock<mock::OutputLayer>};
+
+ StrictMock<mock::Layer> mLayer1;
+ StrictMock<mock::Layer> mLayer2;
+ StrictMock<mock::Layer> mLayer3;
+
+ StrictMock<mock::LayerFE> mLayer1FE;
+ StrictMock<mock::LayerFE> mLayer2FE;
+ StrictMock<mock::LayerFE> mLayer3FE;
+
+ LayerFECompositionState mLayer1FEState;
+ LayerFECompositionState mLayer2FEState;
+ LayerFECompositionState mLayer3FEState;
+};
+
const Rect OutputTest::kDefaultDisplaySize{100, 200};
+using ColorProfile = compositionengine::Output::ColorProfile;
+
+void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) {
+ android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name,
+ toString(profile.mode).c_str(), profile.mode,
+ toString(profile.dataspace).c_str(), profile.dataspace,
+ toString(profile.renderIntent).c_str(), profile.renderIntent,
+ toString(profile.colorSpaceAgnosticDataspace).c_str(),
+ profile.colorSpaceAgnosticDataspace);
+}
+
+// Checks for a ColorProfile match
+MATCHER_P(ColorProfileEq, expected, "") {
+ std::string buf;
+ buf.append("ColorProfiles are not equal\n");
+ dumpColorProfile(expected, buf, "expected value");
+ dumpColorProfile(arg, buf, "actual value");
+ *result_listener << buf;
+
+ return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) &&
+ (expected.renderIntent == arg.renderIntent) &&
+ (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace);
+}
+
/*
* Basic construction
*/
@@ -295,10 +369,12 @@
}
/*
- * Output::setColorMode
+ * Output::setColorProfile
*/
-TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) {
+using OutputSetColorProfileTest = OutputTest;
+
+TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
using ColorProfile = Output::ColorProfile;
EXPECT_CALL(*mDisplayColorProfile,
@@ -319,7 +395,7 @@
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
-TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
+TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) {
using ColorProfile = Output::ColorProfile;
EXPECT_CALL(*mDisplayColorProfile,
@@ -535,26 +611,138 @@
}
/*
+ * Output::updateLayerStateFromFE()
+ */
+
+using OutputUpdateLayerStateFromFETest = OutputLatchFEStateTest;
+
+TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
+ CompositionRefreshArgs refreshArgs;
+
+ mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, latchesContentStateForAllContainedLayers) {
+ EXPECT_CALL(mLayer1FE,
+ latchCompositionState(Ref(mLayer1FEState), LayerFE::StateSubset::Content));
+ EXPECT_CALL(mLayer2FE,
+ latchCompositionState(Ref(mLayer2FEState), LayerFE::StateSubset::Content));
+ EXPECT_CALL(mLayer3FE,
+ latchCompositionState(Ref(mLayer3FEState), LayerFE::StateSubset::Content));
+
+ // Note: Must be performed after any expectations on these mocks
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ CompositionRefreshArgs refreshArgs;
+ refreshArgs.updatingGeometryThisFrame = false;
+
+ mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, latchesGeometryAndContentStateForAllContainedLayers) {
+ EXPECT_CALL(mLayer1FE,
+ latchCompositionState(Ref(mLayer1FEState),
+ LayerFE::StateSubset::GeometryAndContent));
+ EXPECT_CALL(mLayer2FE,
+ latchCompositionState(Ref(mLayer2FEState),
+ LayerFE::StateSubset::GeometryAndContent));
+ EXPECT_CALL(mLayer3FE,
+ latchCompositionState(Ref(mLayer3FEState),
+ LayerFE::StateSubset::GeometryAndContent));
+
+ // Note: Must be performed after any expectations on these mocks
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ CompositionRefreshArgs refreshArgs;
+ refreshArgs.updatingGeometryThisFrame = true;
+
+ mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+/*
* Output::updateAndWriteCompositionState()
*/
-TEST_F(OutputTest, updateAndWriteCompositionState_takesEarlyOutIfNotEnabled) {
- mOutput->editState().isEnabled = false;
+using OutputUpdateAndWriteCompositionStateTest = OutputLatchFEStateTest;
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfLayers) {
+ mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
mOutput->updateAndWriteCompositionState(args);
}
-TEST_F(OutputTest, updateAndWriteCompositionState_updatesLayers) {
- mOutput->editState().isEnabled = true;
- mock::OutputLayer* outputLayer = new StrictMock<mock::OutputLayer>();
- mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer));
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
+ mOutput->editState().isEnabled = false;
- EXPECT_CALL(*outputLayer, updateCompositionState(true, true)).Times(1);
- EXPECT_CALL(*outputLayer, writeStateToHWC(true)).Times(1);
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ CompositionRefreshArgs args;
+ mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
+ EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, false));
+ EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, false));
+ EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, false));
+ EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
+ args.devOptForceClientComposition = false;
+ mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
+ EXPECT_CALL(*mOutputLayer1, updateCompositionState(true, false));
+ EXPECT_CALL(*mOutputLayer1, writeStateToHWC(true));
+ EXPECT_CALL(*mOutputLayer2, updateCompositionState(true, false));
+ EXPECT_CALL(*mOutputLayer2, writeStateToHWC(true));
+ EXPECT_CALL(*mOutputLayer3, updateCompositionState(true, false));
+ EXPECT_CALL(*mOutputLayer3, writeStateToHWC(true));
+
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = true;
+ args.devOptForceClientComposition = false;
+ mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
+ EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = true;
mOutput->updateAndWriteCompositionState(args);
}
@@ -657,29 +845,1191 @@
* Output::updateColorProfile()
*/
-// TODO(b/144060211) - Add coverage
+struct OutputUpdateColorProfileTest : public testing::Test {
+ using TestType = OutputUpdateColorProfileTest;
+
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // All child helper functions Output::present() are defined as mocks,
+ // and those are tested separately, allowing the present() test to
+ // just cover the high level flow.
+ MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
+ };
+
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
+ EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+ EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ }
+
+ StrictMock<mock::OutputLayer> mOutputLayer;
+ StrictMock<mock::Layer> mLayer;
+ StrictMock<mock::LayerFE> mLayerFE;
+ LayerFECompositionState mLayerFEState;
+ };
+
+ OutputUpdateColorProfileTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+ .WillRepeatedly(Return(&mLayer3.mOutputLayer));
+ }
+
+ struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+ void execute() { getInstance()->mOutput.updateColorProfile(getInstance()->mRefreshArgs); }
+ };
+
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ StrictMock<OutputPartialMock> mOutput;
+
+ Layer mLayer1;
+ Layer mLayer2;
+ Layer mLayer3;
+
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+// TODO(b/144522012): Refactor Output::updateColorProfile and the related code
+// to make it easier to write unit tests.
+
+TEST_F(OutputUpdateColorProfileTest, setsAColorProfileWhenUnmanaged) {
+ // When the outputColorSetting is set to kUnmanaged, the implementation sets
+ // a simple default color profile without looking at anything else.
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput,
+ setColorProfile(ColorProfileEq(
+ ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN})));
+
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+ mOutput.updateColorProfile(mRefreshArgs);
+}
+
+struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile
+ : public OutputUpdateColorProfileTest {
+ OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+ }
+
+ struct ExpectBestColorModeCallResultUsedToSetColorProfileState
+ : public CallOrderStateMachineHelper<
+ TestType, ExpectBestColorModeCallResultUsedToSetColorProfileState> {
+ [[nodiscard]] auto expectBestColorModeCallResultUsedToSetColorProfile(
+ ui::ColorMode colorMode, ui::Dataspace dataspace, ui::RenderIntent renderIntent) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _,
+ _))
+ .WillOnce(DoAll(SetArgPointee<2>(dataspace), SetArgPointee<3>(colorMode),
+ SetArgPointee<4>(renderIntent)));
+ EXPECT_CALL(getInstance()->mOutput,
+ setColorProfile(
+ ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent,
+ ui::Dataspace::UNKNOWN})));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() {
+ return ExpectBestColorModeCallResultUsedToSetColorProfileState::make(this);
+ }
+};
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+ Native_Unknown_Colorimetric_Set) {
+ verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::NATIVE,
+ ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+ DisplayP3_DisplayP3_Enhance_Set) {
+ verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::DISPLAY_P3,
+ ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::ENHANCE)
+ .execute();
+}
+
+struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
+ : public OutputUpdateColorProfileTest {
+ OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mDisplayColorProfile,
+ getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
+ .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
+ SetArgPointee<3>(ui::ColorMode::NATIVE),
+ SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC)));
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ }
+
+ struct IfColorSpaceAgnosticDataspaceSetToState
+ : public CallOrderStateMachineHelper<TestType, IfColorSpaceAgnosticDataspaceSetToState> {
+ [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) {
+ getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace;
+ return nextState<ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState>();
+ }
+ };
+
+ struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState
+ : public CallOrderStateMachineHelper<
+ TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> {
+ [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(
+ ui::Dataspace dataspace) {
+ EXPECT_CALL(getInstance()->mOutput,
+ setColorProfile(ColorProfileEq(
+ ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC, dataspace})));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) {
+ verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3)
+ .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) {
+ verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB)
+ .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB)
+ .execute();
+}
+
+struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
+ : public OutputUpdateColorProfileTest {
+ // Internally the implementation looks through the dataspaces of all the
+ // visible layers. The topmost one that also has an actual dataspace
+ // preference set is used to drive subsequent choices.
+
+ OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ }
+
+ struct IfTopLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+ [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer3.mLayerFEState.dataspace = dataspace;
+ return nextState<AndIfMiddleLayerDataspaceState>();
+ }
+ [[nodiscard]] auto ifTopLayerHasNoPreference() {
+ return ifTopLayerIs(ui::Dataspace::UNKNOWN);
+ }
+ };
+
+ struct AndIfMiddleLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, AndIfMiddleLayerDataspaceState> {
+ [[nodiscard]] auto andIfMiddleLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+ return nextState<AndIfBottomLayerDataspaceState>();
+ }
+ [[nodiscard]] auto andIfMiddleLayerHasNoPreference() {
+ return andIfMiddleLayerIs(ui::Dataspace::UNKNOWN);
+ }
+ };
+
+ struct AndIfBottomLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+ [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ [[nodiscard]] auto andIfBottomLayerHasNoPreference() {
+ return andIfBottomLayerIs(ui::Dataspace::UNKNOWN);
+ }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(dataspace, _, _, _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ noStrongLayerPrefenceUses_V0_SRGB) {
+ // If none of the layers indicate a preference, then V0_SRGB is the
+ // preferred choice (subject to additional checks).
+ verify().ifTopLayerHasNoPreference()
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerHasNoPreference()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifTopmostUses_DisplayP3_Then_DisplayP3_Chosen) {
+ // If only the topmost layer has a preference, then that is what is chosen.
+ verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerHasNoPreference()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifMiddleUses_DisplayP3_Then_DisplayP3_Chosen) {
+ // If only the middle layer has a preference, that that is what is chosen.
+ verify().ifTopLayerHasNoPreference()
+ .andIfMiddleLayerIs(ui::Dataspace::DISPLAY_P3)
+ .andIfBottomLayerHasNoPreference()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifBottomUses_DisplayP3_Then_DisplayP3_Chosen) {
+ // If only the middle layer has a preference, that that is what is chosen.
+ verify().ifTopLayerHasNoPreference()
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifTopUses_DisplayBT2020_AndBottomUses_DisplayP3_Then_DisplayBT2020_Chosen) {
+ // If multiple layers have a preference, the topmost value is what is used.
+ verify().ifTopLayerIs(ui::Dataspace::DISPLAY_BT2020)
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifTopUses_DisplayP3_AndBottomUses_V0_SRGB_Then_DisplayP3_Chosen) {
+ // If multiple layers have a preference, the topmost value is what is used.
+ verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerIs(ui::Dataspace::DISPLAY_BT2020)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+struct OutputUpdateColorProfileTest_ForceOutputColorOverrides
+ : public OutputUpdateColorProfileTest {
+ // If CompositionRefreshArgs::forceOutputColorMode is set to some specific
+ // values, it overrides the layer dataspace choice.
+
+ OutputUpdateColorProfileTest_ForceOutputColorOverrides() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+ mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ }
+
+ struct IfForceOutputColorModeState
+ : public CallOrderStateMachineHelper<TestType, IfForceOutputColorModeState> {
+ [[nodiscard]] auto ifForceOutputColorMode(ui::ColorMode colorMode) {
+ getInstance()->mRefreshArgs.forceOutputColorMode = colorMode;
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ [[nodiscard]] auto ifNoOverride() { return ifForceOutputColorMode(ui::ColorMode::NATIVE); }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(dataspace, _, _, _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfForceOutputColorModeState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, NoOverride_DoesNotOverride) {
+ // By default the layer state is used to set the preferred dataspace
+ verify().ifNoOverride()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, SRGB_Override_USES_V0_SRGB) {
+ // Setting ui::ColorMode::SRGB overrides it with ui::Dataspace::V0_SRGB
+ verify().ifForceOutputColorMode(ui::ColorMode::SRGB)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, DisplayP3_Override_Uses_DisplayP3) {
+ // Setting ui::ColorMode::DISPLAY_P3 overrides it with ui::Dataspace::DISPLAY_P3
+ verify().ifForceOutputColorMode(ui::ColorMode::DISPLAY_P3)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+// HDR output requires all layers to be compatible with the chosen HDR
+// dataspace, along with there being proper support.
+struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest {
+ OutputUpdateColorProfileTest_Hdr() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ }
+
+ static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+ static constexpr ui::Dataspace BT2020_PQ = ui::Dataspace::BT2020_PQ;
+ static constexpr ui::Dataspace BT2020_HLG = ui::Dataspace::BT2020_HLG;
+ static constexpr ui::Dataspace DISPLAY_P3 = ui::Dataspace::DISPLAY_P3;
+
+ struct IfTopLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+ [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+ return nextState<AndTopLayerCompositionTypeState>();
+ }
+ [[nodiscard]] auto ifTopLayerIsNotHdr() { return ifTopLayerIs(kNonHdrDataspace); }
+ };
+
+ struct AndTopLayerCompositionTypeState
+ : public CallOrderStateMachineHelper<TestType, AndTopLayerCompositionTypeState> {
+ [[nodiscard]] auto andTopLayerIsREComposed(bool renderEngineComposed) {
+ getInstance()->mLayer2.mLayerFEState.forceClientComposition = renderEngineComposed;
+ return nextState<AndIfBottomLayerDataspaceState>();
+ }
+ };
+
+ struct AndIfBottomLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+ [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+ return nextState<AndBottomLayerCompositionTypeState>();
+ }
+ [[nodiscard]] auto andIfBottomLayerIsNotHdr() {
+ return andIfBottomLayerIs(kNonHdrDataspace);
+ }
+ };
+
+ struct AndBottomLayerCompositionTypeState
+ : public CallOrderStateMachineHelper<TestType, AndBottomLayerCompositionTypeState> {
+ [[nodiscard]] auto andBottomLayerIsREComposed(bool renderEngineComposed) {
+ getInstance()->mLayer1.mLayerFEState.forceClientComposition = renderEngineComposed;
+ return nextState<AndIfHasLegacySupportState>();
+ }
+ };
+
+ struct AndIfHasLegacySupportState
+ : public CallOrderStateMachineHelper<TestType, AndIfHasLegacySupportState> {
+ [[nodiscard]] auto andIfLegacySupportFor(ui::Dataspace dataspace, bool legacySupport) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasLegacyHdrSupport(dataspace))
+ .WillOnce(Return(legacySupport));
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(dataspace, _, _, _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_Uses_PQ) {
+ // If all layers use BT2020_PQ, and there are no other special conditions,
+ // BT2020_PQ is used.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_PQ is not used if there is only legacy support for it.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_RE_Uses_PQ) {
+ // BT2020_PQ is still used if the bottom layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_PQ_HW_Uses_DisplayP3) {
+ // BT2020_PQ is not used if the top layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_Uses_PQ) {
+ // If there is mixed HLG/PQ use, and the topmost layer is PQ, then PQ is used if there
+ // are no other special conditions.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_PQ is not used if there is only legacy support for it.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_RE_Uses_PQ) {
+ // BT2020_PQ is used if the bottom HLG layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_HLG_HW_Uses_DisplayP3) {
+ // BT2020_PQ is not used if the top PQ layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_Uses_PQ) {
+ // If there is mixed HLG/PQ use, and the topmost layer is HLG, then PQ is
+ // used if there are no other special conditions.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_PQ is not used if there is only legacy support for it.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_RE_Uses_DisplayP3) {
+ // BT2020_PQ is not used if the bottom PQ layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_PQ_HW_Uses_PQ) {
+ // BT2020_PQ is still used if the top HLG layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_Uses_HLG) {
+ // If all layers use HLG then HLG is used if there are no other special
+ // conditions.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_HLG is not used if there is legacy support for it.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_HLG, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_RE_Uses_HLG) {
+ // BT2020_HLG is used even if the bottom layer is client composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_HLG_HW_Uses_HLG) {
+ // BT2020_HLG is used even if the top layer is client composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_NonHdr_HW_Uses_PQ) {
+ // Even if there are non-HDR layers present, BT2020_PQ can still be used.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIsNotHdr()
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_NonHdr_RE_Uses_HLG) {
+ // If all layers use HLG then HLG is used if there are no other special
+ // conditions.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIsNotHdr()
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+struct OutputUpdateColorProfile_AffectsChosenRenderIntentTest
+ : public OutputUpdateColorProfileTest {
+ // The various values for CompositionRefreshArgs::outputColorSetting affect
+ // the chosen renderIntent, along with whether the preferred dataspace is an
+ // HDR dataspace or not.
+
+ OutputUpdateColorProfile_AffectsChosenRenderIntentTest() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+ mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ EXPECT_CALL(*mDisplayColorProfile, hasLegacyHdrSupport(ui::Dataspace::BT2020_PQ))
+ .WillRepeatedly(Return(false));
+ }
+
+ // The tests here involve enough state and GMock setup that using a mini-DSL
+ // makes the tests much more readable, and allows the test to focus more on
+ // the intent than on some of the details.
+
+ static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+ static constexpr ui::Dataspace kHdrDataspace = ui::Dataspace::BT2020_PQ;
+
+ struct IfDataspaceChosenState
+ : public CallOrderStateMachineHelper<TestType, IfDataspaceChosenState> {
+ [[nodiscard]] auto ifDataspaceChosenIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+ return nextState<AndOutputColorSettingState>();
+ }
+ [[nodiscard]] auto ifDataspaceChosenIsNonHdr() {
+ return ifDataspaceChosenIs(kNonHdrDataspace);
+ }
+ [[nodiscard]] auto ifDataspaceChosenIsHdr() { return ifDataspaceChosenIs(kHdrDataspace); }
+ };
+
+ struct AndOutputColorSettingState
+ : public CallOrderStateMachineHelper<TestType, AndOutputColorSettingState> {
+ [[nodiscard]] auto andOutputColorSettingIs(OutputColorSetting setting) {
+ getInstance()->mRefreshArgs.outputColorSetting = setting;
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::RenderIntent intent) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(getInstance()->mLayer1.mLayerFEState.dataspace, intent, _,
+ _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Tests call one of these two helper member functions to start using the
+ // mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfDataspaceChosenState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+ Managed_NonHdr_Prefers_Colorimetric) {
+ verify().ifDataspaceChosenIsNonHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kManaged)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::COLORIMETRIC)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+ Managed_Hdr_Prefers_ToneMapColorimetric) {
+ verify().ifDataspaceChosenIsHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kManaged)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_COLORIMETRIC)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Enhanced_NonHdr_Prefers_Enhance) {
+ verify().ifDataspaceChosenIsNonHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::ENHANCE)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+ Enhanced_Hdr_Prefers_ToneMapEnhance) {
+ verify().ifDataspaceChosenIsHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_ENHANCE)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_NonHdr_Prefers_Vendor) {
+ verify().ifDataspaceChosenIsNonHdr()
+ .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+ .thenExpectBestColorModeCallUses(
+ static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_Hdr_Prefers_Vendor) {
+ verify().ifDataspaceChosenIsHdr()
+ .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+ .thenExpectBestColorModeCallUses(
+ static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+ .execute();
+}
/*
* Output::beginFrame()
*/
+struct OutputBeginFrameTest : public ::testing::Test {
+ using TestType = OutputBeginFrameTest;
+
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by begiNFrame to use a mock
+ // implementations.
+ MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ };
+
+ OutputBeginFrameTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ struct IfGetDirtyRegionExpectationState
+ : public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> {
+ [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) {
+ EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false))
+ .WillOnce(Return(dirtyRegion));
+ return nextState<AndIfGetOutputLayerCountExpectationState>();
+ }
+ };
+
+ struct AndIfGetOutputLayerCountExpectationState
+ : public CallOrderStateMachineHelper<TestType, AndIfGetOutputLayerCountExpectationState> {
+ [[nodiscard]] auto andIfGetOutputLayerCountReturns(size_t layerCount) {
+ EXPECT_CALL(getInstance()->mOutput, getOutputLayerCount()).WillOnce(Return(layerCount));
+ return nextState<AndIfLastCompositionHadVisibleLayersState>();
+ }
+ };
+
+ struct AndIfLastCompositionHadVisibleLayersState
+ : public CallOrderStateMachineHelper<TestType,
+ AndIfLastCompositionHadVisibleLayersState> {
+ [[nodiscard]] auto andIfLastCompositionHadVisibleLayersIs(bool hadOutputLayers) {
+ getInstance()->mOutput.mState.lastCompositionHadVisibleLayers = hadOutputLayers;
+ return nextState<ThenExpectRenderSurfaceBeginFrameCallState>();
+ }
+ };
+
+ struct ThenExpectRenderSurfaceBeginFrameCallState
+ : public CallOrderStateMachineHelper<TestType,
+ ThenExpectRenderSurfaceBeginFrameCallState> {
+ [[nodiscard]] auto thenExpectRenderSurfaceBeginFrameCall(bool mustRecompose) {
+ EXPECT_CALL(*getInstance()->mRenderSurface, beginFrame(mustRecompose));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+ [[nodiscard]] auto execute() {
+ getInstance()->mOutput.beginFrame();
+ return nextState<CheckPostconditionHadVisibleLayersState>();
+ }
+ };
+
+ struct CheckPostconditionHadVisibleLayersState
+ : public CallOrderStateMachineHelper<TestType, CheckPostconditionHadVisibleLayersState> {
+ void checkPostconditionHadVisibleLayers(bool expected) {
+ EXPECT_EQ(expected, getInstance()->mOutput.mState.lastCompositionHadVisibleLayers);
+ }
+ };
+
+ // Tests call one of these two helper member functions to start using the
+ // mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfGetDirtyRegionExpectationState::make(this); }
+
+ static const Region kEmptyRegion;
+ static const Region kNotEmptyRegion;
+
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ StrictMock<OutputPartialMock> mOutput;
+};
+
+const Region OutputBeginFrameTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputBeginFrameTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(true)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(true)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(true)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
/*
* Output::devOptRepaintFlash()
*/
+struct OutputDevOptRepaintFlashTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by composeSurfaces to use a mock
+ // implementations.
+ MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+ MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(prepareFrame, void());
+ };
+
+ OutputDevOptRepaintFlashTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ static const Region kEmptyRegion;
+ static const Region kNotEmptyRegion;
+
+ StrictMock<OutputPartialMock> mOutput;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+const Region OutputDevOptRepaintFlashTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputDevOptRepaintFlashTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = {};
+ mRefreshArgs.repaintEverything = true;
+ mOutput.mState.isEnabled = true;
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+ mRefreshArgs.repaintEverything = true;
+ mOutput.mState.isEnabled = false;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, prepareFrame());
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+ mRefreshArgs.repaintEverything = true;
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion));
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, prepareFrame());
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+ mRefreshArgs.repaintEverything = false;
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion)));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, prepareFrame());
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
// TODO(b/144060211) - Add coverage
/*
* Output::finishFrame()
*/
-// TODO(b/144060211) - Add coverage
+struct OutputFinishFrameTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by composeSurfaces to use a mock
+ // implementations.
+ MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+ MOCK_METHOD0(postFramebuffer, void());
+ };
+
+ OutputFinishFrameTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ StrictMock<OutputPartialMock> mOutput;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
+ mOutput.mState.isEnabled = false;
+
+ mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)));
+
+ mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)))
+ .WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+
+ mOutput.finishFrame(mRefreshArgs);
+}
/*
* Output::postFramebuffer()
*/
-// TODO(b/144060211) - Add coverage
+struct OutputPostFramebufferTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by composeSurfaces to use a mock
+ // implementations.
+ MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+ };
+
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
+ }
+
+ StrictMock<mock::OutputLayer> outputLayer;
+ StrictMock<mock::LayerFE> layerFE;
+ StrictMock<HWC2::mock::Layer> hwc2Layer;
+ };
+
+ OutputPostFramebufferTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2u))
+ .WillRepeatedly(Return(&mLayer3.outputLayer));
+ }
+
+ StrictMock<OutputPartialMock> mOutput;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+
+ Layer mLayer1;
+ Layer mLayer2;
+ Layer mLayer3;
+};
+
+TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
+ mOutput.mState.isEnabled = false;
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
+ mOutput.mState.isEnabled = true;
+
+ compositionengine::Output::FrameFences frameFences;
+
+ // This should happen even if there are no output layers.
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // For this test in particular we want to make sure the call expectations
+ // setup below are satisfied in the specific order.
+ InSequence seq;
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
+ // Simulate getting release fences from each layer, and ensure they are passed to the
+ // front-end layer interface for each layer correctly.
+
+ mOutput.mState.isEnabled = true;
+
+ // Create three unique fence instances
+ sp<Fence> layer1Fence = new Fence();
+ sp<Fence> layer2Fence = new Fence();
+ sp<Fence> layer3Fence = new Fence();
+
+ compositionengine::Output::FrameFences frameFences;
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Compare the pointers values of each fence to make sure the correct ones
+ // are passed. This happens to work with the current implementation, but
+ // would not survive certain calls like Fence::merge() which would return a
+ // new instance.
+ EXPECT_CALL(mLayer1.layerFE,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
+ EXPECT_CALL(mLayer2.layerFE,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
+ EXPECT_CALL(mLayer3.layerFE,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ sp<Fence> clientTargetAcquireFence = new Fence();
+ sp<Fence> layer1Fence = new Fence();
+ sp<Fence> layer2Fence = new Fence();
+ sp<Fence> layer3Fence = new Fence();
+ compositionengine::Output::FrameFences frameFences;
+ frameFences.clientTargetAcquireFence = clientTargetAcquireFence;
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Fence::merge is called, and since none of the fences are actually valid,
+ // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
+ // This is the best we can do without creating a real kernel fence object.
+ EXPECT_CALL(mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ // This should happen even if there are no (current) output layers.
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // Load up the released layers with some mock instances
+ sp<StrictMock<mock::LayerFE>> releasedLayer1{new StrictMock<mock::LayerFE>()};
+ sp<StrictMock<mock::LayerFE>> releasedLayer2{new StrictMock<mock::LayerFE>()};
+ sp<StrictMock<mock::LayerFE>> releasedLayer3{new StrictMock<mock::LayerFE>()};
+ Output::ReleasedLayers layers;
+ layers.push_back(releasedLayer1);
+ layers.push_back(releasedLayer2);
+ layers.push_back(releasedLayer3);
+ mOutput.setReleasedLayers(std::move(layers));
+
+ // Set up a fake present fence
+ sp<Fence> presentFence = new Fence();
+ compositionengine::Output::FrameFences frameFences;
+ frameFences.presentFence = presentFence;
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Each released layer should be given the presentFence.
+ EXPECT_CALL(*releasedLayer1,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+ EXPECT_CALL(*releasedLayer2,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+ EXPECT_CALL(*releasedLayer3,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+
+ mOutput.postFramebuffer();
+
+ // After the call the list of released layers should have been cleared.
+ EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
+}
/*
* Output::composeSurfaces()
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index aef1c75..dc71128 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -95,7 +95,7 @@
// assume NO_RESOURCES when Status::isOk returns false
constexpr Error kDefaultError = Error::NO_RESOURCES;
-constexpr V2_4::Error kDefaultError_2_4 = V2_4::Error::NO_RESOURCES;
+constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
template<typename T, typename U>
T unwrapRet(Return<T>& ret, const U& default_val)
@@ -247,7 +247,12 @@
void Composer::registerCallback(const sp<IComposerCallback>& callback)
{
android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
- auto ret = mClient->registerCallback(callback);
+ auto ret = [&]() {
+ if (mClient_2_4) {
+ return mClient_2_4->registerCallback_2_4(callback);
+ }
+ return mClient->registerCallback(callback);
+ }();
if (!ret.isOk()) {
ALOGE("failed to register IComposerCallback");
}
@@ -413,15 +418,28 @@
IComposerClient::Attribute attribute, int32_t* outValue)
{
Error error = kDefaultError;
- mClient->getDisplayAttribute(display, config, attribute,
- [&](const auto& tmpError, const auto& tmpValue) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ if (mClient_2_4) {
+ mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = static_cast<Error>(tmpError);
+ if (error != Error::NONE) {
+ return;
+ }
- *outValue = tmpValue;
- });
+ *outValue = tmpValue;
+ });
+ } else {
+ mClient->getDisplayAttribute(display, config,
+ static_cast<V2_1::IComposerClient::Attribute>(attribute),
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outValue = tmpValue;
+ });
+ }
return error;
}
@@ -1200,13 +1218,14 @@
return static_cast<Error>(error);
}
-Error Composer::getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) {
+V2_4::Error Composer::getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) {
+ using Error = V2_4::Error;
if (!mClient_2_4) {
return Error::UNSUPPORTED;
}
- V2_4::Error error = kDefaultError_2_4;
+ Error error = kDefaultError_2_4;
mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
error = tmpError;
if (error != V2_4::Error::NONE) {
@@ -1216,7 +1235,50 @@
*outType = tmpType;
});
- return static_cast<V2_1::Error>(error);
+ return error;
+}
+
+V2_4::Error Composer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getDisplayVsyncPeriod(display,
+ [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outVsyncPeriod = tmpVsyncPeriod;
+ });
+
+ return error;
+}
+
+V2_4::Error Composer::setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
+ [&](const auto& tmpError, const auto& tmpTimeline) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outTimeline = tmpTimeline;
+ });
+
+ return error;
}
CommandReader::~CommandReader()
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index e743e59..336fdd8 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -62,12 +62,14 @@
using V2_1::Config;
using V2_1::Display;
using V2_1::Error;
-using V2_1::IComposerCallback;
using V2_1::Layer;
using V2_3::CommandReaderBase;
using V2_3::CommandWriterBase;
using V2_4::IComposer;
+using V2_4::IComposerCallback;
using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
using DisplayCapability = IComposerClient::DisplayCapability;
using PerFrameMetadata = IComposerClient::PerFrameMetadata;
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
@@ -208,10 +210,17 @@
virtual Error setDisplayBrightness(Display display, float brightness) = 0;
// Composer HAL 2.4
+ virtual bool isVsyncPeriodSwitchSupported() = 0;
virtual Error getDisplayCapabilities(Display display,
std::vector<DisplayCapability>* outCapabilities) = 0;
- virtual Error getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) = 0;
+ virtual V2_4::Error getDisplayConnectionType(
+ Display display, IComposerClient::DisplayConnectionType* outType) = 0;
+ virtual V2_4::Error getDisplayVsyncPeriod(Display display,
+ VsyncPeriodNanos* outVsyncPeriod) = 0;
+ virtual V2_4::Error setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) = 0;
};
namespace impl {
@@ -423,10 +432,16 @@
Error setDisplayBrightness(Display display, float brightness) override;
// Composer HAL 2.4
+ bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
Error getDisplayCapabilities(Display display,
std::vector<DisplayCapability>* outCapabilities) override;
- Error getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) override;
+ V2_4::Error getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) override;
+ V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+ V2_4::Error setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) override;
private:
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 6f7428a..34254e0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -81,7 +81,26 @@
Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
{
- mCallback->onVsyncReceived(mSequenceId, display, timestamp);
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+ return Void();
+ }
+
+ Return<void> onVsync_2_4(Hwc2::Display display, int64_t timestamp,
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
+ // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp,
+ std::make_optional(vsyncPeriodNanos));
+ return Void();
+ }
+
+ Return<void> onVsyncPeriodTimingChanged(
+ Hwc2::Display display,
+ const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override {
+ hwc_vsync_period_change_timeline_t timeline;
+ timeline.newVsyncAppliedTimeNanos = updatedTimeline.newVsyncAppliedTimeNanos;
+ timeline.refreshRequired = updatedTimeline.refreshRequired;
+ timeline.refreshTimeNanos = updatedTimeline.refreshTimeNanos;
+ mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, timeline);
return Void();
}
@@ -330,6 +349,36 @@
return Error::None;
}
+bool Display::isVsyncPeriodSwitchSupported() const {
+ ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
+
+ return mComposer.isVsyncPeriodSwitchSupported();
+}
+
+Error Display::getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const {
+ ALOGV("[%" PRIu64 "] getDisplayVsyncPeriod", mId);
+
+ Error error;
+
+ if (isVsyncPeriodSwitchSupported()) {
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
+ auto intError = mComposer.getDisplayVsyncPeriod(mId, &vsyncPeriodNanos);
+ error = static_cast<Error>(intError);
+ *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
+ } else {
+ // Get the default vsync period
+ hwc2_config_t configId = 0;
+ auto intError_2_1 = mComposer.getActiveConfig(mId, &configId);
+ error = static_cast<Error>(intError_2_1);
+ if (error == Error::None) {
+ auto config = mConfigs.at(configId);
+ *outVsyncPeriod = config->getVsyncPeriod();
+ }
+ }
+
+ return error;
+}
+
Error Display::getActiveConfigIndex(int* outIndex) const {
ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
hwc2_config_t configId = 0;
@@ -345,6 +394,7 @@
auto pos = mConfigs.find(configId);
if (pos != mConfigs.end()) {
*outIndex = std::distance(mConfigs.begin(), pos);
+ ALOGV("[%" PRIu64 "] index = %d", mId, *outIndex);
} else {
ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
// Return no error, but the caller needs to check for a negative index
@@ -582,6 +632,46 @@
return Error::None;
}
+Error Display::setActiveConfigWithConstraints(
+ const std::shared_ptr<const HWC2::Display::Config>& config,
+ const VsyncPeriodChangeConstraints& constraints, VsyncPeriodChangeTimeline* outTimeline) {
+ ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId);
+ if (config->getDisplayId() != mId) {
+ ALOGE("setActiveConfigWithConstraints received config %u for the wrong display %" PRIu64
+ " (expected %" PRIu64 ")",
+ config->getId(), config->getDisplayId(), mId);
+ return Error::BadConfig;
+ }
+
+ if (isVsyncPeriodSwitchSupported()) {
+ Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
+ hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
+ hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
+
+ Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {};
+ auto intError =
+ mComposer.setActiveConfigWithConstraints(mId, config->getId(), hwc2Constraints,
+ &vsyncPeriodChangeTimeline);
+ outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos;
+ outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired;
+ outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos;
+ return static_cast<Error>(intError);
+ }
+
+ // Use legacy setActiveConfig instead
+ ALOGV("fallback to legacy setActiveConfig");
+ const auto now = systemTime();
+ if (constraints.desiredTimeNanos > now || constraints.seamlessRequired) {
+ ALOGE("setActiveConfigWithConstraints received constraints that can't be satisfied");
+ }
+
+ auto intError_2_4 = mComposer.setActiveConfig(mId, config->getId());
+ outTimeline->newVsyncAppliedTimeNanos = std::max(now, constraints.desiredTimeNanos);
+ outTimeline->refreshRequired = true;
+ outTimeline->refreshTimeNanos = now;
+ return static_cast<Error>(intError_2_4);
+}
+
Error Display::setActiveConfig(const std::shared_ptr<const Config>& config)
{
if (config->getDisplayId() != mId) {
@@ -742,12 +832,13 @@
ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId);
auto config = Config::Builder(*this, configId)
- .setWidth(getAttribute(configId, Attribute::Width))
- .setHeight(getAttribute(configId, Attribute::Height))
- .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
- .setDpiX(getAttribute(configId, Attribute::DpiX))
- .setDpiY(getAttribute(configId, Attribute::DpiY))
- .build();
+ .setWidth(getAttribute(configId, Attribute::Width))
+ .setHeight(getAttribute(configId, Attribute::Height))
+ .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
+ .setDpiX(getAttribute(configId, Attribute::DpiX))
+ .setDpiY(getAttribute(configId, Attribute::DpiY))
+ .setConfigGroup(getAttribute(configId, Attribute::ConfigGroup))
+ .build();
mConfigs.emplace(configId, std::move(config));
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index b7cdf7f..81ae3b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,6 +38,8 @@
#include <unordered_set>
#include <vector>
+#include "../Scheduler/StrongTyping.h"
+
namespace android {
struct DisplayedFrameStats;
class Fence;
@@ -54,6 +56,8 @@
class Display;
class Layer;
+using VsyncPeriodChangeConstraints = hwc_vsync_period_change_constraints_t;
+using VsyncPeriodChangeTimeline = hwc_vsync_period_change_timeline_t;
// Implement this interface to receive hardware composer events.
//
@@ -70,8 +74,12 @@
Connection connection) = 0;
virtual void onRefreshReceived(int32_t sequenceId,
hwc2_display_t display) = 0;
- virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
- int64_t timestamp) = 0;
+ virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display, int64_t timestamp,
+ std::optional<hwc2_vsync_period_t> vsyncPeriod) = 0;
+ virtual void onVsyncPeriodTimingChangedReceived(
+ int32_t sequenceId, hwc2_display_t display,
+ const hwc_vsync_period_change_timeline_t& updatedTimeline) = 0;
+
virtual ~ComposerCallback() = default;
};
@@ -170,6 +178,10 @@
}
return *this;
}
+ Builder& setConfigGroup(int32_t configGroup) {
+ mConfig->mConfigGroup = configGroup;
+ return *this;
+ }
private:
float getDefaultDensity();
@@ -184,6 +196,7 @@
nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
float getDpiX() const { return mDpiX; }
float getDpiY() const { return mDpiY; }
+ int32_t getConfigGroup() const { return mConfigGroup; }
private:
Config(Display& display, hwc2_config_t id);
@@ -196,12 +209,14 @@
nsecs_t mVsyncPeriod;
float mDpiX;
float mDpiY;
+ int32_t mConfigGroup;
};
virtual hwc2_display_t getId() const = 0;
virtual bool isConnected() const = 0;
virtual void setConnected(bool connected) = 0; // For use by Device only
virtual const std::unordered_set<DisplayCapability>& getCapabilities() const = 0;
+ virtual bool isVsyncPeriodSwitchSupported() const = 0;
[[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
[[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
@@ -264,6 +279,12 @@
uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
[[clang::warn_unused_result]] virtual Error setDisplayBrightness(float brightness) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDisplayVsyncPeriod(
+ nsecs_t* outVsyncPeriod) const = 0;
+ [[clang::warn_unused_result]] virtual Error setActiveConfigWithConstraints(
+ const std::shared_ptr<const HWC2::Display::Config>& config,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) = 0;
};
namespace impl {
@@ -323,6 +344,10 @@
Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
Error setDisplayBrightness(float brightness) const override;
+ Error getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const override;
+ Error setActiveConfigWithConstraints(const std::shared_ptr<const HWC2::Display::Config>& config,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) override;
// Other Display methods
hwc2_display_t getId() const override { return mId; }
@@ -331,6 +356,7 @@
const std::unordered_set<DisplayCapability>& getCapabilities() const override {
return mDisplayCapabilities;
};
+ virtual bool isVsyncPeriodSwitchSupported() const override;
private:
int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -355,7 +381,9 @@
bool mIsConnected;
DisplayType mType;
std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
+
std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+
std::once_flag mDisplayCapabilityQueryFlag;
std::unordered_set<DisplayCapability> mDisplayCapabilities;
};
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 286311b..7abcd0f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -601,8 +601,6 @@
virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
- virtual uint64_t getCurrentFrameNumber() const { return mCurrentFrameNumber; }
-
/*
* Returns if a frame is ready
*/
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 14a2ab1..c555f79 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -1363,9 +1363,12 @@
}
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
- int64_t timestamp) {
+ int64_t timestamp,
+ std::optional<hwc2_vsync_period_t> /*vsyncPeriod*/) {
ATRACE_NAME("SF onVsync");
+ // TODO(b/140201379): use vsyncPeriod in the new DispSync
+
Mutex::Autolock lock(mStateLock);
// Ignore any vsyncs from a previous hardware composer.
if (sequenceId != getBE().mComposerSequenceId) {
@@ -1442,6 +1445,12 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
+void SurfaceFlinger::onVsyncPeriodTimingChangedReceived(
+ int32_t /*sequenceId*/, hwc2_display_t /*display*/,
+ const hwc_vsync_period_change_timeline_t& /*updatedTimeline*/) {
+ // TODO(b/142753004): use timeline when changing refresh rate
+}
+
void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
Mutex::Autolock lock(mStateLock);
if (sequenceId != getBE().mComposerSequenceId) {
@@ -1715,12 +1724,7 @@
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (sp<Layer> layer : mLayersWithQueuedFrames) {
auto compositionLayer = layer->getCompositionLayer();
- if (compositionLayer) {
- refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
- mFrameTracer->traceTimestamp(layer->getSequence(), layer->getCurrentBufferId(),
- layer->getCurrentFrameNumber(), systemTime(),
- FrameTracer::FrameEvent::HWC_COMPOSITION_QUEUED);
- }
+ if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
}
refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
@@ -4390,7 +4394,8 @@
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
case GET_DISPLAYED_CONTENT_SAMPLE:
- case NOTIFY_POWER_HINT: {
+ case NOTIFY_POWER_HINT:
+ case SET_GLOBAL_SHADOW_SETTINGS: {
if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -5507,6 +5512,12 @@
getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
}
+status_t SurfaceFlinger::setGlobalShadowSettings(const half4& /*ambientColor*/,
+ const half4& /*spotColor*/, float /*lightPosY*/,
+ float /*lightPosZ*/, float /*lightRadius*/) {
+ return NO_ERROR;
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 50b3ae4..5b1853c 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -470,7 +470,8 @@
bool* outSupport) const override;
status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override;
status_t notifyPowerHint(int32_t hintId) override;
-
+ status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius) override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
*/
@@ -484,11 +485,14 @@
/* ------------------------------------------------------------------------
* HWC2::ComposerCallback / HWComposer::EventHandler interface
*/
- void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
- int64_t timestamp) override;
+ void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, int64_t timestamp,
+ std::optional<hwc2_vsync_period_t> vsyncPeriod) override;
void onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
HWC2::Connection connection) override;
void onRefreshReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId) override;
+ void onVsyncPeriodTimingChangedReceived(
+ int32_t sequenceId, hwc2_display_t display,
+ const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
/* ------------------------------------------------------------------------
* Message handling
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b1a4951..db7d04c 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -469,6 +469,10 @@
getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
IComposerClient::Attribute::DPI_Y, _))
.WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 98c6aa0..2453ccb 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -39,8 +39,8 @@
using android::hardware::graphics::composer::V2_1::Display;
using android::hardware::graphics::composer::V2_1::Error;
using android::hardware::graphics::composer::V2_1::IComposer;
-using android::hardware::graphics::composer::V2_1::IComposerCallback;
using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_4::IComposerCallback;
using android::hardware::graphics::composer::V2_4::IComposerClient;
class Composer : public Hwc2::Composer {
@@ -120,8 +120,16 @@
MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
+ MOCK_METHOD0(isVsyncPeriodSwitchSupported, bool());
MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
- MOCK_METHOD2(getDisplayConnectionType, Error(Display, IComposerClient::DisplayConnectionType*));
+ MOCK_METHOD2(getDisplayConnectionType,
+ V2_4::Error(Display, IComposerClient::DisplayConnectionType*));
+ MOCK_METHOD3(getSupportedDisplayVsyncPeriods,
+ V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
+ MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
+ MOCK_METHOD4(setActiveConfigWithConstraints,
+ V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+ VsyncPeriodChangeTimeline*));
};
} // namespace mock