Merge "GpuStats: track whether the app creates ES1 context"
diff --git a/aidl/gui/android/view/LayerMetadataKey.aidl b/aidl/gui/android/view/LayerMetadataKey.aidl
new file mode 100644
index 0000000..7026ca8
--- /dev/null
+++ b/aidl/gui/android/view/LayerMetadataKey.aidl
@@ -0,0 +1,26 @@
+/*
+ * 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.
+ */
+
+package android.view;
+
+/** @hide */
+@Backing(type="int")
+enum LayerMetadataKey {
+ METADATA_OWNER_UID = 1,
+ METADATA_WINDOW_TYPE = 2,
+ METADATA_TASK_ID = 3,
+ METADATA_MOUSE_CURSOR = 4,
+}
diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk
index 042a6e6..7ccfc13 100644
--- a/build/phone-xhdpi-2048-dalvik-heap.mk
+++ b/build/phone-xhdpi-2048-dalvik-heap.mk
@@ -14,7 +14,7 @@
# limitations under the License.
#
-# Provides overrides to configure the Dalvik heap for a 2G phone
+# Provides overrides to configure the Dalvik heap for a 2GB phone
# 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM.
PRODUCT_PROPERTY_OVERRIDES += \
diff --git a/build/phone-xhdpi-4096-dalvik-heap.mk b/build/phone-xhdpi-4096-dalvik-heap.mk
new file mode 100644
index 0000000..2b84841
--- /dev/null
+++ b/build/phone-xhdpi-4096-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+# Provides overrides to configure the Dalvik heap for a 4GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+ dalvik.vm.heapstartsize=8m \
+ dalvik.vm.heapgrowthlimit=192m \
+ dalvik.vm.heapsize=512m \
+ dalvik.vm.heaptargetutilization=0.6 \
+ dalvik.vm.heapminfree=8m \
+ dalvik.vm.heapmaxfree=16m
diff --git a/build/phone-xhdpi-6144-dalvik-heap.mk b/build/phone-xhdpi-6144-dalvik-heap.mk
new file mode 100644
index 0000000..2bacc4a
--- /dev/null
+++ b/build/phone-xhdpi-6144-dalvik-heap.mk
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+
+# Provides overrides to configure the Dalvik heap for a 6GB phone
+
+PRODUCT_PROPERTY_OVERRIDES += \
+ dalvik.vm.heapstartsize=16m \
+ dalvik.vm.heapgrowthlimit=256m \
+ dalvik.vm.heapsize=512m \
+ dalvik.vm.heaptargetutilization=0.5 \
+ dalvik.vm.heapminfree=8m \
+ dalvik.vm.heapmaxfree=32m
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index ccd74db..87ea520 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -81,8 +81,8 @@
binder::Status DumpstateService::startBugreport(int32_t calling_uid,
const std::string& calling_package,
- const android::base::unique_fd& bugreport_fd,
- const android::base::unique_fd& screenshot_fd,
+ android::base::unique_fd bugreport_fd,
+ android::base::unique_fd screenshot_fd,
int bugreport_mode,
const sp<IDumpstateListener>& listener) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
index 27954ad..6dc0225 100644
--- a/cmds/dumpstate/DumpstateService.h
+++ b/cmds/dumpstate/DumpstateService.h
@@ -39,8 +39,8 @@
status_t dump(int fd, const Vector<String16>& args) override;
binder::Status startBugreport(int32_t calling_uid, const std::string& calling_package,
- const android::base::unique_fd& bugreport_fd,
- const android::base::unique_fd& screenshot_fd, int bugreport_mode,
+ android::base::unique_fd bugreport_fd,
+ android::base::unique_fd screenshot_fd, int bugreport_mode,
const sp<IDumpstateListener>& listener) override;
// No-op
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 9d6a7ff..fc94f23 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -2729,15 +2729,13 @@
DurationReporter::~DurationReporter() {
if (!title_.empty()) {
float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC;
- if (elapsed < .5f && !verbose_) {
- return;
+ if (elapsed >= .5f || verbose_) {
+ MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed);
}
- MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed);
- if (logcat_only_) {
- return;
+ if (!logcat_only_) {
+ // Use "Yoda grammar" to make it easier to grep|sort sections.
+ printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
}
- // Use "Yoda grammar" to make it easier to grep|sort sections.
- printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
}
}
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index f1884f8..dac90d9 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -444,7 +444,7 @@
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
// startBugreport is an async call. Verify binder call succeeded first, then wait till listener
// gets expected callbacks.
@@ -480,7 +480,7 @@
// Call startBugreport with bad arguments.
sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
2000, // invalid bugreport mode
listener);
EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
@@ -501,20 +501,24 @@
// Prepare arguments
unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
+ unique_fd bugreport_fd2(dup(bugreport_fd.get()));
unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+ unique_fd screenshot_fd2(dup(screenshot_fd.get()));
EXPECT_NE(bugreport_fd.get(), -1);
+ EXPECT_NE(bugreport_fd2.get(), -1);
EXPECT_NE(screenshot_fd.get(), -1);
+ EXPECT_NE(screenshot_fd2.get(), -1);
sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
android::binder::Status status =
- ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd), std::move(screenshot_fd),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
EXPECT_TRUE(status.isOk());
// try to make another call to startBugreport. This should fail.
sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
- status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ status = ds_binder->startBugreport(123, "com.dummy.package", std::move(bugreport_fd2), std::move(screenshot_fd2),
Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
EXPECT_FALSE(status.isOk());
WaitTillExecutionComplete(listener2.get());
diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp
index abdf168..5597bcd 100644
--- a/cmds/dumpsys/dumpsys.cpp
+++ b/cmds/dumpsys/dumpsys.cpp
@@ -364,7 +364,7 @@
}
if (err != OK) {
- aerr << "Error dumping service info status_t: (" << err << ") "
+ aerr << "Error dumping service info status_t: " << statusToString(err) << " "
<< serviceName << endl;
}
});
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 5476319..08a31c1 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -20,15 +20,16 @@
"android.hardware.vibrator@1.2",
"android.hardware.vibrator@1.3",
"libbase",
- "libbinder",
+ "libbinder_ndk",
"libhidlbase",
"liblog",
"libutils",
- "vintf-vibrator-cpp",
+ "vintf-vibrator-ndk_platform",
],
cflags: [
"-DLOG_TAG=\"idlcli\"",
],
+ vendor_available: true,
}
cc_library {
diff --git a/cmds/idlcli/vibrator.h b/cmds/idlcli/vibrator.h
index 2f11923..ca5142d 100644
--- a/cmds/idlcli/vibrator.h
+++ b/cmds/idlcli/vibrator.h
@@ -13,13 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#ifndef FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
#define FRAMEWORK_NATIVE_CMDS_IDLCLI_VIBRATOR_H_
+#include <aidl/android/hardware/vibrator/IVibrator.h>
+#include <android/binder_manager.h>
#include <android/hardware/vibrator/1.3/IVibrator.h>
-#include <android/hardware/vibrator/IVibrator.h>
-#include <binder/IServiceManager.h>
#include "utils.h"
@@ -39,22 +38,27 @@
}
template <>
-inline binder::Status NullptrStatus() {
- using binder::Status;
- return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+inline ndk::ScopedAStatus NullptrStatus() {
+ return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_NULL_POINTER));
}
template <typename I>
-inline sp<I> getService() {
+inline auto getService() {
return I::getService();
}
template <>
-inline sp<hardware::vibrator::IVibrator> getService() {
- return waitForVintfService<hardware::vibrator::IVibrator>();
+inline auto getService<aidl::android::hardware::vibrator::IVibrator>() {
+ const auto instance =
+ std::string() + aidl::android::hardware::vibrator::IVibrator::descriptor + "/default";
+ auto vibBinder = ndk::SpAIBinder(AServiceManager_getService(instance.c_str()));
+ return aidl::android::hardware::vibrator::IVibrator::fromBinder(vibBinder);
}
template <typename I>
+using shared_ptr = std::result_of_t<decltype(getService<I>)&()>;
+
+template <typename I>
class HalWrapper {
public:
static std::unique_ptr<HalWrapper> Create() {
@@ -70,10 +74,10 @@
}
private:
- HalWrapper(sp<I>&& hal) : mHal(std::move(hal)) {}
+ HalWrapper(shared_ptr<I>&& hal) : mHal(std::move(hal)) {}
private:
- sp<I> mHal;
+ shared_ptr<I> mHal;
};
template <typename I>
@@ -95,7 +99,7 @@
namespace V1_1 = ::android::hardware::vibrator::V1_1;
namespace V1_2 = ::android::hardware::vibrator::V1_2;
namespace V1_3 = ::android::hardware::vibrator::V1_3;
-namespace aidl = ::android::hardware::vibrator;
+namespace aidl = ::aidl::android::hardware::vibrator;
} // namespace vibrator
} // namespace idlcli
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
index 705e40b..4721a5f 100644
--- a/cmds/idlcli/vibrator/CommandCompose.cpp
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -80,7 +80,7 @@
Status ret;
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
index 30d8587..303a989 100644
--- a/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCapabilities.cpp
@@ -48,7 +48,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::getCapabilities, &cap);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
index b414307..10508bd 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -50,7 +50,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
index 360fc9d..900cb18 100644
--- a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -50,7 +50,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else {
return UNAVAILABLE;
diff --git a/cmds/idlcli/vibrator/CommandOff.cpp b/cmds/idlcli/vibrator/CommandOff.cpp
index 53fada0..cedb9fe 100644
--- a/cmds/idlcli/vibrator/CommandOff.cpp
+++ b/cmds/idlcli/vibrator/CommandOff.cpp
@@ -47,7 +47,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::off);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
auto status = hal->call(&V1_0::IVibrator::off);
diff --git a/cmds/idlcli/vibrator/CommandOn.cpp b/cmds/idlcli/vibrator/CommandOn.cpp
index ccb3c19..4e7e493 100644
--- a/cmds/idlcli/vibrator/CommandOn.cpp
+++ b/cmds/idlcli/vibrator/CommandOn.cpp
@@ -55,7 +55,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::on, mDuration, nullptr);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
auto status = hal->call(&V1_0::IVibrator::on, mDuration);
diff --git a/cmds/idlcli/vibrator/CommandPerform.cpp b/cmds/idlcli/vibrator/CommandPerform.cpp
index 58d4e0a..69c7e37 100644
--- a/cmds/idlcli/vibrator/CommandPerform.cpp
+++ b/cmds/idlcli/vibrator/CommandPerform.cpp
@@ -99,7 +99,7 @@
auto status =
hal->call(&aidl::IVibrator::perform, static_cast<aidl::Effect>(mEffect),
static_cast<aidl::EffectStrength>(mStrength), nullptr, &aidlLengthMs);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
lengthMs = static_cast<uint32_t>(aidlLengthMs);
ret = status.isOk() ? OK : ERROR;
} else {
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 33d7eed..8b8058c 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -56,7 +56,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::setAmplitude,
static_cast<float>(mAmplitude) / UINT8_MAX);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
auto status = hal->call(&V1_0::IVibrator::setAmplitude, mAmplitude);
diff --git a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
index 5bc827e..1795793 100644
--- a/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
+++ b/cmds/idlcli/vibrator/CommandSetExternalControl.cpp
@@ -53,7 +53,7 @@
if (auto hal = getHal<aidl::IVibrator>()) {
auto status = hal->call(&aidl::IVibrator::setExternalControl, mEnable);
- statusStr = status.toString8();
+ statusStr = status.getDescription();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_3::IVibrator>()) {
auto status = hal->call(&V1_3::IVibrator::setExternalControl, mEnable);
diff --git a/cmds/installd/.gitignore b/cmds/installd/.gitignore
new file mode 100644
index 0000000..abc921c
--- /dev/null
+++ b/cmds/installd/.gitignore
@@ -0,0 +1,2 @@
+# ignore the files generated by intellij
+.idea
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 75dec37..8ff4dd8 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -13,6 +13,7 @@
srcs: [
"CacheItem.cpp",
"CacheTracker.cpp",
+ "CrateManager.cpp",
"InstalldNativeService.cpp",
"QuotaUtils.cpp",
"dexopt.cpp",
@@ -163,6 +164,7 @@
name: "installd_aidl",
srcs: [
"binder/android/os/IInstalld.aidl",
+ "binder/android/os/storage/CrateMetadata.aidl",
],
path: "binder",
}
diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp
new file mode 100644
index 0000000..344aefb
--- /dev/null
+++ b/cmds/installd/CrateManager.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#include "CrateManager.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android/log.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <libgen.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <string>
+#include <utils.h>
+
+#include "utils.h"
+
+using android::base::StringPrintf;
+
+namespace android {
+namespace installd {
+
+CrateManager::CrateManager(const char* uuid, userid_t userId, const std::string& packageName) {
+ mPackageName = packageName;
+ mRoot = create_data_user_ce_package_path(uuid, userId, (const char*)packageName.c_str());
+ mCratedFoldersRoot = StringPrintf("%s/crates", mRoot.c_str());
+}
+
+CrateManager::~CrateManager() {}
+
+static std::string getValidatedCratedPath(std::string path) {
+ size_t pos = path.rfind("/");
+ if (pos == std::string::npos) {
+ return path;
+ }
+
+ return path.substr(pos + 1, path.length());
+}
+
+void CrateManager::traverseChildDir(const std::string& targetDir,
+ std::function<void(FTSENT*)>& onVisitChildDir) {
+ char* argv[] = {(char*)targetDir.c_str(), nullptr};
+ FTS* fts = fts_open(argv, FTS_PHYSICAL | FTS_NOCHDIR | FTS_XDEV, nullptr);
+ if (fts == nullptr) {
+ PLOG(WARNING) << "Failed to fts_open " << targetDir;
+ return;
+ }
+
+ FTSENT* p;
+ while ((p = fts_read(fts)) != nullptr) {
+ switch (p->fts_info) {
+ case FTS_D:
+ if (p->fts_level == 1) {
+ onVisitChildDir(p);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (p->fts_level == 1) {
+ fts_set(fts, p, FTS_SKIP);
+ }
+ }
+ fts_close(fts);
+}
+
+void CrateManager::traverseAllPackagesForUser(
+ const std::unique_ptr<std::string>& uuid, userid_t userId,
+ std::function<void(FTSENT*)>& onHandlingPackage) {
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ auto ce_path = create_data_user_ce_path(uuid_, userId);
+ traverseChildDir(ce_path, onHandlingPackage);
+}
+
+void CrateManager::createCrate(
+ CratedFolder cratedFolder,
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+ const char* path = cratedFolder->fts_path;
+ if (path == nullptr || *path == '\0') {
+ return;
+ }
+
+ std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>();
+ crateMetadata->uid = cratedFolder->fts_statp->st_uid;
+ crateMetadata->packageName = mPackageName;
+ crateMetadata->id = getValidatedCratedPath(path);
+
+ onCreateCrate(cratedFolder, crateMetadata);
+}
+
+void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) {
+ std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void {
+ createCrate(cratedFolder, onCreateCrate);
+ };
+ traverseChildDir(mCratedFoldersRoot, onVisitCrateDir);
+}
+
+#if CRATE_DEBUG
+void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) {
+ LOG(DEBUG) << "CrateMetadata = {"
+ << "uid : \"" << CrateMetadata->uid
+ << "\", packageName : \"" << CrateMetadata->packageName
+ << "\", id : \"" << CrateMetadata->id
+ << "\"}";
+}
+#endif
+
+} // namespace installd
+} // namespace android
diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h
new file mode 100644
index 0000000..1776622
--- /dev/null
+++ b/cmds/installd/CrateManager.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+#define ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
+
+#include <android/os/storage/CrateMetadata.h>
+#include <cutils/multiuser.h>
+#include <fts.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#ifndef CRATE_DEBUG
+#define CRATE_DEBUG 1
+#endif
+
+namespace android {
+namespace installd {
+
+using android::os::storage::CrateMetadata;
+
+/**
+ * The crated folder actually is a folder that is the first level child director. In order to
+ * distingish between the crated folder and the other FTSENT*, to define the type "CratedFolder"
+ * make the code easy to identify the difference.
+ */
+typedef FTSENT* CratedFolder;
+
+/**
+ * In order to give the users more fine-grained files controlling, the crate information can help
+ * applications' developers to show the more detail information to the users. The crate information
+ * include the Label, Expiration etc..
+ */
+class CrateManager {
+public:
+ CrateManager(const char* uuid, userid_t userId, const std::string& packageName);
+ ~CrateManager();
+
+ void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+
+ static void traverseChildDir(const std::string& targetDir,
+ std::function<void(FTSENT*)>& onVisitChildDir);
+
+ static void traverseAllPackagesForUser(
+ const std::unique_ptr<std::string>& uuid,
+ userid_t userId,
+ std::function<void(FTSENT*)>& onHandlingPackage);
+
+#if CRATE_DEBUG
+ static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata);
+#endif
+private:
+ std::string mRoot;
+ std::string mCratedFoldersRoot;
+ std::string mPackageName;
+
+ void createCrate(
+ CratedFolder cratedFolder,
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate);
+};
+
+} // namespace installd
+} // namespace android
+
+#endif // ANDROID_INSTALLD_CRATE_INFO_MANAGER_H
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 95957a0..f8a68b4 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -31,6 +31,7 @@
#include <sys/file.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
+#include <sys/mount.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
@@ -65,6 +66,7 @@
#include "view_compiler.h"
#include "CacheTracker.h"
+#include "CrateManager.h"
#include "MatchExtensionGen.h"
#include "QuotaUtils.h"
@@ -86,6 +88,9 @@
static constexpr const char* kCpPath = "/system/bin/cp";
static constexpr const char* kXattrDefault = "user.default";
+static constexpr const char* kDataMirrorCePath = "/data_mirror/data_ce";
+static constexpr const char* kDataMirrorDePath = "/data_mirror/data_de";
+
static constexpr const int MIN_RESTRICTED_HOME_SDK_VERSION = 24; // > M
static constexpr const char* PKG_LIB_POSTFIX = "/lib";
@@ -99,6 +104,13 @@
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
static constexpr const char* kFuseProp = "persist.sys.fuse";
+/**
+ * Property to control if app data isolation is enabled.
+ */
+static constexpr const char* kAppDataIsolationEnabledProperty = "persist.zygote.app_data_isolation";
+
+static std::atomic<bool> sAppDataIsolationEnabled(false);
+
namespace {
constexpr const char* kDump = "android.permission.DUMP";
@@ -258,6 +270,8 @@
sp<ProcessState> ps(ProcessState::self());
ps->startThreadPool();
ps->giveThreadPoolName();
+ sAppDataIsolationEnabled = android::base::GetBoolProperty(
+ kAppDataIsolationEnabledProperty, false);
return android::OK;
}
@@ -450,9 +464,12 @@
// And return the CE inode of the top-level data directory so we can
// clear contents while CE storage is locked
- if ((_aidl_return != nullptr)
- && get_path_inode(path, reinterpret_cast<ino_t*>(_aidl_return)) != 0) {
- return error("Failed to get_path_inode for " + path);
+ if (_aidl_return != nullptr) {
+ ino_t result;
+ if (get_path_inode(path, &result) != 0) {
+ return error("Failed to get_path_inode for " + path);
+ }
+ *_aidl_return = static_cast<uint64_t>(result);
}
}
if (flags & FLAG_STORAGE_DE) {
@@ -2029,6 +2046,82 @@
return ok();
}
+binder::Status InstalldNativeService::getAppCrates(
+ const std::unique_ptr<std::string>& uuid,
+ const std::vector<std::string>& packageNames, int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ for (const auto& packageName : packageNames) {
+ CHECK_ARGUMENT_PACKAGE_NAME(packageName);
+ }
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+ [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ if (cratedFolder == nullptr) {
+ return;
+ }
+ retVector->push_back(std::move(crateMetadata));
+ };
+
+ for (const auto& packageName : packageNames) {
+#if CRATE_DEBUG
+ LOG(DEBUG) << "packageName = " << packageName;
+#endif
+ auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageName);
+ crateManager->traverseAllCrates(onCreateCrate);
+ }
+
+#if CRATE_DEBUG
+ LOG(WARNING) << "retVector->size() =" << retVector->size();
+ for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+ CrateManager::dump(*iter);
+ }
+#endif
+
+ *_aidl_return = std::move(retVector);
+ return ok();
+}
+
+binder::Status InstalldNativeService::getUserCrates(
+ const std::unique_ptr<std::string>& uuid, int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+
+ const char* uuid_ = uuid ? uuid->c_str() : nullptr;
+ auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>();
+
+ std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate =
+ [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void {
+ if (cratedFolder == nullptr) {
+ return;
+ }
+ retVector->push_back(std::move(crateMetadata));
+ };
+
+ std::function<void(FTSENT*)> onHandingPackage = [&](FTSENT* packageDir) -> void {
+ auto crateManager = std::make_unique<CrateManager>(uuid_, userId, packageDir->fts_name);
+ crateManager->traverseAllCrates(onCreateCrate);
+ };
+ CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage);
+
+#if CRATE_DEBUG
+ LOG(DEBUG) << "retVector->size() =" << retVector->size();
+ for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) {
+ CrateManager::dump(*iter);
+ }
+#endif
+
+ *_aidl_return = std::move(retVector);
+ return ok();
+}
+
binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota) {
ENFORCE_UID(AID_SYSTEM);
@@ -2421,7 +2514,7 @@
#endif
binder::Status InstalldNativeService::installApkVerity(const std::string& filePath,
- const ::android::base::unique_fd& verityInputAshmem, int32_t contentSize) {
+ android::base::unique_fd verityInputAshmem, int32_t contentSize) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_PATH(filePath);
std::lock_guard<std::recursive_mutex> lock(mLock);
@@ -2595,6 +2688,8 @@
std::getline(in, ignored);
if (android::base::GetBoolProperty(kFuseProp, false)) {
+ // TODO(b/146139106): Use sdcardfs mounts on devices running sdcardfs so we don't bypass
+ // it's VFS cache
if (target.compare(0, 17, "/mnt/pass_through") == 0) {
LOG(DEBUG) << "Found storage mount " << source << " at " << target;
mStorageMounts[source] = target;
@@ -2611,11 +2706,105 @@
return ok();
}
+// Mount volume's CE and DE storage to mirror
+binder::Status InstalldNativeService::onPrivateVolumeMounted(
+ const std::unique_ptr<std::string>& uuid) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ if (!sAppDataIsolationEnabled) {
+ return ok();
+ }
+ if (!uuid) {
+ return error("Should not happen, mounting uuid == null");
+ }
+
+ const char* uuid_ = uuid->c_str();
+ // Mount CE mirror
+ std::string mirrorVolCePath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+ if (fs_prepare_dir(mirrorVolCePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+ return error("Failed to create CE mirror");
+ }
+ auto cePath = StringPrintf("%s/user_ce", create_data_path(uuid_).c_str());
+ if (TEMP_FAILURE_RETRY(mount(cePath.c_str(), mirrorVolCePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolCePath);
+ }
+
+ // Mount DE mirror
+ std::string mirrorVolDePath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+ if (fs_prepare_dir(mirrorVolDePath.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
+ return error("Failed to create DE mirror");
+ }
+ auto dePath = StringPrintf("%s/user_de", create_data_path(uuid_).c_str());
+ if (TEMP_FAILURE_RETRY(mount(dePath.c_str(), mirrorVolDePath.c_str(), NULL,
+ MS_NOSUID | MS_NODEV | MS_NOATIME | MS_BIND | MS_NOEXEC, nullptr)) == -1) {
+ return error("Failed to mount " + mirrorVolDePath);
+ }
+ return ok();
+}
+
+// Unmount volume's CE and DE storage from mirror
+binder::Status InstalldNativeService::onPrivateVolumeRemoved(
+ const std::unique_ptr<std::string>& uuid) {
+ ENFORCE_UID(AID_SYSTEM);
+ CHECK_ARGUMENT_UUID(uuid);
+ if (!sAppDataIsolationEnabled) {
+ return ok();
+ }
+ if (!uuid) {
+ // It happens when private volume failed to mount.
+ LOG(INFO) << "Ignore unmount uuid=null";
+ return ok();
+ }
+ const char* uuid_ = uuid->c_str();
+
+ binder::Status res = ok();
+
+ std::string mirrorCeVolPath(StringPrintf("%s/%s", kDataMirrorCePath, uuid_));
+ std::string mirrorDeVolPath(StringPrintf("%s/%s", kDataMirrorDePath, uuid_));
+
+ // Unmount CE storage
+ std::lock_guard<std::recursive_mutex> lock(mLock);
+ if (TEMP_FAILURE_RETRY(umount(mirrorCeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorCeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorCeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorCeVolPath);
+ }
+
+ // Unmount DE storage
+ if (TEMP_FAILURE_RETRY(umount(mirrorDeVolPath.c_str())) != 0) {
+ if (errno != ENOENT) {
+ res = error(StringPrintf("Failed to umount %s %s", mirrorDeVolPath.c_str(),
+ strerror(errno)));
+ }
+ }
+ if (delete_dir_contents_and_dir(mirrorDeVolPath, true) != 0) {
+ res = error("Failed to delete " + mirrorDeVolPath);
+ }
+ return res;
+}
+
std::string InstalldNativeService::findDataMediaPath(
const std::unique_ptr<std::string>& uuid, userid_t userid) {
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ // TODO(b/146139106): This is only safe on devices not running sdcardfs where there is no
+ // risk of bypassing the sdcardfs VFS cache
+
+ // Always use the lower filesystem path on FUSE enabled devices not running sdcardfs
+ // The upper filesystem path, /mnt/pass_through/<userid>/<vol>/ which was a bind mount
+ // to the lower filesytem may have been unmounted already when a user is
+ // removed and the path will now be pointing to a tmpfs without content
+ return StringPrintf("%s/%u", path.c_str(), userid);
+ }
+
auto resolved = mStorageMounts[path];
if (resolved.empty()) {
LOG(WARNING) << "Failed to find storage mount for " << path;
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index ef91bf8..bf11002 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -81,6 +81,16 @@
int32_t userId, int32_t flags, const std::vector<int32_t>& appIds,
std::vector<int64_t>* _aidl_return);
+ binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid,
+ const std::vector<std::string>& packageNames,
+ int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ _aidl_return);
+ binder::Status getUserCrates(
+ const std::unique_ptr<std::string>& uuid, int32_t userId,
+ std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>*
+ _aidl_return);
+
binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid,
int32_t userId, int32_t appId, int64_t cacheQuota);
@@ -133,7 +143,7 @@
binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet,
const std::unique_ptr<std::string>& outputPath);
binder::Status installApkVerity(const std::string& filePath,
- const ::android::base::unique_fd& verityInput, int32_t contentSize);
+ android::base::unique_fd verityInput, int32_t contentSize);
binder::Status assertFsverityRootHashMatches(const std::string& filePath,
const std::vector<uint8_t>& expectedHash);
binder::Status reconcileSecondaryDexFile(const std::string& dexPath,
@@ -146,6 +156,8 @@
binder::Status invalidateMounts();
binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid,
bool* _aidl_return);
+ binder::Status onPrivateVolumeMounted(const std::unique_ptr<std::string>& volumeUuid);
+ binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid);
binder::Status prepareAppProfile(const std::string& packageName,
int32_t userId, int32_t appId, const std::string& profileName,
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp
index b238dd3..a71e01c 100644
--- a/cmds/installd/QuotaUtils.cpp
+++ b/cmds/installd/QuotaUtils.cpp
@@ -61,6 +61,10 @@
std::getline(in, target, ' ');
std::getline(in, ignored);
+ if (target.compare(0, 13, "/data_mirror/") == 0) {
+ continue;
+ }
+
if (source.compare(0, 11, "/dev/block/") == 0) {
struct dqblk dq;
if (quotactl(QCMD(Q_GETQUOTA, USRQUOTA), source.c_str(), 0,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 6cc4bde..891b26d 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -40,6 +40,14 @@
long[] getUserSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
long[] getExternalSize(@nullable @utf8InCpp String uuid, int userId, int flags, in int[] appIds);
+ @nullable
+ android.os.storage.CrateMetadata[] getAppCrates(
+ @nullable @utf8InCpp String uuid, in @utf8InCpp String[] packageNames,
+ int userId);
+ @nullable
+ android.os.storage.CrateMetadata[] getUserCrates(
+ @nullable @utf8InCpp String uuid, int userId);
+
void setAppQuota(@nullable @utf8InCpp String uuid, int userId, int appId, long cacheQuota);
void moveCompleteApp(@nullable @utf8InCpp String fromUuid, @nullable @utf8InCpp String toUuid,
@@ -109,6 +117,8 @@
int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
+ void onPrivateVolumeMounted(@nullable @utf8InCpp String volumeUuid);
+ void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid);
void migrateLegacyObbData();
diff --git a/cmds/installd/binder/android/os/storage/CrateMetadata.aidl b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
new file mode 100644
index 0000000..bd6d12d
--- /dev/null
+++ b/cmds/installd/binder/android/os/storage/CrateMetadata.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+package android.os.storage;
+
+/** {@hide} */
+parcelable CrateMetadata {
+ /**
+ * To tell which uid the crate belong to.
+ * <p>Because installd query all of crates in specified userId, the install may return the list
+ * whose elements have the same crate id but different uid and package name.
+ * It needs to tell the caller the difference between these elements.
+ */
+ int uid;
+
+ /**
+ * To tell which the package the crate belong to.
+ * <p>Because installd query all of crates in specified uid, the install may return the list
+ * whose elements have the same uid and crate id but different package name.
+ * It needs to tell the caller the difference between these elements.
+ */
+ @utf8InCpp String packageName;
+
+ /**
+ * To tell the crate id that is the child directory/folder name in crates
+ * root.
+ */
+ @utf8InCpp String id;
+}
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 4eb1df0..6012822 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -278,6 +278,15 @@
return "/data/dalvik-cache";
}
+std::string create_system_user_ce_path(userid_t userId) {
+ return StringPrintf("%s/system_ce/%u", create_data_path(nullptr).c_str(), userId);
+}
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name) {
+ check_package_name(package_name);
+ return StringPrintf("%s/%s", create_system_user_ce_path(userId).c_str(), package_name);
+}
+
// Keep profile paths in sync with ActivityThread and LoadedApk.
const std::string PROFILE_EXT = ".prof";
const std::string CURRENT_PROFILE_EXT = ".cur";
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 6a42026..6a39adc 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -82,6 +82,10 @@
std::string create_data_dalvik_cache_path();
+std::string create_system_user_ce_path(userid_t userId);
+
+std::string create_system_user_ce_package_path(userid_t userId, const char* package_name);
+
std::string create_primary_cur_profile_dir_path(userid_t userid);
std::string create_primary_current_profile_package_dir_path(
userid_t user, const std::string& package_name);
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 141171b..ae74ac3 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -18,6 +18,9 @@
#include <android-base/logging.h>
#include <android-base/properties.h>
+#include <binder/BpBinder.h>
+#include <binder/IPCThreadState.h>
+#include <binder/ProcessState.h>
#include <binder/Stability.h>
#include <cutils/android_filesystem_config.h>
#include <cutils/multiuser.h>
@@ -80,7 +83,7 @@
ServiceManager::~ServiceManager() {
// this should only happen in tests
- for (const auto& [name, callbacks] : mNameToCallback) {
+ for (const auto& [name, callbacks] : mNameToRegistrationCallback) {
CHECK(!callbacks.empty()) << name;
for (const auto& callback : callbacks) {
CHECK(callback != nullptr) << name;
@@ -108,10 +111,11 @@
auto ctx = mAccess->getCallingContext();
sp<IBinder> out;
+ Service* service = nullptr;
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
- const Service& service = it->second;
+ service = &(it->second);
- if (!service.allowIsolated) {
+ if (!service->allowIsolated) {
uid_t appid = multiuser_get_app_id(ctx.uid);
bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
@@ -119,7 +123,7 @@
return nullptr;
}
}
- out = service.binder;
+ out = service->binder;
}
if (!mAccess->canFind(ctx, name)) {
@@ -130,6 +134,12 @@
tryStartService(name);
}
+ if (out) {
+ // Setting this guarantee each time we hand out a binder ensures that the client-checking
+ // loop knows about the event even if the client immediately drops the service
+ service->guaranteeClient = true;
+ }
+
return out;
}
@@ -182,15 +192,17 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- mNameToService[name] = Service {
+ auto entry = mNameToService.emplace(name, Service {
.binder = binder,
.allowIsolated = allowIsolated,
.dumpPriority = dumpPriority,
- };
+ .debugPid = ctx.debugPid,
+ });
- auto it = mNameToCallback.find(name);
- if (it != mNameToCallback.end()) {
+ auto it = mNameToRegistrationCallback.find(name);
+ if (it != mNameToRegistrationCallback.end()) {
for (const sp<IServiceCallback>& cb : it->second) {
+ entry.first->second.guaranteeClient = true;
// permission checked in registerForNotifications
cb->onRegistration(name, binder);
}
@@ -247,7 +259,7 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- mNameToCallback[name].push_back(callback);
+ mNameToRegistrationCallback[name].push_back(callback);
if (auto it = mNameToService.find(name); it != mNameToService.end()) {
const sp<IBinder>& binder = it->second.binder;
@@ -269,9 +281,9 @@
bool found = false;
- auto it = mNameToCallback.find(name);
- if (it != mNameToCallback.end()) {
- removeCallback(IInterface::asBinder(callback), &it, &found);
+ auto it = mNameToRegistrationCallback.find(name);
+ if (it != mNameToRegistrationCallback.end()) {
+ removeRegistrationCallback(IInterface::asBinder(callback), &it, &found);
}
if (!found) {
@@ -297,8 +309,8 @@
return Status::ok();
}
-void ServiceManager::removeCallback(const wp<IBinder>& who,
- CallbackMap::iterator* it,
+void ServiceManager::removeRegistrationCallback(const wp<IBinder>& who,
+ ServiceCallbackMap::iterator* it,
bool* found) {
std::vector<sp<IServiceCallback>>& listeners = (*it)->second;
@@ -312,7 +324,7 @@
}
if (listeners.empty()) {
- *it = mNameToCallback.erase(*it);
+ *it = mNameToRegistrationCallback.erase(*it);
} else {
(*it)++;
}
@@ -327,8 +339,12 @@
}
}
- for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) {
- removeCallback(who, &it, nullptr /*found*/);
+ for (auto it = mNameToRegistrationCallback.begin(); it != mNameToRegistrationCallback.end();) {
+ removeRegistrationCallback(who, &it, nullptr /*found*/);
+ }
+
+ for (auto it = mNameToClientCallback.begin(); it != mNameToClientCallback.end();) {
+ removeClientCallback(who, &it);
}
}
@@ -341,4 +357,183 @@
}).detach();
}
-} // namespace android
+Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
+ const sp<IClientCallback>& cb) {
+ if (cb == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+
+ auto ctx = mAccess->getCallingContext();
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ auto serviceIt = mNameToService.find(name);
+ if (serviceIt == mNameToService.end()) {
+ LOG(ERROR) << "Could not add callback for nonexistent service: " << name;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+ LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ }
+
+ if (serviceIt->second.binder != service) {
+ LOG(WARNING) << "Tried to register client callback for " << name
+ << " but a different service is registered under this name.";
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
+ }
+
+ if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
+ LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ mNameToClientCallback[name].push_back(cb);
+
+ return Status::ok();
+}
+
+void ServiceManager::removeClientCallback(const wp<IBinder>& who,
+ ClientCallbackMap::iterator* it) {
+ std::vector<sp<IClientCallback>>& listeners = (*it)->second;
+
+ for (auto lit = listeners.begin(); lit != listeners.end();) {
+ if (IInterface::asBinder(*lit) == who) {
+ lit = listeners.erase(lit);
+ } else {
+ ++lit;
+ }
+ }
+
+ if (listeners.empty()) {
+ *it = mNameToClientCallback.erase(*it);
+ } else {
+ (*it)++;
+ }
+}
+
+ssize_t ServiceManager::Service::getNodeStrongRefCount() {
+ sp<BpBinder> bpBinder = binder->remoteBinder();
+ if (bpBinder == nullptr) return -1;
+
+ return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
+}
+
+void ServiceManager::handleClientCallbacks() {
+ for (const auto& [name, service] : mNameToService) {
+ handleServiceClientCallback(name);
+ }
+}
+
+ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) {
+ auto serviceIt = mNameToService.find(serviceName);
+ if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
+ return -1;
+ }
+
+ Service& service = serviceIt->second;
+ ssize_t count = service.getNodeStrongRefCount();
+
+ // binder driver doesn't support this feature
+ if (count == -1) return count;
+
+ bool hasClients = count > 1; // this process holds a strong count
+
+ if (service.guaranteeClient) {
+ // we have no record of this client
+ if (!service.hasClients && !hasClients) {
+ sendClientCallbackNotifications(serviceName, true);
+ }
+
+ // guarantee is temporary
+ service.guaranteeClient = false;
+ }
+
+ if (hasClients && !service.hasClients) {
+ // client was retrieved in some other way
+ sendClientCallbackNotifications(serviceName, true);
+ }
+
+ // there are no more clients, but the callback has not been called yet
+ if (!hasClients && service.hasClients) {
+ sendClientCallbackNotifications(serviceName, false);
+ }
+
+ return count;
+}
+
+void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
+ auto serviceIt = mNameToService.find(serviceName);
+ if (serviceIt == mNameToService.end()) {
+ LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName;
+ return;
+ }
+ Service& service = serviceIt->second;
+
+ CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
+ << " so we can't tell clients again that we have client: " << hasClients;
+
+ LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients;
+
+ auto ccIt = mNameToClientCallback.find(serviceName);
+ CHECK(ccIt != mNameToClientCallback.end())
+ << "sendClientCallbackNotifications could not find callbacks for service ";
+
+ for (const auto& callback : ccIt->second) {
+ callback->onClients(service.binder, hasClients);
+ }
+
+ service.hasClients = hasClients;
+}
+
+Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
+ if (binder == nullptr) {
+ return Status::fromExceptionCode(Status::EX_NULL_POINTER);
+ }
+
+ auto ctx = mAccess->getCallingContext();
+ if (!mAccess->canAdd(ctx, name)) {
+ return Status::fromExceptionCode(Status::EX_SECURITY);
+ }
+
+ auto serviceIt = mNameToService.find(name);
+ if (serviceIt == mNameToService.end()) {
+ LOG(WARNING) << "Tried to unregister " << name
+ << ", but that service wasn't registered to begin with.";
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
+ LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
+ return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
+ }
+
+ sp<IBinder> storedBinder = serviceIt->second.binder;
+
+ if (binder != storedBinder) {
+ LOG(WARNING) << "Tried to unregister " << name
+ << ", but a different service is registered under this name.";
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ int clients = handleServiceClientCallback(name);
+
+ // clients < 0: feature not implemented or other error. Assume clients.
+ // Otherwise:
+ // - kernel driver will hold onto one refcount (during this transaction)
+ // - servicemanager has a refcount (guaranteed by this transaction)
+ // So, if clients > 2, then at least one other service on the system must hold a refcount.
+ if (clients < 0 || clients > 2) {
+ // client callbacks are either disabled or there are other clients
+ LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients;
+ return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
+ }
+
+ mNameToService.erase(name);
+
+ return Status::ok();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 7dcdaa4..77f5250 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -17,12 +17,14 @@
#pragma once
#include <android/os/BnServiceManager.h>
+#include <android/os/IClientCallback.h>
#include <android/os/IServiceCallback.h>
#include "Access.h"
namespace android {
+using os::IClientCallback;
using os::IServiceCallback;
class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
@@ -40,9 +42,13 @@
const sp<IServiceCallback>& callback) override;
binder::Status unregisterForNotifications(const std::string& name,
const sp<IServiceCallback>& callback) override;
- binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+ binder::Status isDeclared(const std::string& name, bool* outReturn) override;
+ binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
+ const sp<IClientCallback>& cb) override;
+ binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
void binderDied(const wp<IBinder>& who) override;
+ void handleClientCallbacks();
protected:
virtual void tryStartService(const std::string& name);
@@ -52,20 +58,35 @@
sp<IBinder> binder; // not null
bool allowIsolated;
int32_t dumpPriority;
+ bool hasClients = false; // notifications sent on true -> false.
+ bool guaranteeClient = false; // forces the client check to true
+ pid_t debugPid = 0; // the process in which this service runs
+
+ // the number of clients of the service, including servicemanager itself
+ ssize_t getNodeStrongRefCount();
};
- using CallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
+ using ServiceCallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
+ using ClientCallbackMap = std::map<std::string, std::vector<sp<IClientCallback>>>;
using ServiceMap = std::map<std::string, Service>;
- // removes a callback from mNameToCallback, removing it if the vector is empty
+ // removes a callback from mNameToRegistrationCallback, removing it if the vector is empty
// this updates iterator to the next location
- void removeCallback(const wp<IBinder>& who,
- CallbackMap::iterator* it,
+ void removeRegistrationCallback(const wp<IBinder>& who,
+ ServiceCallbackMap::iterator* it,
bool* found);
+ ssize_t handleServiceClientCallback(const std::string& serviceName);
+ // Also updates mHasClients (of what the last callback was)
+ void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
+ // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
+ // this updates the iterator to the next location
+ void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);
+
sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);
- CallbackMap mNameToCallback;
ServiceMap mNameToService;
+ ServiceCallbackMap mNameToRegistrationCallback;
+ ClientCallbackMap mNameToClientCallback;
std::unique_ptr<Access> mAccess;
};
diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp
index 4b12fc6..2618906 100644
--- a/cmds/servicemanager/main.cpp
+++ b/cmds/servicemanager/main.cpp
@@ -18,18 +18,101 @@
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/Status.h>
+#include <sys/timerfd.h>
+#include <utils/Looper.h>
#include <utils/StrongPointer.h>
#include "Access.h"
#include "ServiceManager.h"
using ::android::Access;
+using ::android::sp;
+using ::android::Looper;
+using ::android::LooperCallback;
+using ::android::ProcessState;
using ::android::IPCThreadState;
using ::android::ProcessState;
using ::android::ServiceManager;
using ::android::os::IServiceManager;
using ::android::sp;
+class BinderCallback : public LooperCallback {
+public:
+ static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
+ sp<BinderCallback> cb = new BinderCallback;
+
+ int binder_fd = -1;
+ IPCThreadState::self()->setupPolling(&binder_fd);
+ LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);
+
+ // Flush after setupPolling(), to make sure the binder driver
+ // knows about this thread handling commands.
+ IPCThreadState::self()->flushCommands();
+
+ int ret = looper->addFd(binder_fd,
+ Looper::POLL_CALLBACK,
+ Looper::EVENT_INPUT,
+ cb,
+ nullptr /*data*/);
+ LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");
+
+ return cb;
+ }
+
+ int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
+ IPCThreadState::self()->handlePolledCommands();
+ return 1; // Continue receiving callbacks.
+ }
+};
+
+// LooperCallback for IClientCallback
+class ClientCallbackCallback : public LooperCallback {
+public:
+ static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
+ sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);
+
+ int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
+ LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);
+
+ itimerspec timespec {
+ .it_interval = {
+ .tv_sec = 5,
+ .tv_nsec = 0,
+ },
+ .it_value = {
+ .tv_sec = 5,
+ .tv_nsec = 0,
+ },
+ };
+
+ int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, ×pec, nullptr);
+ LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);
+
+ int addRes = looper->addFd(fdTimer,
+ Looper::POLL_CALLBACK,
+ Looper::EVENT_INPUT,
+ cb,
+ nullptr);
+ LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");
+
+ return cb;
+ }
+
+ int handleEvent(int fd, int /*events*/, void* /*data*/) override {
+ uint64_t expirations;
+ int ret = read(fd, &expirations, sizeof(expirations));
+ if (ret != sizeof(expirations)) {
+ ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
+ }
+
+ mManager->handleClientCallbacks();
+ return 1; // Continue receiving callbacks.
+ }
+private:
+ ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
+ sp<ServiceManager> mManager;
+};
+
int main(int argc, char** argv) {
if (argc > 2) {
LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
@@ -49,7 +132,14 @@
IPCThreadState::self()->setTheContextObject(manager);
ps->becomeContextManager(nullptr, nullptr);
- IPCThreadState::self()->joinThreadPool();
+ sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
+
+ BinderCallback::setupTo(looper);
+ ClientCallbackCallback::setupTo(looper, manager);
+
+ while(true) {
+ looper->pollAll(-1);
+ }
// should not be reached
return EXIT_FAILURE;
diff --git a/cmds/surfacereplayer/proto/src/trace.proto b/cmds/surfacereplayer/proto/src/trace.proto
index 792ff91..7f2f949 100644
--- a/cmds/surfacereplayer/proto/src/trace.proto
+++ b/cmds/surfacereplayer/proto/src/trace.proto
@@ -51,6 +51,7 @@
RelativeParentChange relative_parent = 18;
DetachChildrenChange detach_children = 19;
ReparentChildrenChange reparent_children = 20;
+ ShadowRadiusChange shadow_radius = 22;
}
}
@@ -199,3 +200,7 @@
message DetachChildrenChange {
required bool detach_children = 1;
}
+
+message ShadowRadiusChange {
+ required float radius = 1;
+}
\ No newline at end of file
diff --git a/cmds/surfacereplayer/replayer/Replayer.cpp b/cmds/surfacereplayer/replayer/Replayer.cpp
index a4a9b6a..0d6c31e 100644
--- a/cmds/surfacereplayer/replayer/Replayer.cpp
+++ b/cmds/surfacereplayer/replayer/Replayer.cpp
@@ -424,6 +424,9 @@
case SurfaceChange::SurfaceChangeCase::kDetachChildren:
setDetachChildrenChange(transaction, change.id(), change.detach_children());
break;
+ case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+ setShadowRadiusChange(transaction, change.id(), change.shadow_radius());
+ break;
default:
status = 1;
break;
@@ -724,3 +727,8 @@
}
t.reparentChildren(mLayers[id], mLayers[c.parent_id()]->getHandle());
}
+
+void Replayer::setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const ShadowRadiusChange& c) {
+ t.setShadowRadius(mLayers[id], c.radius());
+}
diff --git a/cmds/surfacereplayer/replayer/Replayer.h b/cmds/surfacereplayer/replayer/Replayer.h
index 3b94618..b547834 100644
--- a/cmds/surfacereplayer/replayer/Replayer.h
+++ b/cmds/surfacereplayer/replayer/Replayer.h
@@ -118,6 +118,8 @@
layer_id id, const DetachChildrenChange& c);
void setReparentChildrenChange(SurfaceComposerClient::Transaction& t,
layer_id id, const ReparentChildrenChange& c);
+ void setShadowRadiusChange(SurfaceComposerClient::Transaction& t,
+ layer_id id, const ShadowRadiusChange& c);
void setDisplaySurface(SurfaceComposerClient::Transaction& t,
display_id id, const DispSurfaceChange& dsc);
diff --git a/data/etc/android.hardware.reboot_escrow.xml b/data/etc/android.hardware.reboot_escrow.xml
new file mode 100644
index 0000000..5db9f4c
--- /dev/null
+++ b/data/etc/android.hardware.reboot_escrow.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!--
+ This is the feature indicating that the device has support for the
+ RebootEscrow HAL.
+-->
+
+<permissions>
+ <feature name="android.hardware.reboot_escrow" />
+</permissions>
diff --git a/include/android/configuration.h b/include/android/configuration.h
index 3310722..05f4340 100644
--- a/include/android/configuration.h
+++ b/include/android/configuration.h
@@ -645,10 +645,14 @@
*/
void AConfiguration_setScreenLong(AConfiguration* config, int32_t screenLong);
+#if __ANDROID_API__ >= 30
/**
* Return the current ACONFIGURATION_SCREENROUND_* set in the configuration.
+ *
+ * Available since API level 30.
*/
-int32_t AConfiguration_getScreenRound(AConfiguration* config);
+int32_t AConfiguration_getScreenRound(AConfiguration* config) __INTRODUCED_IN(30);
+#endif
/**
* Set the current screen round in the configuration.
diff --git a/include/input/InputEventLabels.h b/include/input/InputEventLabels.h
index eaa562b..b327d76 100644
--- a/include/input/InputEventLabels.h
+++ b/include/input/InputEventLabels.h
@@ -405,13 +405,12 @@
{ nullptr, 0 }
};
-static const InputEventLabel FLAGS[] = {
- DEFINE_FLAG(VIRTUAL),
- DEFINE_FLAG(FUNCTION),
- DEFINE_FLAG(GESTURE),
+static const InputEventLabel FLAGS[] = {DEFINE_FLAG(VIRTUAL),
+ DEFINE_FLAG(FUNCTION),
+ DEFINE_FLAG(GESTURE),
+ DEFINE_FLAG(WAKE),
- { nullptr, 0 }
-};
+ {nullptr, 0}};
static int lookupValueByLabel(const char* literal, const InputEventLabel *list) {
while (list->literal) {
diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h
index 916af69..c44db51 100644
--- a/include/input/InputWindow.h
+++ b/include/input/InputWindow.h
@@ -63,7 +63,6 @@
FLAG_DISMISS_KEYGUARD = 0x00400000,
FLAG_SPLIT_TOUCH = 0x00800000,
FLAG_SLIPPERY = 0x20000000,
- FLAG_NEEDS_MENU_KEY = 0x40000000,
};
// Window types from WindowManager.LayoutParams
@@ -120,7 +119,11 @@
/* These values are filled in by the WM and passed through SurfaceFlinger
* unless specified otherwise.
*/
+ // This value should NOT be used to uniquely identify the window. There may be different
+ // input windows that have the same token.
sp<IBinder> token;
+ // This uniquely identifies the input window.
+ int32_t id = 0;
std::string name;
int32_t layoutParamsFlags;
int32_t layoutParamsType;
@@ -158,7 +161,6 @@
bool hasFocus;
bool hasWallpaper;
bool paused;
- int32_t layer;
int32_t ownerPid;
int32_t ownerUid;
int32_t inputFeatures;
@@ -204,6 +206,8 @@
sp<IBinder> getToken() const;
+ int32_t getId() const { return mInfo.id; }
+
sp<IBinder> getApplicationToken() {
return mInfo.applicationInfo.token;
}
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 7ee4882..079dd82 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -90,6 +90,7 @@
"IResultReceiver.cpp",
"IServiceManager.cpp",
"IShellCallback.cpp",
+ "LazyServiceRegistrar.cpp",
"MemoryBase.cpp",
"MemoryDealer.cpp",
"MemoryHeapBase.cpp",
@@ -160,6 +161,7 @@
name: "libbinder_aidl",
srcs: [
"aidl/android/content/pm/IPackageManagerNative.aidl",
+ "aidl/android/os/IClientCallback.aidl",
"aidl/android/os/IServiceCallback.aidl",
"aidl/android/os/IServiceManager.aidl",
],
diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp
index 4f0b7d3..ca9c608 100644
--- a/libs/binder/AppOpsManager.cpp
+++ b/libs/binder/AppOpsManager.cpp
@@ -44,16 +44,16 @@
} // namespace
static String16 _appops("appops");
-static pthread_mutex_t gTokenMutex = PTHREAD_MUTEX_INITIALIZER;
-static sp<IBinder> gToken;
+static pthread_mutex_t gClientIdMutex = PTHREAD_MUTEX_INITIALIZER;
+static sp<IBinder> gClientId;
-static const sp<IBinder>& getToken(const sp<IAppOpsService>& service) {
- pthread_mutex_lock(&gTokenMutex);
- if (gToken == nullptr || gToken->pingBinder() != NO_ERROR) {
- gToken = service->getToken(new BBinder());
+static const sp<IBinder>& getClientId() {
+ pthread_mutex_lock(&gClientIdMutex);
+ if (gClientId == nullptr) {
+ gClientId = new BBinder();
}
- pthread_mutex_unlock(&gTokenMutex);
- return gToken;
+ pthread_mutex_unlock(&gClientIdMutex);
+ return gClientId;
}
thread_local uint64_t notedAppOpsInThisBinderTransaction[2];
@@ -144,7 +144,7 @@
const String16& message) {
sp<IAppOpsService> service = getService();
int32_t mode = service != nullptr
- ? service->startOperation(getToken(service), op, uid, callingPackage,
+ ? service->startOperation(getClientId(), op, uid, callingPackage,
featureId, startIfModeDefault) : APP_OPS_MANAGER_UNAVAILABLE_MODE;
if (mode == AppOpsManager::MODE_ALLOWED) {
@@ -162,7 +162,7 @@
const std::unique_ptr<String16>& callingFeatureId) {
sp<IAppOpsService> service = getService();
if (service != nullptr) {
- service->finishOperation(getToken(service), op, uid, callingPackage, callingFeatureId);
+ service->finishOperation(getClientId(), op, uid, callingPackage, callingFeatureId);
}
}
diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp
index b85a5f2..7384466 100644
--- a/libs/binder/IAppOpsService.cpp
+++ b/libs/binder/IAppOpsService.cpp
@@ -106,16 +106,6 @@
remote()->transact(STOP_WATCHING_MODE_TRANSACTION, data, &reply);
}
- virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) {
- Parcel data, reply;
- data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
- data.writeStrongBinder(clientToken);
- remote()->transact(GET_TOKEN_TRANSACTION, data, &reply);
- // fail on exception
- if (reply.readExceptionCode() != 0) return nullptr;
- return reply.readStrongBinder();
- }
-
virtual int32_t permissionToOpCode(const String16& permission) {
Parcel data, reply;
data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor());
@@ -251,14 +241,6 @@
reply->writeNoException();
return NO_ERROR;
} break;
- case GET_TOKEN_TRANSACTION: {
- CHECK_INTERFACE(IAppOpsService, data, reply);
- sp<IBinder> clientToken = data.readStrongBinder();
- sp<IBinder> token = getToken(clientToken);
- reply->writeNoException();
- reply->writeStrongBinder(token);
- return NO_ERROR;
- } break;
case PERMISSION_TO_OP_CODE_TRANSACTION: {
CHECK_INTERFACE(IAppOpsService, data, reply);
String16 permission = data.readString16();
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index bac8b66..5ca9156 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -271,6 +271,8 @@
std::unique_lock<std::mutex> lock(mMutex);
mBinder = binder;
lock.unlock();
+ // Flushing here helps ensure the service's ref count remains accurate
+ IPCThreadState::self()->flushCommands();
mCv.notify_one();
return Status::ok();
}
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
new file mode 100644
index 0000000..dc9482c
--- /dev/null
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AidlLazyServiceRegistrar"
+
+#include <binder/LazyServiceRegistrar.h>
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+#include <android/os/BnClientCallback.h>
+#include <android/os/IServiceManager.h>
+#include <utils/Log.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+
+using AidlServiceManager = android::os::IServiceManager;
+
+class ClientCounterCallback : public ::android::os::BnClientCallback {
+public:
+ ClientCounterCallback() : mNumConnectedServices(0) {}
+
+ bool registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags);
+
+protected:
+ Status onClients(const sp<IBinder>& service, bool clients) override;
+
+private:
+ /**
+ * Unregisters all services that we can. If we can't unregister all, re-register other
+ * services.
+ */
+ void tryShutdown();
+
+ /**
+ * Counter of the number of services that currently have at least one client.
+ */
+ size_t mNumConnectedServices;
+
+ struct Service {
+ sp<IBinder> service;
+ std::string name;
+ bool allowIsolated;
+ int dumpFlags;
+ };
+ /**
+ * Number of services that have been registered.
+ */
+ std::vector<Service> mRegisteredServices;
+};
+
+bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ auto manager = interface_cast<AidlServiceManager>(
+ ProcessState::self()->getContextObject(nullptr));
+
+ ALOGI("Registering service %s", name.c_str());
+
+ if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
+ ALOGE("Failed to register service %s", name.c_str());
+ return false;
+ }
+
+ if (!manager->registerClientCallback(name, service, this).isOk())
+ {
+ ALOGE("Failed to add client callback for service %s", name.c_str());
+ return false;
+ }
+
+ mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags});
+
+ return true;
+}
+
+/**
+ * onClients is oneway, so no need to worry about multi-threading. Note that this means multiple
+ * invocations could occur on different threads however.
+ */
+Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) {
+ if (clients) {
+ mNumConnectedServices++;
+ } else {
+ mNumConnectedServices--;
+ }
+
+ ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d",
+ mNumConnectedServices, mRegisteredServices.size(),
+ String8(service->getInterfaceDescriptor()).string(), clients);
+
+ if (mNumConnectedServices == 0) {
+ tryShutdown();
+ }
+
+ return Status::ok();
+}
+
+void ClientCounterCallback::tryShutdown() {
+ ALOGI("Trying to shut down the service. No clients in use for any service in process.");
+
+ // This makes the same assumption as IServiceManager.cpp. Could dedupe if used in more places.
+ auto manager = interface_cast<AidlServiceManager>(
+ ProcessState::self()->getContextObject(nullptr));
+
+ auto unRegisterIt = mRegisteredServices.begin();
+ for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
+ auto& entry = (*unRegisterIt);
+
+ bool success = manager->tryUnregisterService(entry.name, entry.service).isOk();
+
+ if (!success) {
+ ALOGI("Failed to unregister service %s", entry.name.c_str());
+ break;
+ }
+ }
+
+ if (unRegisterIt == mRegisteredServices.end()) {
+ ALOGI("Unregistered all clients and exiting");
+ exit(EXIT_SUCCESS);
+ }
+
+ for (auto reRegisterIt = mRegisteredServices.begin(); reRegisterIt != unRegisterIt;
+ reRegisterIt++) {
+ auto& entry = (*reRegisterIt);
+
+ // re-register entry
+ if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) {
+ // Must restart. Otherwise, clients will never be able to get a hold of this service.
+ ALOGE("Bad state: could not re-register services");
+ }
+ }
+}
+
+} // namespace internal
+
+LazyServiceRegistrar::LazyServiceRegistrar() {
+ mClientCC = std::make_shared<internal::ClientCounterCallback>();
+}
+
+LazyServiceRegistrar& LazyServiceRegistrar::getInstance() {
+ static auto registrarInstance = new LazyServiceRegistrar();
+ return *registrarInstance;
+}
+
+status_t LazyServiceRegistrar::registerService(const sp<IBinder>& service, const std::string& name,
+ bool allowIsolated, int dumpFlags) {
+ if (!mClientCC->registerService(service, name, allowIsolated, dumpFlags)) {
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+} // namespace hardware
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/aidl/android/os/IClientCallback.aidl b/libs/binder/aidl/android/os/IClientCallback.aidl
new file mode 100644
index 0000000..36d7ee6
--- /dev/null
+++ b/libs/binder/aidl/android/os/IClientCallback.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.os;
+
+/**
+ * @hide
+ */
+oneway interface IClientCallback {
+ /**
+ * This is called when there is a transition between having >= 1 clients and having 0 clients
+ * (or vice versa).
+ *
+ * Upon receiving hasClients false, if the process decides to exit, it is recommended to try to
+ * unregister using IServiceManager's tryUnregister before quitting in case another client
+ * associates.
+ *
+ * @param registered binder 'server' registered with IServiceManager's registerClientCallback
+ * @param hasClients whether there are currently clients
+ * true - when there are >= 1 clients. This must be called as soon as IServiceManager::get
+ * is called (no race).
+ * false - when there are 0 clients. This may be delayed if it is thought that another
+ * may be used again soon.
+ */
+ void onClients(IBinder registered, boolean hasClients);
+}
diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl
index b965881..ff15460 100644
--- a/libs/binder/aidl/android/os/IServiceManager.aidl
+++ b/libs/binder/aidl/android/os/IServiceManager.aidl
@@ -16,6 +16,7 @@
package android.os;
+import android.os.IClientCallback;
import android.os.IServiceCallback;
/**
@@ -96,4 +97,15 @@
* manifest.
*/
boolean isDeclared(@utf8InCpp String name);
+
+ /**
+ * Request a callback when the number of clients of the service changes.
+ * Used by LazyServiceRegistrar to dynamically stop services that have no clients.
+ */
+ void registerClientCallback(@utf8InCpp String name, IBinder service, IClientCallback callback);
+
+ /**
+ * Attempt to unregister and remove a service. Will fail if the service is still in use.
+ */
+ void tryUnregisterService(@utf8InCpp String name, IBinder service);
}
diff --git a/libs/binder/include/binder/Enums.h b/libs/binder/include/binder/Enums.h
new file mode 100644
index 0000000..aec6f70
--- /dev/null
+++ b/libs/binder/include/binder/Enums.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <iterator>
+#include <type_traits>
+
+namespace android {
+
+namespace internal {
+
+// Never instantiated. Used as a placeholder for template variables.
+template <typename T>
+struct invalid_type;
+
+// AIDL generates specializations of this for enums.
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+constexpr invalid_type<EnumType> enum_values;
+} // namespace internal
+
+// Usage: for (const auto v : enum_range<EnumType>() ) { ... }
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+struct enum_range {
+ constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); }
+ constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
+};
+
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h
index 15ba005..68a917e 100644
--- a/libs/binder/include/binder/IAppOpsService.h
+++ b/libs/binder/include/binder/IAppOpsService.h
@@ -45,7 +45,6 @@
virtual void startWatchingMode(int32_t op, const String16& packageName,
const sp<IAppOpsCallback>& callback) = 0;
virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0;
- virtual sp<IBinder> getToken(const sp<IBinder>& clientToken) = 0;
virtual int32_t permissionToOpCode(const String16& permission) = 0;
virtual int32_t checkAudioOperation(int32_t code, int32_t usage,int32_t uid,
const String16& packageName) = 0;
@@ -62,12 +61,11 @@
FINISH_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+3,
START_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+4,
STOP_WATCHING_MODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+5,
- GET_TOKEN_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
- PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
- CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
- NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
- SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
- SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+11,
+ PERMISSION_TO_OP_CODE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+6,
+ CHECK_AUDIO_OPERATION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+7,
+ NOTE_ASYNC_OP_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+8,
+ SHOULD_COLLECT_NOTES_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+9,
+ SET_CAMERA_AUDIO_RESTRICTION_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION+10,
};
enum {
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 79d9b79..3a401ad 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -253,7 +253,6 @@
"android.media.IDrmClient",
"android.media.IEffect",
"android.media.IEffectClient",
- "android.media.IMediaAnalyticsService",
"android.media.IMediaCodecList",
"android.media.IMediaDrmService",
"android.media.IMediaExtractor",
@@ -262,6 +261,7 @@
"android.media.IMediaHTTPService",
"android.media.IMediaLogService",
"android.media.IMediaMetadataRetriever",
+ "android.media.IMediaMetricsService",
"android.media.IMediaPlayer",
"android.media.IMediaPlayerClient",
"android.media.IMediaPlayerService",
diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h
new file mode 100644
index 0000000..efdecc4
--- /dev/null
+++ b/libs/binder/include/binder/LazyServiceRegistrar.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <binder/IServiceManager.h>
+#include <binder/Status.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace binder {
+namespace internal {
+class ClientCounterCallback;
+} // namespace internal
+
+/** Exits when all services registered through this object have 0 clients */
+class LazyServiceRegistrar {
+ public:
+ static LazyServiceRegistrar& getInstance();
+ status_t registerService(const sp<IBinder>& service,
+ const std::string& name = "default",
+ bool allowIsolated = false,
+ int dumpFlags = IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
+
+ private:
+ std::shared_ptr<internal::ClientCounterCallback> mClientCC;
+ LazyServiceRegistrar();
+};
+
+} // namespace binder
+} // namespace android
\ No newline at end of file
diff --git a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
index 946ccb7..2b61cf1 100644
--- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_auto_utils.h
@@ -34,6 +34,7 @@
#include <unistd.h>
#include <cstddef>
+#include <string>
namespace ndk {
@@ -228,6 +229,13 @@
*/
const char* getMessage() const { return AStatus_getMessage(get()); }
+ std::string getDescription() const {
+ const char* cStr = AStatus_getDescription(get());
+ std::string ret = cStr;
+ AStatus_deleteDescription(cStr);
+ return ret;
+ }
+
/**
* Convenience methods for creating scoped statuses.
*/
diff --git a/libs/binder/ndk/include_ndk/android/binder_enums.h b/libs/binder/ndk/include_ndk/android/binder_enums.h
new file mode 100644
index 0000000..ee819c0
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/binder_enums.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_enums.h
+ * @brief Helpers for AIDL enum types.
+ */
+
+#pragma once
+
+#include <iterator>
+#include <type_traits>
+
+namespace ndk {
+
+namespace internal {
+/**
+ * Never instantiated. Used as a placeholder for template variables.
+ */
+template <typename T>
+struct invalid_type;
+
+/**
+ * AIDL generates specializations of this for enums.
+ */
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+constexpr invalid_type<EnumType> enum_values;
+} // namespace internal
+
+/**
+ * Iterable interface to enumerate all values of AIDL enum types.
+ */
+template <typename EnumType, typename = std::enable_if_t<std::is_enum<EnumType>::value>>
+struct enum_range {
+ /**
+ * Return an iterator pointing to the first enum value.
+ */
+ constexpr auto begin() const { return std::begin(internal::enum_values<EnumType>); }
+ /**
+ * Return an iterator pointing to one past the last enum value.
+ */
+ constexpr auto end() const { return std::end(internal::enum_values<EnumType>); }
+};
+
+} // namespace ndk
+
+/** @} */
\ No newline at end of file
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
index 7871667..df5df13 100644
--- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h
@@ -421,13 +421,76 @@
}
/**
+ * Convenience API for writing a non-null parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeParcelable(AParcel* parcel, const P& p) {
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p.writeToParcel(parcel);
+}
+
+/**
+ * Convenience API for reading a non-null parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_readParcelable(const AParcel* parcel, P* p) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+ return p->readFromParcel(parcel);
+}
+
+/**
+ * Convenience API for writing a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_writeNullableParcelable(AParcel* parcel,
+ const std::optional<P>& p) {
+ if (p == std::nullopt) {
+ return AParcel_writeInt32(parcel, 0); // null
+ }
+ binder_status_t status = AParcel_writeInt32(parcel, 1); // non-null
+ if (status != STATUS_OK) {
+ return status;
+ }
+ return p->writeToParcel(parcel);
+}
+
+/**
+ * Convenience API for reading a nullable parcelable.
+ */
+template <typename P>
+static inline binder_status_t AParcel_readNullableParcelable(const AParcel* parcel,
+ std::optional<P>* p) {
+ int32_t null;
+ binder_status_t status = AParcel_readInt32(parcel, &null);
+ if (status != STATUS_OK) {
+ return status;
+ }
+ if (null == 0) {
+ *p = std::nullopt;
+ return STATUS_OK;
+ }
+ *p = std::optional<P>(P{});
+ return (*p)->readFromParcel(parcel);
+}
+
+/**
* Writes a parcelable object of type P inside a std::vector<P> at index 'index' to 'parcel'.
*/
template <typename P>
binder_status_t AParcel_writeStdVectorParcelableElement(AParcel* parcel, const void* vectorData,
size_t index) {
const std::vector<P>* vector = static_cast<const std::vector<P>*>(vectorData);
- return vector->at(index).writeToParcel(parcel);
+ return AParcel_writeParcelable(parcel, vector->at(index));
}
/**
@@ -437,7 +500,7 @@
binder_status_t AParcel_readStdVectorParcelableElement(const AParcel* parcel, void* vectorData,
size_t index) {
std::vector<P>* vector = static_cast<std::vector<P>*>(vectorData);
- return vector->at(index).readFromParcel(parcel);
+ return AParcel_readParcelable(parcel, &vector->at(index));
}
/**
diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h
index 78d70f8..ab9a144 100644
--- a/libs/binder/ndk/include_ndk/android/binder_status.h
+++ b/libs/binder/ndk/include_ndk/android/binder_status.h
@@ -247,6 +247,25 @@
const char* AStatus_getMessage(const AStatus* status) __INTRODUCED_IN(29);
/**
+ * Get human-readable description for debugging.
+ *
+ * Available since API level 30.
+ *
+ * \param status the status being queried.
+ *
+ * \return a description, must be deleted with AStatus_deleteDescription.
+ */
+__attribute__((warn_unused_result)) const char* AStatus_getDescription(const AStatus* status)
+ __INTRODUCED_IN(30);
+
+/**
+ * Delete description.
+ *
+ * \param description value from AStatus_getDescription
+ */
+void AStatus_deleteDescription(const char* description) __INTRODUCED_IN(30);
+
+/**
* Deletes memory associated with the status instance.
*
* Available since API level 29.
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index d59d6e4..71d8103 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -103,6 +103,8 @@
global:
AIBinder_getExtension;
AIBinder_setExtension;
+ AStatus_getDescription;
+ AStatus_deleteDescription;
AIBinder_markSystemStability; # apex
AIBinder_markVendorStability; # llndk
diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp
index 1f75b0b..87e1341 100644
--- a/libs/binder/ndk/status.cpp
+++ b/libs/binder/ndk/status.cpp
@@ -66,6 +66,17 @@
return status->get()->exceptionMessage().c_str();
}
+const char* AStatus_getDescription(const AStatus* status) {
+ android::String8 description = status->get()->toString8();
+ char* cStr = new char[description.size() + 1];
+ memcpy(cStr, description.c_str(), description.size() + 1);
+ return cStr;
+}
+
+void AStatus_deleteDescription(const char* description) {
+ delete[] const_cast<char*>(description);
+}
+
void AStatus_delete(AStatus* status) {
delete status;
}
diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
index d3ccdc2..ad78e31 100644
--- a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
+++ b/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp
@@ -105,7 +105,8 @@
std::string outString;
ScopedAStatus status = server->RepeatString("foo", &outString);
- EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get())) << serviceName;
+ EXPECT_EQ(STATUS_OK, AStatus_getExceptionCode(status.get()))
+ << serviceName << " " << status.getDescription();
EXPECT_EQ("foo", outString) << serviceName;
}
}
diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS
new file mode 100644
index 0000000..67743cd
--- /dev/null
+++ b/libs/gralloc/OWNERS
@@ -0,0 +1,2 @@
+marissaw@google.com
+vhau@google.com
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
index cb5305f..e1693cf 100644
--- a/libs/gralloc/types/Android.bp
+++ b/libs/gralloc/types/Android.bp
@@ -41,6 +41,7 @@
export_shared_lib_headers: [
"android.hardware.graphics.mapper@4.0",
+ "libhidlbase",
"vintf-graphics-common-ndk_platform",
],
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
index 0330dac..4851b44 100644
--- a/libs/gralloc/types/Gralloc4.cpp
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#define LOG_TAG "libgralloctypes"
+
#include <cstring>
#include <cinttypes>
#include <limits>
@@ -25,15 +27,22 @@
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 aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
-using MetadataType = ::android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
namespace android {
@@ -61,6 +70,7 @@
return BAD_VALUE;
}
mVec->resize(mNeededResize);
+ mResized = true;
return NO_ERROR;
}
@@ -68,11 +78,17 @@
if (!mVec) {
return BAD_VALUE;
}
- if (mVec->size() == 0) {
+ if (!mResized) {
if (hasAdditionOverflow(mNeededResize, size)) {
clear();
return BAD_VALUE;
}
+ /**
+ * Update mNeededResize and return NO_ERROR here because if (!mResized), the
+ * caller hasn't called resize(). No data will be written into the mVec until
+ * the caller resizes. We can't resize here for the caller because hidl_vec::resize()
+ * allocates a new backing array every time.
+ */
mNeededResize += size;
return NO_ERROR;
}
@@ -93,12 +109,14 @@
mVec->resize(0);
}
mNeededResize = 0;
+ mResized = false;
mOffset = 0;
}
private:
hidl_vec<uint8_t>* mVec;
size_t mNeededResize = 0;
+ size_t mResized = false;
size_t mOffset = 0;
};
@@ -138,6 +156,13 @@
if (!mVec) {
return false;
}
+ return mVec->size() > mOffset;
+ }
+
+ size_t getRemainingSize() {
+ if (!mVec) {
+ return 0;
+ }
return mVec->size() - mOffset;
}
@@ -166,6 +191,9 @@
template<class T>
using ErrorHandler = void(*)(T*);
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output);
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType);
+
/**
* encode is the main encoding function. It takes in T and uses the encodeHelper function to turn T
* into the hidl_vec byte stream.
@@ -175,9 +203,16 @@
* encodes T into the hidl_vec byte stream.
*/
template <class T>
-status_t encode(const T& input, hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+status_t encode(const MetadataType& metadataType, const T& input, hidl_vec<uint8_t>* output,
+ EncodeHelper<T> encodeHelper) {
OutputHidlVec outputHidlVec{output};
- status_t err = encodeHelper(input, &outputHidlVec);
+
+ status_t err = encodeMetadataType(metadataType, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = encodeHelper(input, &outputHidlVec);
if (err) {
return err;
}
@@ -187,19 +222,39 @@
return err;
}
+ err = encodeMetadataType(metadataType, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
return encodeHelper(input, &outputHidlVec);
}
+template <class T>
+status_t encodeOptional(const MetadataType& metadataType, const std::optional<T>& input,
+ hidl_vec<uint8_t>* output, EncodeHelper<T> encodeHelper) {
+ if (!input) {
+ return NO_ERROR;
+ }
+ return encode(metadataType, *input, output, encodeHelper);
+}
+
/**
* decode is the main decode function. It takes in a hidl_vec and uses the decodeHelper function to
* turn the hidl_vec byte stream into T. If an error occurs, the errorHandler function cleans up
* T.
*/
template <class T>
-status_t decode(const hidl_vec<uint8_t>& input, T* output, DecodeHelper<T> decodeHelper,
- ErrorHandler<T> errorHandler = nullptr) {
+status_t decode(const MetadataType& metadataType, const hidl_vec<uint8_t>& input, T* output,
+ DecodeHelper<T> decodeHelper, ErrorHandler<T> errorHandler = nullptr) {
InputHidlVec inputHidlVec{&input};
- status_t err = decodeHelper(&inputHidlVec, output);
+
+ status_t err = validateMetadataType(&inputHidlVec, metadataType);
+ if (err) {
+ return err;
+ }
+
+ err = decodeHelper(&inputHidlVec, output);
if (err) {
return err;
}
@@ -215,13 +270,32 @@
return NO_ERROR;
}
+template <class T>
+status_t decodeOptional(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ std::optional<T>* output, DecodeHelper<T> decodeHelper) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+ if (input.size() <= 0) {
+ output->reset();
+ return NO_ERROR;
+ }
+ T tmp;
+ status_t err = decode(metadataType, input, &tmp, decodeHelper);
+ if (!err) {
+ *output = tmp;
+ }
+ return err;
+}
+
/**
* Private helper functions
*/
template <class T>
status_t encodeInteger(const T& input, OutputHidlVec* 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);
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+ std::is_same<T, float>::value || std::is_same<T, double>::value);
if (!output) {
return BAD_VALUE;
}
@@ -233,7 +307,8 @@
template <class T>
status_t decodeInteger(InputHidlVec* input, 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);
+ std::is_same<T, uint64_t>::value || std::is_same<T, int64_t>::value ||
+ std::is_same<T, float>::value || std::is_same<T, double>::value);
if (!output) {
return BAD_VALUE;
}
@@ -252,7 +327,7 @@
return err;
}
- return output->encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
+ return output->encode(reinterpret_cast<const uint8_t*>(input.data()), input.size());
}
status_t decodeString(InputHidlVec* input, std::string* output) {
@@ -262,11 +337,46 @@
int64_t size = 0;
status_t err = decodeInteger<int64_t>(input, &size);
+ if (err) {
+ return err;
+ }
+ if (size < 0) {
+ return BAD_VALUE;
+ }
+
+ return input->decode(output, size);
+}
+
+status_t encodeByteVector(const std::vector<uint8_t>& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(input.size(), output);
+ if (err) {
+ return err;
+ }
+
+ return output->encode(input.data(), input.size());
+}
+
+status_t decodeByteVector(InputHidlVec* input, std::vector<uint8_t>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size);
if (err || size < 0) {
return err;
}
- return input->decode(output, size);
+ if (size > input->getRemainingSize()) {
+ return BAD_VALUE;
+ }
+ output->resize(size);
+
+ return input->decode(output->data(), size);
}
status_t encodeExtendableType(const ExtendableType& input, OutputHidlVec* output) {
@@ -305,6 +415,79 @@
output->value = 0;
}
+status_t encodeMetadataType(const MetadataType& input, OutputHidlVec* output) {
+ status_t err = encodeString(input.name, output);
+ if (err) {
+ return err;
+ }
+
+ err = encodeInteger<int64_t>(input.value, output);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodeMetadataType(InputHidlVec* input, MetadataType* output) {
+ std::string name;
+ status_t err = decodeString(input, &name);
+ if (err) {
+ return err;
+ }
+ output->name = name;
+
+ err = decodeInteger<int64_t>(input, &output->value);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+status_t validateMetadataType(InputHidlVec* input, const MetadataType& expectedMetadataType) {
+ MetadataType receivedMetadataType;
+
+ status_t err = decodeMetadataType(input, &receivedMetadataType);
+ if (err) {
+ return err;
+ }
+
+ if (expectedMetadataType.name != receivedMetadataType.name) {
+ return BAD_VALUE;
+ }
+
+ if (receivedMetadataType.value != expectedMetadataType.value) {
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+status_t encodeXyColor(const XyColor& input, OutputHidlVec* output) {
+ status_t err = encodeInteger<float>(input.x, output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(input.y, output);
+}
+
+status_t decodeXyColor(InputHidlVec* input, XyColor* output) {
+ status_t err = decodeInteger<float>(input, &output->x);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(input, &output->y);
+}
+
+void clearXyColor(XyColor* output) {
+ if (!output) {
+ return;
+ }
+ output->x = 0;
+ output->y = 0;
+}
+
status_t encodeRect(const Rect& input, OutputHidlVec* output) {
status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
if (err) {
@@ -396,13 +579,17 @@
int64_t size = 0;
status_t err = decodeInteger<int64_t>(input, &size);
- if (err || size < 0) {
+ if (err) {
return err;
}
+ if (size < 0 || size > 10000) {
+ return BAD_VALUE;
+ }
- for (int i = 0; i < size; i++) {
- output->emplace_back();
- err = decodePlaneLayoutComponent(input, &output->back());
+ output->resize(size);
+
+ for (auto& planeLayoutComponent : *output) {
+ err = decodePlaneLayoutComponent(input, &planeLayoutComponent);
if (err) {
return err;
}
@@ -521,9 +708,12 @@
status_t decodePlaneLayoutsHelper(InputHidlVec* inputHidlVec, std::vector<PlaneLayout>* outPlaneLayouts) {
int64_t size = 0;
status_t err = decodeInteger<int64_t>(inputHidlVec, &size);
- if (err || size < 0) {
+ if (err) {
return err;
}
+ if (size < 0) {
+ return BAD_VALUE;
+ }
for (size_t i = 0; i < size; i++) {
outPlaneLayouts->emplace_back();
@@ -542,153 +732,445 @@
output->clear();
}
+status_t encodeSmpte2086Helper(const Smpte2086& smpte2086, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeXyColor(smpte2086.primaryRed, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.primaryGreen, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.primaryBlue, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeXyColor(smpte2086.whitePoint, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<float>(smpte2086.maxLuminance, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(smpte2086.minLuminance, outOutputHidlVec);
+}
+
+status_t decodeSmpte2086Helper(InputHidlVec* inputHidlVec, Smpte2086* outSmpte2086) {
+ status_t err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryRed);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryGreen);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->primaryBlue);
+ if (err) {
+ return err;
+ }
+ err = decodeXyColor(inputHidlVec, &outSmpte2086->whitePoint);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<float>(inputHidlVec, &outSmpte2086->maxLuminance);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(inputHidlVec, &outSmpte2086->minLuminance);
+}
+
+status_t encodeCta861_3Helper(const Cta861_3& cta861_3, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeInteger<float>(cta861_3.maxContentLightLevel, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<float>(cta861_3.maxFrameAverageLightLevel, outOutputHidlVec);
+}
+
+status_t decodeCta861_3Helper(InputHidlVec* inputHidlVec, Cta861_3* outCta861_3) {
+ status_t err = decodeInteger<float>(inputHidlVec, &outCta861_3->maxContentLightLevel);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<float>(inputHidlVec, &outCta861_3->maxFrameAverageLightLevel);
+}
+
/**
* Public API functions
*/
+
+status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
+ return encode(MetadataType_BufferId, bufferId, outBufferId, encodeInteger);
+}
+
+status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
+ return decode(MetadataType_BufferId, bufferId, outBufferId, decodeInteger);
+}
+
+status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
+ return encode(MetadataType_Name, name, outName, encodeString);
+}
+
+status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
+ return decode(MetadataType_Name, name, outName, decodeString);
+}
+
+status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
+ return encode(MetadataType_Width, width, outWidth, encodeInteger);
+}
+
+status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
+ return decode(MetadataType_Width, width, outWidth, decodeInteger);
+}
+
+status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
+ return encode(MetadataType_Height, height, outHeight, encodeInteger);
+}
+
+status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
+ return decode(MetadataType_Height, height, outHeight, decodeInteger);
+}
+
+status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
+ return encode(MetadataType_LayerCount, layerCount, outLayerCount, encodeInteger);
+}
+
+status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
+ return decode(MetadataType_LayerCount, layerCount, outLayerCount, decodeInteger);
+}
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
+ hidl_vec<uint8_t>* outPixelFormatRequested) {
+ return encode(MetadataType_PixelFormatRequested, static_cast<int32_t>(pixelFormatRequested),
+ outPixelFormatRequested, encodeInteger);
+}
+
+status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
+ hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
+ return decode(MetadataType_PixelFormatRequested, pixelFormatRequested,
+ reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
+}
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
+ return encode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ encodeInteger);
+}
+
+status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
+ return decode(MetadataType_PixelFormatFourCC, pixelFormatFourCC, outPixelFormatFourCC,
+ decodeInteger);
+}
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
+ return encode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ encodeInteger);
+}
+
+status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
+ return decode(MetadataType_PixelFormatModifier, pixelFormatModifier, outPixelFormatModifier,
+ decodeInteger);
+}
+
+status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
+ return encode(MetadataType_Usage, usage, outUsage, encodeInteger);
+}
+
+status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
+ return decode(MetadataType_Usage, usage, outUsage, decodeInteger);
+}
+
+status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
+ return encode(MetadataType_AllocationSize, allocationSize, outAllocationSize, encodeInteger);
+}
+
+status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
+ return decode(MetadataType_AllocationSize, allocationSize, outAllocationSize, decodeInteger);
+}
+
+status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
+ return encode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ encodeInteger);
+}
+
+status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
+ return decode(MetadataType_ProtectedContent, protectedContent, outProtectedContent,
+ decodeInteger);
+}
+
+status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
+ return encode(MetadataType_Compression, compression, outCompression, encodeExtendableType);
+}
+
+status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
+ return decode(MetadataType_Compression, compression, outCompression, decodeExtendableType,
+ clearExtendableType);
+}
+
+status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
+ return encode(MetadataType_Interlaced, interlaced, outInterlaced, encodeExtendableType);
+}
+
+status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
+ return decode(MetadataType_Interlaced, interlaced, outInterlaced, decodeExtendableType,
+ clearExtendableType);
+}
+
+status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
+ return encode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, encodeExtendableType);
+}
+
+status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
+ return decode(MetadataType_ChromaSiting, chromaSiting, outChromaSiting, decodeExtendableType,
+ clearExtendableType);
+}
+
+status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
+ return encode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ encodePlaneLayoutsHelper);
+}
+
+status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
+ return decode(MetadataType_PlaneLayouts, planeLayouts, outPlaneLayouts,
+ decodePlaneLayoutsHelper, clearPlaneLayouts);
+}
+
+status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
+ return encode(MetadataType_Dataspace, static_cast<int32_t>(dataspace), outDataspace,
+ encodeInteger);
+}
+
+status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
+ return decode(MetadataType_Dataspace, dataspace, reinterpret_cast<int32_t*>(outDataspace),
+ decodeInteger);
+}
+
+status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
+ return encode(MetadataType_BlendMode, static_cast<int32_t>(blendMode), outBlendMode,
+ encodeInteger);
+}
+
+status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
+ return decode(MetadataType_BlendMode, blendMode, reinterpret_cast<int32_t*>(outBlendMode),
+ decodeInteger);
+}
+
+status_t encodeSmpte2086(const std::optional<Smpte2086>& smpte2086,
+ hidl_vec<uint8_t>* outSmpte2086) {
+ return encodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, encodeSmpte2086Helper);
+}
+
+status_t decodeSmpte2086(const hidl_vec<uint8_t>& smpte2086,
+ std::optional<Smpte2086>* outSmpte2086) {
+ return decodeOptional(MetadataType_Smpte2086, smpte2086, outSmpte2086, decodeSmpte2086Helper);
+}
+
+status_t encodeCta861_3(const std::optional<Cta861_3>& cta861_3, hidl_vec<uint8_t>* outCta861_3) {
+ return encodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, encodeCta861_3Helper);
+}
+
+status_t decodeCta861_3(const hidl_vec<uint8_t>& cta861_3, std::optional<Cta861_3>* outCta861_3) {
+ return decodeOptional(MetadataType_Cta861_3, cta861_3, outCta861_3, decodeCta861_3Helper);
+}
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+ hidl_vec<uint8_t>* outSmpte2094_40) {
+ return encodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ encodeByteVector);
+}
+
+status_t decodeSmpte2094_40(const hidl_vec<uint8_t>& smpte2094_40,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+ return decodeOptional(MetadataType_Smpte2094_40, smpte2094_40, outSmpte2094_40,
+ decodeByteVector);
+}
+
+status_t encodeUint32(const MetadataType& metadataType, uint32_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ uint32_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt32(const MetadataType& metadataType, int32_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt32(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ int32_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeUint64(const MetadataType& metadataType, uint64_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeUint64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ uint64_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeInt64(const MetadataType& metadataType, int64_t input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeInt64(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ int64_t* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeFloat(const MetadataType& metadataType, float input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeFloat(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ float* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeDouble(const MetadataType& metadataType, double input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeInteger);
+}
+
+status_t decodeDouble(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ double* output) {
+ return decode(metadataType, input, output, decodeInteger);
+}
+
+status_t encodeString(const MetadataType& metadataType, const std::string& input,
+ hidl_vec<uint8_t>* output) {
+ return encode(metadataType, input, output, encodeString);
+}
+
+status_t decodeString(const MetadataType& metadataType, const hidl_vec<uint8_t>& input,
+ std::string* output) {
+ return decode(metadataType, input, output, decodeString);
+}
+
bool isStandardMetadataType(const MetadataType& metadataType) {
- return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
+ return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE,
+ metadataType.name.size());
+}
+
+bool isStandardCompression(const ExtendableType& compression) {
+ return !std::strncmp(compression.name.c_str(), GRALLOC4_STANDARD_COMPRESSION,
+ compression.name.size());
+}
+
+bool isStandardInterlaced(const ExtendableType& interlaced) {
+ return !std::strncmp(interlaced.name.c_str(), GRALLOC4_STANDARD_INTERLACED,
+ interlaced.name.size());
+}
+
+bool isStandardChromaSiting(const ExtendableType& chromaSiting) {
+ return !std::strncmp(chromaSiting.name.c_str(), GRALLOC4_STANDARD_CHROMA_SITING,
+ chromaSiting.name.size());
+}
+
+bool isStandardPlaneLayoutComponentType(const ExtendableType& planeLayoutComponentType) {
+ return !std::strncmp(planeLayoutComponentType.name.c_str(), GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE,
+ planeLayoutComponentType.name.size());
}
StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
return static_cast<StandardMetadataType>(metadataType.value);
}
-status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
- return encode(bufferId, outBufferId, encodeInteger);
+Compression getStandardCompressionValue(const ExtendableType& compression) {
+ return static_cast<Compression>(compression.value);
}
-status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
- return decode(bufferId, outBufferId, decodeInteger);
+Interlaced getStandardInterlacedValue(const ExtendableType& interlaced) {
+ return static_cast<Interlaced>(interlaced.value);
}
-status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
- return encode(name, outName, encodeString);
+ChromaSiting getStandardChromaSitingValue(const ExtendableType& chromaSiting) {
+ return static_cast<ChromaSiting>(chromaSiting.value);
}
-status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
- return decode(name, outName, decodeString);
+PlaneLayoutComponentType getStandardPlaneLayoutComponentTypeValue(
+ const ExtendableType& planeLayoutComponentType) {
+ return static_cast<PlaneLayoutComponentType>(planeLayoutComponentType.value);
}
-status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
- return encode(width, outWidth, encodeInteger);
+std::string getCompressionName(const ExtendableType& compression) {
+ if (!isStandardCompression(compression)) {
+ std::ostringstream stream;
+ stream << compression.name << "#" << compression.value;
+ return stream.str();
+ }
+ switch (getStandardCompressionValue(compression)) {
+ case Compression::NONE:
+ return "None";
+ case Compression::DISPLAY_STREAM_COMPRESSION:
+ return "DisplayStreamCompression";
+ }
}
-status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
- return decode(width, outWidth, decodeInteger);
+std::string getInterlacedName(const ExtendableType& interlaced) {
+ if (!isStandardInterlaced(interlaced)) {
+ std::ostringstream stream;
+ stream << interlaced.name << "#" << interlaced.value;
+ return stream.str();
+ }
+ switch (getStandardInterlacedValue(interlaced)) {
+ case Interlaced::NONE:
+ return "None";
+ case Interlaced::TOP_BOTTOM:
+ return "TopBottom";
+ case Interlaced::RIGHT_LEFT:
+ return "RightLeft";
+ }
}
-status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
- return encode(height, outHeight, encodeInteger);
+std::string getChromaSitingName(const ExtendableType& chromaSiting) {
+ if (!isStandardChromaSiting(chromaSiting)) {
+ std::ostringstream stream;
+ stream << chromaSiting.name << "#" << chromaSiting.value;
+ return stream.str();
+ }
+ switch (getStandardChromaSitingValue(chromaSiting)) {
+ case ChromaSiting::NONE:
+ return "None";
+ case ChromaSiting::UNKNOWN:
+ return "Unknown";
+ case ChromaSiting::SITED_INTERSTITIAL:
+ return "SitedInterstitial";
+ case ChromaSiting::COSITED_HORIZONTAL:
+ return "CositedHorizontal";
+ }
}
-status_t decodeHeight(const hidl_vec<uint8_t>& height, uint64_t* outHeight) {
- return decode(height, outHeight, decodeInteger);
-}
-
-status_t encodeLayerCount(uint64_t layerCount, hidl_vec<uint8_t>* outLayerCount) {
- return encode(layerCount, outLayerCount, encodeInteger);
-}
-
-status_t decodeLayerCount(const hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount) {
- return decode(layerCount, outLayerCount, decodeInteger);
-}
-
-status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested,
- hidl_vec<uint8_t>* outPixelFormatRequested) {
- return encode(static_cast<int32_t>(pixelFormatRequested), outPixelFormatRequested, encodeInteger);
-}
-
-status_t decodePixelFormatRequested(const hidl_vec<uint8_t>& pixelFormatRequested,
- hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested) {
- return decode(pixelFormatRequested, reinterpret_cast<int32_t*>(outPixelFormatRequested), decodeInteger);
-}
-
-status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, hidl_vec<uint8_t>* outPixelFormatFourCC) {
- return encode(pixelFormatFourCC, outPixelFormatFourCC, encodeInteger);
-}
-
-status_t decodePixelFormatFourCC(const hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC) {
- return decode(pixelFormatFourCC, outPixelFormatFourCC, decodeInteger);
-}
-
-status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, hidl_vec<uint8_t>* outPixelFormatModifier) {
- return encode(pixelFormatModifier, outPixelFormatModifier, encodeInteger);
-}
-
-status_t decodePixelFormatModifier(const hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier) {
- return decode(pixelFormatModifier, outPixelFormatModifier, decodeInteger);
-}
-
-status_t encodeUsage(uint64_t usage, hidl_vec<uint8_t>* outUsage) {
- return encode(usage, outUsage, encodeInteger);
-}
-
-status_t decodeUsage(const hidl_vec<uint8_t>& usage, uint64_t* outUsage) {
- return decode(usage, outUsage, decodeInteger);
-}
-
-status_t encodeAllocationSize(uint64_t allocationSize, hidl_vec<uint8_t>* outAllocationSize) {
- return encode(allocationSize, outAllocationSize, encodeInteger);
-}
-
-status_t decodeAllocationSize(const hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize) {
- return decode(allocationSize, outAllocationSize, decodeInteger);
-}
-
-status_t encodeProtectedContent(uint64_t protectedContent, hidl_vec<uint8_t>* outProtectedContent) {
- return encode(protectedContent, outProtectedContent, encodeInteger);
-}
-
-status_t decodeProtectedContent(const hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent) {
- return decode(protectedContent, outProtectedContent, decodeInteger);
-}
-
-status_t encodeCompression(const ExtendableType& compression, hidl_vec<uint8_t>* outCompression) {
- return encode(compression, outCompression, encodeExtendableType);
-}
-
-status_t decodeCompression(const hidl_vec<uint8_t>& compression, ExtendableType* outCompression) {
- return decode(compression, outCompression, decodeExtendableType, clearExtendableType);
-}
-
-status_t encodeInterlaced(const ExtendableType& interlaced, hidl_vec<uint8_t>* outInterlaced) {
- return encode(interlaced, outInterlaced, encodeExtendableType);
-}
-
-status_t decodeInterlaced(const hidl_vec<uint8_t>& interlaced, ExtendableType* outInterlaced) {
- return decode(interlaced, outInterlaced, decodeExtendableType, clearExtendableType);
-}
-
-status_t encodeChromaSiting(const ExtendableType& chromaSiting, hidl_vec<uint8_t>* outChromaSiting) {
- return encode(chromaSiting, outChromaSiting, encodeExtendableType);
-}
-
-status_t decodeChromaSiting(const hidl_vec<uint8_t>& chromaSiting, ExtendableType* outChromaSiting) {
- return decode(chromaSiting, outChromaSiting, decodeExtendableType, clearExtendableType);
-}
-
-status_t encodePlaneLayouts(const std::vector<PlaneLayout>& planeLayouts, hidl_vec<uint8_t>* outPlaneLayouts) {
- return encode(planeLayouts, outPlaneLayouts, encodePlaneLayoutsHelper);
-}
-
-status_t decodePlaneLayouts(const hidl_vec<uint8_t>& planeLayouts, std::vector<PlaneLayout>* outPlaneLayouts) {
- return decode(planeLayouts, outPlaneLayouts, decodePlaneLayoutsHelper, clearPlaneLayouts);
-}
-
-status_t encodeDataspace(const Dataspace& dataspace, hidl_vec<uint8_t>* outDataspace) {
- return encode(static_cast<int32_t>(dataspace), outDataspace, encodeInteger);
-}
-
-status_t decodeDataspace(const hidl_vec<uint8_t>& dataspace, Dataspace* outDataspace) {
- return decode(dataspace, reinterpret_cast<int32_t*>(outDataspace), decodeInteger);
-}
-
-status_t encodeBlendMode(const BlendMode& blendMode, hidl_vec<uint8_t>* outBlendMode) {
- return encode(static_cast<int32_t>(blendMode), outBlendMode, encodeInteger);
-}
-
-status_t decodeBlendMode(const hidl_vec<uint8_t>& blendMode, BlendMode* outBlendMode) {
- return decode(blendMode, reinterpret_cast<int32_t*>(outBlendMode), decodeInteger);
+std::string getPlaneLayoutComponentTypeName(const ExtendableType& planeLayoutComponentType) {
+ if (!isStandardPlaneLayoutComponentType(planeLayoutComponentType)) {
+ std::ostringstream stream;
+ stream << planeLayoutComponentType.name << "#" << planeLayoutComponentType.value;
+ return stream.str();
+ }
+ switch (getStandardPlaneLayoutComponentTypeValue(planeLayoutComponentType)) {
+ case PlaneLayoutComponentType::Y:
+ return "Y";
+ case PlaneLayoutComponentType::CB:
+ return "Cb";
+ case PlaneLayoutComponentType::CR:
+ return "Cr";
+ case PlaneLayoutComponentType::R:
+ return "R";
+ case PlaneLayoutComponentType::G:
+ return "G";
+ case PlaneLayoutComponentType::B:
+ return "B";
+ case PlaneLayoutComponentType::A:
+ return "A";
+ }
}
} // namespace gralloc4
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
index 23c90b8..b18f407 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.cpp
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -30,9 +30,10 @@
#include "util.h"
using ::android::status_t;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
#define GRALLOCTYPES_DECODE(T, FUNC) \
- [] (const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t /*data*/) {\
+ [] (const ::android::hardware::hidl_vec<uint8_t>& vec) {\
FUZZ_LOG() << "about to read " #T " using " #FUNC;\
T t;\
status_t err = FUNC(vec, &t);\
@@ -40,6 +41,15 @@
FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
}
+#define GRALLOCTYPES_DECODE_VENDOR_HELPER(T, FUNC) \
+ [] (const MetadataType& metadataType, const ::android::hardware::hidl_vec<uint8_t>& vec) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+ T t;\
+ status_t err = FUNC(metadataType, vec, &t);\
+ (void) err;\
+ FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+ }
+
// clang-format off
std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
@@ -60,5 +70,18 @@
GRALLOCTYPES_DECODE(std::vector<aidl::android::hardware::graphics::common::PlaneLayout>, ::android::gralloc4::decodePlaneLayouts),
GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::Dataspace, ::android::gralloc4::decodeDataspace),
GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::BlendMode, ::android::gralloc4::decodeBlendMode),
+ GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Smpte2086>, ::android::gralloc4::decodeSmpte2086),
+ GRALLOCTYPES_DECODE(std::optional<aidl::android::hardware::graphics::common::Cta861_3>, ::android::gralloc4::decodeCta861_3),
+ GRALLOCTYPES_DECODE(std::optional<std::vector<uint8_t>>, ::android::gralloc4::decodeSmpte2094_40),
+};
+
+std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS {
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(uint32_t, ::android::gralloc4::decodeUint32),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(int32_t, ::android::gralloc4::decodeInt32),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(uint64_t, ::android::gralloc4::decodeUint64),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(int64_t, ::android::gralloc4::decodeInt64),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(float, ::android::gralloc4::decodeFloat),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(double, ::android::gralloc4::decodeDouble),
+ GRALLOCTYPES_DECODE_VENDOR_HELPER(std::string, ::android::gralloc4::decodeString),
};
// clang-format on
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.h b/libs/gralloc/types/fuzzer/gralloctypes.h
index b995fce..a3fe2d9 100644
--- a/libs/gralloc/types/fuzzer/gralloctypes.h
+++ b/libs/gralloc/types/fuzzer/gralloctypes.h
@@ -20,6 +20,8 @@
#include <vector>
-using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t data)>;
+using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec)>;
+using GrallocTypesVendorHelperDecode = std::function<void(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType&, const ::android::hardware::hidl_vec<uint8_t>& vec)>;
extern std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS;
+extern std::vector<GrallocTypesVendorHelperDecode> GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS;
diff --git a/libs/gralloc/types/fuzzer/main.cpp b/libs/gralloc/types/fuzzer/main.cpp
index 2807878..8779fa2 100644
--- a/libs/gralloc/types/fuzzer/main.cpp
+++ b/libs/gralloc/types/fuzzer/main.cpp
@@ -24,52 +24,99 @@
#include <cstdlib>
#include <ctime>
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
void doFuzz(
- const std::vector<GrallocTypesDecode>& decodes,
- const std::vector<uint8_t>& input,
- const std::vector<uint8_t>& instructions) {
+ const std::vector<GrallocTypesDecode>& decodes, uint8_t instruction,
+ const std::vector<uint8_t>& input) {
::android::hardware::hidl_vec<uint8_t> vec;
vec.setToExternal(const_cast<uint8_t*>(input.data()), input.size(), false /*shouldOwn*/);
// since we are only using a byte to index
CHECK(decodes.size() <= 255) << decodes.size();
+ uint8_t decodeIdx = instruction % decodes.size();
- for (size_t i = 0; i < instructions.size() - 1; i += 2) {
- uint8_t a = instructions[i];
- uint8_t decodeIdx = a % decodes.size();
+ FUZZ_LOG() << "Instruction: " << instruction << " idx: " << static_cast<size_t>(decodeIdx)
+ << " size: " << vec.size();
- uint8_t b = instructions[i + 1];
+ decodes[decodeIdx](vec);
+}
- FUZZ_LOG() << "Instruction: " << (i / 2) + 1 << "/" << instructions.size() / 2
- << " cmd: " << static_cast<size_t>(a) << " (" << static_cast<size_t>(decodeIdx)
- << ") arg: " << static_cast<size_t>(b) << " size: " << vec.size();
+size_t fillInMetadataType(const std::vector<uint8_t>& input, MetadataType* outMetadataType) {
+ if (input.size() < sizeof(outMetadataType->value) + 1) {
+ return 0;
+ }
+ size_t size = 0;
- decodes[decodeIdx](vec, b);
+ outMetadataType->value = *(reinterpret_cast<const int64_t*>(input.data()));
+ size += sizeof(outMetadataType->value);
+
+ uint8_t nameLen = *(input.data() + size);
+ size += 1;
+
+ if (input.size() < size + nameLen) {
+ return 0;
+ }
+ std::string name(reinterpret_cast<const char*>(input.data()) + size, nameLen);
+ outMetadataType->name = name;
+ return size + nameLen;
+}
+
+void doFuzzVendorHelper(
+ const std::vector<GrallocTypesVendorHelperDecode>& decodes, uint8_t instruction,
+ const std::vector<uint8_t>& input) {
+
+ MetadataType metadataType;
+ size_t sizeUsed = fillInMetadataType(input, &metadataType);
+ if (sizeUsed <= 0) {
+ return;
+ }
+
+ ::android::hardware::hidl_vec<uint8_t> vec;
+ vec.setToExternal(const_cast<uint8_t*>(input.data() + sizeUsed), input.size() - sizeUsed,
+ false /*shouldOwn*/);
+
+ // since we are only using a byte to index
+ CHECK(decodes.size() <= 255) << decodes.size();
+ uint8_t decodeIdx = instruction % decodes.size();
+
+ FUZZ_LOG() << "Vendor Helper instruction: " << instruction << " idx: "
+ << static_cast<size_t>(decodeIdx) << " size: " << vec.size();
+
+ decodes[decodeIdx](metadataType, vec);
+}
+
+void fuzz(uint8_t options, uint8_t instruction, const std::vector<uint8_t>& input) {
+ uint8_t option = options & 0x1;
+
+ switch (option) {
+ case 0x0:
+ doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, instruction, input);
+ break;
+ case 0x1:
+ doFuzzVendorHelper(GRALLOCTYPES_DECODE_VENDOR_HELPER_FUNCTIONS, instruction, input);
+ break;
+ default:
+ LOG_ALWAYS_FATAL("unknown gralloc types %d", static_cast<int>(option));
}
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
if (size <= 1) return 0; // no use
- // data to fill out parcel
- size_t inputLen = size / 2;
- std::vector<uint8_t> input(data, data + inputLen);
- data += inputLen;
- size -= inputLen;
+ uint8_t options = *data;
+ data++;
+ size--;
- // data to use to determine what to do
- size_t instructionLen = size;
- std::vector<uint8_t> instructions(data, data + instructionLen);
- data += instructionLen;
- size -= instructionLen;
+ uint8_t instruction = *data;
+ data++;
+ size--;
- CHECK(size == 0) << "size: " << size;
+ std::vector<uint8_t> input(data, data + size);
- FUZZ_LOG() << "inputLen: " << inputLen << " instructionLen: " << instructionLen;
FUZZ_LOG() << "input: " << hexString(input);
- FUZZ_LOG() << "instructions: " << hexString(instructions);
- doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, input, instructions);
+ fuzz(options, instruction, input);
return 0;
}
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
index 80588cd..ca0d4b5 100644
--- a/libs/gralloc/types/include/gralloctypes/Gralloc4.h
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -17,27 +17,209 @@
#pragma once
#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.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 <aidl/android/hardware/graphics/common/Smpte2086.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/XyColor.h>
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+/**
+ * Define equality operators for Stable AIDL types.
+ */
+inline bool operator==(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+ const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+ return !std::strcmp(lhs.name.c_str(), rhs.name.c_str()) && lhs.value == rhs.value;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::ExtendableType& lhs,
+ const aidl::android::hardware::graphics::common::ExtendableType& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+ if (lhs.type.name != rhs.type.name) {
+ return false;
+ }
+ if (lhs.type.value != rhs.type.value) {
+ return false;
+ }
+ if (lhs.sizeInBits != rhs.sizeInBits) {
+ return false;
+ }
+ if (lhs.offsetInBits != rhs.offsetInBits) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayoutComponent& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayoutComponent& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Rect& lhs,
+ const aidl::android::hardware::graphics::common::Rect& rhs) {
+ if (lhs.left != rhs.left) {
+ return false;
+ }
+ if (lhs.top != rhs.top) {
+ return false;
+ }
+ if (lhs.right != rhs.right) {
+ return false;
+ }
+ if (lhs.bottom != rhs.bottom) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Rect& lhs,
+ const aidl::android::hardware::graphics::common::Rect& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+ if (lhs.offsetInBytes != rhs.offsetInBytes) {
+ return false;
+ }
+ if (lhs.sampleIncrementInBits != rhs.sampleIncrementInBits) {
+ return false;
+ }
+ if (lhs.strideInBytes != rhs.strideInBytes) {
+ return false;
+ }
+ if (lhs.widthInSamples != rhs.widthInSamples) {
+ return false;
+ }
+ if (lhs.heightInSamples != rhs.heightInSamples) {
+ return false;
+ }
+ if (lhs.totalSizeInBytes != rhs.totalSizeInBytes) {
+ return false;
+ }
+ if (lhs.horizontalSubsampling != rhs.horizontalSubsampling) {
+ return false;
+ }
+ if (lhs.verticalSubsampling != rhs.verticalSubsampling) {
+ return false;
+ }
+ if (lhs.crop != rhs.crop) {
+ return false;
+ }
+ if (lhs.components.size() != rhs.components.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs.components.size(); i++) {
+ if (lhs.components[i] != rhs.components[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::PlaneLayout& lhs,
+ const aidl::android::hardware::graphics::common::PlaneLayout& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+ if (lhs.size() != rhs.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < lhs.size(); i++) {
+ if (lhs[i] != rhs[i]) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline bool operator!=(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& lhs,
+ const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::XyColor& lhs,
+ const aidl::android::hardware::graphics::common::XyColor& rhs) {
+ if (lhs.x != rhs.x) {
+ return false;
+ }
+ if (lhs.y != rhs.y) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::XyColor& lhs,
+ const aidl::android::hardware::graphics::common::XyColor& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+ const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+ if (lhs.primaryRed != rhs.primaryRed) {
+ return false;
+ }
+ if (lhs.primaryGreen != rhs.primaryGreen) {
+ return false;
+ }
+ if (lhs.primaryBlue != rhs.primaryBlue) {
+ return false;
+ }
+ if (lhs.whitePoint != rhs.whitePoint) {
+ return false;
+ }
+ if (lhs.maxLuminance != rhs.maxLuminance) {
+ return false;
+ }
+ if (lhs.minLuminance != rhs.minLuminance) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Smpte2086& lhs,
+ const aidl::android::hardware::graphics::common::Smpte2086& rhs) {
+ return !(lhs == rhs);
+}
+
+inline bool operator==(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+ const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+ if (lhs.maxContentLightLevel != rhs.maxContentLightLevel) {
+ return false;
+ }
+ if (lhs.maxFrameAverageLightLevel != rhs.maxFrameAverageLightLevel) {
+ return false;
+ }
+ return true;
+}
+
+inline bool operator!=(const aidl::android::hardware::graphics::common::Cta861_3& lhs,
+ const aidl::android::hardware::graphics::common::Cta861_3& rhs) {
+ return !(lhs == rhs);
+}
+
namespace android {
namespace gralloc4 {
#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"
+#define GRALLOC4_STANDARD_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
+#define GRALLOC4_STANDARD_COMPRESSION "android.hardware.graphics.common.Compression"
+#define GRALLOC4_STANDARD_INTERLACED "android.hardware.graphics.common.Interlaced"
+#define GRALLOC4_STANDARD_PLANE_LAYOUT_COMPONENT_TYPE \
+ "android.hardware.graphics.common.PlaneLayoutComponentType"
/*---------------------------------------------------------------------------------------------*/
/**
@@ -112,19 +294,37 @@
GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
};
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2086 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2086)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Cta861_3 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::
+ StandardMetadataType::CTA861_3)};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType
+ MetadataType_Smpte2094_40 = {GRALLOC4_STANDARD_METADATA_TYPE,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ StandardMetadataType::SMPTE2094_40)};
+
/*---------------------------------------------------------------------------------------------*/
/**
* 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_None =
+ {GRALLOC4_STANDARD_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)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+ Compression_DisplayStreamCompression =
+ {GRALLOC4_STANDARD_COMPRESSION,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::
+ DISPLAY_STREAM_COMPRESSION)};
/*---------------------------------------------------------------------------------------------*/
@@ -132,17 +332,17 @@
* 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_None =
+ {GRALLOC4_STANDARD_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_TopBottom =
+ {GRALLOC4_STANDARD_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)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft =
+ {GRALLOC4_STANDARD_INTERLACED,
+ static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)};
/*---------------------------------------------------------------------------------------------*/
@@ -150,21 +350,25 @@
* 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_None =
+ {GRALLOC4_STANDARD_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_Unknown =
+ {GRALLOC4_STANDARD_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_SitedInterstitial = {GRALLOC4_STANDARD_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)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType
+ ChromaSiting_CositedHorizontal = {GRALLOC4_STANDARD_CHROMA_SITING,
+ static_cast<int64_t>(
+ aidl::android::hardware::graphics::common::
+ ChromaSiting::COSITED_HORIZONTAL)};
/*---------------------------------------------------------------------------------------------*/
@@ -172,44 +376,44 @@
* 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_Y =
+ {GRALLOC4_STANDARD_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_CB =
+ {GRALLOC4_STANDARD_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_CR =
+ {GRALLOC4_STANDARD_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_R =
+ {GRALLOC4_STANDARD_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_G =
+ {GRALLOC4_STANDARD_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_B =
+ {GRALLOC4_STANDARD_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)
-};
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A =
+ {GRALLOC4_STANDARD_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).
@@ -265,6 +469,115 @@
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);
+status_t encodeSmpte2086(
+ const std::optional<aidl::android::hardware::graphics::common::Smpte2086>& smpte2086,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2086);
+status_t decodeSmpte2086(
+ const android::hardware::hidl_vec<uint8_t>& smpte2086,
+ std::optional<aidl::android::hardware::graphics::common::Smpte2086>* outSmpte2086);
+
+status_t encodeCta861_3(
+ const std::optional<aidl::android::hardware::graphics::common::Cta861_3>& cta861_3,
+ android::hardware::hidl_vec<uint8_t>* outCta861_3);
+status_t decodeCta861_3(
+ const android::hardware::hidl_vec<uint8_t>& cta861_3,
+ std::optional<aidl::android::hardware::graphics::common::Cta861_3>* outCta861_3);
+
+status_t encodeSmpte2094_40(const std::optional<std::vector<uint8_t>>& smpte2094_40,
+ android::hardware::hidl_vec<uint8_t>* outSmpte2094_40);
+status_t decodeSmpte2094_40(const android::hardware::hidl_vec<uint8_t>& smpte2094_40,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+/**
+ * The functions below can be used to encode and decode vendor metadata types.
+ */
+status_t encodeUint32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ uint32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, uint32_t* output);
+
+status_t encodeInt32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ int32_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt32(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, int32_t* output);
+
+status_t encodeUint64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ uint64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeUint64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, uint64_t* output);
+
+status_t encodeInt64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ int64_t input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeInt64(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, int64_t* output);
+
+status_t encodeFloat(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ float input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeFloat(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, float* output);
+
+status_t encodeDouble(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ double input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeDouble(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, double* output);
+
+status_t encodeString(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const std::string& input, android::hardware::hidl_vec<uint8_t>* output);
+status_t decodeString(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ const android::hardware::hidl_vec<uint8_t>& input, std::string* output);
+
+/**
+ * The functions below can be used to parse extendable types.
+ */
+bool isStandardMetadataType(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+bool isStandardCompression(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+bool isStandardInterlaced(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+bool isStandardChromaSiting(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+bool isStandardPlaneLayoutComponentType(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+aidl::android::hardware::graphics::common::Compression getStandardCompressionValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+aidl::android::hardware::graphics::common::Interlaced getStandardInterlacedValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+aidl::android::hardware::graphics::common::ChromaSiting getStandardChromaSitingValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+aidl::android::hardware::graphics::common::PlaneLayoutComponentType
+getStandardPlaneLayoutComponentTypeValue(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
+/**
+ * The functions below return string representations of ExtendableTypes
+ */
+std::string getCompressionName(
+ const aidl::android::hardware::graphics::common::ExtendableType& compression);
+std::string getInterlacedName(
+ const aidl::android::hardware::graphics::common::ExtendableType& interlaced);
+std::string getChromaSitingName(
+ const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting);
+std::string getPlaneLayoutComponentTypeName(
+ const aidl::android::hardware::graphics::common::ExtendableType& planeLayoutComponentType);
+
} // namespace gralloc4
} // namespace android
diff --git a/libs/gralloc/types/tests/Android.bp b/libs/gralloc/types/tests/Android.bp
new file mode 100644
index 0000000..b939c1d
--- /dev/null
+++ b/libs/gralloc/types/tests/Android.bp
@@ -0,0 +1,25 @@
+//
+// 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.
+//
+
+cc_test {
+ name: "GrallocTypes_test",
+ shared_libs: [
+ "libgralloctypes",
+ "libhidlbase",
+ ],
+ srcs: ["Gralloc4_test.cpp"],
+ cflags: ["-Wall", "-Werror"],
+}
diff --git a/libs/gralloc/types/tests/Gralloc4_test.cpp b/libs/gralloc/types/tests/Gralloc4_test.cpp
new file mode 100644
index 0000000..3542ed9
--- /dev/null
+++ b/libs/gralloc/types/tests/Gralloc4_test.cpp
@@ -0,0 +1,552 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Gralloc4Test"
+
+#include <limits>
+
+#include <gralloctypes/Gralloc4.h>
+
+#include <gtest/gtest.h>
+
+using android::hardware::hidl_vec;
+
+using android::hardware::graphics::common::V1_2::PixelFormat;
+
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::ChromaSiting;
+using aidl::android::hardware::graphics::common::Compression;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::Dataspace;
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::Interlaced;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::Rect;
+using aidl::android::hardware::graphics::common::Smpte2086;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using aidl::android::hardware::graphics::common::XyColor;
+
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+template<class T>
+using EncodeFunction = status_t(*)(T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeConstFunction = status_t(*)(const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeFunction = status_t(*)(const MetadataType&, T, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeMetadataTypeConstFunction = status_t(*)(const MetadataType&, const T&, hidl_vec<uint8_t>*);
+
+template<class T>
+using EncodeOptionalFunction = status_t(*)(const std::optional<T>&, hidl_vec<uint8_t>*);
+
+template<class T>
+using DecodeFunction = status_t(*)(const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeMetadataTypeFunction = status_t(*)(const MetadataType&, const hidl_vec<uint8_t>&, T*);
+
+template<class T>
+using DecodeOptionalFunction = status_t(*)(const hidl_vec<uint8_t>&, std::optional<T>*);
+
+template<class T>
+void testHelper(const T& input, EncodeFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperConst(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataType(const T& input, EncodeMetadataTypeFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperMetadataTypeConst(const T& input, EncodeMetadataTypeConstFunction<T> encode, DecodeMetadataTypeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ MetadataType metadataType{"vendor.mycompanyname.graphics.common.MetadataType", 0};
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(metadataType, input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(metadataType, vec, &output));
+ ASSERT_EQ(input, output);
+}
+
+template<class T>
+void testHelperStableAidlType(const T& input, EncodeConstFunction<T> encode, DecodeFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ T output;
+ ASSERT_EQ(NO_ERROR, encode(input, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_TRUE(input == output);
+}
+
+template<class T>
+void testHelperStableAidlTypeOptional(const std::optional<T>& input, EncodeOptionalFunction<T> encode,
+ DecodeOptionalFunction<T> decode) {
+ hidl_vec<uint8_t> vec;
+ std::optional<T> tmp = input;
+ std::optional<T> output;
+ ASSERT_EQ(NO_ERROR, encode(tmp, &vec));
+ ASSERT_EQ(NO_ERROR, decode(vec, &output));
+ ASSERT_EQ(tmp.has_value(), output.has_value());
+ if (!tmp.has_value()) {
+ return;
+ }
+ ASSERT_TRUE(*tmp == *output);
+}
+
+class Gralloc4TestUint32 : public testing::TestWithParam<uint32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestUint32Params, Gralloc4TestUint32,
+ ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint32_t>::min(),
+ std::numeric_limits<uint32_t>::max()));
+
+TEST_P(Gralloc4TestUint32, Uint32) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint32, gralloc4::decodeUint32));
+}
+
+TEST_P(Gralloc4TestUint32, PixelFormatFourCC) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatFourCC, gralloc4::decodePixelFormatFourCC));
+}
+
+class Gralloc4TestInt32 : public testing::TestWithParam<int32_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInt32Params, Gralloc4TestInt32,
+ ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int32_t>::min(),
+ std::numeric_limits<int32_t>::max()));
+
+TEST_P(Gralloc4TestInt32, Int32) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt32, gralloc4::decodeInt32));
+}
+
+class Gralloc4TestUint64 : public testing::TestWithParam<uint64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestUint64Params, Gralloc4TestUint64,
+ ::testing::Values(0, -1, 1, 5, 100, 0xFF, std::numeric_limits<uint64_t>::min(),
+ std::numeric_limits<uint64_t>::max()));
+
+TEST_P(Gralloc4TestUint64, Uint64) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeUint64, gralloc4::decodeUint64));
+}
+
+TEST_P(Gralloc4TestUint64, BufferId) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeBufferId, gralloc4::decodeBufferId));
+}
+
+TEST_P(Gralloc4TestUint64, Width) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeWidth, gralloc4::decodeWidth));
+}
+
+TEST_P(Gralloc4TestUint64, Height) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeHeight, gralloc4::decodeHeight));
+}
+
+TEST_P(Gralloc4TestUint64, LayerCount) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeLayerCount, gralloc4::decodeLayerCount));
+}
+
+TEST_P(Gralloc4TestUint64, PixelFormatModifier) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodePixelFormatModifier, gralloc4::decodePixelFormatModifier));
+}
+
+TEST_P(Gralloc4TestUint64, Usage) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeUsage, gralloc4::decodeUsage));
+}
+
+TEST_P(Gralloc4TestUint64, AllocationSize) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeAllocationSize, gralloc4::decodeAllocationSize));
+}
+
+TEST_P(Gralloc4TestUint64, ProtectedContent) {
+ ASSERT_NO_FATAL_FAILURE(testHelper(GetParam(), gralloc4::encodeProtectedContent, gralloc4::decodeProtectedContent));
+}
+
+class Gralloc4TestInt64 : public testing::TestWithParam<int64_t> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInt64Params, Gralloc4TestInt64,
+ ::testing::Values(0, 1, 5, 100, 0xFF, std::numeric_limits<int64_t>::min(),
+ std::numeric_limits<int64_t>::max()));
+
+TEST_P(Gralloc4TestInt64, Int64) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeInt64, gralloc4::decodeInt64));
+}
+
+class Gralloc4TestFloat : public testing::TestWithParam<float> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestFloatParams, Gralloc4TestFloat,
+ ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<float>::min(),
+ std::numeric_limits<float>::max()));
+
+TEST_P(Gralloc4TestFloat, Float) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeFloat, gralloc4::decodeFloat));
+}
+
+class Gralloc4TestDouble : public testing::TestWithParam<double> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestDoubleParams, Gralloc4TestDouble,
+ ::testing::Values(0.0, 1.999999, 5.5, 100.1, 1234.5678, std::numeric_limits<double>::min(),
+ std::numeric_limits<double>::max()));
+
+TEST_P(Gralloc4TestDouble, Double) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataType(GetParam(), gralloc4::encodeDouble, gralloc4::decodeDouble));
+}
+
+class Gralloc4TestString : public testing::TestWithParam<std::string> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestStringParams, Gralloc4TestString,
+ ::testing::Values("name", "aaaaa", "", "abcdefghijklmnopqrstuvwxyz", "0xFF"));
+
+TEST_P(Gralloc4TestString, String) {
+ ASSERT_NO_FATAL_FAILURE(testHelperMetadataTypeConst(GetParam(), gralloc4::encodeString, gralloc4::decodeString));
+}
+
+TEST_P(Gralloc4TestString, Name) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeName, gralloc4::decodeName));
+}
+
+class Gralloc4TestPixelFormat : public testing::TestWithParam<PixelFormat> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestPixelFormatParams, Gralloc4TestPixelFormat,
+ ::testing::Values(PixelFormat::RGBA_8888, PixelFormat::BLOB,
+ PixelFormat::IMPLEMENTATION_DEFINED, PixelFormat::YCBCR_420_888,
+ PixelFormat::YV12));
+
+TEST_P(Gralloc4TestPixelFormat, PixelFormatRequested) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodePixelFormatRequested, gralloc4::decodePixelFormatRequested));
+}
+
+class Gralloc4TestCompression : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestCompressionParams, Gralloc4TestCompression,
+ ::testing::Values(gralloc4::Compression_None, gralloc4::Compression_DisplayStreamCompression,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Compression", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Compression", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestCompression, Compression) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeCompression, gralloc4::decodeCompression));
+}
+
+class Gralloc4TestInterlaced : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestInterlacedParams, Gralloc4TestInterlaced,
+ ::testing::Values(gralloc4::Interlaced_None, gralloc4::Interlaced_TopBottom,
+ gralloc4::Interlaced_RightLeft,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.Interlaced", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestInterlaced, Interlaced) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeInterlaced, gralloc4::decodeInterlaced));
+}
+
+class Gralloc4TestChromaSiting : public testing::TestWithParam<ExtendableType> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestChromaSitingParams, Gralloc4TestChromaSiting,
+ ::testing::Values(gralloc4::ChromaSiting_None, gralloc4::ChromaSiting_Unknown,
+ gralloc4::ChromaSiting_SitedInterstitial, gralloc4::ChromaSiting_CositedHorizontal,
+ ExtendableType{"", 0},
+ ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", 0xFF},
+ ExtendableType{"vendor.mycompanyname.graphics.common.ChromaSiting", std::numeric_limits<int64_t>::max()}));
+
+TEST_P(Gralloc4TestChromaSiting, ChromaSiting) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(GetParam(), gralloc4::encodeChromaSiting, gralloc4::decodeChromaSiting));
+}
+
+class Gralloc4TestPlaneLayouts : public testing::Test { };
+
+TEST_F(Gralloc4TestPlaneLayouts, PlaneLayouts) {
+ uint32_t width = 64;
+ uint32_t height = 64;
+
+ std::vector<PlaneLayout> planeLayouts;
+ PlaneLayout planeLayoutA;
+ PlaneLayout planeLayoutRGB;
+ PlaneLayoutComponent component;
+
+ planeLayoutA.offsetInBytes = 0;
+ planeLayoutA.sampleIncrementInBits = 8;
+ planeLayoutA.strideInBytes = width + 20;
+ planeLayoutA.widthInSamples = width;
+ planeLayoutA.heightInSamples = height;
+ planeLayoutA.totalSizeInBytes = planeLayoutA.strideInBytes * height;
+ planeLayoutA.horizontalSubsampling = 1;
+ planeLayoutA.verticalSubsampling = 1;
+ planeLayoutA.crop.left = 0;
+ planeLayoutA.crop.top = 0;
+ planeLayoutA.crop.right = width;
+ planeLayoutA.crop.bottom = height;
+
+ component.type = gralloc4::PlaneLayoutComponentType_A;
+ component.offsetInBits = 0;
+ component.sizeInBits = 8;
+ planeLayoutA.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutA);
+
+ planeLayoutRGB.offsetInBytes = 0;
+ planeLayoutRGB.sampleIncrementInBits = 32;
+ planeLayoutRGB.strideInBytes = width + 20;
+ planeLayoutRGB.widthInSamples = width;
+ planeLayoutRGB.heightInSamples = height;
+ planeLayoutRGB.totalSizeInBytes = planeLayoutRGB.strideInBytes * height;
+ planeLayoutRGB.horizontalSubsampling = 1;
+ planeLayoutRGB.verticalSubsampling = 1;
+ planeLayoutRGB.crop.left = 0;
+ planeLayoutRGB.crop.top = 0;
+ planeLayoutRGB.crop.right = width;
+ planeLayoutRGB.crop.bottom = height;
+
+ component.type = gralloc4::PlaneLayoutComponentType_R;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_G;
+ planeLayoutRGB.components.push_back(component);
+ component.type = gralloc4::PlaneLayoutComponentType_B;
+ planeLayoutRGB.components.push_back(component);
+
+ planeLayouts.push_back(planeLayoutRGB);
+
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlType(planeLayouts, gralloc4::encodePlaneLayouts, gralloc4::decodePlaneLayouts));
+}
+
+class Gralloc4TestDataspace : public testing::TestWithParam<Dataspace> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestDataspaceParams, Gralloc4TestDataspace,
+ ::testing::Values(Dataspace::UNKNOWN, Dataspace::ARBITRARY, Dataspace::DISPLAY_P3,
+ Dataspace::ADOBE_RGB));
+
+TEST_P(Gralloc4TestDataspace, DataspaceRequested) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeDataspace, gralloc4::decodeDataspace));
+}
+
+class Gralloc4TestBlendMode : public testing::TestWithParam<BlendMode> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestBlendModeParams, Gralloc4TestBlendMode,
+ ::testing::Values(BlendMode::INVALID, BlendMode::NONE, BlendMode::PREMULTIPLIED,
+ BlendMode::COVERAGE));
+
+TEST_P(Gralloc4TestBlendMode, BlendMode) {
+ ASSERT_NO_FATAL_FAILURE(testHelperConst(GetParam(), gralloc4::encodeBlendMode, gralloc4::decodeBlendMode));
+}
+
+class Gralloc4TestSmpte2086 : public testing::TestWithParam<std::optional<Smpte2086>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2086Params, Gralloc4TestSmpte2086,
+ ::testing::Values(std::optional<Smpte2086>(Smpte2086{XyColor{0.680, 0.320},
+ XyColor{0.265, 0.690},
+ XyColor{0.150, 0.060},
+ XyColor{0.3127, 0.3290},
+ 100.0, 0.1}),
+ std::optional<Smpte2086>(Smpte2086{XyColor{-1.0, 100.0},
+ XyColor{0xFF, -0xFF},
+ XyColor{999.9, 0.0},
+ XyColor{0.0, -1.0},
+ -0.1, -100.0}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2086, Smpte2086) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2086, gralloc4::decodeSmpte2086));
+}
+
+class Gralloc4TestCta861_3 : public testing::TestWithParam<std::optional<Cta861_3>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestCta861_3Params, Gralloc4TestCta861_3,
+ ::testing::Values(std::optional<Cta861_3>(Cta861_3{78.0, 62.0}),
+ std::optional<Cta861_3>(Cta861_3{10.0, 10.0}),
+ std::optional<Cta861_3>(Cta861_3{0.0, 0.0}),
+ std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::min(), std::numeric_limits<float>::min()}),
+ std::optional<Cta861_3>(Cta861_3{std::numeric_limits<float>::max(), std::numeric_limits<float>::max()}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestCta861_3, Cta861_3) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeCta861_3, gralloc4::decodeCta861_3));
+}
+
+class Gralloc4TestSmpte2094_40 : public testing::TestWithParam<std::optional<std::vector<uint8_t>>> { };
+
+INSTANTIATE_TEST_CASE_P(
+ Gralloc4TestSmpte2094_40Params, Gralloc4TestSmpte2094_40,
+ ::testing::Values(std::optional<std::vector<uint8_t>>({}),
+ std::optional<std::vector<uint8_t>>({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::min(),
+ std::numeric_limits<uint8_t>::min() + 1,
+ std::numeric_limits<uint8_t>::min() + 2,
+ std::numeric_limits<uint8_t>::min() + 3,
+ std::numeric_limits<uint8_t>::min() + 4}),
+ std::optional<std::vector<uint8_t>>({std::numeric_limits<uint8_t>::max(),
+ std::numeric_limits<uint8_t>::max() - 1,
+ std::numeric_limits<uint8_t>::max() - 2,
+ std::numeric_limits<uint8_t>::max() - 3,
+ std::numeric_limits<uint8_t>::max() - 4}),
+ std::nullopt));
+
+TEST_P(Gralloc4TestSmpte2094_40, Smpte2094_40) {
+ ASSERT_NO_FATAL_FAILURE(testHelperStableAidlTypeOptional(GetParam(), gralloc4::encodeSmpte2094_40, gralloc4::decodeSmpte2094_40));
+}
+
+class Gralloc4TestErrors : public testing::Test { };
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestEncodeNull) {
+ ASSERT_NE(NO_ERROR, gralloc4::encodeBufferId(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeName("", nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeWidth(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeHeight(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeLayerCount(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatRequested(PixelFormat::RGBA_8888, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatFourCC(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePixelFormatModifier(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeUsage(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeAllocationSize(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeProtectedContent(0, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeCompression(gralloc4::Compression_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeInterlaced(gralloc4::Interlaced_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeChromaSiting(gralloc4::ChromaSiting_None, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodePlaneLayouts({}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeDataspace(Dataspace::UNKNOWN, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeBlendMode(BlendMode::NONE, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2086({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeCta861_3({{}}, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::encodeSmpte2094_40({{}}, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeNull) {
+ hidl_vec<uint8_t> vec;
+
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, nullptr));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, nullptr));
+}
+
+TEST_F(Gralloc4TestErrors, Gralloc4TestDecodeBadVec) {
+ hidl_vec<uint8_t> vec = { 0 };
+
+ uint64_t bufferId, width, height, layerCount, pixelFormatModifier, usage, allocationSize,
+ protectedContent;
+ std::string name;
+ PixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ ExtendableType compression, interlaced, chromaSiting;
+ std::vector<PlaneLayout> planeLayouts;
+ Dataspace dataspace;
+ BlendMode blendMode;
+ std::optional<Smpte2086> smpte2086;
+ std::optional<Cta861_3> cta861_3;
+ std::optional<std::vector<uint8_t>> smpte2094_40;
+
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBufferId(vec, &bufferId));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeName(vec, &name));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeWidth(vec, &width));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeHeight(vec, &height));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeLayerCount(vec, &layerCount));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatRequested(vec, &pixelFormatRequested));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatFourCC(vec, &pixelFormatFourCC));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePixelFormatModifier(vec, &pixelFormatModifier));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeUsage(vec, &usage));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeAllocationSize(vec, &allocationSize));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeProtectedContent(vec, &protectedContent));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCompression(vec, &compression));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeInterlaced(vec, &interlaced));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeChromaSiting(vec, &chromaSiting));
+ ASSERT_NE(NO_ERROR, gralloc4::decodePlaneLayouts(vec, &planeLayouts));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeDataspace(vec, &dataspace));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeBlendMode(vec, &blendMode));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2086(vec, &smpte2086));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeCta861_3(vec, &cta861_3));
+ ASSERT_NE(NO_ERROR, gralloc4::decodeSmpte2094_40(vec, &smpte2094_40));
+}
+
+class Gralloc4TestHelpers : public testing::Test { };
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsStandard) {
+ ASSERT_TRUE(gralloc4::isStandardMetadataType(gralloc4::MetadataType_BufferId));
+ ASSERT_TRUE(gralloc4::isStandardCompression(gralloc4::Compression_None));
+ ASSERT_TRUE(gralloc4::isStandardInterlaced(gralloc4::Interlaced_None));
+ ASSERT_TRUE(gralloc4::isStandardChromaSiting(gralloc4::ChromaSiting_None));
+ ASSERT_TRUE(gralloc4::isStandardPlaneLayoutComponentType(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestIsNotStandard) {
+ ASSERT_FALSE(gralloc4::isStandardMetadataType({"vendor.mycompanyname.graphics.common.MetadataType", 0}));
+ ASSERT_FALSE(gralloc4::isStandardCompression({"vendor.mycompanyname.graphics.common.Compression", 0}));
+ ASSERT_FALSE(gralloc4::isStandardInterlaced({"vendor.mycompanyname.graphics.common.Interlaced", 0}));
+ ASSERT_FALSE(gralloc4::isStandardChromaSiting({"vendor.mycompanyname.graphics.common.ChromaSiting", 0}));
+ ASSERT_FALSE(gralloc4::isStandardPlaneLayoutComponentType({"vendor.mycompanyname.graphics.common.PlaneLayoutComponentType", 0}));
+}
+
+TEST_F(Gralloc4TestHelpers, Gralloc4TestGetStandardValue) {
+ ASSERT_EQ(StandardMetadataType::BUFFER_ID, gralloc4::getStandardMetadataTypeValue(gralloc4::MetadataType_BufferId));
+ ASSERT_EQ(Compression::NONE, gralloc4::getStandardCompressionValue(gralloc4::Compression_None));
+ ASSERT_EQ(Interlaced::NONE, gralloc4::getStandardInterlacedValue(gralloc4::Interlaced_None));
+ ASSERT_EQ(ChromaSiting::NONE, gralloc4::getStandardChromaSitingValue(gralloc4::ChromaSiting_None));
+ ASSERT_EQ(PlaneLayoutComponentType::Y, gralloc4::getStandardPlaneLayoutComponentTypeValue(gralloc4::PlaneLayoutComponentType_Y));
+}
+
+} // namespace android
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 1ae148c..5959340 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -40,6 +40,7 @@
defaults: ["libgui_bufferqueue-defaults"],
srcs: [
+ ":framework_native_aidl",
":libgui_bufferqueue_sources",
"BitTube.cpp",
@@ -106,6 +107,10 @@
"libdvr_headers",
"libpdx_headers",
],
+
+ aidl: {
+ export_aidl_headers: true,
+ }
}
// Used by media codec services exclusively as a static lib for
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 29ea84e..a5e5693 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -17,10 +17,14 @@
#undef LOG_TAG
#define LOG_TAG "BLASTBufferQueue"
+#define ATRACE_TAG ATRACE_TAG_GRAPHICS
+
#include <gui/BLASTBufferQueue.h>
#include <gui/BufferItemConsumer.h>
#include <gui/GLConsumer.h>
+#include <utils/Trace.h>
+
#include <chrono>
using namespace std::chrono_literals;
@@ -29,23 +33,29 @@
BLASTBufferQueue::BLASTBufferQueue(const sp<SurfaceControl>& surface, int width, int height)
: mSurfaceControl(surface),
- mPendingCallbacks(0),
mWidth(width),
mHeight(height),
mNextTransaction(nullptr) {
BufferQueue::createBufferQueue(&mProducer, &mConsumer);
mConsumer->setMaxBufferCount(MAX_BUFFERS);
mProducer->setMaxDequeuedBufferCount(MAX_BUFFERS - 1);
+ mConsumer->setMaxAcquiredBufferCount(MAX_ACQUIRED_BUFFERS);
mBufferItemConsumer =
new BufferItemConsumer(mConsumer, AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER, 1, true);
- mBufferItemConsumer->setName(String8("BLAST Consumer"));
+ static int32_t id = 0;
+ auto name = std::string("BLAST Consumer") + std::to_string(id);
+ id++;
+ mBufferItemConsumer->setName(String8(name.c_str()));
mBufferItemConsumer->setFrameAvailableListener(this);
mBufferItemConsumer->setBufferFreedListener(this);
mBufferItemConsumer->setDefaultBufferSize(mWidth, mHeight);
mBufferItemConsumer->setDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888);
mBufferItemConsumer->setTransformHint(mSurfaceControl->getTransformHint());
- mAcquired = false;
+ mNumAcquired = 0;
+ mNumFrameAvailable = 0;
+ mPendingReleaseItem.item = BufferItem();
+ mPendingReleaseItem.releaseFence = nullptr;
}
void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, int width, int height) {
@@ -70,34 +80,46 @@
void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence>& /*presentFence*/,
const std::vector<SurfaceControlStats>& stats) {
std::unique_lock _lock{mMutex};
+ ATRACE_CALL();
- if (stats.size() > 0 && !mShadowQueue.empty()) {
- mBufferItemConsumer->releaseBuffer(mNextCallbackBufferItem,
- stats[0].previousReleaseFence
- ? stats[0].previousReleaseFence
+ if (mPendingReleaseItem.item.mGraphicBuffer != nullptr) {
+ if (stats.size() > 0) {
+ mPendingReleaseItem.releaseFence = stats[0].previousReleaseFence;
+ mTransformHint = stats[0].transformHint;
+ } else {
+ ALOGE("Warning: no SurfaceControlStats returned in BLASTBufferQueue callback");
+ mPendingReleaseItem.releaseFence = nullptr;
+ }
+ mBufferItemConsumer->releaseBuffer(mPendingReleaseItem.item,
+ mPendingReleaseItem.releaseFence
+ ? mPendingReleaseItem.releaseFence
: Fence::NO_FENCE);
- mAcquired = false;
- mNextCallbackBufferItem = BufferItem();
- mBufferItemConsumer->setTransformHint(stats[0].transformHint);
+ mNumAcquired--;
+ mPendingReleaseItem.item = BufferItem();
+ mPendingReleaseItem.releaseFence = nullptr;
}
- mPendingCallbacks--;
+
+ if (mSubmitted.empty()) {
+ ALOGE("ERROR: callback with no corresponding submitted buffer item");
+ }
+ mPendingReleaseItem.item = std::move(mSubmitted.front());
+ mSubmitted.pop();
processNextBufferLocked();
mCallbackCV.notify_all();
decStrong((void*)transactionCallbackThunk);
}
void BLASTBufferQueue::processNextBufferLocked() {
- if (mShadowQueue.empty()) {
+ ATRACE_CALL();
+ if (mNumFrameAvailable == 0) {
return;
}
- if (mAcquired) {
+ if (mSurfaceControl == nullptr) {
+ ALOGE("ERROR : surface control is null");
return;
}
- BufferItem item = std::move(mShadowQueue.front());
- mShadowQueue.pop();
-
SurfaceComposerClient::Transaction localTransaction;
bool applyTransaction = true;
SurfaceComposerClient::Transaction* t = &localTransaction;
@@ -107,32 +129,34 @@
applyTransaction = false;
}
- mNextCallbackBufferItem = mLastSubmittedBufferItem;
- mLastSubmittedBufferItem = BufferItem();
+ BufferItem bufferItem;
- status_t status = mBufferItemConsumer->acquireBuffer(&mLastSubmittedBufferItem, -1, false);
- mAcquired = true;
+ status_t status = mBufferItemConsumer->acquireBuffer(&bufferItem, -1, false);
if (status != OK) {
- ALOGE("Failed to acquire?");
- }
-
- auto buffer = mLastSubmittedBufferItem.mGraphicBuffer;
-
- if (buffer == nullptr) {
- ALOGE("Null buffer");
return;
}
+ auto buffer = bufferItem.mGraphicBuffer;
+ mNumFrameAvailable--;
+
+ if (buffer == nullptr) {
+ mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
+ return;
+ }
+
+ mNumAcquired++;
+ mSubmitted.push(bufferItem);
// Ensure BLASTBufferQueue stays alive until we receive the transaction complete callback.
incStrong((void*)transactionCallbackThunk);
t->setBuffer(mSurfaceControl, buffer);
t->setAcquireFence(mSurfaceControl,
- item.mFence ? new Fence(item.mFence->dup()) : Fence::NO_FENCE);
+ bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE);
t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
t->setFrame(mSurfaceControl, {0, 0, (int32_t)buffer->getWidth(), (int32_t)buffer->getHeight()});
- t->setCrop(mSurfaceControl, computeCrop(mLastSubmittedBufferItem));
+ t->setCrop(mSurfaceControl, computeCrop(bufferItem));
+ t->setTransform(mSurfaceControl, bufferItem.mTransform);
if (applyTransaction) {
t->apply();
@@ -146,13 +170,13 @@
return item.mCrop;
}
-void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
+void BLASTBufferQueue::onFrameAvailable(const BufferItem& /*item*/) {
+ ATRACE_CALL();
std::lock_guard _lock{mMutex};
// add to shadow queue
- mShadowQueue.push(item);
+ mNumFrameAvailable++;
processNextBufferLocked();
- mPendingCallbacks++;
}
void BLASTBufferQueue::setNextTransaction(SurfaceComposerClient::Transaction* t) {
diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp
index 3a7cb44..6418e8c 100644
--- a/libs/gui/BufferQueueConsumer.cpp
+++ b/libs/gui/BufferQueueConsumer.cpp
@@ -44,6 +44,28 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+
BufferQueueConsumer::BufferQueueConsumer(const sp<BufferQueueCore>& core) :
mCore(core),
mSlots(core->mSlots),
diff --git a/libs/gui/BufferQueueCore.cpp b/libs/gui/BufferQueueCore.cpp
index d6009d6..3b0120b 100644
--- a/libs/gui/BufferQueueCore.cpp
+++ b/libs/gui/BufferQueueCore.cpp
@@ -42,6 +42,23 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), mUniqueId, \
+ mConnectedApi, mConnectedPid, mUniqueId >> 32, ##__VA_ARGS__)
+
static String8 getUniqueName() {
static volatile int32_t counter = 0;
return String8::format("unnamed-%d-%d", getpid(),
@@ -54,6 +71,19 @@
return id | counter++;
}
+static status_t getProcessName(int pid, String8& name) {
+ FILE* fp = fopen(String8::format("/proc/%d/cmdline", pid), "r");
+ if (NULL != fp) {
+ const size_t size = 64;
+ char proc_name[size];
+ fgets(proc_name, size, fp);
+ fclose(fp);
+ name = proc_name;
+ return NO_ERROR;
+ }
+ return INVALID_OPERATION;
+}
+
BufferQueueCore::BufferQueueCore() :
mMutex(),
mIsAbandoned(false),
@@ -132,6 +162,20 @@
mTransformHintInUse, mAutoPrerotation);
outResult->appendFormat("%sFIFO(%zu):\n", prefix.string(), mQueue.size());
+
+ outResult->appendFormat("%s(mConsumerName=%s, ", prefix.string(), mConsumerName.string());
+
+ outResult->appendFormat("mConnectedApi=%d, mConsumerUsageBits=%" PRIu64 ", ", mConnectedApi,
+ mConsumerUsageBits);
+
+ String8 producerProcName = String8("\?\?\?");
+ String8 consumerProcName = String8("\?\?\?");
+ int32_t pid = getpid();
+ getProcessName(mConnectedPid, producerProcName);
+ getProcessName(pid, consumerProcName);
+ outResult->appendFormat("mId=%" PRIx64 ", producer=[%d:%s], consumer=[%d:%s])\n", mUniqueId,
+ mConnectedPid, producerProcName.string(), pid,
+ consumerProcName.string());
Fifo::const_iterator current(mQueue.begin());
while (current != mQueue.end()) {
double timestamp = current->mTimestamp / 1e9;
diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp
index a307d04..e6df757 100644
--- a/libs/gui/BufferQueueProducer.cpp
+++ b/libs/gui/BufferQueueProducer.cpp
@@ -44,6 +44,28 @@
namespace android {
+// Macros for include BufferQueueCore information in log messages
+#define BQ_LOGV(x, ...) \
+ ALOGV("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGD(x, ...) \
+ ALOGD("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGI(x, ...) \
+ ALOGI("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGW(x, ...) \
+ ALOGW("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+#define BQ_LOGE(x, ...) \
+ ALOGE("[%s](id:%" PRIx64 ",api:%d,p:%d,c:%" PRIu64 ") " x, mConsumerName.string(), \
+ mCore->mUniqueId, mCore->mConnectedApi, mCore->mConnectedPid, (mCore->mUniqueId) >> 32, \
+ ##__VA_ARGS__)
+
static constexpr uint32_t BQ_LAYER_COUNT = 1;
BufferQueueProducer::BufferQueueProducer(const sp<BufferQueueCore>& core,
@@ -998,6 +1020,17 @@
item.mGraphicBuffer.clear();
}
+ // Update and get FrameEventHistory.
+ nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ NewFrameEventsEntry newFrameEventsEntry = {
+ currentFrameNumber,
+ postedTime,
+ requestedPresentTimestamp,
+ std::move(acquireFenceTime)
+ };
+ addAndGetFrameTimestamps(&newFrameEventsEntry,
+ getFrameTimestamps ? &output->frameTimestamps : nullptr);
+
// Call back without the main BufferQueue lock held, but with the callback
// lock held so we can ensure that callbacks occur in order
@@ -1027,17 +1060,6 @@
mCallbackCondition.notify_all();
}
- // Update and get FrameEventHistory.
- nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
- NewFrameEventsEntry newFrameEventsEntry = {
- currentFrameNumber,
- postedTime,
- requestedPresentTimestamp,
- std::move(acquireFenceTime)
- };
- addAndGetFrameTimestamps(&newFrameEventsEntry,
- getFrameTimestamps ? &output->frameTimestamps : nullptr);
-
// Wait without lock held
if (connectedApi == NATIVE_WINDOW_API_EGL) {
// Waiting here allows for two full buffers to be queued but not a
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 546757b..ab4d51e 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -525,6 +525,88 @@
return static_cast<status_t>(reply.readInt32());
}
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_AUTO_LOW_LATENCY_MODE_SUPPORT, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeInterfaceToken: %d", result);
+ return;
+ }
+
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_AUTO_LOW_LATENCY_MODE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to transact: %d", result);
+ return;
+ }
+ }
+
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display, bool* outSupport) const {
+ Parcel data, reply;
+ data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ status_t result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_GAME_CONTENT_TYPE_SUPPORT, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to transact: %d", result);
+ return result;
+ }
+ return reply.readBool(outSupport);
+ }
+
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeInterfaceToken: %d", result);
+ return;
+ }
+ result = data.writeStrongBinder(display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeStrongBinder: %d", result);
+ return;
+ }
+ result = data.writeBool(on);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to writeBool: %d", result);
+ return;
+ }
+ result = remote()->transact(BnSurfaceComposer::SET_GAME_CONTENT_TYPE, data, &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to transact: %d", result);
+ }
+ }
+
virtual status_t clearAnimationFrameStats() {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -852,61 +934,8 @@
return error;
}
- virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs) {
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(displayToken);
- if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
- return result;
- }
- result = data.writeInt32Vector(allowedConfigs);
- if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("setAllowedDisplayConfigs failed to transact: %d", result);
- return result;
- }
- return reply.readInt32();
- }
-
- virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) {
- if (!outAllowedConfigs) return BAD_VALUE;
- Parcel data, reply;
- status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
- if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result);
- return result;
- }
- result = data.writeStrongBinder(displayToken);
- if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result);
- return result;
- }
- result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply);
- if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to transact: %d", result);
- return result;
- }
- result = reply.readInt32Vector(outAllowedConfigs);
- if (result != NO_ERROR) {
- ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result);
- return result;
- }
- return reply.readInt32();
- }
-
virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultModeId, float minRefreshRate,
+ int32_t defaultConfig, float minRefreshRate,
float maxRefreshRate) {
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
@@ -919,9 +948,9 @@
ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
return result;
}
- result = data.writeInt32(defaultModeId);
+ result = data.writeInt32(defaultConfig);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs failed to write defaultModeId: %d", result);
+ ALOGE("setDesiredDisplayConfigSpecs failed to write defaultConfig: %d", result);
return result;
}
result = data.writeFloat(minRefreshRate);
@@ -945,10 +974,10 @@
}
virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultModeId,
+ int32_t* outDefaultConfig,
float* outMinRefreshRate,
float* outMaxRefreshRate) {
- if (!outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
+ if (!outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
Parcel data, reply;
status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
if (result != NO_ERROR) {
@@ -966,9 +995,9 @@
ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
return result;
}
- result = reply.readInt32(outDefaultModeId);
+ result = reply.readInt32(outDefaultConfig);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs failed to read defaultModeId: %d", result);
+ ALOGE("getDesiredDisplayConfigSpecs failed to read defaultConfig: %d", result);
return result;
}
result = reply.readFloat(outMinRefreshRate);
@@ -1407,6 +1436,75 @@
result = reply->writeInt32(result);
return result;
}
+
+ case GET_AUTO_LOW_LATENCY_MODE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getAutoLowLatencyModeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getAutoLowLatencyModeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_AUTO_LOW_LATENCY_MODE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setAllm = false;
+ result = data.readBool(&setAllm);
+ if (result != NO_ERROR) {
+ ALOGE("setAutoLowLatencyMode failed to readBool: %d", result);
+ return result;
+ }
+ setAutoLowLatencyMode(display, setAllm);
+ return result;
+ }
+
+ case GET_GAME_CONTENT_TYPE_SUPPORT: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("getGameContentTypeSupport failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool supported = false;
+ result = getGameContentTypeSupport(display, &supported);
+ if (result == NO_ERROR) {
+ result = reply->writeBool(supported);
+ }
+ return result;
+ }
+
+ case SET_GAME_CONTENT_TYPE: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> display = nullptr;
+ status_t result = data.readStrongBinder(&display);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readStrongBinder: %d", result);
+ return result;
+ }
+ bool setGameContentTypeOn = false;
+ result = data.readBool(&setGameContentTypeOn);
+ if (result != NO_ERROR) {
+ ALOGE("setGameContentType failed to readBool: %d", result);
+ return result;
+ }
+ setGameContentType(display, setGameContentTypeOn);
+ return result;
+ }
+
case CLEAR_ANIMATION_FRAME_STATS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
status_t result = clearAnimationFrameStats();
@@ -1644,31 +1742,13 @@
}
return removeRegionSamplingListener(listener);
}
- case SET_ALLOWED_DISPLAY_CONFIGS: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> displayToken = data.readStrongBinder();
- std::vector<int32_t> allowedConfigs;
- data.readInt32Vector(&allowedConfigs);
- status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs);
- reply->writeInt32(result);
- return result;
- }
- case GET_ALLOWED_DISPLAY_CONFIGS: {
- CHECK_INTERFACE(ISurfaceComposer, data, reply);
- sp<IBinder> displayToken = data.readStrongBinder();
- std::vector<int32_t> allowedConfigs;
- status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs);
- reply->writeInt32Vector(allowedConfigs);
- reply->writeInt32(result);
- return result;
- }
case SET_DESIRED_DISPLAY_CONFIG_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
- int32_t defaultModeId;
- status_t result = data.readInt32(&defaultModeId);
+ int32_t defaultConfig;
+ status_t result = data.readInt32(&defaultConfig);
if (result != NO_ERROR) {
- ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultModeId: %d", result);
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultConfig: %d", result);
return result;
}
float minRefreshRate;
@@ -1683,7 +1763,7 @@
ALOGE("setDesiredDisplayConfigSpecs: failed to read maxRefreshRate: %d", result);
return result;
}
- result = setDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate,
+ result = setDesiredDisplayConfigSpecs(displayToken, defaultConfig, minRefreshRate,
maxRefreshRate);
if (result != NO_ERROR) {
ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
@@ -1697,11 +1777,11 @@
case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken = data.readStrongBinder();
- int32_t defaultModeId;
+ int32_t defaultConfig;
float minRefreshRate;
float maxRefreshRate;
- status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultModeId,
+ status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultConfig,
&minRefreshRate, &maxRefreshRate);
if (result != NO_ERROR) {
ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
@@ -1710,9 +1790,9 @@
return result;
}
- result = reply->writeInt32(defaultModeId);
+ result = reply->writeInt32(defaultConfig);
if (result != NO_ERROR) {
- ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultModeId: %d", result);
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultConfig: %d", result);
return result;
}
result = reply->writeFloat(minRefreshRate);
diff --git a/libs/gui/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp
index 04d2871..b3eb994 100644
--- a/libs/gui/LayerMetadata.cpp
+++ b/libs/gui/LayerMetadata.cpp
@@ -18,6 +18,8 @@
#include <binder/Parcel.h>
#include <gui/LayerMetadata.h>
+#include "android/view/LayerMetadataKey.h"
+
using android::base::StringPrintf;
namespace android {
@@ -113,12 +115,12 @@
std::string LayerMetadata::itemToString(uint32_t key, const char* separator) const {
if (!has(key)) return std::string();
- switch (key) {
- case METADATA_OWNER_UID:
+ switch (static_cast<view::LayerMetadataKey>(key)) {
+ case view::LayerMetadataKey::METADATA_OWNER_UID:
return StringPrintf("ownerUID%s%d", separator, getInt32(key, 0));
- case METADATA_WINDOW_TYPE:
+ case view::LayerMetadataKey::METADATA_WINDOW_TYPE:
return StringPrintf("windowType%s%d", separator, getInt32(key, 0));
- case METADATA_TASK_ID:
+ case view::LayerMetadataKey::METADATA_TASK_ID:
return StringPrintf("taskId%s%d", separator, getInt32(key, 0));
default:
return StringPrintf("%d%s%dbytes", key, separator,
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index e392bc5..e033f93 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -109,7 +109,7 @@
return err;
}
}
-
+ output.writeFloat(shadowRadius);
return NO_ERROR;
}
@@ -187,6 +187,7 @@
input.readInt64Vector(&callbackIds);
listeners.emplace_back(listener, callbackIds);
}
+ shadowRadius = input.readFloat();
return NO_ERROR;
}
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 73150dc..274153c 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -4,4 +4,5 @@
marissaw@google.com
mathias@google.com
racarr@google.com
+steventhomas@google.com
stoza@google.com
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 9d7d7d0..1d887ea 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1605,34 +1605,22 @@
return ComposerService::getComposerService()->setActiveConfig(display, id);
}
-status_t SurfaceComposerClient::setAllowedDisplayConfigs(
- const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) {
- return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken,
- allowedConfigs);
-}
-
-status_t SurfaceComposerClient::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) {
- return ComposerService::getComposerService()->getAllowedDisplayConfigs(displayToken,
- outAllowedConfigs);
-}
-
status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultModeId,
+ int32_t defaultConfig,
float minRefreshRate,
float maxRefreshRate) {
return ComposerService::getComposerService()->setDesiredDisplayConfigSpecs(displayToken,
- defaultModeId,
+ defaultConfig,
minRefreshRate,
maxRefreshRate);
}
status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultModeId,
+ int32_t* outDefaultConfig,
float* outMinRefreshRate,
float* outMaxRefreshRate) {
return ComposerService::getComposerService()->getDesiredDisplayConfigSpecs(displayToken,
- outDefaultModeId,
+ outDefaultConfig,
outMinRefreshRate,
outMaxRefreshRate);
}
@@ -1656,6 +1644,26 @@
return ComposerService::getComposerService()->setActiveColorMode(display, colorMode);
}
+bool SurfaceComposerClient::getAutoLowLatencyModeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getAutoLowLatencyModeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setAutoLowLatencyMode(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setAutoLowLatencyMode(display, on);
+}
+
+bool SurfaceComposerClient::getGameContentTypeSupport(const sp<IBinder>& display) {
+ bool supported = false;
+ ComposerService::getComposerService()->getGameContentTypeSupport(display, &supported);
+ return supported;
+}
+
+void SurfaceComposerClient::setGameContentType(const sp<IBinder>& display, bool on) {
+ ComposerService::getComposerService()->setGameContentType(display, on);
+}
+
void SurfaceComposerClient::setDisplayPowerMode(const sp<IBinder>& token,
int mode) {
ComposerService::getComposerService()->setPowerMode(token, mode);
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index dd0b470..0a0a03c 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -68,23 +68,25 @@
std::mutex mMutex;
std::condition_variable mCallbackCV;
- uint64_t mPendingCallbacks GUARDED_BY(mMutex);
static const int MAX_BUFFERS = 3;
- struct BufferInfo {
- sp<GraphicBuffer> buffer;
- int fence;
+ static const int MAX_ACQUIRED_BUFFERS = 2;
+
+ int32_t mNumFrameAvailable GUARDED_BY(mMutex);
+ int32_t mNumAcquired GUARDED_BY(mMutex);
+
+ struct PendingReleaseItem {
+ BufferItem item;
+ sp<Fence> releaseFence;
};
- std::queue<const BufferItem> mShadowQueue GUARDED_BY(mMutex);
- bool mAcquired GUARDED_BY(mMutex);
+ std::queue<const BufferItem> mSubmitted GUARDED_BY(mMutex);
+ PendingReleaseItem mPendingReleaseItem GUARDED_BY(mMutex);
int mWidth GUARDED_BY(mMutex);
int mHeight GUARDED_BY(mMutex);
- BufferItem mLastSubmittedBufferItem GUARDED_BY(mMutex);
- BufferItem mNextCallbackBufferItem GUARDED_BY(mMutex);
- sp<Fence> mLastFence GUARDED_BY(mMutex);
+ uint32_t mTransformHint GUARDED_BY(mMutex);
sp<IGraphicBufferConsumer> mConsumer;
sp<IGraphicBufferProducer> mProducer;
diff --git a/libs/gui/include/gui/BufferQueueCore.h b/libs/gui/include/gui/BufferQueueCore.h
index 3c96089..557c28b 100644
--- a/libs/gui/include/gui/BufferQueueCore.h
+++ b/libs/gui/include/gui/BufferQueueCore.h
@@ -34,12 +34,6 @@
#include <mutex>
#include <condition_variable>
-#define BQ_LOGV(x, ...) ALOGV("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGD(x, ...) ALOGD("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGI(x, ...) ALOGI("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGW(x, ...) ALOGW("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-#define BQ_LOGE(x, ...) ALOGE("[%s] " x, mConsumerName.string(), ##__VA_ARGS__)
-
#define ATRACE_BUFFER_INDEX(index) \
do { \
if (ATRACE_ENABLED()) { \
diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h
index abe1e3f..25ce1ca 100644
--- a/libs/gui/include/gui/IGraphicBufferProducer.h
+++ b/libs/gui/include/gui/IGraphicBufferProducer.h
@@ -286,36 +286,6 @@
virtual status_t attachBuffer(int* outSlot,
const sp<GraphicBuffer>& buffer) = 0;
- // queueBuffer indicates that the client has finished filling in the
- // contents of the buffer associated with slot and transfers ownership of
- // that slot back to the server.
- //
- // It is not valid to call queueBuffer on a slot that is not owned
- // by the client or one for which a buffer associated via requestBuffer
- // (an attempt to do so will fail with a return value of BAD_VALUE).
- //
- // In addition, the input must be described by the client (as documented
- // below). Any other properties (zero point, etc)
- // are client-dependent, and should be documented by the client.
- //
- // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
- //
- // Upon success, the output will be filled with meaningful values
- // (refer to the documentation below).
- //
- // Return of a value other than NO_ERROR means an error has occurred:
- // * NO_INIT - the buffer queue has been abandoned or the producer is not
- // connected.
- // * BAD_VALUE - one of the below conditions occurred:
- // * fence was NULL
- // * scaling mode was unknown
- // * both in async mode and buffer count was less than the
- // max numbers of buffers that can be allocated at once
- // * slot index was out of range (see above).
- // * the slot was not in the dequeued state
- // * the slot was enqueued without requesting a buffer
- // * crop rect is out of bounds of the buffer dimensions
-
struct QueueBufferInput : public Flattenable<QueueBufferInput> {
friend class Flattenable<QueueBufferInput>;
explicit inline QueueBufferInput(const Parcel& parcel);
@@ -415,6 +385,35 @@
int maxBufferCount{0};
};
+ // queueBuffer indicates that the client has finished filling in the
+ // contents of the buffer associated with slot and transfers ownership of
+ // that slot back to the server.
+ //
+ // It is not valid to call queueBuffer on a slot that is not owned
+ // by the client or one for which a buffer associated via requestBuffer
+ // (an attempt to do so will fail with a return value of BAD_VALUE).
+ //
+ // In addition, the input must be described by the client (as documented
+ // below). Any other properties (zero point, etc)
+ // are client-dependent, and should be documented by the client.
+ //
+ // The slot must be in the range of [0, NUM_BUFFER_SLOTS).
+ //
+ // Upon success, the output will be filled with meaningful values
+ // (refer to the documentation below).
+ //
+ // Return of a value other than NO_ERROR means an error has occurred:
+ // * NO_INIT - the buffer queue has been abandoned or the producer is not
+ // connected.
+ // * BAD_VALUE - one of the below conditions occurred:
+ // * fence was NULL
+ // * scaling mode was unknown
+ // * both in async mode and buffer count was less than the
+ // max numbers of buffers that can be allocated at once
+ // * slot index was out of range (see above).
+ // * the slot was not in the dequeued state
+ // * the slot was enqueued without requesting a buffer
+ // * crop rect is out of bounds of the buffer dimensions
virtual status_t queueBuffer(int slot, const QueueBufferInput& input,
QueueBufferOutput* output) = 0;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 345425d..998973c 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -195,6 +195,37 @@
ui::ColorMode colorMode) = 0;
/**
+ * Returns true if the connected display reports support for HDMI 2.1 Auto
+ * Low Latency Mode.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual status_t getAutoLowLatencyModeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * Switches Auto Low Latency Mode on/off on the connected display, if it is
+ * available. This should only be called if #getAutoLowLatencyMode returns
+ * true.
+ * For more information, see the HDMI 2.1 specification.
+ */
+ virtual void setAutoLowLatencyMode(const sp<IBinder>& display, bool on) = 0;
+
+ /**
+ * Returns true if the connected display reports support for Game Content Type.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual status_t getGameContentTypeSupport(const sp<IBinder>& display,
+ bool* outSupport) const = 0;
+
+ /**
+ * This will start sending infoframes to the connected display with
+ * ContentType=Game (if on=true). This will switch the disply to Game mode.
+ * This should only be called if #getGameContentTypeSupport returns true.
+ * For more information, see the HDMI 1.4 specification.
+ */
+ virtual void setGameContentType(const sp<IBinder>& display, bool on) = 0;
+
+ /**
* Capture the specified screen. This requires READ_FRAME_BUFFER
* permission. This function will fail if there is a secure window on
* screen.
@@ -386,31 +417,16 @@
virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0;
/*
- * Sets the allowed display configurations to be used.
- * The allowedConfigs in a vector of indexes corresponding to the configurations
- * returned from getDisplayConfigs().
- */
- virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs) = 0;
-
- /*
- * Returns the allowed display configurations currently set.
- * The allowedConfigs in a vector of indexes corresponding to the configurations
- * returned from getDisplayConfigs().
- */
- virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) = 0;
- /*
* Sets the refresh rate boundaries for display configuration.
* For all other parameters, default configuration is used. The index for the default is
* corresponding to the configs returned from getDisplayConfigs().
*/
virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultModeId, float minRefreshRate,
+ int32_t defaultConfig, float minRefreshRate,
float maxRefreshRate) = 0;
virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultModeId,
+ int32_t* outDefaultConfig,
float* outMinRefreshRate,
float* outMaxRefreshRate) = 0;
/*
@@ -523,8 +539,6 @@
GET_PHYSICAL_DISPLAY_IDS,
ADD_REGION_SAMPLING_LISTENER,
REMOVE_REGION_SAMPLING_LISTENER,
- SET_ALLOWED_DISPLAY_CONFIGS,
- GET_ALLOWED_DISPLAY_CONFIGS,
SET_DESIRED_DISPLAY_CONFIG_SPECS,
GET_DESIRED_DISPLAY_CONFIG_SPECS,
GET_DISPLAY_BRIGHTNESS_SUPPORT,
@@ -532,6 +546,10 @@
CAPTURE_SCREEN_BY_ID,
NOTIFY_POWER_HINT,
SET_GLOBAL_SHADOW_SETTINGS,
+ GET_AUTO_LOW_LATENCY_MODE_SUPPORT,
+ SET_AUTO_LOW_LATENCY_MODE,
+ GET_GAME_CONTENT_TYPE_SUPPORT,
+ SET_GAME_CONTENT_TYPE,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 2c0b143..86468a4 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -117,23 +117,11 @@
// returned by getDisplayInfo
static status_t setActiveConfig(const sp<IBinder>& display, int id);
- // Sets the allowed display configurations to be used.
- // The allowedConfigs in a vector of indexes corresponding to the configurations
- // returned from getDisplayConfigs().
- static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs);
-
- // Returns the allowed display configurations currently set.
- // The allowedConfigs in a vector of indexes corresponding to the configurations
- // returned from getDisplayConfigs().
- static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs);
-
// Sets the refresh rate boundaries for display configuration.
// For all other parameters, default configuration is used. The index for the default is
// corresponting to the configs returned from getDisplayConfigs().
static status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultModeId, float minRefreshRate,
+ int32_t defaultConfig, float minRefreshRate,
float maxRefreshRate);
// Gets the refresh rate boundaries for display configuration.
// For all other parameters, default configuration is used. The index for the default is
@@ -141,7 +129,7 @@
// The reason is passed in for telemetry tracking, and it corresponds to the list of all
// the policy rules that were used.
static status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultModeId,
+ int32_t* outDefaultConfig,
float* outMinRefreshRate,
float* outMaxRefreshRate);
@@ -160,6 +148,21 @@
static status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode);
+ // Reports whether the connected display supports Auto Low Latency Mode
+ static bool getAutoLowLatencyModeSupport(const sp<IBinder>& display);
+
+ // Switches on/off Auto Low Latency Mode on the connected display. This should only be
+ // called if the connected display supports Auto Low Latency Mode as reported by
+ // #getAutoLowLatencyModeSupport
+ static void setAutoLowLatencyMode(const sp<IBinder>& display, bool on);
+
+ // Reports whether the connected display supports Game content type
+ static bool getGameContentTypeSupport(const sp<IBinder>& display);
+
+ // Turns Game mode on/off on the connected display. This should only be called
+ // if the display supports Game content type, as reported by #getGameContentTypeSupport
+ static void setGameContentType(const sp<IBinder>& display, bool on);
+
/* Triggers screen on/off or low power mode and waits for it to complete */
static void setDisplayPowerMode(const sp<IBinder>& display, int mode);
diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp
index ae6c5cf..0f618f1 100644
--- a/libs/gui/tests/BLASTBufferQueue_test.cpp
+++ b/libs/gui/tests/BLASTBufferQueue_test.cpp
@@ -69,7 +69,7 @@
void waitForCallbacks() {
std::unique_lock lock{mBlastBufferQueueAdapter->mMutex};
- while (mBlastBufferQueueAdapter->mPendingCallbacks > 0) {
+ while (mBlastBufferQueueAdapter->mSubmitted.size() > 0) {
mBlastBufferQueueAdapter->mCallbackCV.wait(lock);
}
}
@@ -144,7 +144,25 @@
}
}
- void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region) {
+ void fillQuadrants(sp<GraphicBuffer>& buf) {
+ const auto bufWidth = buf->getWidth();
+ const auto bufHeight = buf->getHeight();
+ uint32_t* bufData;
+ buf->lock(static_cast<uint32_t>(GraphicBuffer::USAGE_SW_WRITE_OFTEN),
+ reinterpret_cast<void**>(&bufData));
+ fillBuffer(bufData, Rect(0, 0, bufWidth / 2, bufHeight / 2), buf->getStride(), 0, 0, 0);
+ fillBuffer(bufData, Rect(bufWidth / 2, 0, bufWidth, bufHeight / 2), buf->getStride(), 255,
+ 0, 0);
+ fillBuffer(bufData, Rect(bufWidth / 2, bufHeight / 2, bufWidth, bufHeight),
+ buf->getStride(), 0, 255, 0);
+ fillBuffer(bufData, Rect(0, bufHeight / 2, bufWidth / 2, bufHeight), buf->getStride(), 0, 0,
+ 255);
+ buf->unlock();
+ }
+
+ void checkScreenCapture(uint8_t r, uint8_t g, uint8_t b, Rect region, int32_t border = 0,
+ bool outsideRegion = false) {
+ const auto epsilon = 3;
const auto width = mScreenCaptureBuf->getWidth();
const auto height = mScreenCaptureBuf->getHeight();
const auto stride = mScreenCaptureBuf->getStride();
@@ -156,15 +174,22 @@
for (uint32_t row = 0; row < height; row++) {
for (uint32_t col = 0; col < width; col++) {
uint8_t* pixel = (uint8_t*)(bufData + (row * stride) + col);
- if (row >= region.top && row < region.bottom && col >= region.left &&
- col < region.right) {
- EXPECT_EQ(r, *(pixel));
- EXPECT_EQ(g, *(pixel + 1));
- EXPECT_EQ(b, *(pixel + 2));
+ bool inRegion;
+ if (!outsideRegion) {
+ inRegion = row >= region.top + border && row < region.bottom - border &&
+ col >= region.left + border && col < region.right - border;
} else {
- EXPECT_EQ(0, *(pixel));
- EXPECT_EQ(0, *(pixel + 1));
- EXPECT_EQ(0, *(pixel + 2));
+ inRegion = row >= region.top - border && row < region.bottom + border &&
+ col >= region.left - border && col < region.right + border;
+ }
+ if (!outsideRegion && inRegion) {
+ EXPECT_GE(epsilon, abs(r - *(pixel)));
+ EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+ EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
+ } else if (outsideRegion && !inRegion) {
+ EXPECT_GE(epsilon, abs(r - *(pixel)));
+ EXPECT_GE(epsilon, abs(g - *(pixel + 1)));
+ EXPECT_GE(epsilon, abs(b - *(pixel + 2)));
}
}
}
@@ -277,7 +302,7 @@
igbProducer->cancelBuffer(allocated[i].first, allocated[i].second);
}
- for (int i = 0; i < 10; i++) {
+ for (int i = 0; i < 100; i++) {
int slot;
sp<Fence> fence;
sp<GraphicBuffer> buf;
@@ -398,6 +423,199 @@
ASSERT_NO_FATAL_FAILURE(
checkScreenCapture(r, g, b,
{0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength}));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)bufferSideLength, (int32_t)bufferSideLength},
+ /*border*/ 0, /*outsideRegion*/ true));
}
+class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest {
+public:
+ void test(uint32_t tr) {
+ BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ sp<IGraphicBufferProducer> igbProducer;
+ setUpProducer(adapter, igbProducer);
+
+ auto bufWidth = mDisplayWidth;
+ auto bufHeight = mDisplayHeight;
+ int slot;
+ sp<Fence> fence;
+ sp<GraphicBuffer> buf;
+
+ auto ret = igbProducer->dequeueBuffer(&slot, &fence, bufWidth, bufHeight,
+ PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN,
+ nullptr, nullptr);
+ ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret);
+ ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf));
+
+ fillQuadrants(buf);
+
+ IGraphicBufferProducer::QueueBufferOutput qbOutput;
+ IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN,
+ Rect(bufWidth, bufHeight),
+ NATIVE_WINDOW_SCALING_MODE_FREEZE, tr,
+ Fence::NO_FENCE);
+ igbProducer->queueBuffer(slot, input, &qbOutput);
+ ASSERT_NE(ui::Transform::orientation_flags::ROT_INVALID, qbOutput.transformHint);
+
+ adapter.waitForCallbacks();
+ bool capturedSecureLayers;
+ ASSERT_EQ(NO_ERROR,
+ mComposer->captureScreen(mDisplayToken, &mScreenCaptureBuf, capturedSecureLayers,
+ ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
+ Rect(), mDisplayWidth, mDisplayHeight,
+ /*useIdentityTransform*/ false));
+ switch (tr) {
+ case ui::Transform::ROT_0:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::FLIP_H:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::FLIP_V:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::ROT_90:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 0, 255,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::ROT_180:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(0, 255, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(255, 0, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ break;
+ case ui::Transform::ROT_270:
+ ASSERT_NO_FATAL_FAILURE(checkScreenCapture(255, 0, 0,
+ {0, 0, (int32_t)mDisplayWidth / 2,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 255, 0,
+ {(int32_t)mDisplayWidth / 2, 0, (int32_t)mDisplayWidth,
+ (int32_t)mDisplayHeight / 2},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 255,
+ {(int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth, (int32_t)mDisplayHeight},
+ 1));
+ ASSERT_NO_FATAL_FAILURE(
+ checkScreenCapture(0, 0, 0,
+ {0, (int32_t)mDisplayHeight / 2,
+ (int32_t)mDisplayWidth / 2, (int32_t)mDisplayHeight},
+ 1));
+ }
+ }
+};
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_0) {
+ test(ui::Transform::ROT_0);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_H) {
+ test(ui::Transform::FLIP_H);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_FLIP_V) {
+ test(ui::Transform::FLIP_V);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_90) {
+ test(ui::Transform::ROT_90);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_180) {
+ test(ui::Transform::ROT_180);
+}
+
+TEST_F(BLASTBufferQueueTransformTest, setTransform_ROT_270) {
+ test(ui::Transform::ROT_270);
+}
} // namespace android
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 3d90369..0445755 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -750,6 +750,16 @@
bool /*captureSecureLayers*/) override {
return NO_ERROR;
}
+ status_t getAutoLowLatencyModeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setAutoLowLatencyMode(const sp<IBinder>& /*display*/, bool /*on*/) override {}
+ status_t getGameContentTypeSupport(const sp<IBinder>& /*display*/,
+ bool* /*outSupport*/) const override {
+ return NO_ERROR;
+ }
+ void setGameContentType(const sp<IBinder>& /*display*/, bool /*on*/) override {}
status_t captureScreen(uint64_t /*displayOrLayerStack*/, ui::Dataspace* /*outDataspace*/,
sp<GraphicBuffer>* /*outBuffer*/) override {
return NO_ERROR;
@@ -823,21 +833,13 @@
const sp<IRegionSamplingListener>& /*listener*/) override {
return NO_ERROR;
}
- status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
- const std::vector<int32_t>& /*allowedConfigs*/) override {
- return NO_ERROR;
- }
- status_t getAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/,
- std::vector<int32_t>* /*outAllowedConfigs*/) override {
- return NO_ERROR;
- }
status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
- int32_t /*defaultModeId*/, float /*minRefreshRate*/,
- float /*maxRefreshRate*/) override {
+ int32_t /*defaultConfig*/, float /*minRefreshRate*/,
+ float /*maxRefreshRate*/) {
return NO_ERROR;
}
status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
- int32_t* /*outDefaultModeId*/,
+ int32_t* /*outDefaultConfig*/,
float* /*outMinRefreshRate*/,
float* /*outMaxRefreshRate*/) override {
return NO_ERROR;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index 200e1f3..d53a557 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -301,8 +301,8 @@
if (nWrite < 0) {
int error = errno;
#if DEBUG_CHANNEL_MESSAGES
- ALOGD("channel '%s' ~ error sending message of type %d, errno=%d", mName.c_str(),
- msg->header.type, error);
+ ALOGD("channel '%s' ~ error sending message of type %d, %s", mName.c_str(),
+ msg->header.type, strerror(error));
#endif
if (error == EAGAIN || error == EWOULDBLOCK) {
return WOULD_BLOCK;
diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp
index ec28757..03ca459 100644
--- a/libs/input/InputWindow.cpp
+++ b/libs/input/InputWindow.cpp
@@ -73,6 +73,7 @@
status_t s = output.writeStrongBinder(token);
if (s != OK) return s;
+ output.writeInt32(id);
output.writeString8(String8(name.c_str()));
output.writeInt32(layoutParamsFlags);
output.writeInt32(layoutParamsType);
@@ -90,7 +91,6 @@
output.writeBool(hasFocus);
output.writeBool(hasWallpaper);
output.writeBool(paused);
- output.writeInt32(layer);
output.writeInt32(ownerPid);
output.writeInt32(ownerUid);
output.writeInt32(inputFeatures);
@@ -116,6 +116,7 @@
}
ret.token = token;
+ ret.id = from.readInt32();
ret.name = from.readString8().c_str();
ret.layoutParamsFlags = from.readInt32();
ret.layoutParamsType = from.readInt32();
@@ -133,7 +134,6 @@
ret.hasFocus = from.readBool();
ret.hasWallpaper = from.readBool();
ret.paused = from.readBool();
- ret.layer = from.readInt32();
ret.ownerPid = from.readInt32();
ret.ownerUid = from.readInt32();
ret.inputFeatures = from.readInt32();
diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp
index 6db18ab..d1cb527 100644
--- a/libs/input/tests/InputWindow_test.cpp
+++ b/libs/input/tests/InputWindow_test.cpp
@@ -40,6 +40,7 @@
sp<IBinder> touchableRegionCropHandle = new BBinder();
InputWindowInfo i;
i.token = new BBinder();
+ i.id = 1;
i.name = "Foobar";
i.layoutParamsFlags = 7;
i.layoutParamsType = 39;
@@ -57,7 +58,6 @@
i.hasFocus = false;
i.hasWallpaper = false;
i.paused = false;
- i.layer = 7;
i.ownerPid = 19;
i.ownerUid = 24;
i.inputFeatures = 29;
@@ -72,6 +72,7 @@
p.setDataPosition(0);
InputWindowInfo i2 = InputWindowInfo::read(p);
ASSERT_EQ(i.token, i2.token);
+ ASSERT_EQ(i.id, i2.id);
ASSERT_EQ(i.name, i2.name);
ASSERT_EQ(i.layoutParamsFlags, i2.layoutParamsFlags);
ASSERT_EQ(i.layoutParamsType, i2.layoutParamsType);
@@ -89,7 +90,6 @@
ASSERT_EQ(i.hasFocus, i2.hasFocus);
ASSERT_EQ(i.hasWallpaper, i2.hasWallpaper);
ASSERT_EQ(i.paused, i2.paused);
- ASSERT_EQ(i.layer, i2.layer);
ASSERT_EQ(i.ownerPid, i2.ownerPid);
ASSERT_EQ(i.ownerUid, i2.ownerUid);
ASSERT_EQ(i.inputFeatures, i2.inputFeatures);
diff --git a/libs/renderengine/Android.bp b/libs/renderengine/Android.bp
index 136ad0d..348377e 100644
--- a/libs/renderengine/Android.bp
+++ b/libs/renderengine/Android.bp
@@ -52,6 +52,8 @@
"gl/GLExtensions.cpp",
"gl/GLFramebuffer.cpp",
"gl/GLImage.cpp",
+ "gl/GLShadowVertexGenerator.cpp",
+ "gl/GLSkiaShadowPort.cpp",
"gl/ImageManager.cpp",
"gl/Program.cpp",
"gl/ProgramCache.cpp",
diff --git a/libs/renderengine/Mesh.cpp b/libs/renderengine/Mesh.cpp
index f5387f2..ed2f45f 100644
--- a/libs/renderengine/Mesh.cpp
+++ b/libs/renderengine/Mesh.cpp
@@ -21,38 +21,46 @@
namespace android {
namespace renderengine {
-Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize)
+Mesh::Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize,
+ size_t indexCount)
: mVertexCount(vertexCount),
mVertexSize(vertexSize),
mTexCoordsSize(texCoordSize),
- mPrimitive(primitive) {
+ mCropCoordsSize(cropCoordsSize),
+ mShadowColorSize(shadowColorSize),
+ mShadowParamsSize(shadowParamsSize),
+ mPrimitive(primitive),
+ mIndexCount(indexCount) {
if (vertexCount == 0) {
mVertices.resize(1);
mVertices[0] = 0.0f;
mStride = 0;
return;
}
-
- const size_t CROP_COORD_SIZE = 2;
- size_t stride = vertexSize + texCoordSize + CROP_COORD_SIZE;
+ size_t stride = vertexSize + texCoordSize + cropCoordsSize + shadowColorSize + shadowParamsSize;
size_t remainder = (stride * vertexCount) / vertexCount;
// Since all of the input parameters are unsigned, if stride is less than
// either vertexSize or texCoordSize, it must have overflowed. remainder
// will be equal to stride as long as stride * vertexCount doesn't overflow.
if ((stride < vertexSize) || (remainder != stride)) {
- ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu)", vertexCount, vertexSize, texCoordSize,
- CROP_COORD_SIZE);
+ ALOGE("Overflow in Mesh(..., %zu, %zu, %zu, %zu, %zu, %zu)", vertexCount, vertexSize,
+ texCoordSize, cropCoordsSize, shadowColorSize, shadowParamsSize);
mVertices.resize(1);
mVertices[0] = 0.0f;
mVertexCount = 0;
mVertexSize = 0;
mTexCoordsSize = 0;
+ mCropCoordsSize = 0;
+ mShadowColorSize = 0;
+ mShadowParamsSize = 0;
mStride = 0;
return;
}
mVertices.resize(stride * vertexCount);
mStride = stride;
+ mIndices.resize(indexCount);
}
Mesh::Primitive Mesh::getPrimitive() const {
@@ -80,6 +88,28 @@
return mVertices.data() + mVertexSize + mTexCoordsSize;
}
+float const* Mesh::getShadowColor() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+float* Mesh::getShadowColor() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize;
+}
+
+float const* Mesh::getShadowParams() const {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+float* Mesh::getShadowParams() {
+ return mVertices.data() + mVertexSize + mTexCoordsSize + mCropCoordsSize + mShadowColorSize;
+}
+
+uint16_t const* Mesh::getIndices() const {
+ return mIndices.data();
+}
+
+uint16_t* Mesh::getIndices() {
+ return mIndices.data();
+}
+
size_t Mesh::getVertexCount() const {
return mVertexCount;
}
@@ -92,6 +122,14 @@
return mTexCoordsSize;
}
+size_t Mesh::getShadowColorSize() const {
+ return mShadowColorSize;
+}
+
+size_t Mesh::getShadowParamsSize() const {
+ return mShadowParamsSize;
+}
+
size_t Mesh::getByteStride() const {
return mStride * sizeof(float);
}
@@ -100,5 +138,9 @@
return mStride;
}
+size_t Mesh::getIndexCount() const {
+ return mIndexCount;
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 394d05a..0748dfb 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -46,6 +46,7 @@
#include "GLExtensions.h"
#include "GLFramebuffer.h"
#include "GLImage.h"
+#include "GLShadowVertexGenerator.h"
#include "Program.h"
#include "ProgramCache.h"
@@ -566,7 +567,10 @@
float alpha) {
size_t c;
Rect const* r = region.getArray(&c);
- Mesh mesh(Mesh::TRIANGLES, c * 6, 2);
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(c * 6 /* count */, 2 /* size */)
+ .build();
Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>());
for (size_t i = 0; i < c; i++, r++) {
position[i * 6 + 0].x = r->left;
@@ -981,7 +985,12 @@
fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0);
}
- Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLE_FAN)
+ .setVertices(4 /* count */, 2 /* size */)
+ .setTexCoords(2 /* size */)
+ .setCropCoords(2 /* size */)
+ .build();
for (auto layer : layers) {
mState.maxMasteringLuminance = layer.source.buffer.maxMasteringLuminance;
mState.maxContentLuminance = layer.source.buffer.maxContentLuminance;
@@ -1037,10 +1046,14 @@
}
setSourceDataSpace(layer.sourceDataspace);
+ if (layer.shadow.length > 0.0f) {
+ handleShadow(layer.geometry.boundaries, layer.geometry.roundedCornersRadius,
+ layer.shadow);
+ }
// We only want to do a special handling for rounded corners when having rounded corners
// is the only reason it needs to turn on blending, otherwise, we handle it like the
// usual way since it needs to turn on blending anyway.
- if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
+ else if (layer.geometry.roundedCornersRadius > 0.0 && color.a >= 1.0f && isOpaque) {
handleRoundedCorners(display, layer, mesh);
} else {
drawMesh(mesh);
@@ -1178,13 +1191,23 @@
mesh.getByteStride(), mesh.getCropCoords());
}
+ if (mState.drawShadows) {
+ glEnableVertexAttribArray(Program::shadowColor);
+ glVertexAttribPointer(Program::shadowColor, mesh.getShadowColorSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowColor());
+
+ glEnableVertexAttribArray(Program::shadowParams);
+ glVertexAttribPointer(Program::shadowParams, mesh.getShadowParamsSize(), GL_FLOAT, GL_FALSE,
+ mesh.getByteStride(), mesh.getShadowParams());
+ }
+
+ Description managedState = mState;
// By default, DISPLAY_P3 is the only supported wide color output. However,
// when HDR content is present, hardware composer may be able to handle
// BT2020 data space, in that case, the output data space is set to be
// BT2020_HLG or BT2020_PQ respectively. In GPU fall back we need
// to respect this and convert non-HDR content to HDR format.
if (mUseColorManagement) {
- Description managedState = mState;
Dataspace inputStandard = static_cast<Dataspace>(mDataSpace & Dataspace::STANDARD_MASK);
Dataspace inputTransfer = static_cast<Dataspace>(mDataSpace & Dataspace::TRANSFER_MASK);
Dataspace outputStandard =
@@ -1275,27 +1298,25 @@
managedState.outputTransferFunction =
Description::dataSpaceToTransferFunction(outputTransfer);
}
+ }
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
- : mEGLContext,
- managedState);
+ ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext : mEGLContext,
+ managedState);
- glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
-
- if (outputDebugPPMs) {
- static uint64_t managedColorFrameCount = 0;
- std::ostringstream out;
- out << "/data/texture_out" << managedColorFrameCount++;
- writePPM(out.str().c_str(), mVpWidth, mVpHeight);
- }
+ if (mState.drawShadows) {
+ glDrawElements(mesh.getPrimitive(), mesh.getIndexCount(), GL_UNSIGNED_SHORT,
+ mesh.getIndices());
} else {
- ProgramCache::getInstance().useProgram(mInProtectedContext ? mProtectedEGLContext
- : mEGLContext,
- mState);
-
glDrawArrays(mesh.getPrimitive(), 0, mesh.getVertexCount());
}
+ if (mUseColorManagement && outputDebugPPMs) {
+ static uint64_t managedColorFrameCount = 0;
+ std::ostringstream out;
+ out << "/data/texture_out" << managedColorFrameCount++;
+ writePPM(out.str().c_str(), mVpWidth, mVpHeight);
+ }
+
if (mesh.getTexCoordsSize()) {
glDisableVertexAttribArray(Program::texCoords);
}
@@ -1303,6 +1324,11 @@
if (mState.cornerRadius > 0.0f) {
glDisableVertexAttribArray(Program::cropCoords);
}
+
+ if (mState.drawShadows) {
+ glDisableVertexAttribArray(Program::shadowColor);
+ glDisableVertexAttribArray(Program::shadowParams);
+ }
}
size_t GLESRenderEngine::getMaxTextureSize() const {
@@ -1537,6 +1563,35 @@
}
}
+void GLESRenderEngine::handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& settings) {
+ ATRACE_CALL();
+ const float casterZ = settings.length / 2.0f;
+ const GLShadowVertexGenerator shadows(casterRect, casterCornerRadius, casterZ,
+ settings.casterIsTranslucent, settings.ambientColor,
+ settings.spotColor, settings.lightPos,
+ settings.lightRadius);
+
+ // setup mesh for both shadows
+ Mesh mesh = Mesh::Builder()
+ .setPrimitive(Mesh::TRIANGLES)
+ .setVertices(shadows.getVertexCount(), 2 /* size */)
+ .setShadowAttrs()
+ .setIndices(shadows.getIndexCount())
+ .build();
+
+ Mesh::VertexArray<vec2> position = mesh.getPositionArray<vec2>();
+ Mesh::VertexArray<vec4> shadowColor = mesh.getShadowColorArray<vec4>();
+ Mesh::VertexArray<vec3> shadowParams = mesh.getShadowParamsArray<vec3>();
+ shadows.fillVertices(position, shadowColor, shadowParams);
+ shadows.fillIndices(mesh.getIndicesArray());
+
+ mState.cornerRadius = 0.0f;
+ mState.drawShadows = true;
+ drawMesh(mesh);
+ mState.drawShadows = false;
+}
+
} // namespace gl
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h
index 397bc16..f41eda2 100644
--- a/libs/renderengine/gl/GLESRenderEngine.h
+++ b/libs/renderengine/gl/GLESRenderEngine.h
@@ -148,6 +148,8 @@
bool waitFence(base::unique_fd fenceFd);
void clearWithColor(float red, float green, float blue, float alpha);
void fillRegionWithColor(const Region& region, float red, float green, float blue, float alpha);
+ void handleShadow(const FloatRect& casterRect, float casterCornerRadius,
+ const ShadowSettings& shadowSettings);
void setupLayerBlending(bool premultipliedAlpha, bool opaque, bool disableTexture,
const half4& color, float cornerRadius);
void setupLayerTexturing(const Texture& texture);
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.cpp b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
new file mode 100644
index 0000000..3181f9b
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 <renderengine/Mesh.h>
+
+#include <math/vec4.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include "GLShadowVertexGenerator.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+GLShadowVertexGenerator::GLShadowVertexGenerator(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition,
+ float lightRadius) {
+ mDrawAmbientShadow = ambientColor.a > 0.f;
+ mDrawSpotShadow = spotColor.a > 0.f;
+
+ // Generate geometries and find number of vertices to generate
+ if (mDrawAmbientShadow) {
+ mAmbientShadowGeometry = getAmbientShadowGeometry(casterRect, casterCornerRadius, casterZ,
+ casterIsTranslucent, ambientColor);
+ mAmbientShadowVertexCount = getVertexCountForGeometry(*mAmbientShadowGeometry.get());
+ mAmbientShadowIndexCount = getIndexCountForGeometry(*mAmbientShadowGeometry.get());
+ } else {
+ mAmbientShadowVertexCount = 0;
+ mAmbientShadowIndexCount = 0;
+ }
+
+ if (mDrawSpotShadow) {
+ mSpotShadowGeometry =
+ getSpotShadowGeometry(casterRect, casterCornerRadius, casterZ, casterIsTranslucent,
+ spotColor, lightPosition, lightRadius);
+ mSpotShadowVertexCount = getVertexCountForGeometry(*mSpotShadowGeometry.get());
+ mSpotShadowIndexCount = getIndexCountForGeometry(*mSpotShadowGeometry.get());
+ } else {
+ mSpotShadowVertexCount = 0;
+ mSpotShadowIndexCount = 0;
+ }
+}
+
+size_t GLShadowVertexGenerator::getVertexCount() const {
+ return mAmbientShadowVertexCount + mSpotShadowVertexCount;
+}
+
+size_t GLShadowVertexGenerator::getIndexCount() const {
+ return mAmbientShadowIndexCount + mSpotShadowIndexCount;
+}
+
+void GLShadowVertexGenerator::fillVertices(Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const {
+ if (mDrawAmbientShadow) {
+ fillVerticesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowVertexCount, position,
+ color, params);
+ }
+ if (mDrawSpotShadow) {
+ fillVerticesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowVertexCount,
+ Mesh::VertexArray<vec2>(position, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec4>(color, mAmbientShadowVertexCount),
+ Mesh::VertexArray<vec3>(params, mAmbientShadowVertexCount));
+ }
+}
+
+void GLShadowVertexGenerator::fillIndices(uint16_t* indices) const {
+ if (mDrawAmbientShadow) {
+ fillIndicesForGeometry(*mAmbientShadowGeometry.get(), mAmbientShadowIndexCount,
+ 0 /* starting vertex offset */, indices);
+ }
+ if (mDrawSpotShadow) {
+ fillIndicesForGeometry(*mSpotShadowGeometry.get(), mSpotShadowIndexCount,
+ mAmbientShadowVertexCount /* starting vertex offset */,
+ &(indices[mAmbientShadowIndexCount]));
+ }
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLShadowVertexGenerator.h b/libs/renderengine/gl/GLShadowVertexGenerator.h
new file mode 100644
index 0000000..112f976
--- /dev/null
+++ b/libs/renderengine/gl/GLShadowVertexGenerator.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <math/vec4.h>
+#include <ui/Rect.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * Generates gl attributes required to draw shadow spot and/or ambient shadows.
+ *
+ * Each shadow can support different colors. This class generates three vertex attributes for
+ * each shadow, its position, color and shadow params(offset and distance). These can be sent
+ * using a single glDrawElements call.
+ */
+class GLShadowVertexGenerator {
+public:
+ GLShadowVertexGenerator(const FloatRect& casterRect, float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& ambientColor,
+ const vec4& spotColor, const vec3& lightPosition, float lightRadius);
+ ~GLShadowVertexGenerator() = default;
+
+ size_t getVertexCount() const;
+ size_t getIndexCount() const;
+ void fillVertices(Mesh::VertexArray<vec2>& position, Mesh::VertexArray<vec4>& color,
+ Mesh::VertexArray<vec3>& params) const;
+ void fillIndices(uint16_t* indices) const;
+
+private:
+ bool mDrawAmbientShadow;
+ std::unique_ptr<Geometry> mAmbientShadowGeometry;
+ int mAmbientShadowVertexCount = 0;
+ int mAmbientShadowIndexCount = 0;
+
+ bool mDrawSpotShadow;
+ std::unique_ptr<Geometry> mSpotShadowGeometry;
+ int mSpotShadowVertexCount = 0;
+ int mSpotShadowIndexCount = 0;
+};
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.cpp b/libs/renderengine/gl/GLSkiaShadowPort.cpp
new file mode 100644
index 0000000..224ce6c
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.cpp
@@ -0,0 +1,649 @@
+/*
+ * 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 <math/vec4.h>
+
+#include <renderengine/Mesh.h>
+
+#include <ui/Rect.h>
+#include <ui/Transform.h>
+
+#include <utils/Log.h>
+
+#include "GLSkiaShadowPort.h"
+
+namespace android {
+namespace renderengine {
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+static inline float divide_and_pin(float numer, float denom, float min, float max) {
+ if (denom == 0.0f) return min;
+ return std::clamp(numer / denom, min, max);
+}
+
+static constexpr auto SK_ScalarSqrt2 = 1.41421356f;
+static constexpr auto kAmbientHeightFactor = 1.0f / 128.0f;
+static constexpr auto kAmbientGeomFactor = 64.0f;
+// Assuming that we have a light height of 600 for the spot shadow,
+// the spot values will reach their maximum at a height of approximately 292.3077.
+// We'll round up to 300 to keep it simple.
+static constexpr auto kMaxAmbientRadius = 300 * kAmbientHeightFactor * kAmbientGeomFactor;
+
+inline float AmbientBlurRadius(float height) {
+ return std::min(height * kAmbientHeightFactor * kAmbientGeomFactor, kMaxAmbientRadius);
+}
+inline float AmbientRecipAlpha(float height) {
+ return 1.0f + std::max(height * kAmbientHeightFactor, 0.0f);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+// Circle Data
+//
+// We have two possible cases for geometry for a circle:
+
+// In the case of a normal fill, we draw geometry for the circle as an octagon.
+static const uint16_t gFillCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 8, 1, 2, 8,
+ 2, 3, 8, 3, 4, 8,
+ 4, 5, 8, 5, 6, 8,
+ 6, 7, 8, 7, 0, 8,
+ // clang-format on
+};
+
+// For stroked circles, we use two nested octagons.
+static const uint16_t gStrokeCircleIndices[] = {
+ // enter the octagon
+ // clang-format off
+ 0, 1, 9, 0, 9, 8,
+ 1, 2, 10, 1, 10, 9,
+ 2, 3, 11, 2, 11, 10,
+ 3, 4, 12, 3, 12, 11,
+ 4, 5, 13, 4, 13, 12,
+ 5, 6, 14, 5, 14, 13,
+ 6, 7, 15, 6, 15, 14,
+ 7, 0, 8, 7, 8, 15,
+ // clang-format on
+};
+
+#define SK_ARRAY_COUNT(a) (sizeof(a) / sizeof((a)[0]))
+static const int kIndicesPerFillCircle = SK_ARRAY_COUNT(gFillCircleIndices);
+static const int kIndicesPerStrokeCircle = SK_ARRAY_COUNT(gStrokeCircleIndices);
+static const int kVertsPerStrokeCircle = 16;
+static const int kVertsPerFillCircle = 9;
+
+static int circle_type_to_vert_count(bool stroked) {
+ return stroked ? kVertsPerStrokeCircle : kVertsPerFillCircle;
+}
+
+static int circle_type_to_index_count(bool stroked) {
+ return stroked ? kIndicesPerStrokeCircle : kIndicesPerFillCircle;
+}
+
+static const uint16_t* circle_type_to_indices(bool stroked) {
+ return stroked ? gStrokeCircleIndices : gFillCircleIndices;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// RoundRect Data
+//
+// The geometry for a shadow roundrect is similar to a 9-patch:
+// ____________
+// |_|________|_|
+// | | | |
+// | | | |
+// | | | |
+// |_|________|_|
+// |_|________|_|
+//
+// However, each corner is rendered as a fan rather than a simple quad, as below. (The diagram
+// shows the upper part of the upper left corner. The bottom triangle would similarly be split
+// into two triangles.)
+// ________
+// |\ \ |
+// | \ \ |
+// | \\ |
+// | \|
+// --------
+//
+// The center of the fan handles the curve of the corner. For roundrects where the stroke width
+// is greater than the corner radius, the outer triangles blend from the curve to the straight
+// sides. Otherwise these triangles will be degenerate.
+//
+// In the case where the stroke width is greater than the corner radius and the
+// blur radius (overstroke), we add additional geometry to mark out the rectangle in the center.
+// This rectangle extends the coverage values of the center edges of the 9-patch.
+// ____________
+// |_|________|_|
+// | |\ ____ /| |
+// | | | | | |
+// | | |____| | |
+// |_|/______\|_|
+// |_|________|_|
+//
+// For filled rrects we reuse the stroke geometry but add an additional quad to the center.
+
+static const uint16_t gRRectIndices[] = {
+ // clang-format off
+ // overstroke quads
+ // we place this at the beginning so that we can skip these indices when rendering as filled
+ 0, 6, 25, 0, 25, 24,
+ 6, 18, 27, 6, 27, 25,
+ 18, 12, 26, 18, 26, 27,
+ 12, 0, 24, 12, 24, 26,
+
+ // corners
+ 0, 1, 2, 0, 2, 3, 0, 3, 4, 0, 4, 5,
+ 6, 11, 10, 6, 10, 9, 6, 9, 8, 6, 8, 7,
+ 12, 17, 16, 12, 16, 15, 12, 15, 14, 12, 14, 13,
+ 18, 19, 20, 18, 20, 21, 18, 21, 22, 18, 22, 23,
+
+ // edges
+ 0, 5, 11, 0, 11, 6,
+ 6, 7, 19, 6, 19, 18,
+ 18, 23, 17, 18, 17, 12,
+ 12, 13, 1, 12, 1, 0,
+
+ // fill quad
+ // we place this at the end so that we can skip these indices when rendering as stroked
+ 0, 6, 18, 0, 18, 12,
+ // clang-format on
+};
+
+// overstroke count
+static const int kIndicesPerOverstrokeRRect = SK_ARRAY_COUNT(gRRectIndices) - 6;
+// simple stroke count skips overstroke indices
+static const int kIndicesPerStrokeRRect = kIndicesPerOverstrokeRRect - 6 * 4;
+// fill count adds final quad to stroke count
+static const int kIndicesPerFillRRect = kIndicesPerStrokeRRect + 6;
+static const int kVertsPerStrokeRRect = 24;
+static const int kVertsPerOverstrokeRRect = 28;
+static const int kVertsPerFillRRect = 24;
+
+static int rrect_type_to_vert_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kVertsPerFillRRect;
+ case kStroke_RRectType:
+ return kVertsPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kVertsPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static int rrect_type_to_index_count(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ return kIndicesPerFillRRect;
+ case kStroke_RRectType:
+ return kIndicesPerStrokeRRect;
+ case kOverstroke_RRectType:
+ return kIndicesPerOverstrokeRRect;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return -1;
+}
+
+static const uint16_t* rrect_type_to_indices(RRectType type) {
+ switch (type) {
+ case kFill_RRectType:
+ case kStroke_RRectType:
+ return gRRectIndices + 6 * 4;
+ case kOverstroke_RRectType:
+ return gRRectIndices;
+ }
+ ALOGE("Invalid rect type: %d", type);
+ return nullptr;
+}
+
+static void fillInCircleVerts(const Geometry& args, bool isStroked,
+ Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+ float innerRadius = args.fInnerRadius;
+ float blurRadius = args.fBlurRadius;
+ float distanceCorrection = outerRadius / blurRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ // The inner radius in the vertex data must be specified in normalized space.
+ innerRadius = innerRadius / outerRadius;
+
+ vec2 center = vec2(bounds.getWidth() / 2.0f, bounds.getHeight() / 2.0f);
+ float halfWidth = 0.5f * bounds.getWidth();
+ float octOffset = 0.41421356237f; // sqrt(2) - 1
+ int vertexCount = 0;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, -halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-octOffset * halfWidth, halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-octOffset, 1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, octOffset, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-halfWidth, -octOffset * halfWidth);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-1, -octOffset, distanceCorrection);
+ vertexCount++;
+
+ if (isStroked) {
+ // compute the inner ring
+
+ // cosine and sine of pi/8
+ float c = 0.923579533f;
+ float s = 0.382683432f;
+ float r = args.fInnerRadius;
+
+ position[vertexCount] = center + vec2(-s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, -c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, -c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-s * r, c * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-s * innerRadius, c * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, s * innerRadius, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = center + vec2(-c * r, -s * r);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(-c * innerRadius, -s * innerRadius, distanceCorrection);
+ vertexCount++;
+ } else {
+ // filled
+ position[vertexCount] = center;
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+static void fillInRRectVerts(const Geometry& args, Mesh::VertexArray<vec2>& position,
+ Mesh::VertexArray<vec4>& shadowColor,
+ Mesh::VertexArray<vec3>& shadowParams) {
+ vec4 color = args.fColor;
+ float outerRadius = args.fOuterRadius;
+
+ const FloatRect& bounds = args.fDevBounds;
+
+ float umbraInset = args.fUmbraInset;
+ float minDim = 0.5f * std::min(bounds.getWidth(), bounds.getHeight());
+ if (umbraInset > minDim) {
+ umbraInset = minDim;
+ }
+
+ float xInner[4] = {bounds.left + umbraInset, bounds.right - umbraInset,
+ bounds.left + umbraInset, bounds.right - umbraInset};
+ float xMid[4] = {bounds.left + outerRadius, bounds.right - outerRadius,
+ bounds.left + outerRadius, bounds.right - outerRadius};
+ float xOuter[4] = {bounds.left, bounds.right, bounds.left, bounds.right};
+ float yInner[4] = {bounds.top + umbraInset, bounds.top + umbraInset, bounds.bottom - umbraInset,
+ bounds.bottom - umbraInset};
+ float yMid[4] = {bounds.top + outerRadius, bounds.top + outerRadius,
+ bounds.bottom - outerRadius, bounds.bottom - outerRadius};
+ float yOuter[4] = {bounds.top, bounds.top, bounds.bottom, bounds.bottom};
+
+ float blurRadius = args.fBlurRadius;
+
+ // In the case where we have to inset more for the umbra, our two triangles in the
+ // corner get skewed to a diamond rather than a square. To correct for that,
+ // we also skew the vectors we send to the shader that help define the circle.
+ // By doing so, we end up with a quarter circle in the corner rather than the
+ // elliptical curve.
+
+ // This is a bit magical, but it gives us the correct results at extrema:
+ // a) umbraInset == outerRadius produces an orthogonal vector
+ // b) outerRadius == 0 produces a diagonal vector
+ // And visually the corner looks correct.
+ vec2 outerVec = vec2(outerRadius - umbraInset, -outerRadius - umbraInset);
+ outerVec = normalize(outerVec);
+ // We want the circle edge to fall fractionally along the diagonal at
+ // (sqrt(2)*(umbraInset - outerRadius) + outerRadius)/sqrt(2)*umbraInset
+ //
+ // Setting the components of the diagonal offset to the following value will give us that.
+ float diagVal = umbraInset / (SK_ScalarSqrt2 * (outerRadius - umbraInset) - outerRadius);
+ vec2 diagVec = vec2(diagVal, diagVal);
+ float distanceCorrection = umbraInset / blurRadius;
+
+ int vertexCount = 0;
+ // build corner by corner
+ for (int i = 0; i < 4; ++i) {
+ // inner point
+ position[vertexCount] = vec2(xInner[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // outer points
+ position[vertexCount] = vec2(xOuter[i], yInner[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yMid[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xOuter[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(diagVec.x, diagVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xMid[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(outerVec.x, outerVec.y, distanceCorrection);
+ vertexCount++;
+
+ position[vertexCount] = vec2(xInner[i], yOuter[i]);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, -1, distanceCorrection);
+ vertexCount++;
+ }
+
+ // Add the additional vertices for overstroked rrects.
+ // Effectively this is an additional stroked rrect, with its
+ // parameters equal to those in the center of the 9-patch. This will
+ // give constant values across this inner ring.
+ if (kOverstroke_RRectType == args.fType) {
+ float inset = umbraInset + args.fInnerRadius;
+
+ // TL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // TR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.top + inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BL
+ position[vertexCount] = vec2(bounds.left + inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+
+ // BR
+ position[vertexCount] = vec2(bounds.right - inset, bounds.bottom - inset);
+ shadowColor[vertexCount] = color;
+ shadowParams[vertexCount] = vec3(0, 0, distanceCorrection);
+ vertexCount++;
+ }
+}
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_vert_count(shadowGeometry.fType);
+ }
+
+ return rrect_type_to_vert_count(shadowGeometry.fType);
+}
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry) {
+ if (shadowGeometry.fIsCircle) {
+ return circle_type_to_index_count(kStroke_RRectType == shadowGeometry.fType);
+ }
+
+ return rrect_type_to_index_count(shadowGeometry.fType);
+}
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int /* vertexCount */,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams) {
+ if (shadowGeometry.fIsCircle) {
+ fillInCircleVerts(shadowGeometry, shadowGeometry.fIsStroked, position, shadowColor,
+ shadowParams);
+ } else {
+ fillInRRectVerts(shadowGeometry, position, shadowColor, shadowParams);
+ }
+}
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices) {
+ if (shadowGeometry.fIsCircle) {
+ const uint16_t* primIndices = circle_type_to_indices(shadowGeometry.fIsStroked);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ } else {
+ const uint16_t* primIndices = rrect_type_to_indices(shadowGeometry.fType);
+ for (int i = 0; i < indexCount; ++i) {
+ indices[i] = primIndices[i] + startingVertexOffset;
+ }
+ }
+}
+
+inline void GetSpotParams(float occluderZ, float lightX, float lightY, float lightZ,
+ float lightRadius, float& blurRadius, float& scale, vec2& translate) {
+ float zRatio = divide_and_pin(occluderZ, lightZ - occluderZ, 0.0f, 0.95f);
+ blurRadius = lightRadius * zRatio;
+ scale = divide_and_pin(lightZ, lightZ - occluderZ, 1.0f, 1.95f);
+ translate.x = -zRatio * lightX;
+ translate.y = -zRatio * lightY;
+}
+
+static std::unique_ptr<Geometry> getShadowGeometry(const vec4& color, const FloatRect& devRect,
+ float devRadius, float blurRadius,
+ float insetWidth) {
+ // An insetWidth > 1/2 rect width or height indicates a simple fill.
+ const bool isCircle = ((devRadius >= devRect.getWidth()) && (devRadius >= devRect.getHeight()));
+
+ FloatRect bounds = devRect;
+ float innerRadius = 0.0f;
+ float outerRadius = devRadius;
+ float umbraInset;
+
+ RRectType type = kFill_RRectType;
+ if (isCircle) {
+ umbraInset = 0;
+ } else {
+ umbraInset = std::max(outerRadius, blurRadius);
+ }
+
+ // If stroke is greater than width or height, this is still a fill,
+ // otherwise we compute stroke params.
+ if (isCircle) {
+ innerRadius = devRadius - insetWidth;
+ type = innerRadius > 0 ? kStroke_RRectType : kFill_RRectType;
+ } else {
+ if (insetWidth <= 0.5f * std::min(devRect.getWidth(), devRect.getHeight())) {
+ // We don't worry about a real inner radius, we just need to know if we
+ // need to create overstroke vertices.
+ innerRadius = std::max(insetWidth - umbraInset, 0.0f);
+ type = innerRadius > 0 ? kOverstroke_RRectType : kStroke_RRectType;
+ }
+ }
+ const bool isStroked = (kStroke_RRectType == type);
+ return std::make_unique<Geometry>(Geometry{color, outerRadius, umbraInset, innerRadius,
+ blurRadius, bounds, type, isCircle, isStroked});
+}
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor) {
+ float devSpaceInsetWidth = AmbientBlurRadius(casterZ);
+ const float umbraRecipAlpha = AmbientRecipAlpha(casterZ);
+ const float devSpaceAmbientBlur = devSpaceInsetWidth * umbraRecipAlpha;
+
+ // Outset the shadow rrect to the border of the penumbra
+ float ambientPathOutset = devSpaceInsetWidth;
+ FloatRect outsetRect(casterRect);
+ outsetRect.left -= ambientPathOutset;
+ outsetRect.top -= ambientPathOutset;
+ outsetRect.right += ambientPathOutset;
+ outsetRect.bottom += ambientPathOutset;
+
+ float outsetRad = casterCornerRadius + ambientPathOutset;
+ if (casterIsTranslucent) {
+ // set a large inset to force a fill
+ devSpaceInsetWidth = outsetRect.getWidth();
+ }
+
+ return getShadowGeometry(ambientColor, outsetRect, std::abs(outsetRad), devSpaceAmbientBlur,
+ std::abs(devSpaceInsetWidth));
+}
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius) {
+ float devSpaceSpotBlur;
+ float spotScale;
+ vec2 spotOffset;
+ GetSpotParams(casterZ, lightPosition.x, lightPosition.y, lightPosition.z, lightRadius,
+ devSpaceSpotBlur, spotScale, spotOffset);
+ // handle scale of radius due to CTM
+ const float srcSpaceSpotBlur = devSpaceSpotBlur;
+
+ // Adjust translate for the effect of the scale.
+ spotOffset.x += spotScale;
+ spotOffset.y += spotScale;
+
+ // Compute the transformed shadow rect
+ ui::Transform shadowTransform;
+ shadowTransform.set(spotOffset.x, spotOffset.y);
+ shadowTransform.set(spotScale, 0, 0, spotScale);
+ FloatRect spotShadowRect = shadowTransform.transform(casterRect);
+ float spotShadowRadius = casterCornerRadius * spotScale;
+
+ // Compute the insetWidth
+ float blurOutset = srcSpaceSpotBlur;
+ float insetWidth = blurOutset;
+ if (casterIsTranslucent) {
+ // If transparent, just do a fill
+ insetWidth += spotShadowRect.getWidth();
+ } else {
+ // For shadows, instead of using a stroke we specify an inset from the penumbra
+ // border. We want to extend this inset area so that it meets up with the caster
+ // geometry. The inset geometry will by default already be inset by the blur width.
+ //
+ // We compare the min and max corners inset by the radius between the original
+ // rrect and the shadow rrect. The distance between the two plus the difference
+ // between the scaled radius and the original radius gives the distance from the
+ // transformed shadow shape to the original shape in that corner. The max
+ // of these gives the maximum distance we need to cover.
+ //
+ // Since we are outsetting by 1/2 the blur distance, we just add the maxOffset to
+ // that to get the full insetWidth.
+ float maxOffset;
+ if (casterCornerRadius <= 0.f) {
+ // Manhattan distance works better for rects
+ maxOffset = std::max(std::max(std::abs(spotShadowRect.left - casterRect.left),
+ std::abs(spotShadowRect.top - casterRect.top)),
+ std::max(std::abs(spotShadowRect.right - casterRect.right),
+ std::abs(spotShadowRect.bottom - casterRect.bottom)));
+ } else {
+ float dr = spotShadowRadius - casterCornerRadius;
+ vec2 upperLeftOffset = vec2(spotShadowRect.left - casterRect.left + dr,
+ spotShadowRect.top - casterRect.top + dr);
+ vec2 lowerRightOffset = vec2(spotShadowRect.right - casterRect.right - dr,
+ spotShadowRect.bottom - casterRect.bottom - dr);
+ maxOffset = sqrt(std::max(dot(upperLeftOffset, lowerRightOffset),
+ dot(lowerRightOffset, lowerRightOffset))) +
+ dr;
+ }
+ insetWidth += std::max(blurOutset, maxOffset);
+ }
+
+ // Outset the shadow rrect to the border of the penumbra
+ spotShadowRadius += blurOutset;
+ spotShadowRect.left -= blurOutset;
+ spotShadowRect.top -= blurOutset;
+ spotShadowRect.right += blurOutset;
+ spotShadowRect.bottom += blurOutset;
+
+ return getShadowGeometry(spotColor, spotShadowRect, std::abs(spotShadowRadius),
+ 2.0f * devSpaceSpotBlur, std::abs(insetWidth));
+}
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/GLSkiaShadowPort.h b/libs/renderengine/gl/GLSkiaShadowPort.h
new file mode 100644
index 0000000..e7d1861
--- /dev/null
+++ b/libs/renderengine/gl/GLSkiaShadowPort.h
@@ -0,0 +1,84 @@
+/*
+ * 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 <math/vec4.h>
+#include <ui/Rect.h>
+
+namespace android {
+namespace renderengine {
+
+class Mesh;
+
+namespace gl {
+
+/**
+ * The shadow geometry logic and vertex generation code has been ported from skia shadow
+ * fast path OpenGL implementation to draw shadows around rects and rounded rects including
+ * circles.
+ *
+ * path: skia/src/gpu/GrRenderTargetContext.cpp GrRenderTargetContext::drawFastShadow
+ *
+ * Modifications made:
+ * - Switched to using std lib math functions
+ * - Fall off function is implemented in vertex shader rather than a shadow texture
+ * - Removed transformations applied on the caster rect since the caster will be in local
+ * coordinate space and will be transformed by the vertex shader.
+ */
+
+enum RRectType {
+ kFill_RRectType,
+ kStroke_RRectType,
+ kOverstroke_RRectType,
+};
+
+struct Geometry {
+ vec4 fColor;
+ float fOuterRadius;
+ float fUmbraInset;
+ float fInnerRadius;
+ float fBlurRadius;
+ FloatRect fDevBounds;
+ RRectType fType;
+ bool fIsCircle;
+ bool fIsStroked;
+};
+
+std::unique_ptr<Geometry> getSpotShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent, const vec4& spotColor,
+ const vec3& lightPosition, float lightRadius);
+
+std::unique_ptr<Geometry> getAmbientShadowGeometry(const FloatRect& casterRect,
+ float casterCornerRadius, float casterZ,
+ bool casterIsTranslucent,
+ const vec4& ambientColor);
+
+int getVertexCountForGeometry(const Geometry& shadowGeometry);
+
+int getIndexCountForGeometry(const Geometry& shadowGeometry);
+
+void fillVerticesForGeometry(const Geometry& shadowGeometry, int vertexCount,
+ Mesh::VertexArray<vec2> position, Mesh::VertexArray<vec4> shadowColor,
+ Mesh::VertexArray<vec3> shadowParams);
+
+void fillIndicesForGeometry(const Geometry& shadowGeometry, int indexCount,
+ int startingVertexOffset, uint16_t* indices);
+
+} // namespace gl
+} // namespace renderengine
+} // namespace android
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index 4eb5eb6..f4fbf35 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -37,6 +37,8 @@
glBindAttribLocation(programId, position, "position");
glBindAttribLocation(programId, texCoords, "texCoords");
glBindAttribLocation(programId, cropCoords, "cropCoords");
+ glBindAttribLocation(programId, shadowColor, "shadowColor");
+ glBindAttribLocation(programId, shadowParams, "shadowParams");
glLinkProgram(programId);
GLint status;
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index c9beb68..fc3755e 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -44,7 +44,13 @@
texCoords = 1,
/* Crop coordinates, in pixels */
- cropCoords = 2
+ cropCoords = 2,
+
+ /* Shadow color */
+ shadowColor = 3,
+
+ /* Shadow params */
+ shadowParams = 4,
};
Program(const ProgramCache::Key& needs, const char* vertex, const char* fragment);
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index e2757e1..ba0e4ad 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -174,16 +174,15 @@
.set(Key::OPACITY_MASK,
description.isOpaque ? Key::OPACITY_OPAQUE : Key::OPACITY_TRANSLUCENT)
.set(Key::Key::INPUT_TRANSFORM_MATRIX_MASK,
- description.hasInputTransformMatrix()
- ? Key::INPUT_TRANSFORM_MATRIX_ON : Key::INPUT_TRANSFORM_MATRIX_OFF)
+ description.hasInputTransformMatrix() ? Key::INPUT_TRANSFORM_MATRIX_ON
+ : Key::INPUT_TRANSFORM_MATRIX_OFF)
.set(Key::Key::OUTPUT_TRANSFORM_MATRIX_MASK,
description.hasOutputTransformMatrix() || description.hasColorMatrix()
? Key::OUTPUT_TRANSFORM_MATRIX_ON
: Key::OUTPUT_TRANSFORM_MATRIX_OFF)
.set(Key::ROUNDED_CORNERS_MASK,
- description.cornerRadius > 0
- ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF);
-
+ description.cornerRadius > 0 ? Key::ROUNDED_CORNERS_ON : Key::ROUNDED_CORNERS_OFF)
+ .set(Key::SHADOW_MASK, description.drawShadows ? Key::SHADOW_ON : Key::SHADOW_OFF);
needs.set(Key::Y410_BT2020_MASK,
description.isY410BT2020 ? Key::Y410_BT2020_ON : Key::Y410_BT2020_OFF);
@@ -559,6 +558,12 @@
vs << "attribute lowp vec4 cropCoords;";
vs << "varying lowp vec2 outCropCoords;";
}
+ if (needs.drawShadows()) {
+ vs << "attribute vec4 shadowColor;";
+ vs << "varying vec4 outShadowColor;";
+ vs << "attribute vec4 shadowParams;";
+ vs << "varying vec3 outShadowParams;";
+ }
vs << "attribute vec4 position;"
<< "uniform mat4 projection;"
<< "uniform mat4 texture;"
@@ -569,6 +574,10 @@
if (needs.hasRoundedCorners()) {
vs << "outCropCoords = cropCoords.st;";
}
+ if (needs.drawShadows()) {
+ vs << "outShadowColor = shadowColor;";
+ vs << "outShadowParams = shadowParams.xyz;";
+ }
vs << dedent << "}";
return vs.getString();
}
@@ -614,6 +623,26 @@
)__SHADER__";
}
+ if (needs.drawShadows()) {
+ fs << R"__SHADER__(
+ varying vec4 outShadowColor;
+ varying vec3 outShadowParams;
+
+ /**
+ * Returns the shadow color.
+ */
+ vec4 getShadowColor()
+ {
+ // exponential falloff function provided by UX
+ float d = length(outShadowParams.xy);
+ float distance = outShadowParams.z * (1.0 - d);
+ float factor = 1.0 - clamp(distance, 0.0, 1.0);
+ factor = exp(-factor * factor * 4.0) - 0.018;
+ return outShadowColor * factor;
+ }
+ )__SHADER__";
+ }
+
if (needs.getTextureTarget() == Key::TEXTURE_OFF || needs.hasAlpha()) {
fs << "uniform vec4 color;";
}
@@ -677,25 +706,29 @@
}
fs << "void main(void) {" << indent;
- if (needs.isTexturing()) {
- fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
- if (needs.isY410BT2020()) {
- fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
- }
+ if (needs.drawShadows()) {
+ fs << "gl_FragColor = getShadowColor();";
} else {
- fs << "gl_FragColor.rgb = color.rgb;";
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.isOpaque()) {
- fs << "gl_FragColor.a = 1.0;";
- }
- if (needs.hasAlpha()) {
- // modulate the current alpha value with alpha set
- if (needs.isPremultiplied()) {
- // ... and the color too if we're premultiplied
- fs << "gl_FragColor *= color.a;";
+ if (needs.isTexturing()) {
+ fs << "gl_FragColor = texture2D(sampler, outTexCoords);";
+ if (needs.isY410BT2020()) {
+ fs << "gl_FragColor.rgb = convertY410BT2020(gl_FragColor.rgb);";
+ }
} else {
- fs << "gl_FragColor.a *= color.a;";
+ fs << "gl_FragColor.rgb = color.rgb;";
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.isOpaque()) {
+ fs << "gl_FragColor.a = 1.0;";
+ }
+ if (needs.hasAlpha()) {
+ // modulate the current alpha value with alpha set
+ if (needs.isPremultiplied()) {
+ // ... and the color too if we're premultiplied
+ fs << "gl_FragColor *= color.a;";
+ } else {
+ fs << "gl_FragColor.a *= color.a;";
+ }
}
}
diff --git a/libs/renderengine/gl/ProgramCache.h b/libs/renderengine/gl/ProgramCache.h
index 175c6e8..c8b6da7 100644
--- a/libs/renderengine/gl/ProgramCache.h
+++ b/libs/renderengine/gl/ProgramCache.h
@@ -112,6 +112,11 @@
Y410_BT2020_MASK = 1 << Y410_BT2020_SHIFT,
Y410_BT2020_OFF = 0 << Y410_BT2020_SHIFT,
Y410_BT2020_ON = 1 << Y410_BT2020_SHIFT,
+
+ SHADOW_SHIFT = 13,
+ SHADOW_MASK = 1 << SHADOW_SHIFT,
+ SHADOW_OFF = 0 << SHADOW_SHIFT,
+ SHADOW_ON = 1 << SHADOW_SHIFT,
};
inline Key() : mKey(0) {}
@@ -130,6 +135,7 @@
inline bool hasRoundedCorners() const {
return (mKey & ROUNDED_CORNERS_MASK) == ROUNDED_CORNERS_ON;
}
+ inline bool drawShadows() const { return (mKey & SHADOW_MASK) == SHADOW_ON; }
inline bool hasInputTransformMatrix() const {
return (mKey & INPUT_TRANSFORM_MATRIX_MASK) == INPUT_TRANSFORM_MATRIX_ON;
}
diff --git a/libs/renderengine/include/renderengine/DisplaySettings.h b/libs/renderengine/include/renderengine/DisplaySettings.h
index 9c9884a..c4a29a9 100644
--- a/libs/renderengine/include/renderengine/DisplaySettings.h
+++ b/libs/renderengine/include/renderengine/DisplaySettings.h
@@ -16,6 +16,8 @@
#pragma once
+#include <iosfwd>
+
#include <math/mat4.h>
#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
@@ -62,5 +64,31 @@
uint32_t orientation = ui::Transform::ROT_0;
};
+static inline bool operator==(const DisplaySettings& lhs, const DisplaySettings& rhs) {
+ return lhs.physicalDisplay == rhs.physicalDisplay && lhs.clip == rhs.clip &&
+ lhs.globalTransform == rhs.globalTransform && lhs.maxLuminance == rhs.maxLuminance &&
+ lhs.outputDataspace == rhs.outputDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.clearRegion.hasSameRects(rhs.clearRegion) && lhs.orientation == rhs.orientation;
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const DisplaySettings& settings, ::std::ostream* os) {
+ *os << "DisplaySettings {";
+ *os << "\n .physicalDisplay = ";
+ PrintTo(settings.physicalDisplay, os);
+ *os << "\n .clip = ";
+ PrintTo(settings.clip, os);
+ *os << "\n .globalTransform = " << settings.globalTransform;
+ *os << "\n .maxLuminance = " << settings.maxLuminance;
+ *os << "\n .outputDataspace = ";
+ PrintTo(settings.outputDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .clearRegion = ";
+ PrintTo(settings.clearRegion, os);
+ *os << "\n .orientation = " << settings.orientation;
+ *os << "\n}";
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index d890ccd..5aa3f3b 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -16,6 +16,8 @@
#pragma once
+#include <iosfwd>
+
#include <math/mat4.h>
#include <math/vec3.h>
#include <renderengine/Texture.h>
@@ -149,5 +151,102 @@
ShadowSettings shadow;
};
+static inline bool operator==(const Buffer& lhs, const Buffer& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.fence == rhs.fence &&
+ lhs.textureName == rhs.textureName &&
+ lhs.useTextureFiltering == rhs.useTextureFiltering &&
+ lhs.textureTransform == rhs.textureTransform &&
+ lhs.usePremultipliedAlpha == rhs.usePremultipliedAlpha &&
+ lhs.isOpaque == rhs.isOpaque && lhs.isY410BT2020 == rhs.isY410BT2020 &&
+ lhs.maxMasteringLuminance == rhs.maxMasteringLuminance &&
+ lhs.maxContentLuminance == rhs.maxContentLuminance;
+}
+
+static inline bool operator==(const Geometry& lhs, const Geometry& rhs) {
+ return lhs.boundaries == rhs.boundaries && lhs.positionTransform == rhs.positionTransform &&
+ lhs.roundedCornersRadius == rhs.roundedCornersRadius &&
+ lhs.roundedCornersCrop == rhs.roundedCornersCrop;
+}
+
+static inline bool operator==(const PixelSource& lhs, const PixelSource& rhs) {
+ return lhs.buffer == rhs.buffer && lhs.solidColor == rhs.solidColor;
+}
+
+static inline bool operator==(const ShadowSettings& lhs, const ShadowSettings& rhs) {
+ return lhs.ambientColor == rhs.ambientColor && lhs.spotColor == rhs.spotColor &&
+ lhs.lightPos == rhs.lightPos && lhs.lightRadius == rhs.lightRadius &&
+ lhs.length == rhs.length && lhs.casterIsTranslucent == rhs.casterIsTranslucent;
+}
+
+static inline bool operator==(const LayerSettings& lhs, const LayerSettings& rhs) {
+ return lhs.geometry == rhs.geometry && lhs.source == rhs.source && lhs.alpha == rhs.alpha &&
+ lhs.sourceDataspace == rhs.sourceDataspace &&
+ lhs.colorTransform == rhs.colorTransform &&
+ lhs.disableBlending == rhs.disableBlending && lhs.shadow == rhs.shadow;
+}
+
+// Defining PrintTo helps with Google Tests.
+
+static inline void PrintTo(const Buffer& settings, ::std::ostream* os) {
+ *os << "Buffer {";
+ *os << "\n .buffer = " << settings.buffer.get();
+ *os << "\n .fence = " << settings.fence.get();
+ *os << "\n .textureName = " << settings.textureName;
+ *os << "\n .useTextureFiltering = " << settings.useTextureFiltering;
+ *os << "\n .textureTransform = " << settings.textureTransform;
+ *os << "\n .usePremultipliedAlpha = " << settings.usePremultipliedAlpha;
+ *os << "\n .isOpaque = " << settings.isOpaque;
+ *os << "\n .isY410BT2020 = " << settings.isY410BT2020;
+ *os << "\n .maxMasteringLuminance = " << settings.maxMasteringLuminance;
+ *os << "\n .maxContentLuminance = " << settings.maxContentLuminance;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const Geometry& settings, ::std::ostream* os) {
+ *os << "Geometry {";
+ *os << "\n .boundaries = ";
+ PrintTo(settings.boundaries, os);
+ *os << "\n .positionTransform = " << settings.positionTransform;
+ *os << "\n .roundedCornersRadius = " << settings.roundedCornersRadius;
+ *os << "\n .roundedCornersCrop = ";
+ PrintTo(settings.roundedCornersCrop, os);
+ *os << "\n}";
+}
+
+static inline void PrintTo(const PixelSource& settings, ::std::ostream* os) {
+ *os << "PixelSource {";
+ *os << "\n .buffer = ";
+ PrintTo(settings.buffer, os);
+ *os << "\n .solidColor = " << settings.solidColor;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const ShadowSettings& settings, ::std::ostream* os) {
+ *os << "ShadowSettings {";
+ *os << "\n .ambientColor = " << settings.ambientColor;
+ *os << "\n .spotColor = " << settings.spotColor;
+ *os << "\n .lightPos = " << settings.lightPos;
+ *os << "\n .lightRadius = " << settings.lightRadius;
+ *os << "\n .length = " << settings.length;
+ *os << "\n .casterIsTranslucent = " << settings.casterIsTranslucent;
+ *os << "\n}";
+}
+
+static inline void PrintTo(const LayerSettings& settings, ::std::ostream* os) {
+ *os << "LayerSettings {";
+ *os << "\n .geometry = ";
+ PrintTo(settings.geometry, os);
+ *os << "\n .source = ";
+ PrintTo(settings.source, os);
+ *os << "\n .alpha = " << settings.alpha;
+ *os << "\n .sourceDataspace = ";
+ PrintTo(settings.sourceDataspace, os);
+ *os << "\n .colorTransform = " << settings.colorTransform;
+ *os << "\n .disableBlending = " << settings.disableBlending;
+ *os << "\n .shadow = ";
+ PrintTo(settings.shadow, os);
+ *os << "\n}";
+}
+
} // namespace renderengine
} // namespace android
diff --git a/libs/renderengine/include/renderengine/Mesh.h b/libs/renderengine/include/renderengine/Mesh.h
index 7618424..167f13f 100644
--- a/libs/renderengine/include/renderengine/Mesh.h
+++ b/libs/renderengine/include/renderengine/Mesh.h
@@ -26,13 +26,14 @@
class Mesh {
public:
+ class Builder;
+
enum Primitive {
TRIANGLES = 0x0004, // GL_TRIANGLES
TRIANGLE_STRIP = 0x0005, // GL_TRIANGLE_STRIP
TRIANGLE_FAN = 0x0006 // GL_TRIANGLE_FAN
};
- Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordsSize = 0);
~Mesh() = default;
/*
@@ -43,12 +44,20 @@
friend class Mesh;
float* mData;
size_t mStride;
+ size_t mOffset = 0;
VertexArray(float* data, size_t stride) : mData(data), mStride(stride) {}
public:
- TYPE& operator[](size_t index) { return *reinterpret_cast<TYPE*>(&mData[index * mStride]); }
+ // Returns a vertex array at an offset so its easier to append attributes from
+ // multiple sources.
+ VertexArray(VertexArray<TYPE>& other, size_t offset)
+ : mData(other.mData), mStride(other.mStride), mOffset(offset) {}
+
+ TYPE& operator[](size_t index) {
+ return *reinterpret_cast<TYPE*>(&mData[(index + mOffset) * mStride]);
+ }
TYPE const& operator[](size_t index) const {
- return *reinterpret_cast<TYPE const*>(&mData[index * mStride]);
+ return *reinterpret_cast<TYPE const*>(&mData[(index + mOffset) * mStride]);
}
};
@@ -67,6 +76,18 @@
return VertexArray<TYPE>(getCropCoords(), mStride);
}
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowColorArray() {
+ return VertexArray<TYPE>(getShadowColor(), mStride);
+ }
+
+ template <typename TYPE>
+ VertexArray<TYPE> getShadowParamsArray() {
+ return VertexArray<TYPE>(getShadowParams(), mStride);
+ }
+
+ uint16_t* getIndicesArray() { return getIndices(); }
+
Primitive getPrimitive() const;
// returns a pointer to the vertices positions
@@ -78,6 +99,15 @@
// returns a pointer to the vertices crop coordinates
float const* getCropCoords() const;
+ // returns a pointer to colors
+ float const* getShadowColor() const;
+
+ // returns a pointer to the shadow params
+ float const* getShadowParams() const;
+
+ // returns a pointer to indices
+ uint16_t const* getIndices() const;
+
// number of vertices in this mesh
size_t getVertexCount() const;
@@ -87,6 +117,12 @@
// dimension of texture coordinates
size_t getTexCoordsSize() const;
+ size_t getShadowParamsSize() const;
+
+ size_t getShadowColorSize() const;
+
+ size_t getIndexCount() const;
+
// return stride in bytes
size_t getByteStride() const;
@@ -94,6 +130,8 @@
size_t getStride() const;
private:
+ Mesh(Primitive primitive, size_t vertexCount, size_t vertexSize, size_t texCoordSize,
+ size_t cropCoordsSize, size_t shadowColorSize, size_t shadowParamsSize, size_t indexCount);
Mesh(const Mesh&);
Mesh& operator=(const Mesh&);
Mesh const& operator=(const Mesh&) const;
@@ -101,13 +139,65 @@
float* getPositions();
float* getTexCoords();
float* getCropCoords();
+ float* getShadowColor();
+ float* getShadowParams();
+ uint16_t* getIndices();
std::vector<float> mVertices;
size_t mVertexCount;
size_t mVertexSize;
size_t mTexCoordsSize;
+ size_t mCropCoordsSize;
+ size_t mShadowColorSize;
+ size_t mShadowParamsSize;
size_t mStride;
Primitive mPrimitive;
+ std::vector<uint16_t> mIndices;
+ size_t mIndexCount;
+};
+
+class Mesh::Builder {
+public:
+ Builder& setPrimitive(Primitive primitive) {
+ mPrimitive = primitive;
+ return *this;
+ };
+ Builder& setVertices(size_t vertexCount, size_t vertexSize) {
+ mVertexCount = vertexCount;
+ mVertexSize = vertexSize;
+ return *this;
+ };
+ Builder& setTexCoords(size_t texCoordsSize) {
+ mTexCoordsSize = texCoordsSize;
+ return *this;
+ };
+ Builder& setCropCoords(size_t cropCoordsSize) {
+ mCropCoordsSize = cropCoordsSize;
+ return *this;
+ };
+ Builder& setShadowAttrs() {
+ mShadowParamsSize = 3;
+ mShadowColorSize = 4;
+ return *this;
+ };
+ Builder& setIndices(size_t indexCount) {
+ mIndexCount = indexCount;
+ return *this;
+ };
+ Mesh build() const {
+ return Mesh{mPrimitive, mVertexCount, mVertexSize, mTexCoordsSize,
+ mCropCoordsSize, mShadowColorSize, mShadowParamsSize, mIndexCount};
+ }
+
+private:
+ size_t mVertexCount = 0;
+ size_t mVertexSize = 0;
+ size_t mTexCoordsSize = 0;
+ size_t mCropCoordsSize = 0;
+ size_t mShadowColorSize = 0;
+ size_t mShadowParamsSize = 0;
+ size_t mIndexCount = 0;
+ Primitive mPrimitive;
};
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index bad64c2..a62161a 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -81,6 +81,9 @@
mat4 colorMatrix;
mat4 inputTransformMatrix;
mat4 outputTransformMatrix;
+
+ // True if this layer will draw a shadow.
+ bool drawShadows = false;
};
} // namespace renderengine
diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp
index d01c740..ba5a3f5 100644
--- a/libs/renderengine/tests/RenderEngineTest.cpp
+++ b/libs/renderengine/tests/RenderEngineTest.cpp
@@ -16,6 +16,7 @@
#include <chrono>
#include <condition_variable>
+#include <fstream>
#include <gtest/gtest.h>
#include <renderengine/RenderEngine.h>
@@ -26,6 +27,7 @@
constexpr int DEFAULT_DISPLAY_WIDTH = 128;
constexpr int DEFAULT_DISPLAY_HEIGHT = 256;
constexpr int DEFAULT_DISPLAY_OFFSET = 64;
+constexpr bool WRITE_BUFFER_TO_FILE_ON_FAILURE = false;
namespace android {
@@ -68,21 +70,80 @@
RenderEngineTest() { mBuffer = allocateDefaultBuffer(); }
~RenderEngineTest() {
+ if (WRITE_BUFFER_TO_FILE_ON_FAILURE && ::testing::Test::HasFailure()) {
+ writeBufferToFile("/data/texture_out_");
+ }
for (uint32_t texName : mTexNames) {
sRE->deleteTextures(1, &texName);
}
}
- void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
- uint8_t tolerance = 0) {
+ void writeBufferToFile(const char* basename) {
+ std::string filename(basename);
+ filename.append(::testing::UnitTest::GetInstance()->current_test_info()->name());
+ filename.append(".ppm");
+ std::ofstream file(filename.c_str(), std::ios::binary);
+ if (!file.is_open()) {
+ ALOGE("Unable to open file: %s", filename.c_str());
+ ALOGE("You may need to do: \"adb shell setenforce 0\" to enable "
+ "surfaceflinger to write debug images");
+ return;
+ }
+
uint8_t* pixels;
mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
reinterpret_cast<void**>(&pixels));
- auto colorCompare = [tolerance](uint8_t a, uint8_t b) {
- uint8_t tmp = a >= b ? a - b : b - a;
- return tmp <= tolerance;
+ file << "P6\n";
+ file << mBuffer->getWidth() << "\n";
+ file << mBuffer->getHeight() << "\n";
+ file << 255 << "\n";
+
+ std::vector<uint8_t> outBuffer(mBuffer->getWidth() * mBuffer->getHeight() * 3);
+ auto outPtr = reinterpret_cast<uint8_t*>(outBuffer.data());
+
+ for (int32_t j = 0; j < mBuffer->getHeight(); j++) {
+ const uint8_t* src = pixels + (mBuffer->getStride() * j) * 4;
+ for (int32_t i = 0; i < mBuffer->getWidth(); i++) {
+ // Only copy R, G and B components
+ outPtr[0] = src[0];
+ outPtr[1] = src[1];
+ outPtr[2] = src[2];
+ outPtr += 3;
+
+ src += 4;
+ }
+ }
+ file.write(reinterpret_cast<char*>(outBuffer.data()), outBuffer.size());
+ mBuffer->unlock();
+ }
+
+ void expectBufferColor(const Region& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a) {
+ size_t c;
+ Rect const* rect = region.getArray(&c);
+ for (size_t i = 0; i < c; i++, rect++) {
+ expectBufferColor(*rect, r, g, b, a);
+ }
+ }
+
+ void expectBufferColor(const Rect& rect, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ uint8_t tolerance = 0) {
+ auto colorCompare = [tolerance](const uint8_t* colorA, const uint8_t* colorB) {
+ auto colorBitCompare = [tolerance](uint8_t a, uint8_t b) {
+ uint8_t tmp = a >= b ? a - b : b - a;
+ return tmp <= tolerance;
+ };
+ return std::equal(colorA, colorA + 4, colorB, colorBitCompare);
};
+
+ expectBufferColor(rect, r, g, b, a, colorCompare);
+ }
+
+ void expectBufferColor(const Rect& region, uint8_t r, uint8_t g, uint8_t b, uint8_t a,
+ std::function<bool(const uint8_t* a, const uint8_t* b)> colorCompare) {
+ uint8_t* pixels;
+ mBuffer->lock(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+ reinterpret_cast<void**>(&pixels));
int32_t maxFails = 10;
int32_t fails = 0;
for (int32_t j = 0; j < region.getHeight(); j++) {
@@ -90,7 +151,7 @@
pixels + (mBuffer->getStride() * (region.top + j) + region.left) * 4;
for (int32_t i = 0; i < region.getWidth(); i++) {
const uint8_t expected[4] = {r, g, b, a};
- bool equal = std::equal(src, src + 4, expected, colorCompare);
+ bool equal = colorCompare(src, expected);
EXPECT_TRUE(equal)
<< "pixel @ (" << region.left + i << ", " << region.top + j << "): "
<< "expected (" << static_cast<uint32_t>(r) << ", "
@@ -111,6 +172,64 @@
mBuffer->unlock();
}
+ void expectAlpha(const Rect& rect, uint8_t a) {
+ auto colorCompare = [](const uint8_t* colorA, const uint8_t* colorB) {
+ return colorA[3] == colorB[3];
+ };
+ expectBufferColor(rect, 0.0f /* r */, 0.0f /*g */, 0.0f /* b */, a, colorCompare);
+ }
+
+ void expectShadowColor(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor) {
+ const Rect casterRect(castingLayer.geometry.boundaries);
+ Region casterRegion = Region(casterRect);
+ const float casterCornerRadius = castingLayer.geometry.roundedCornersRadius;
+ if (casterCornerRadius > 0.0f) {
+ // ignore the corners if a corner radius is set
+ Rect cornerRect(casterCornerRadius, casterCornerRadius);
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.left, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.right - casterCornerRadius, casterRect.top));
+ casterRegion.subtractSelf(
+ cornerRect.offsetTo(casterRect.left, casterRect.bottom - casterCornerRadius));
+ casterRegion.subtractSelf(cornerRect.offsetTo(casterRect.right - casterCornerRadius,
+ casterRect.bottom - casterCornerRadius));
+ }
+
+ const float shadowInset = shadow.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterRect).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region shadowRegion = Region(casterWithShadow).subtractSelf(casterRect);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+
+ // verify casting layer
+ expectBufferColor(casterRegion, casterColor.r, casterColor.g, casterColor.b, casterColor.a);
+
+ // verify shadows by testing just the alpha since its difficult to validate the shadow color
+ size_t c;
+ Rect const* r = shadowRegion.getArray(&c);
+ for (size_t i = 0; i < c; i++, r++) {
+ expectAlpha(*r, 255);
+ }
+
+ // verify background
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+ }
+
+ static renderengine::ShadowSettings getShadowSettings(const vec2& casterPos, float shadowLength,
+ bool casterIsTranslucent) {
+ renderengine::ShadowSettings shadow;
+ shadow.ambientColor = {0.0f, 0.0f, 0.0f, 0.039f};
+ shadow.spotColor = {0.0f, 0.0f, 0.0f, 0.19f};
+ shadow.lightPos = vec3(casterPos.x, casterPos.y, 0);
+ shadow.lightRadius = 0.0f;
+ shadow.length = shadowLength;
+ shadow.casterIsTranslucent = casterIsTranslucent;
+ return shadow;
+ }
+
static Rect fullscreenRect() { return Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); }
static Rect offsetRect() {
@@ -225,6 +344,11 @@
void clearRegion();
+ template <typename SourceVariant>
+ void drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow, const ubyte4& casterColor,
+ const ubyte4& backgroundColor);
+
// Keep around the same renderengine object to save on initialization time.
// For now, exercise the GL backend directly so that some caching specifics
// can be tested without changing the interface.
@@ -766,6 +890,40 @@
0, 0, 0, 0);
}
+template <typename SourceVariant>
+void RenderEngineTest::drawShadow(const renderengine::LayerSettings& castingLayer,
+ const renderengine::ShadowSettings& shadow,
+ const ubyte4& casterColor, const ubyte4& backgroundColor) {
+ renderengine::DisplaySettings settings;
+ settings.physicalDisplay = fullscreenRect();
+ settings.clip = fullscreenRect();
+
+ std::vector<renderengine::LayerSettings> layers;
+
+ // add background layer
+ renderengine::LayerSettings bgLayer;
+ bgLayer.geometry.boundaries = fullscreenRect().toFloatRect();
+ ColorSourceVariant::fillColor(bgLayer, backgroundColor.r / 255.0f, backgroundColor.g / 255.0f,
+ backgroundColor.b / 255.0f, this);
+ bgLayer.alpha = backgroundColor.a / 255.0f;
+ layers.push_back(bgLayer);
+
+ // add shadow layer
+ renderengine::LayerSettings shadowLayer;
+ shadowLayer.geometry.boundaries = castingLayer.geometry.boundaries;
+ shadowLayer.alpha = castingLayer.alpha;
+ shadowLayer.shadow = shadow;
+ layers.push_back(shadowLayer);
+
+ // add layer casting the shadow
+ renderengine::LayerSettings layer = castingLayer;
+ SourceVariant::fillColor(layer, casterColor.r / 255.0f, casterColor.g / 255.0f,
+ casterColor.b / 255.0f, this);
+ layers.push_back(layer);
+
+ invokeDraw(settings, layers, mBuffer);
+}
+
TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) {
drawEmptyLayers();
}
@@ -1083,4 +1241,101 @@
EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId));
}
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterLayerMinSize) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(1, 1);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterColorLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<ColorSourceVariant>(castingLayer, settings, casterColor, backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterOpaqueBufferLayer) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_casterWithRoundedCorner) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.geometry.roundedCornersRadius = 3.0f;
+ castingLayer.geometry.roundedCornersCrop = casterBounds.toFloatRect();
+ castingLayer.alpha = 1.0f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ false /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<ForceOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+ expectShadowColor(castingLayer, settings, casterColor, backgroundColor);
+}
+
+TEST_F(RenderEngineTest, drawLayers_fillShadow_translucentCasterWithAlpha) {
+ const ubyte4 casterColor(255, 0, 0, 255);
+ const ubyte4 backgroundColor(255, 255, 255, 255);
+ const float shadowLength = 5.0f;
+ Rect casterBounds(DEFAULT_DISPLAY_WIDTH / 3.0f, DEFAULT_DISPLAY_HEIGHT / 3.0f);
+ casterBounds.offsetBy(shadowLength + 1, shadowLength + 1);
+ renderengine::LayerSettings castingLayer;
+ castingLayer.geometry.boundaries = casterBounds.toFloatRect();
+ castingLayer.alpha = 0.5f;
+ renderengine::ShadowSettings settings =
+ getShadowSettings(vec2(casterBounds.left, casterBounds.top), shadowLength,
+ true /* casterIsTranslucent */);
+
+ drawShadow<BufferSourceVariant<RelaxOpaqueBufferVariant>>(castingLayer, settings, casterColor,
+ backgroundColor);
+
+ // verify only the background since the shadow will draw behind the caster
+ const float shadowInset = settings.length * -1.0f;
+ const Rect casterWithShadow =
+ Rect(casterBounds).inset(shadowInset, shadowInset, shadowInset, shadowInset);
+ const Region backgroundRegion = Region(fullscreenRect()).subtractSelf(casterWithShadow);
+ expectBufferColor(backgroundRegion, backgroundColor.r, backgroundColor.g, backgroundColor.b,
+ backgroundColor.a);
+}
+
} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 3775e2b..124bda2 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -89,14 +89,19 @@
"android.hardware.graphics.mapper@4.0",
"libbase",
"libcutils",
+ "libgralloctypes",
"libhidlbase",
"libsync",
"libutils",
"liblog",
+ "vintf-graphics-common-ndk_platform",
],
export_shared_lib_headers: [
"android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.mapper@4.0",
+ "libgralloctypes",
+ "vintf-graphics-common-ndk_platform",
],
static_libs: [
diff --git a/libs/ui/Fence.cpp b/libs/ui/Fence.cpp
index 4ce891e..33ab7c4 100644
--- a/libs/ui/Fence.cpp
+++ b/libs/ui/Fence.cpp
@@ -62,8 +62,26 @@
int warningTimeout = 3000;
int err = sync_wait(mFenceFd, warningTimeout);
if (err < 0 && errno == ETIME) {
- ALOGE("%s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
- warningTimeout);
+ ALOGE("waitForever: %s: fence %d didn't signal in %u ms", logname, mFenceFd.get(),
+ warningTimeout);
+
+ struct sync_file_info* finfo = sync_file_info(mFenceFd);
+ if (finfo) {
+ // status: active(0) signaled(1) error(<0)
+ ALOGI("waitForever: fence(%s) status(%d)", finfo->name, finfo->status);
+
+ struct sync_fence_info* pinfo = sync_get_fence_info(finfo);
+ for (uint32_t i = 0; i < finfo->num_fences; i++) {
+ uint64_t ts_sec = pinfo[i].timestamp_ns / 1000000000LL;
+ uint64_t ts_usec = (pinfo[i].timestamp_ns % 1000000000LL) / 1000LL;
+
+ ALOGI("waitForever: sync point: timeline(%s) drv(%s) status(%d) timestamp(%" PRIu64
+ ".%06" PRIu64 ")",
+ pinfo[i].obj_name, pinfo[i].driver_name, pinfo[i].status, ts_sec, ts_usec);
+ }
+ sync_file_info_free(finfo);
+ }
+
err = sync_wait(mFenceFd, TIMEOUT_NEVER);
}
return err < 0 ? -errno : status_t(NO_ERROR);
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 0e23ddf..040a62b 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -351,12 +351,6 @@
return releaseFence;
}
-status_t Gralloc2Mapper::isSupported(uint32_t /*width*/, uint32_t /*height*/,
- android::PixelFormat /*format*/, uint32_t /*layerCount*/,
- uint64_t /*usage*/, bool* /*outSupported*/) const {
- return INVALID_OPERATION;
-}
-
Gralloc2Allocator::Gralloc2Allocator(const Gralloc2Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
@@ -369,7 +363,7 @@
return mAllocator != nullptr;
}
-std::string Gralloc2Allocator::dumpDebugInfo() const {
+std::string Gralloc2Allocator::dumpDebugInfo(bool /*less*/) const {
std::string debugInfo;
mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) {
@@ -379,10 +373,10 @@
return debugInfo;
}
-status_t Gralloc2Allocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers) const {
+status_t Gralloc2Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index e189281..882674f 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -352,7 +352,7 @@
return mAllocator != nullptr;
}
-std::string Gralloc3Allocator::dumpDebugInfo() const {
+std::string Gralloc3Allocator::dumpDebugInfo(bool /*less*/) const {
std::string debugInfo;
mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
@@ -360,10 +360,10 @@
return debugInfo;
}
-status_t Gralloc3Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers) const {
+status_t Gralloc3Allocator::allocate(std::string /*requestorName*/, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index afe26b7..30c48c8 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -27,11 +27,20 @@
#include <sync/sync.h>
#pragma clang diagnostic pop
+using aidl::android::hardware::graphics::common::ExtendableType;
+using aidl::android::hardware::graphics::common::PlaneLayoutComponentType;
+using aidl::android::hardware::graphics::common::StandardMetadataType;
+using android::hardware::hidl_vec;
using android::hardware::graphics::allocator::V4_0::IAllocator;
using android::hardware::graphics::common::V1_2::BufferUsage;
using android::hardware::graphics::mapper::V4_0::BufferDescriptor;
using android::hardware::graphics::mapper::V4_0::Error;
using android::hardware::graphics::mapper::V4_0::IMapper;
+using BufferDump = android::hardware::graphics::mapper::V4_0::IMapper::BufferDump;
+using MetadataDump = android::hardware::graphics::mapper::V4_0::IMapper::MetadataDump;
+using MetadataType = android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+using MetadataTypeDescription =
+ android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription;
namespace android {
@@ -59,10 +68,10 @@
outRect.height = rect.height();
return outRect;
}
-static inline void sBufferDescriptorInfo(uint32_t width, uint32_t height,
- android::PixelFormat format, uint32_t layerCount,
- uint64_t usage,
+static inline void sBufferDescriptorInfo(std::string name, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
IMapper::BufferDescriptorInfo* outDescriptorInfo) {
+ outDescriptorInfo->name = name;
outDescriptorInfo->width = width;
outDescriptorInfo->height = height;
outDescriptorInfo->layerCount = layerCount;
@@ -151,11 +160,12 @@
}
status_t Gralloc4Mapper::validateBufferSize(buffer_handle_t bufferHandle, uint32_t width,
- uint32_t height, android::PixelFormat format,
+ uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
uint32_t stride) const {
IMapper::BufferDescriptorInfo descriptorInfo;
- sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+ sBufferDescriptorInfo("validateBufferSize", width, height, format, layerCount, usage,
+ &descriptorInfo);
auto buffer = const_cast<native_handle_t*>(bufferHandle);
auto ret = mMapper->validateBufferSize(buffer, descriptorInfo, stride);
@@ -189,14 +199,36 @@
status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
int acquireFence, void** outData, int32_t* outBytesPerPixel,
int32_t* outBytesPerStride) const {
- // In Gralloc 4 we can get this info per plane. Clients should check per plane.
- if (outBytesPerPixel) {
- // TODO add support to check per plane
- *outBytesPerPixel = -1;
- }
- if (outBytesPerStride) {
- // TODO add support to check per plane
- *outBytesPerStride = -1;
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t err = getPlaneLayouts(bufferHandle, &planeLayouts);
+
+ if (err != NO_ERROR && !planeLayouts.empty()) {
+ if (outBytesPerPixel) {
+ int32_t bitsPerPixel = planeLayouts.front().sampleIncrementInBits;
+ for (const auto& planeLayout : planeLayouts) {
+ if (bitsPerPixel != planeLayout.sampleIncrementInBits) {
+ bitsPerPixel = -1;
+ }
+ }
+ if (bitsPerPixel >= 0 && bitsPerPixel % 8 == 0) {
+ *outBytesPerPixel = bitsPerPixel / 8;
+ } else {
+ *outBytesPerPixel = -1;
+ }
+ }
+ if (outBytesPerStride) {
+ int32_t bytesPerStride = planeLayouts.front().strideInBytes;
+ for (const auto& planeLayout : planeLayouts) {
+ if (bytesPerStride != planeLayout.strideInBytes) {
+ bytesPerStride = -1;
+ }
+ }
+ if (bytesPerStride >= 0) {
+ *outBytesPerStride = bytesPerStride;
+ } else {
+ *outBytesPerStride = -1;
+ }
+ }
}
auto buffer = const_cast<native_handle_t*>(bufferHandle);
@@ -234,10 +266,104 @@
return static_cast<status_t>(error);
}
-status_t Gralloc4Mapper::lock(buffer_handle_t /*bufferHandle*/, uint64_t /*usage*/,
- const Rect& /*bounds*/, int /*acquireFence*/,
- android_ycbcr* /*ycbcr*/) const {
- // TODO add lockYCbCr support
+status_t Gralloc4Mapper::lock(buffer_handle_t bufferHandle, uint64_t usage, const Rect& bounds,
+ int acquireFence, android_ycbcr* outYcbcr) const {
+ if (!outYcbcr) {
+ return BAD_VALUE;
+ }
+
+ std::vector<ui::PlaneLayout> planeLayouts;
+ status_t error = getPlaneLayouts(bufferHandle, &planeLayouts);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ void* data = nullptr;
+ error = lock(bufferHandle, usage, bounds, acquireFence, &data, nullptr, nullptr);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ android_ycbcr ycbcr;
+
+ ycbcr.y = nullptr;
+ ycbcr.cb = nullptr;
+ ycbcr.cr = nullptr;
+ ycbcr.ystride = 0;
+ ycbcr.cstride = 0;
+ ycbcr.chroma_step = 0;
+
+ for (const auto& planeLayout : planeLayouts) {
+ for (const auto& planeLayoutComponent : planeLayout.components) {
+ if (!gralloc4::isStandardPlaneLayoutComponentType(planeLayoutComponent.type)) {
+ continue;
+ }
+ if (0 != planeLayoutComponent.offsetInBits % 8) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ uint8_t* tmpData = static_cast<uint8_t*>(data) + planeLayout.offsetInBytes +
+ (planeLayoutComponent.offsetInBits / 8);
+ uint64_t sampleIncrementInBytes;
+
+ auto type = static_cast<PlaneLayoutComponentType>(planeLayoutComponent.type.value);
+ switch (type) {
+ case PlaneLayoutComponentType::Y:
+ if ((ycbcr.y != nullptr) || (planeLayoutComponent.sizeInBits != 8) ||
+ (planeLayout.sampleIncrementInBits != 8)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.y = tmpData;
+ ycbcr.ystride = planeLayout.strideInBytes;
+ break;
+
+ case PlaneLayoutComponentType::CB:
+ case PlaneLayoutComponentType::CR:
+ if (planeLayout.sampleIncrementInBits % 8 != 0) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ sampleIncrementInBytes = planeLayout.sampleIncrementInBits / 8;
+ if ((sampleIncrementInBytes != 1) && (sampleIncrementInBytes != 2)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+
+ if (ycbcr.cstride == 0 && ycbcr.chroma_step == 0) {
+ ycbcr.cstride = planeLayout.strideInBytes;
+ ycbcr.chroma_step = sampleIncrementInBytes;
+ } else {
+ if ((static_cast<int64_t>(ycbcr.cstride) != planeLayout.strideInBytes) ||
+ (ycbcr.chroma_step != sampleIncrementInBytes)) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ }
+
+ if (type == PlaneLayoutComponentType::CB) {
+ if (ycbcr.cb != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cb = tmpData;
+ } else {
+ if (ycbcr.cr != nullptr) {
+ unlock(bufferHandle);
+ return BAD_VALUE;
+ }
+ ycbcr.cr = tmpData;
+ }
+ break;
+ default:
+ break;
+ };
+ }
+ }
+
+ *outYcbcr = ycbcr;
return static_cast<status_t>(Error::UNSUPPORTED);
}
@@ -275,11 +401,11 @@
return releaseFence;
}
-status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
+status_t Gralloc4Mapper::isSupported(uint32_t width, uint32_t height, PixelFormat format,
uint32_t layerCount, uint64_t usage,
bool* outSupported) const {
IMapper::BufferDescriptorInfo descriptorInfo;
- sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+ sBufferDescriptorInfo("isSupported", width, height, format, layerCount, usage, &descriptorInfo);
Error error;
auto ret = mMapper->isSupported(descriptorInfo,
@@ -305,6 +431,623 @@
return static_cast<status_t>(error);
}
+template <class T>
+status_t Gralloc4Mapper::get(buffer_handle_t bufferHandle, const MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const {
+ if (!outMetadata) {
+ return BAD_VALUE;
+ }
+
+ hidl_vec<uint8_t> vec;
+ Error error;
+ auto ret = mMapper->get(const_cast<native_handle_t*>(bufferHandle), metadataType,
+ [&](const auto& tmpError, const hidl_vec<uint8_t>& tmpVec) {
+ error = tmpError;
+ vec = tmpVec;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("get(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+ metadataType.value, error);
+ return static_cast<status_t>(error);
+ }
+
+ return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const {
+ return get(bufferHandle, gralloc4::MetadataType_BufferId, gralloc4::decodeBufferId,
+ outBufferId);
+}
+
+status_t Gralloc4Mapper::getName(buffer_handle_t bufferHandle, std::string* outName) const {
+ return get(bufferHandle, gralloc4::MetadataType_Name, gralloc4::decodeName, outName);
+}
+
+status_t Gralloc4Mapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const {
+ return get(bufferHandle, gralloc4::MetadataType_Width, gralloc4::decodeWidth, outWidth);
+}
+
+status_t Gralloc4Mapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const {
+ return get(bufferHandle, gralloc4::MetadataType_Height, gralloc4::decodeHeight, outHeight);
+}
+
+status_t Gralloc4Mapper::getLayerCount(buffer_handle_t bufferHandle,
+ uint64_t* outLayerCount) const {
+ return get(bufferHandle, gralloc4::MetadataType_LayerCount, gralloc4::decodeLayerCount,
+ outLayerCount);
+}
+
+status_t Gralloc4Mapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatRequested,
+ gralloc4::decodePixelFormatRequested, outPixelFormatRequested);
+}
+
+status_t Gralloc4Mapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatFourCC,
+ gralloc4::decodePixelFormatFourCC, outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) const {
+ return get(bufferHandle, gralloc4::MetadataType_PixelFormatModifier,
+ gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const {
+ return get(bufferHandle, gralloc4::MetadataType_Usage, gralloc4::decodeUsage, outUsage);
+}
+
+status_t Gralloc4Mapper::getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) const {
+ return get(bufferHandle, gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+ outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) const {
+ return get(bufferHandle, gralloc4::MetadataType_ProtectedContent,
+ gralloc4::decodeProtectedContent, outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+ ExtendableType* outCompression) const {
+ return get(bufferHandle, gralloc4::MetadataType_Compression, gralloc4::decodeCompression,
+ outCompression);
+}
+
+status_t Gralloc4Mapper::getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) const {
+ if (!outCompression) {
+ return BAD_VALUE;
+ }
+ ExtendableType compression;
+ status_t error = getCompression(bufferHandle, &compression);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardCompression(compression)) {
+ return BAD_TYPE;
+ }
+ *outCompression = gralloc4::getStandardCompressionValue(compression);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+ ExtendableType* outInterlaced) const {
+ return get(bufferHandle, gralloc4::MetadataType_Interlaced, gralloc4::decodeInterlaced,
+ outInterlaced);
+}
+
+status_t Gralloc4Mapper::getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) const {
+ if (!outInterlaced) {
+ return BAD_VALUE;
+ }
+ ExtendableType interlaced;
+ status_t error = getInterlaced(bufferHandle, &interlaced);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardInterlaced(interlaced)) {
+ return BAD_TYPE;
+ }
+ *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ExtendableType* outChromaSiting) const {
+ return get(bufferHandle, gralloc4::MetadataType_ChromaSiting, gralloc4::decodeChromaSiting,
+ outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) const {
+ if (!outChromaSiting) {
+ return BAD_VALUE;
+ }
+ ExtendableType chromaSiting;
+ status_t error = getChromaSiting(bufferHandle, &chromaSiting);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+ return BAD_TYPE;
+ }
+ *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+ return get(bufferHandle, gralloc4::MetadataType_PlaneLayouts, gralloc4::decodePlaneLayouts,
+ outPlaneLayouts);
+}
+
+status_t Gralloc4Mapper::getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace* outDataspace) const {
+ if (!outDataspace) {
+ return BAD_VALUE;
+ }
+ aidl::android::hardware::graphics::common::Dataspace dataspace;
+ status_t error = get(bufferHandle, gralloc4::MetadataType_Dataspace, gralloc4::decodeDataspace,
+ &dataspace);
+ if (error) {
+ return error;
+ }
+
+ // Gralloc4 uses stable AIDL dataspace but the rest of the system still uses HIDL dataspace
+ *outDataspace = static_cast<ui::Dataspace>(dataspace);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode* outBlendMode) const {
+ return get(bufferHandle, gralloc4::MetadataType_BlendMode, gralloc4::decodeBlendMode,
+ outBlendMode);
+}
+
+status_t Gralloc4Mapper::getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2086, gralloc4::decodeSmpte2086,
+ outSmpte2086);
+}
+
+status_t Gralloc4Mapper::getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) const {
+ return get(bufferHandle, gralloc4::MetadataType_Cta861_3, gralloc4::decodeCta861_3,
+ outCta861_3);
+}
+
+status_t Gralloc4Mapper::getSmpte2094_40(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) const {
+ return get(bufferHandle, gralloc4::MetadataType_Smpte2094_40, gralloc4::decodeSmpte2094_40,
+ outSmpte2094_40);
+}
+
+template <class T>
+status_t Gralloc4Mapper::getDefault(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ const MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const {
+ if (!outMetadata) {
+ return BAD_VALUE;
+ }
+
+ IMapper::BufferDescriptorInfo descriptorInfo;
+ sBufferDescriptorInfo("getDefault", width, height, format, layerCount, usage, &descriptorInfo);
+
+ hidl_vec<uint8_t> vec;
+ Error error;
+ auto ret = mMapper->getFromBufferDescriptorInfo(descriptorInfo, metadataType,
+ [&](const auto& tmpError,
+ const hidl_vec<uint8_t>& tmpVec) {
+ error = tmpError;
+ vec = tmpVec;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("getDefault(%s, %" PRIu64 ", ...) failed with %d", metadataType.name.c_str(),
+ metadataType.value, error);
+ return static_cast<status_t>(error);
+ }
+
+ return decodeFunction(vec, outMetadata);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint32_t* outPixelFormatFourCC) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_PixelFormatFourCC, gralloc4::decodePixelFormatFourCC,
+ outPixelFormatFourCC);
+}
+
+status_t Gralloc4Mapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outPixelFormatModifier) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_PixelFormatModifier,
+ gralloc4::decodePixelFormatModifier, outPixelFormatModifier);
+}
+
+status_t Gralloc4Mapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outAllocationSize) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_AllocationSize, gralloc4::decodeAllocationSize,
+ outAllocationSize);
+}
+
+status_t Gralloc4Mapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outProtectedContent) const {
+ return getDefault(width, height, format, layerCount, usage,
+ gralloc4::MetadataType_ProtectedContent, gralloc4::decodeProtectedContent,
+ outProtectedContent);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outCompression) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Compression,
+ gralloc4::decodeCompression, outCompression);
+}
+
+status_t Gralloc4Mapper::getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression) const {
+ if (!outCompression) {
+ return BAD_VALUE;
+ }
+ ExtendableType compression;
+ status_t error = getDefaultCompression(width, height, format, layerCount, usage, &compression);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardCompression(compression)) {
+ return BAD_TYPE;
+ }
+ *outCompression = gralloc4::getStandardCompressionValue(compression);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outInterlaced) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_Interlaced,
+ gralloc4::decodeInterlaced, outInterlaced);
+}
+
+status_t Gralloc4Mapper::getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced) const {
+ if (!outInterlaced) {
+ return BAD_VALUE;
+ }
+ ExtendableType interlaced;
+ status_t error = getDefaultInterlaced(width, height, format, layerCount, usage, &interlaced);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardInterlaced(interlaced)) {
+ return BAD_TYPE;
+ }
+ *outInterlaced = gralloc4::getStandardInterlacedValue(interlaced);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ExtendableType* outChromaSiting) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_ChromaSiting,
+ gralloc4::decodeChromaSiting, outChromaSiting);
+}
+
+status_t Gralloc4Mapper::getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) const {
+ if (!outChromaSiting) {
+ return BAD_VALUE;
+ }
+ ExtendableType chromaSiting;
+ status_t error =
+ getDefaultChromaSiting(width, height, format, layerCount, usage, &chromaSiting);
+ if (error) {
+ return error;
+ }
+ if (!gralloc4::isStandardChromaSiting(chromaSiting)) {
+ return BAD_TYPE;
+ }
+ *outChromaSiting = gralloc4::getStandardChromaSitingValue(chromaSiting);
+ return NO_ERROR;
+}
+
+status_t Gralloc4Mapper::getDefaultPlaneLayouts(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const {
+ return getDefault(width, height, format, layerCount, usage, gralloc4::MetadataType_PlaneLayouts,
+ gralloc4::decodePlaneLayouts, outPlaneLayouts);
+}
+
+std::vector<MetadataTypeDescription> Gralloc4Mapper::listSupportedMetadataTypes() const {
+ hidl_vec<MetadataTypeDescription> descriptions;
+ Error error;
+ auto ret = mMapper->listSupportedMetadataTypes(
+ [&](const auto& tmpError, const auto& tmpDescriptions) {
+ error = tmpError;
+ descriptions = tmpDescriptions;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("listSupportedMetadataType() failed with %d", error);
+ return {};
+ }
+
+ return static_cast<std::vector<MetadataTypeDescription>>(descriptions);
+}
+
+template <class T>
+status_t Gralloc4Mapper::metadataDumpHelper(const BufferDump& bufferDump,
+ StandardMetadataType metadataType,
+ DecodeFunction<T> decodeFunction, T* outT) const {
+ const auto& metadataDump = bufferDump.metadataDump;
+
+ auto itr =
+ std::find_if(metadataDump.begin(), metadataDump.end(),
+ [&](const MetadataDump& tmpMetadataDump) {
+ if (!gralloc4::isStandardMetadataType(tmpMetadataDump.metadataType)) {
+ return false;
+ }
+ return metadataType ==
+ gralloc4::getStandardMetadataTypeValue(
+ tmpMetadataDump.metadataType);
+ });
+ if (itr == metadataDump.end()) {
+ return BAD_VALUE;
+ }
+
+ return decodeFunction(itr->metadata, outT);
+}
+
+status_t Gralloc4Mapper::bufferDumpHelper(const BufferDump& bufferDump, std::ostringstream* outDump,
+ uint64_t* outAllocationSize, bool less) const {
+ uint64_t bufferId;
+ std::string name;
+ uint64_t width;
+ uint64_t height;
+ uint64_t layerCount;
+ ui::PixelFormat pixelFormatRequested;
+ uint32_t pixelFormatFourCC;
+ uint64_t pixelFormatModifier;
+ uint64_t usage;
+ uint64_t allocationSize;
+ uint64_t protectedContent;
+ ExtendableType compression;
+ ExtendableType interlaced;
+ ExtendableType chromaSiting;
+ std::vector<ui::PlaneLayout> planeLayouts;
+
+ status_t error = metadataDumpHelper(bufferDump, StandardMetadataType::BUFFER_ID,
+ gralloc4::decodeBufferId, &bufferId);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::NAME, gralloc4::decodeName, &name);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::WIDTH, gralloc4::decodeWidth,
+ &width);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::HEIGHT, gralloc4::decodeHeight,
+ &height);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::LAYER_COUNT,
+ gralloc4::decodeLayerCount, &layerCount);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_REQUESTED,
+ gralloc4::decodePixelFormatRequested, &pixelFormatRequested);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_FOURCC,
+ gralloc4::decodePixelFormatFourCC, &pixelFormatFourCC);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PIXEL_FORMAT_MODIFIER,
+ gralloc4::decodePixelFormatModifier, &pixelFormatModifier);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::USAGE, gralloc4::decodeUsage,
+ &usage);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::ALLOCATION_SIZE,
+ gralloc4::decodeAllocationSize, &allocationSize);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PROTECTED_CONTENT,
+ gralloc4::decodeProtectedContent, &protectedContent);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::COMPRESSION,
+ gralloc4::decodeCompression, &compression);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::INTERLACED,
+ gralloc4::decodeInterlaced, &interlaced);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::CHROMA_SITING,
+ gralloc4::decodeChromaSiting, &chromaSiting);
+ if (error != NO_ERROR) {
+ return error;
+ }
+ error = metadataDumpHelper(bufferDump, StandardMetadataType::PLANE_LAYOUTS,
+ gralloc4::decodePlaneLayouts, &planeLayouts);
+ if (error != NO_ERROR) {
+ return error;
+ }
+
+ if (outAllocationSize) {
+ *outAllocationSize = allocationSize;
+ }
+ double allocationSizeKiB = static_cast<double>(allocationSize) / 1024;
+
+ *outDump << "+ name:" << name << ", id:" << bufferId << ", size:" << allocationSizeKiB
+ << "KiB, w/h:" << width << "x" << height << ", usage: 0x" << std::hex << usage
+ << std::dec << ", req fmt:" << static_cast<int32_t>(pixelFormatRequested)
+ << ", fourcc/mod:" << pixelFormatFourCC << "/" << pixelFormatModifier
+ << ", compressed: ";
+
+ if (less) {
+ bool isCompressed = !gralloc4::isStandardCompression(compression) ||
+ (gralloc4::getStandardCompressionValue(compression) != ui::Compression::NONE);
+ *outDump << std::boolalpha << isCompressed << "\n";
+ } else {
+ *outDump << gralloc4::getCompressionName(compression) << "\n";
+ }
+
+ bool firstPlane = true;
+ for (const auto& planeLayout : planeLayouts) {
+ if (firstPlane) {
+ firstPlane = false;
+ *outDump << "\tplanes: ";
+ } else {
+ *outDump << "\t ";
+ }
+
+ for (size_t i = 0; i < planeLayout.components.size(); i++) {
+ const auto& planeLayoutComponent = planeLayout.components[i];
+ *outDump << gralloc4::getPlaneLayoutComponentTypeName(planeLayoutComponent.type);
+ if (i < planeLayout.components.size() - 1) {
+ *outDump << "/";
+ } else {
+ *outDump << ":\t";
+ }
+ }
+ *outDump << " w/h:" << planeLayout.widthInSamples << "x" << planeLayout.heightInSamples
+ << ", stride:" << planeLayout.strideInBytes
+ << " bytes, size:" << planeLayout.totalSizeInBytes;
+ if (!less) {
+ *outDump << ", inc:" << planeLayout.sampleIncrementInBits
+ << " bits, subsampling w/h:" << planeLayout.horizontalSubsampling << "x"
+ << planeLayout.verticalSubsampling;
+ }
+ *outDump << "\n";
+ }
+
+ if (!less) {
+ *outDump << "\tlayer cnt: " << layerCount << ", protected content: " << protectedContent
+ << ", interlaced: " << gralloc4::getInterlacedName(interlaced)
+ << ", chroma siting:" << gralloc4::getChromaSitingName(chromaSiting) << "\n";
+ }
+
+ return NO_ERROR;
+}
+
+std::string Gralloc4Mapper::dumpBuffer(buffer_handle_t bufferHandle, bool less) const {
+ auto buffer = const_cast<native_handle_t*>(bufferHandle);
+
+ BufferDump bufferDump;
+ Error error;
+ auto ret = mMapper->dumpBuffer(buffer, [&](const auto& tmpError, const auto& tmpBufferDump) {
+ error = tmpError;
+ bufferDump = tmpBufferDump;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("dumpBuffer() failed with %d", error);
+ return "";
+ }
+
+ std::ostringstream stream;
+ stream.precision(2);
+
+ status_t err = bufferDumpHelper(bufferDump, &stream, nullptr, less);
+ if (err != NO_ERROR) {
+ ALOGE("bufferDumpHelper() failed with %d", err);
+ return "";
+ }
+
+ return stream.str();
+}
+
+std::string Gralloc4Mapper::dumpBuffers(bool less) const {
+ hidl_vec<BufferDump> bufferDumps;
+ Error error;
+ auto ret = mMapper->dumpBuffers([&](const auto& tmpError, const auto& tmpBufferDump) {
+ error = tmpError;
+ bufferDumps = tmpBufferDump;
+ });
+
+ if (!ret.isOk()) {
+ error = kTransactionError;
+ }
+
+ if (error != Error::NONE) {
+ ALOGE("dumpBuffer() failed with %d", error);
+ return "";
+ }
+
+ uint64_t totalAllocationSize = 0;
+ std::ostringstream stream;
+ stream.precision(2);
+
+ stream << "Imported gralloc buffers:\n";
+
+ for (const auto& bufferDump : bufferDumps) {
+ uint64_t allocationSize = 0;
+ status_t err = bufferDumpHelper(bufferDump, &stream, &allocationSize, less);
+ if (err != NO_ERROR) {
+ ALOGE("bufferDumpHelper() failed with %d", err);
+ return "";
+ }
+ totalAllocationSize += allocationSize;
+ }
+
+ double totalAllocationSizeKiB = static_cast<double>(totalAllocationSize) / 1024;
+ stream << "Total imported by gralloc: " << totalAllocationSizeKiB << "KiB\n";
+ return stream.str();
+}
+
Gralloc4Allocator::Gralloc4Allocator(const Gralloc4Mapper& mapper) : mMapper(mapper) {
mAllocator = IAllocator::getService();
if (mAllocator == nullptr) {
@@ -317,20 +1060,16 @@
return mAllocator != nullptr;
}
-std::string Gralloc4Allocator::dumpDebugInfo() const {
- std::string debugInfo;
-
- mAllocator->dumpDebugInfo([&](const auto& tmpDebugInfo) { debugInfo = tmpDebugInfo.c_str(); });
-
- return debugInfo;
+std::string Gralloc4Allocator::dumpDebugInfo(bool less) const {
+ return mMapper.dumpBuffers(less);
}
-status_t Gralloc4Allocator::allocate(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
- bool importBuffers) const {
+status_t Gralloc4Allocator::allocate(std::string requestorName, uint32_t width, uint32_t height,
+ android::PixelFormat format, uint32_t layerCount,
+ uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles, bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
- sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
+ sBufferDescriptorInfo(requestorName, width, height, format, layerCount, usage, &descriptorInfo);
BufferDescriptor descriptor;
status_t error = mMapper.createDescriptor(static_cast<void*>(&descriptorInfo),
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index b2b9680..943d13e 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -69,20 +69,20 @@
GraphicBufferAllocator::~GraphicBufferAllocator() {}
-size_t GraphicBufferAllocator::getTotalSize() const {
+uint64_t GraphicBufferAllocator::getTotalSize() const {
Mutex::Autolock _l(sLock);
- size_t total = 0;
+ uint64_t total = 0;
for (size_t i = 0; i < sAllocList.size(); ++i) {
total += sAllocList.valueAt(i).size;
}
return total;
}
-void GraphicBufferAllocator::dump(std::string& result) const {
+void GraphicBufferAllocator::dump(std::string& result, bool less) const {
Mutex::Autolock _l(sLock);
KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
- size_t total = 0;
- result.append("Allocated buffers:\n");
+ uint64_t total = 0;
+ result.append("GraphicBufferAllocator buffers:\n");
const size_t c = list.size();
for (size_t i=0 ; i<c ; i++) {
const alloc_rec_t& rec(list.valueAt(i));
@@ -99,15 +99,15 @@
}
total += rec.size;
}
- StringAppendF(&result, "Total allocated (estimate): %.2f KB\n", static_cast<double>(total) / 1024.0);
+ StringAppendF(&result, "Total allocated by GraphicBufferAllocator (estimate): %.2f KB\n",
+ static_cast<double>(total) / 1024.0);
- result.append(mAllocator->dumpDebugInfo());
+ result.append(mAllocator->dumpDebugInfo(less));
}
-void GraphicBufferAllocator::dumpToSystemLog()
-{
+void GraphicBufferAllocator::dumpToSystemLog(bool less) {
std::string s;
- GraphicBufferAllocator::getInstance().dump(s);
+ GraphicBufferAllocator::getInstance().dump(s, less);
ALOGD("%s", s.c_str());
}
@@ -137,8 +137,8 @@
// TODO(b/72323293, b/72703005): Remove these invalid bits from callers
usage &= ~static_cast<uint64_t>((1 << 10) | (1 << 13));
- status_t error = mAllocator->allocate(width, height, format, layerCount, usage, 1, stride,
- handle, importBuffer);
+ status_t error = mAllocator->allocate(requestorName, width, height, format, layerCount, usage,
+ 1, stride, handle, importBuffer);
if (error != NO_ERROR) {
ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
"usage %" PRIx64 ": %d",
diff --git a/libs/ui/GraphicBufferMapper.cpp b/libs/ui/GraphicBufferMapper.cpp
index 4d087d1..d20bd7a 100644
--- a/libs/ui/GraphicBufferMapper.cpp
+++ b/libs/ui/GraphicBufferMapper.cpp
@@ -71,6 +71,17 @@
LOG_ALWAYS_FATAL("gralloc-mapper is missing");
}
+void GraphicBufferMapper::dumpBuffer(buffer_handle_t bufferHandle, std::string& result,
+ bool less) const {
+ result.append(mMapper->dumpBuffer(bufferHandle, less));
+}
+
+void GraphicBufferMapper::dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less) {
+ std::string s;
+ GraphicBufferMapper::getInstance().dumpBuffer(bufferHandle, s, less);
+ ALOGD("%s", s.c_str());
+}
+
status_t GraphicBufferMapper::importBuffer(buffer_handle_t rawHandle,
uint32_t width, uint32_t height, uint32_t layerCount,
PixelFormat format, uint64_t usage, uint32_t stride,
@@ -177,5 +188,197 @@
uint64_t usage, bool* outSupported) {
return mMapper->isSupported(width, height, format, layerCount, usage, outSupported);
}
+
+status_t GraphicBufferMapper::getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) {
+ return mMapper->getBufferId(bufferHandle, outBufferId);
+}
+
+status_t GraphicBufferMapper::getName(buffer_handle_t bufferHandle, std::string* outName) {
+ return mMapper->getName(bufferHandle, outName);
+}
+
+status_t GraphicBufferMapper::getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) {
+ return mMapper->getWidth(bufferHandle, outWidth);
+}
+
+status_t GraphicBufferMapper::getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) {
+ return mMapper->getHeight(bufferHandle, outHeight);
+}
+
+status_t GraphicBufferMapper::getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) {
+ return mMapper->getLayerCount(bufferHandle, outLayerCount);
+}
+
+status_t GraphicBufferMapper::getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) {
+ return mMapper->getPixelFormatRequested(bufferHandle, outPixelFormatRequested);
+}
+
+status_t GraphicBufferMapper::getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) {
+ return mMapper->getPixelFormatFourCC(bufferHandle, outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) {
+ return mMapper->getPixelFormatModifier(bufferHandle, outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) {
+ return mMapper->getUsage(bufferHandle, outUsage);
+}
+
+status_t GraphicBufferMapper::getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) {
+ return mMapper->getAllocationSize(bufferHandle, outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) {
+ return mMapper->getProtectedContent(bufferHandle, outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getCompression(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+ return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) {
+ return mMapper->getCompression(bufferHandle, outCompression);
+}
+
+status_t GraphicBufferMapper::getInterlaced(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+ return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) {
+ return mMapper->getInterlaced(bufferHandle, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+ return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) {
+ return mMapper->getChromaSiting(bufferHandle, outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+ return mMapper->getPlaneLayouts(bufferHandle, outPlaneLayouts);
+}
+
+status_t GraphicBufferMapper::getDataspace(buffer_handle_t bufferHandle,
+ ui::Dataspace* outDataspace) {
+ return mMapper->getDataspace(bufferHandle, outDataspace);
+}
+
+status_t GraphicBufferMapper::getBlendMode(buffer_handle_t bufferHandle,
+ ui::BlendMode* outBlendMode) {
+ return mMapper->getBlendMode(bufferHandle, outBlendMode);
+}
+
+status_t GraphicBufferMapper::getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) {
+ return mMapper->getSmpte2086(bufferHandle, outSmpte2086);
+}
+
+status_t GraphicBufferMapper::getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) {
+ return mMapper->getCta861_3(bufferHandle, outCta861_3);
+}
+
+status_t GraphicBufferMapper::getSmpte2094_40(
+ buffer_handle_t bufferHandle, std::optional<std::vector<uint8_t>>* outSmpte2094_40) {
+ return mMapper->getSmpte2094_40(bufferHandle, outSmpte2094_40);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatFourCC(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint32_t* outPixelFormatFourCC) {
+ return mMapper->getDefaultPixelFormatFourCC(width, height, format, layerCount, usage,
+ outPixelFormatFourCC);
+}
+
+status_t GraphicBufferMapper::getDefaultPixelFormatModifier(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outPixelFormatModifier) {
+ return mMapper->getDefaultPixelFormatModifier(width, height, format, layerCount, usage,
+ outPixelFormatModifier);
+}
+
+status_t GraphicBufferMapper::getDefaultAllocationSize(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outAllocationSize) {
+ return mMapper->getDefaultAllocationSize(width, height, format, layerCount, usage,
+ outAllocationSize);
+}
+
+status_t GraphicBufferMapper::getDefaultProtectedContent(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ uint64_t* outProtectedContent) {
+ return mMapper->getDefaultProtectedContent(width, height, format, layerCount, usage,
+ outProtectedContent);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression) {
+ return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultCompression(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::Compression* outCompression) {
+ return mMapper->getDefaultCompression(width, height, format, layerCount, usage, outCompression);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced) {
+ return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultInterlaced(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, ui::Interlaced* outInterlaced) {
+ return mMapper->getDefaultInterlaced(width, height, format, layerCount, usage, outInterlaced);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting) {
+ return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+ outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultChromaSiting(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) {
+ return mMapper->getDefaultChromaSiting(width, height, format, layerCount, usage,
+ outChromaSiting);
+}
+
+status_t GraphicBufferMapper::getDefaultPlaneLayouts(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) {
+ return mMapper->getDefaultPlaneLayouts(width, height, format, layerCount, usage,
+ outPlaneLayouts);
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 83ebeca..bf487c4 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -280,6 +280,20 @@
return begin() == region.begin();
}
+bool Region::hasSameRects(const Region& other) const {
+ size_t thisRectCount = 0;
+ android::Rect const* thisRects = getArray(&thisRectCount);
+ size_t otherRectCount = 0;
+ android::Rect const* otherRects = other.getArray(&otherRectCount);
+
+ if (thisRectCount != otherRectCount) return false;
+
+ for (size_t i = 0; i < thisRectCount; i++) {
+ if (thisRects[i] != otherRects[i]) return false;
+ }
+ return true;
+}
+
// ----------------------------------------------------------------------------
void Region::addRectUnchecked(int l, int t, int r, int b)
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 85abb38..06b6bfe 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -49,6 +49,15 @@
return isZero(fabs(f) - 1.0f);
}
+bool Transform::operator==(const Transform& other) const {
+ return mMatrix[0][0] == other.mMatrix[0][0] && mMatrix[0][1] == other.mMatrix[0][1] &&
+ mMatrix[0][2] == other.mMatrix[0][2] && mMatrix[1][0] == other.mMatrix[1][0] &&
+ mMatrix[1][1] == other.mMatrix[1][1] && mMatrix[1][2] == other.mMatrix[1][2] &&
+ mMatrix[2][0] == other.mMatrix[2][0] && mMatrix[2][1] == other.mMatrix[2][1] &&
+ mMatrix[2][2] == other.mMatrix[2][2];
+ ;
+}
+
Transform Transform::operator * (const Transform& rhs) const
{
if (CC_LIKELY(mType == IDENTITY))
diff --git a/libs/ui/include/ui/FloatRect.h b/libs/ui/include/ui/FloatRect.h
index 4cd9a0b..bec2552 100644
--- a/libs/ui/include/ui/FloatRect.h
+++ b/libs/ui/include/ui/FloatRect.h
@@ -16,6 +16,8 @@
#pragma once
+#include <ostream>
+
namespace android {
class FloatRect {
@@ -52,4 +54,9 @@
return a.left == b.left && a.top == b.top && a.right == b.right && a.bottom == b.bottom;
}
+static inline void PrintTo(const FloatRect& rect, ::std::ostream* os) {
+ *os << "FloatRect(" << rect.left << ", " << rect.top << ", " << rect.right << ", "
+ << rect.bottom << ")";
+}
+
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index c28f7a5..e199648 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -17,13 +17,15 @@
#ifndef ANDROID_UI_GRALLOC_H
#define ANDROID_UI_GRALLOC_H
-#include <string>
-
+#include <gralloctypes/Gralloc4.h>
#include <hidl/HidlSupport.h>
+#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include <string>
+
namespace android {
// A wrapper to IMapper
@@ -33,6 +35,10 @@
virtual bool isLoaded() const = 0;
+ virtual std::string dumpBuffer(buffer_handle_t /*bufferHandle*/, bool /*less*/) const {
+ return "";
+ }
+
virtual status_t createDescriptor(void* bufferDescriptorInfo,
void* outBufferDescriptor) const = 0;
@@ -74,8 +80,176 @@
// allocated if resources are available. If false, a buffer with the given specifications will
// never successfully allocate on this device. Note that this function is not guaranteed to be
// supported on all devices, in which case a status_t of INVALID_OPERATION will be returned.
- virtual status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const = 0;
+ virtual status_t isSupported(uint32_t /*width*/, uint32_t /*height*/,
+ android::PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/, bool* /*outSupported*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t getBufferId(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outBufferId*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getName(buffer_handle_t /*bufferHandle*/, std::string* /*outName*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getWidth(buffer_handle_t /*bufferHandle*/, uint64_t* /*outWidth*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getHeight(buffer_handle_t /*bufferHandle*/, uint64_t* /*outHeight*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getLayerCount(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outLayerCount*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatRequested(buffer_handle_t /*bufferHandle*/,
+ ui::PixelFormat* /*outPixelFormatRequested*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatFourCC(buffer_handle_t /*bufferHandle*/,
+ uint32_t* /*outPixelFormatFourCC*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPixelFormatModifier(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outPixelFormatModifier*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getUsage(buffer_handle_t /*bufferHandle*/, uint64_t* /*outUsage*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getAllocationSize(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outAllocationSize*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getProtectedContent(buffer_handle_t /*bufferHandle*/,
+ uint64_t* /*outProtectedContent*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCompression(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCompression(buffer_handle_t /*bufferHandle*/,
+ ui::Compression* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getInterlaced(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getInterlaced(buffer_handle_t /*bufferHandle*/,
+ ui::Interlaced* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getChromaSiting(
+ buffer_handle_t /*bufferHandle*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getChromaSiting(buffer_handle_t /*bufferHandle*/,
+ ui::ChromaSiting* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getPlaneLayouts(buffer_handle_t /*bufferHandle*/,
+ std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDataspace(buffer_handle_t /*bufferHandle*/,
+ ui::Dataspace* /*outDataspace*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getBlendMode(buffer_handle_t /*bufferHandle*/,
+ ui::BlendMode* /*outBlendMode*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getSmpte2086(buffer_handle_t /*bufferHandle*/,
+ std::optional<ui::Smpte2086>* /*outSmpte2086*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getCta861_3(buffer_handle_t /*bufferHandle*/,
+ std::optional<ui::Cta861_3>* /*outCta861_3*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getSmpte2094_40(
+ buffer_handle_t /*bufferHandle*/,
+ std::optional<std::vector<uint8_t>>* /*outSmpte2094_40*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual status_t getDefaultPixelFormatFourCC(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint32_t* /*outPixelFormatFourCC*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultPixelFormatModifier(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outPixelFormatModifier*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultAllocationSize(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outAllocationSize*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultProtectedContent(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ uint64_t* /*outProtectedContent*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultCompression(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultCompression(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::Compression* /*outCompression*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultInterlaced(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultInterlaced(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::Interlaced* /*outInterlaced*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultChromaSiting(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ aidl::android::hardware::graphics::common::ExtendableType* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultChromaSiting(uint32_t /*width*/, uint32_t /*height*/,
+ PixelFormat /*format*/, uint32_t /*layerCount*/,
+ uint64_t /*usage*/,
+ ui::ChromaSiting* /*outChromaSiting*/) const {
+ return INVALID_OPERATION;
+ }
+ virtual status_t getDefaultPlaneLayouts(
+ uint32_t /*width*/, uint32_t /*height*/, PixelFormat /*format*/,
+ uint32_t /*layerCount*/, uint64_t /*usage*/,
+ std::vector<ui::PlaneLayout>* /*outPlaneLayouts*/) const {
+ return INVALID_OPERATION;
+ }
+
+ virtual std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+ listSupportedMetadataTypes() const {
+ return {};
+ }
};
// A wrapper to IAllocator
@@ -85,16 +259,17 @@
virtual bool isLoaded() const = 0;
- virtual std::string dumpDebugInfo() const = 0;
+ virtual std::string dumpDebugInfo(bool less = true) const = 0;
/*
* The returned buffers are already imported and must not be imported
* again. outBufferHandles must point to a space that can contain at
* least "bufferCount" buffer_handle_t.
*/
- virtual status_t allocate(uint32_t width, uint32_t height, PixelFormat format,
- uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
- uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ virtual status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
+ uint32_t bufferCount, uint32_t* outStride,
+ buffer_handle_t* outBufferHandles,
bool importBuffers = true) const = 0;
};
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 12c772a..f570c42 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -61,9 +61,6 @@
int unlock(buffer_handle_t bufferHandle) const override;
- status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
-
private:
// Determines whether the passed info is compatible with the mapper.
status_t validateBufferDescriptorInfo(
@@ -81,11 +78,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc2Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc3.h b/libs/ui/include/ui/Gralloc3.h
index bfbc2aa..93a5077 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -79,11 +79,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc3Mapper& mMapper;
diff --git a/libs/ui/include/ui/Gralloc4.h b/libs/ui/include/ui/Gralloc4.h
index 60115f9..4729cba 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -17,16 +17,17 @@
#ifndef ANDROID_UI_GRALLOC4_H
#define ANDROID_UI_GRALLOC4_H
-#include <string>
-
#include <android/hardware/graphics/allocator/4.0/IAllocator.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+#include <gralloctypes/Gralloc4.h>
#include <ui/Gralloc.h>
-#include <ui/PixelFormat.h>
+#include <ui/GraphicTypes.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include <string>
+
namespace android {
class Gralloc4Mapper : public GrallocMapper {
@@ -37,6 +38,9 @@
bool isLoaded() const override;
+ std::string dumpBuffer(buffer_handle_t bufferHandle, bool less = true) const override;
+ std::string dumpBuffers(bool less = true) const;
+
status_t createDescriptor(void* bufferDescriptorInfo, void* outBufferDescriptor) const override;
status_t importBuffer(const hardware::hidl_handle& rawHandle,
@@ -45,7 +49,7 @@
void freeBuffer(buffer_handle_t bufferHandle) const override;
status_t validateBufferSize(buffer_handle_t bufferHandle, uint32_t width, uint32_t height,
- android::PixelFormat format, uint32_t layerCount, uint64_t usage,
+ PixelFormat format, uint32_t layerCount, uint64_t usage,
uint32_t stride) const override;
void getTransportSize(buffer_handle_t bufferHandle, uint32_t* outNumFds,
@@ -60,14 +64,123 @@
int unlock(buffer_handle_t bufferHandle) const override;
- status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
- uint32_t layerCount, uint64_t usage, bool* outSupported) const override;
+ status_t isSupported(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, bool* outSupported) const override;
+
+ status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId) const override;
+ status_t getName(buffer_handle_t bufferHandle, std::string* outName) const override;
+ status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth) const override;
+ status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight) const override;
+ status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount) const override;
+ status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested) const override;
+ status_t getPixelFormatFourCC(buffer_handle_t bufferHandle,
+ uint32_t* outPixelFormatFourCC) const override;
+ status_t getPixelFormatModifier(buffer_handle_t bufferHandle,
+ uint64_t* outPixelFormatModifier) const override;
+ status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage) const override;
+ status_t getAllocationSize(buffer_handle_t bufferHandle,
+ uint64_t* outAllocationSize) const override;
+ status_t getProtectedContent(buffer_handle_t bufferHandle,
+ uint64_t* outProtectedContent) const override;
+ status_t getCompression(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outCompression) const override;
+ status_t getCompression(buffer_handle_t bufferHandle,
+ ui::Compression* outCompression) const override;
+ status_t getInterlaced(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced)
+ const override;
+ status_t getInterlaced(buffer_handle_t bufferHandle,
+ ui::Interlaced* outInterlaced) const override;
+ status_t getChromaSiting(buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outChromaSiting) const override;
+ status_t getChromaSiting(buffer_handle_t bufferHandle,
+ ui::ChromaSiting* outChromaSiting) const override;
+ status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+ status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace) const override;
+ status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode) const override;
+ status_t getSmpte2086(buffer_handle_t bufferHandle,
+ std::optional<ui::Smpte2086>* outSmpte2086) const override;
+ status_t getCta861_3(buffer_handle_t bufferHandle,
+ std::optional<ui::Cta861_3>* outCta861_3) const override;
+ status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40) const override;
+
+ status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t* outPixelFormatFourCC) const override;
+ status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outPixelFormatModifier) const override;
+ status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outAllocationSize) const override;
+ status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outProtectedContent) const override;
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outCompression) const override;
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression) const override;
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outInterlaced) const override;
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced) const override;
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType*
+ outChromaSiting) const override;
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting) const override;
+ status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts) const override;
+
+ std::vector<android::hardware::graphics::mapper::V4_0::IMapper::MetadataTypeDescription>
+ listSupportedMetadataTypes() const;
private:
+ friend class GraphicBufferAllocator;
+
// Determines whether the passed info is compatible with the mapper.
status_t validateBufferDescriptorInfo(
hardware::graphics::mapper::V4_0::IMapper::BufferDescriptorInfo* descriptorInfo) const;
+ template <class T>
+ using DecodeFunction = status_t (*)(const hardware::hidl_vec<uint8_t>& input, T* output);
+
+ template <class T>
+ status_t get(
+ buffer_handle_t bufferHandle,
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+ template <class T>
+ status_t getDefault(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType,
+ DecodeFunction<T> decodeFunction, T* outMetadata) const;
+
+ template <class T>
+ status_t metadataDumpHelper(
+ const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+ aidl::android::hardware::graphics::common::StandardMetadataType metadataType,
+ DecodeFunction<T> decodeFunction, T* outT) const;
+ status_t bufferDumpHelper(
+ const android::hardware::graphics::mapper::V4_0::IMapper::BufferDump& bufferDump,
+ std::ostringstream* outDump, uint64_t* outAllocationSize, bool less) const;
+
sp<hardware::graphics::mapper::V4_0::IMapper> mMapper;
};
@@ -79,11 +192,12 @@
bool isLoaded() const override;
- std::string dumpDebugInfo() const override;
+ std::string dumpDebugInfo(bool less = true) const override;
- status_t allocate(uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
- buffer_handle_t* outBufferHandles, bool importBuffers = true) const override;
+ status_t allocate(std::string requestorName, uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount, uint64_t usage, uint32_t bufferCount,
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const override;
private:
const Gralloc4Mapper& mMapper;
diff --git a/libs/ui/include/ui/GraphicBufferAllocator.h b/libs/ui/include/ui/GraphicBufferAllocator.h
index 34a5b17..3ed988c 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2009, 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.
-*/
+ *
+ * Copyright 2009, 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.
+ */
#ifndef ANDROID_BUFFER_ALLOCATOR_H
#define ANDROID_BUFFER_ALLOCATOR_H
@@ -72,10 +72,10 @@
status_t free(buffer_handle_t handle);
- size_t getTotalSize() const;
+ uint64_t getTotalSize() const;
- void dump(std::string& res) const;
- static void dumpToSystemLog();
+ void dump(std::string& res, bool less = true) const;
+ static void dumpToSystemLog(bool less = true);
protected:
struct alloc_rec_t {
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index 83fb144..837e3d8 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -22,11 +22,11 @@
#include <memory>
+#include <ui/GraphicTypes.h>
#include <ui/PixelFormat.h>
#include <ui/Rect.h>
#include <utils/Singleton.h>
-
// Needed by code that still uses the GRALLOC_USAGE_* constants.
// when/if we get rid of gralloc, we should provide aliases or fix call sites.
#include <hardware/gralloc.h>
@@ -49,6 +49,9 @@
static void preloadHal();
static inline GraphicBufferMapper& get() { return getInstance(); }
+ void dumpBuffer(buffer_handle_t bufferHandle, std::string& result, bool less = true) const;
+ static void dumpBufferToSystemLog(buffer_handle_t bufferHandle, bool less = true);
+
// The imported outHandle must be freed with freeBuffer when no longer
// needed. rawHandle is owned by the caller.
status_t importBuffer(buffer_handle_t rawHandle,
@@ -86,6 +89,86 @@
status_t isSupported(uint32_t width, uint32_t height, android::PixelFormat format,
uint32_t layerCount, uint64_t usage, bool* outSupported);
+ /**
+ * Gets the gralloc metadata associated with the buffer.
+ *
+ * These functions are supported by gralloc 4.0+.
+ */
+ status_t getBufferId(buffer_handle_t bufferHandle, uint64_t* outBufferId);
+ status_t getName(buffer_handle_t bufferHandle, std::string* outName);
+ status_t getWidth(buffer_handle_t bufferHandle, uint64_t* outWidth);
+ status_t getHeight(buffer_handle_t bufferHandle, uint64_t* outHeight);
+ status_t getLayerCount(buffer_handle_t bufferHandle, uint64_t* outLayerCount);
+ status_t getPixelFormatRequested(buffer_handle_t bufferHandle,
+ ui::PixelFormat* outPixelFormatRequested);
+ status_t getPixelFormatFourCC(buffer_handle_t bufferHandle, uint32_t* outPixelFormatFourCC);
+ status_t getPixelFormatModifier(buffer_handle_t bufferHandle, uint64_t* outPixelFormatModifier);
+ status_t getUsage(buffer_handle_t bufferHandle, uint64_t* outUsage);
+ status_t getAllocationSize(buffer_handle_t bufferHandle, uint64_t* outAllocationSize);
+ status_t getProtectedContent(buffer_handle_t bufferHandle, uint64_t* outProtectedContent);
+ status_t getCompression(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+ status_t getCompression(buffer_handle_t bufferHandle, ui::Compression* outCompression);
+ status_t getInterlaced(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+ status_t getInterlaced(buffer_handle_t bufferHandle, ui::Interlaced* outInterlaced);
+ status_t getChromaSiting(
+ buffer_handle_t bufferHandle,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+ status_t getChromaSiting(buffer_handle_t bufferHandle, ui::ChromaSiting* outChromaSiting);
+ status_t getPlaneLayouts(buffer_handle_t bufferHandle,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts);
+ status_t getDataspace(buffer_handle_t bufferHandle, ui::Dataspace* outDataspace);
+ status_t getBlendMode(buffer_handle_t bufferHandle, ui::BlendMode* outBlendMode);
+ status_t getSmpte2086(buffer_handle_t bufferHandle, std::optional<ui::Smpte2086>* outSmpte2086);
+ status_t getCta861_3(buffer_handle_t bufferHandle, std::optional<ui::Cta861_3>* outCta861_3);
+ status_t getSmpte2094_40(buffer_handle_t bufferHandle,
+ std::optional<std::vector<uint8_t>>* outSmpte2094_40);
+
+ /**
+ * Gets the default metadata for a gralloc buffer allocated with the given parameters.
+ *
+ * These functions are supported by gralloc 4.0+.
+ */
+ status_t getDefaultPixelFormatFourCC(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint32_t* outPixelFormatFourCC);
+ status_t getDefaultPixelFormatModifier(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outPixelFormatModifier);
+ status_t getDefaultAllocationSize(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outAllocationSize);
+ status_t getDefaultProtectedContent(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ uint64_t* outProtectedContent);
+ status_t getDefaultCompression(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+ status_t getDefaultCompression(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Compression* outCompression);
+ status_t getDefaultInterlaced(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+ status_t getDefaultInterlaced(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::Interlaced* outInterlaced);
+ status_t getDefaultChromaSiting(
+ uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
+ uint64_t usage,
+ aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+ status_t getDefaultChromaSiting(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ ui::ChromaSiting* outChromaSiting);
+ status_t getDefaultPlaneLayouts(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ std::vector<ui::PlaneLayout>* outPlaneLayouts);
+
const GrallocMapper& getGrallocMapper() const {
return reinterpret_cast<const GrallocMapper&>(*mMapper);
}
diff --git a/libs/ui/include/ui/GraphicTypes.h b/libs/ui/include/ui/GraphicTypes.h
index d7411ea..4bdacb0 100644
--- a/libs/ui/include/ui/GraphicTypes.h
+++ b/libs/ui/include/ui/GraphicTypes.h
@@ -16,21 +16,51 @@
#pragma once
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Cta861_3.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/Smpte2086.h>
#include <android/hardware/graphics/common/1.1/types.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <system/graphics.h>
namespace android {
-// android::ui::* in this header file will alias different types as
-// the HIDL interface is updated.
+/**
+ * android::ui::* in this header file will alias different types as
+ * the HIDL and stable AIDL interfaces are updated.
+ */
namespace ui {
+/**
+ * The following HIDL types should be moved to their stable AIDL
+ * equivalents when composer moves to stable AIDL.
+ */
using android::hardware::graphics::common::V1_1::RenderIntent;
using android::hardware::graphics::common::V1_2::ColorMode;
using android::hardware::graphics::common::V1_2::Dataspace;
using android::hardware::graphics::common::V1_2::Hdr;
using android::hardware::graphics::common::V1_2::PixelFormat;
+/**
+ * Stable AIDL types
+ */
+using aidl::android::hardware::graphics::common::BlendMode;
+using aidl::android::hardware::graphics::common::Cta861_3;
+using aidl::android::hardware::graphics::common::PlaneLayout;
+using aidl::android::hardware::graphics::common::Smpte2086;
+
+/**
+ * The following stable AIDL types below have standard platform definitions
+ * that can be extended by vendors. The extensions are not defined here
+ * because they cannot be understood by the framework.
+ */
+using ChromaSiting = aidl::android::hardware::graphics::common::ChromaSiting;
+using Compression = aidl::android::hardware::graphics::common::Compression;
+using Interlaced = aidl::android::hardware::graphics::common::Interlaced;
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/include/ui/Rect.h b/libs/ui/include/ui/Rect.h
index 1768805..2f2229e 100644
--- a/libs/ui/include/ui/Rect.h
+++ b/libs/ui/include/ui/Rect.h
@@ -17,6 +17,8 @@
#ifndef ANDROID_UI_RECT
#define ANDROID_UI_RECT
+#include <ostream>
+
#include <utils/Flattenable.h>
#include <utils/Log.h>
#include <utils/TypeHelpers.h>
@@ -214,6 +216,12 @@
}
};
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Rect& rect, ::std::ostream* os) {
+ *os << "Rect(" << rect.left << ", " << rect.top << ", " << rect.right << ", " << rect.bottom
+ << ")";
+}
+
ANDROID_BASIC_TYPES_TRAITS(Rect)
}; // namespace android
diff --git a/libs/ui/include/ui/Region.h b/libs/ui/include/ui/Region.h
index 79642ae..2db3b10 100644
--- a/libs/ui/include/ui/Region.h
+++ b/libs/ui/include/ui/Region.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <ostream>
#include <utils/Vector.h>
@@ -119,6 +120,8 @@
// returns true if the regions share the same underlying storage
bool isTriviallyEqual(const Region& region) const;
+ // returns true if the regions consist of the same rectangle sequence
+ bool hasSameRects(const Region& region) const;
/* various ways to access the rectangle list */
@@ -213,6 +216,21 @@
Region& Region::operator += (const Point& pt) {
return translateSelf(pt.x, pt.y);
}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const Region& region, ::std::ostream* os) {
+ Region::const_iterator head = region.begin();
+ Region::const_iterator const tail = region.end();
+ bool first = true;
+ while (head != tail) {
+ *os << (first ? "Region(" : ", ");
+ PrintTo(*head, os);
+ head++;
+ first = false;
+ }
+ *os << ")";
+}
+
// ---------------------------------------------------------------------------
}; // namespace android
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index fab2d9e..de07684 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -19,6 +19,7 @@
#include <stdint.h>
#include <sys/types.h>
+#include <ostream>
#include <string>
#include <hardware/hardware.h>
@@ -63,6 +64,7 @@
bool preserveRects() const;
uint32_t getType() const;
uint32_t getOrientation() const;
+ bool operator==(const Transform& other) const;
const vec3& operator [] (size_t i) const; // returns column i
float tx() const;
@@ -115,6 +117,12 @@
mutable uint32_t mType;
};
+static inline void PrintTo(const Transform& t, ::std::ostream* os) {
+ std::string out;
+ t.dump(out, "ui::Transform");
+ *os << out;
+}
+
} // namespace ui
} // namespace android
diff --git a/libs/ui/include_vndk/ui/Gralloc2.h b/libs/ui/include_vndk/ui/Gralloc2.h
deleted file mode 120000
index 66098c4..0000000
--- a/libs/ui/include_vndk/ui/Gralloc2.h
+++ /dev/null
@@ -1 +0,0 @@
-../../include/ui/Gralloc2.h
\ No newline at end of file
diff --git a/libs/ui/tests/Android.bp b/libs/ui/tests/Android.bp
index d2ad242..ff55bc2 100644
--- a/libs/ui/tests/Android.bp
+++ b/libs/ui/tests/Android.bp
@@ -38,8 +38,9 @@
"libgmock",
],
shared_libs: [
+ "libhidlbase",
"liblog",
- "libui",
+ "libui",
],
srcs: [
"GraphicBufferAllocator_test.cpp",
diff --git a/libs/ui/tests/GraphicBufferAllocator_test.cpp b/libs/ui/tests/GraphicBufferAllocator_test.cpp
index efca083..f4c0afa 100644
--- a/libs/ui/tests/GraphicBufferAllocator_test.cpp
+++ b/libs/ui/tests/GraphicBufferAllocator_test.cpp
@@ -51,7 +51,7 @@
std::cout << "Setting expected stride to " << stride << std::endl;
EXPECT_CALL(*(reinterpret_cast<const mock::MockGrallocAllocator*>(mAllocator.get())),
allocate)
- .WillOnce(DoAll(SetArgPointee<6>(stride), Return(err)));
+ .WillOnce(DoAll(SetArgPointee<7>(stride), Return(err)));
}
std::unique_ptr<const GrallocAllocator>& getAllocator() { return mAllocator; }
};
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 7660e9f..d62e3e2 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -32,10 +32,10 @@
~MockGrallocAllocator() override;
MOCK_METHOD(bool, isLoaded, (), (const, override));
- MOCK_METHOD(std::string, dumpDebugInfo, (), (const, override));
+ MOCK_METHOD(std::string, dumpDebugInfo, (bool less), (const, override));
MOCK_METHOD(status_t, allocate,
- (uint32_t width, uint32_t height, PixelFormat format, uint32_t layerCount,
- uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
+ (std::string requestorName, uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage, uint32_t bufferCount, uint32_t* outStride,
buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/libs/vr/OWNERS b/libs/vr/OWNERS
index ec2d712..098c80e 100644
--- a/libs/vr/OWNERS
+++ b/libs/vr/OWNERS
@@ -1,4 +1,4 @@
hendrikw@google.com
jwcai@google.com
steventhomas@google.com
-
+patplunkett@google.com
diff --git a/libs/vr/libpdx/Android.bp b/libs/vr/libpdx/Android.bp
index 1a9d727..23a4224 100644
--- a/libs/vr/libpdx/Android.bp
+++ b/libs/vr/libpdx/Android.bp
@@ -53,6 +53,8 @@
"libpdx",
"liblog",
"libutils",
+ ],
+ shared_libs: [
"libvndksupport",
],
}
diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen
index da9bb49..41fcf1b 100755
--- a/opengl/tools/glgen/gen
+++ b/opengl/tools/glgen/gen
@@ -91,8 +91,8 @@
rm src/*.class
# Add UnsupportedAppUsage.java to known sources.
-mkdir -p out/android/annotation
-cp ../../../../base/core/java/android/annotation/UnsupportedAppUsage.java out/android/annotation
+mkdir -p out/android/compat/annotation
+cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation
pushd out > /dev/null
mkdir classes
@@ -114,7 +114,7 @@
android/opengl/GLES31.java \
android/opengl/GLES31Ext.java \
android/opengl/GLES32.java \
- android/annotation/UnsupportedAppUsage.java
+ android/compat/annotation/UnsupportedAppUsage.java
popd > /dev/null
JAVA_RESULT=$?
if [ $JAVA_RESULT -ne 0 ]; then
diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
index 12728f5..9932556 100644
--- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
+++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if
@@ -18,7 +18,7 @@
package android.opengl;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.SurfaceTexture;
import android.view.Surface;
import android.view.SurfaceView;
diff --git a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
index c2711aa..7db1101 100644
--- a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
+++ b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if
@@ -19,7 +19,7 @@
package android.opengl;
-import android.annotation.UnsupportedAppUsage;
+import android.compat.annotation.UnsupportedAppUsage;
/** OpenGL ES 2.0
*/
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 7c061c5..ae9a348 100644
--- a/services/inputflinger/InputClassifier.cpp
+++ b/services/inputflinger/InputClassifier.cpp
@@ -82,7 +82,7 @@
// Check if the "deep touch" feature is on.
static bool deepPressEnabled() {
std::string flag_value = server_configurable_flags::GetServerConfigurableFlag(
- INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "false");
+ INPUT_NATIVE_BOOT, DEEP_PRESS_ENABLED, "true");
std::transform(flag_value.begin(), flag_value.end(), flag_value.begin(), ::tolower);
if (flag_value == "1" || flag_value == "true") {
ALOGI("Deep press feature enabled.");
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index 1043390..c7c61cf 100644
--- a/services/inputflinger/InputManager.cpp
+++ b/services/inputflinger/InputManager.cpp
@@ -20,7 +20,6 @@
#include "InputManager.h"
#include "InputDispatcherFactory.h"
-#include "InputDispatcherThread.h"
#include "InputReaderFactory.h"
#include <binder/IPCThreadState.h>
@@ -38,19 +37,14 @@
mDispatcher = createInputDispatcher(dispatcherPolicy);
mClassifier = new InputClassifier(mDispatcher);
mReader = createInputReader(readerPolicy, mClassifier);
- initialize();
}
InputManager::~InputManager() {
stop();
}
-void InputManager::initialize() {
- mDispatcherThread = new InputDispatcherThread(mDispatcher);
-}
-
status_t InputManager::start() {
- status_t result = mDispatcherThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+ status_t result = mDispatcher->start();
if (result) {
ALOGE("Could not start InputDispatcher thread due to error %d.", result);
return result;
@@ -60,7 +54,7 @@
if (result) {
ALOGE("Could not start InputReader due to error %d.", result);
- mDispatcherThread->requestExit();
+ mDispatcher->stop();
return result;
}
@@ -68,17 +62,21 @@
}
status_t InputManager::stop() {
+ status_t status = OK;
+
status_t result = mReader->stop();
if (result) {
ALOGW("Could not stop InputReader due to error %d.", result);
+ status = result;
}
- result = mDispatcherThread->requestExitAndWait();
+ result = mDispatcher->stop();
if (result) {
ALOGW("Could not stop InputDispatcher thread due to error %d.", result);
+ status = result;
}
- return OK;
+ return status;
}
sp<InputReaderInterface> InputManager::getReader() {
diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h
index 2a7ed0f..586097f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -47,10 +47,10 @@
*
* 1. The InputReader class starts a thread that reads and preprocesses raw input events, applies
* policy, and posts messages to a queue managed by the InputDispatcherThread.
- * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
+ * 2. The InputDispatcher class starts a thread that waits for new events on the
* queue and asynchronously dispatches them to applications.
*
- * By design, the InputReader class and InputDispatcherThread class do not share any
+ * By design, the InputReader class and InputDispatcher class do not share any
* internal state. Moreover, all communication is done one way from the InputReader
* into the InputDispatcherThread and never the reverse. Both classes may interact with the
* InputDispatchPolicy, however.
@@ -65,10 +65,10 @@
virtual ~InputManagerInterface() { }
public:
- /* Starts the input manager threads. */
+ /* Starts the input threads. */
virtual status_t start() = 0;
- /* Stops the input manager threads and waits for them to exit. */
+ /* Stops the input threads and waits for them to exit. */
virtual status_t stop() = 0;
/* Gets the input reader. */
@@ -106,9 +106,6 @@
sp<InputClassifierInterface> mClassifier;
sp<InputDispatcherInterface> mDispatcher;
- sp<InputDispatcherThread> mDispatcherThread;
-
- void initialize();
};
} // namespace android
diff --git a/services/inputflinger/benchmarks/Android.bp b/services/inputflinger/benchmarks/Android.bp
new file mode 100644
index 0000000..385b981
--- /dev/null
+++ b/services/inputflinger/benchmarks/Android.bp
@@ -0,0 +1,22 @@
+cc_benchmark {
+ name: "inputflinger_benchmarks",
+ srcs: [
+ "InputDispatcher_benchmarks.cpp",
+ ],
+ defaults: ["inputflinger_defaults"],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libinput",
+ "libinputflinger_base",
+ "libinputreporter",
+ "liblog",
+ "libstatslog",
+ "libui",
+ "libutils",
+ ],
+ static_libs: [
+ "libinputdispatcher",
+ ],
+}
diff --git a/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
new file mode 100644
index 0000000..0d3c821
--- /dev/null
+++ b/services/inputflinger/benchmarks/InputDispatcher_benchmarks.cpp
@@ -0,0 +1,313 @@
+/*
+ * 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.
+ */
+
+#include <benchmark/benchmark.h>
+
+#include <binder/Binder.h>
+#include "../dispatcher/InputDispatcher.h"
+
+namespace android::inputdispatcher {
+
+// An arbitrary device id.
+static const int32_t DEVICE_ID = 1;
+
+// An arbitrary injector pid / uid pair that has permission to inject events.
+static const int32_t INJECTOR_PID = 999;
+static const int32_t INJECTOR_UID = 1001;
+
+static const int32_t INJECT_EVENT_TIMEOUT = 5000;
+static const int32_t DISPATCHING_TIMEOUT = 100000;
+
+static nsecs_t now() {
+ return systemTime(SYSTEM_TIME_MONOTONIC);
+}
+
+// --- FakeInputDispatcherPolicy ---
+
+class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
+public:
+ FakeInputDispatcherPolicy() {}
+
+protected:
+ virtual ~FakeInputDispatcherPolicy() {}
+
+private:
+ virtual void notifyConfigurationChanged(nsecs_t) override {}
+
+ virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&, const sp<IBinder>&,
+ const std::string& name) override {
+ ALOGE("The window is not responding : %s", name.c_str());
+ return 0;
+ }
+
+ virtual void notifyInputChannelBroken(const sp<IBinder>&) override {}
+
+ virtual void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}
+
+ virtual void getDispatcherConfiguration(InputDispatcherConfiguration* outConfig) override {
+ *outConfig = mConfig;
+ }
+
+ virtual bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override {
+ return true;
+ }
+
+ virtual void interceptKeyBeforeQueueing(const KeyEvent*, uint32_t&) override {}
+
+ virtual void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}
+
+ virtual nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent*,
+ uint32_t) override {
+ return 0;
+ }
+
+ virtual bool dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent*, uint32_t,
+ KeyEvent*) override {
+ return false;
+ }
+
+ virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}
+
+ virtual void pokeUserActivity(nsecs_t, int32_t) override {}
+
+ virtual bool checkInjectEventsPermissionNonReentrant(int32_t, int32_t) override {
+ return false;
+ }
+
+ virtual void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}
+
+ InputDispatcherConfiguration mConfig;
+};
+
+class FakeApplicationHandle : public InputApplicationHandle {
+public:
+ FakeApplicationHandle() {}
+ virtual ~FakeApplicationHandle() {}
+
+ virtual bool updateInfo() {
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ return true;
+ }
+};
+
+class FakeInputReceiver {
+public:
+ void consumeEvent() {
+ uint32_t consumeSeq;
+ InputEvent* event;
+
+ status_t result = WOULD_BLOCK;
+ while (result == WOULD_BLOCK) {
+ result = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
+ &event);
+ }
+ if (result != OK) {
+ ALOGE("Received result = %d from consume()", result);
+ }
+ result = mConsumer->sendFinishedSignal(consumeSeq, true);
+ if (result != OK) {
+ ALOGE("Received result = %d from sendFinishedSignal", result);
+ }
+ }
+
+protected:
+ explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher, const std::string name)
+ : mDispatcher(dispatcher) {
+ InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
+ mConsumer = std::make_unique<InputConsumer>(mClientChannel);
+ }
+
+ virtual ~FakeInputReceiver() {}
+
+ sp<InputDispatcher> mDispatcher;
+ sp<InputChannel> mServerChannel, mClientChannel;
+ std::unique_ptr<InputConsumer> mConsumer;
+ PreallocatedInputEventFactory mEventFactory;
+};
+
+class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
+public:
+ static const int32_t WIDTH = 200;
+ static const int32_t HEIGHT = 200;
+
+ FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputDispatcher>& dispatcher, const std::string name)
+ : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
+ mDispatcher->registerInputChannel(mServerChannel);
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+ }
+
+ virtual bool updateInfo() override {
+ mInfo.token = mServerChannel->getConnectionToken();
+ mInfo.name = "FakeWindowHandle";
+ mInfo.layoutParamsFlags = 0;
+ mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.frameLeft = mFrame.left;
+ mInfo.frameTop = mFrame.top;
+ mInfo.frameRight = mFrame.right;
+ mInfo.frameBottom = mFrame.bottom;
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(mFrame);
+ mInfo.visible = true;
+ mInfo.canReceiveKeys = true;
+ mInfo.hasFocus = true;
+ mInfo.hasWallpaper = false;
+ mInfo.paused = false;
+ mInfo.ownerPid = INJECTOR_PID;
+ mInfo.ownerUid = INJECTOR_UID;
+ mInfo.inputFeatures = 0;
+ mInfo.displayId = ADISPLAY_ID_DEFAULT;
+
+ return true;
+ }
+
+protected:
+ Rect mFrame;
+};
+
+static MotionEvent generateMotionEvent() {
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100);
+
+ const nsecs_t currentTime = now();
+
+ MotionEvent event;
+ event.initialize(DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ AMOTION_EVENT_ACTION_DOWN, /* actionButton */ 0, /* flags */ 0,
+ /* edgeFlags */ 0, AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
+ /* xOffset */ 0, /* yOffset */ 0, /* xPrecision */ 0,
+ /* yPrecision */ 0, AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, currentTime,
+ /*pointerCount*/ 1, pointerProperties, pointerCoords);
+ return event;
+}
+
+static NotifyMotionArgs generateMotionArgs() {
+ PointerProperties pointerProperties[1];
+ PointerCoords pointerCoords[1];
+
+ pointerProperties[0].clear();
+ pointerProperties[0].id = 0;
+ pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[0].clear();
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
+ pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 100);
+
+ const nsecs_t currentTime = now();
+ // Define a valid motion event.
+ NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, POLICY_FLAG_PASS_TO_USER, AMOTION_EVENT_ACTION_DOWN,
+ /* actionButton */ 0, /* flags */ 0, AMETA_NONE, /* buttonState */ 0,
+ MotionClassification::NONE, AMOTION_EVENT_EDGE_FLAG_NONE, 1,
+ pointerProperties, pointerCoords,
+ /* xPrecision */ 0, /* yPrecision */ 0,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION,
+ AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
+
+ return args;
+}
+
+static void benchmarkNotifyMotion(benchmark::State& state) {
+ // Create dispatcher
+ sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+ sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher->start();
+
+ // Create a window that will receive motion events
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+ dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs = generateMotionArgs();
+
+ for (auto _ : state) {
+ // Send ACTION_DOWN
+ motionArgs.action = AMOTION_EVENT_ACTION_DOWN;
+ motionArgs.sequenceNum = 0;
+ motionArgs.downTime = now();
+ motionArgs.eventTime = motionArgs.downTime;
+ dispatcher->notifyMotion(&motionArgs);
+
+ // Send ACTION_UP
+ motionArgs.action = AMOTION_EVENT_ACTION_UP;
+ motionArgs.sequenceNum = 1;
+ motionArgs.eventTime = now();
+ dispatcher->notifyMotion(&motionArgs);
+
+ window->consumeEvent();
+ window->consumeEvent();
+ }
+
+ dispatcher->stop();
+}
+
+static void benchmarkInjectMotion(benchmark::State& state) {
+ // Create dispatcher
+ sp<FakeInputDispatcherPolicy> fakePolicy = new FakeInputDispatcherPolicy();
+ sp<InputDispatcher> dispatcher = new InputDispatcher(fakePolicy);
+ dispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
+ dispatcher->start();
+
+ // Create a window that will receive motion events
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window = new FakeWindowHandle(application, dispatcher, "Fake Window");
+
+ dispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ MotionEvent event = generateMotionEvent();
+
+ for (auto _ : state) {
+ // Send ACTION_DOWN
+ event.setAction(AMOTION_EVENT_ACTION_DOWN);
+ event.setDownTime(now());
+ dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+
+ // Send ACTION_UP
+ event.setAction(AMOTION_EVENT_ACTION_UP);
+ dispatcher->injectInputEvent(&event, INJECTOR_PID, INJECTOR_UID,
+ INPUT_EVENT_INJECTION_SYNC_NONE, INJECT_EVENT_TIMEOUT,
+ POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);
+
+ window->consumeEvent();
+ window->consumeEvent();
+ }
+
+ dispatcher->stop();
+}
+
+BENCHMARK(benchmarkNotifyMotion);
+BENCHMARK(benchmarkInjectMotion);
+
+} // namespace android::inputdispatcher
+
+BENCHMARK_MAIN();
diff --git a/services/inputflinger/dispatcher/Android.bp b/services/inputflinger/dispatcher/Android.bp
index 9185e00..a556aad 100644
--- a/services/inputflinger/dispatcher/Android.bp
+++ b/services/inputflinger/dispatcher/Android.bp
@@ -21,7 +21,6 @@
"InjectionState.cpp",
"InputDispatcher.cpp",
"InputDispatcherFactory.cpp",
- "InputDispatcherThread.cpp",
"InputState.cpp",
"InputTarget.cpp",
"Monitor.cpp",
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index b9bec44..4766bce 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -240,6 +240,36 @@
return removed;
}
+static bool haveSameToken(const sp<InputWindowHandle>& first, const sp<InputWindowHandle>& second) {
+ if (first == second) {
+ return true;
+ }
+
+ if (first == nullptr || second == nullptr) {
+ return false;
+ }
+
+ return first->getToken() == second->getToken();
+}
+
+// --- InputDispatcherThread ---
+
+class InputDispatcher::InputDispatcherThread : public Thread {
+public:
+ explicit InputDispatcherThread(InputDispatcher* dispatcher)
+ : Thread(/* canCallJava */ true), mDispatcher(dispatcher) {}
+
+ ~InputDispatcherThread() {}
+
+private:
+ InputDispatcher* mDispatcher;
+
+ virtual bool threadLoop() override {
+ mDispatcher->dispatchOnce();
+ return true;
+ }
+};
+
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy)
@@ -264,6 +294,8 @@
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
+
+ mThread = new InputDispatcherThread(this);
}
InputDispatcher::~InputDispatcher() {
@@ -281,6 +313,28 @@
}
}
+status_t InputDispatcher::start() {
+ if (mThread->isRunning()) {
+ return ALREADY_EXISTS;
+ }
+ return mThread->run("InputDispatcher", PRIORITY_URGENT_DISPLAY);
+}
+
+status_t InputDispatcher::stop() {
+ if (!mThread->isRunning()) {
+ return OK;
+ }
+ if (gettid() == mThread->getTid()) {
+ ALOGE("InputDispatcher can only be stopped from outside of the InputDispatcherThread!");
+ return INVALID_OPERATION;
+ }
+ // Directly calling requestExitAndWait() causes the thread to not exit
+ // if mLooper is waiting for a long timeout.
+ mThread->requestExit();
+ mLooper->wake();
+ return mThread->requestExitAndWait();
+}
+
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
@@ -3236,9 +3290,9 @@
// Since we compare the pointer of input window handles across window updates, we need
// to make sure the handle object for the same window stays unchanged across updates.
const std::vector<sp<InputWindowHandle>>& oldHandles = getWindowHandlesLocked(displayId);
- std::unordered_map<sp<IBinder>, sp<InputWindowHandle>, IBinderHash> oldHandlesByTokens;
+ std::unordered_map<int32_t /*id*/, sp<InputWindowHandle>> oldHandlesById;
for (const sp<InputWindowHandle>& handle : oldHandles) {
- oldHandlesByTokens[handle->getToken()] = handle;
+ oldHandlesById[handle->getId()] = handle;
}
std::vector<sp<InputWindowHandle>> newHandles;
@@ -3269,8 +3323,8 @@
continue;
}
- if (oldHandlesByTokens.find(handle->getToken()) != oldHandlesByTokens.end()) {
- const sp<InputWindowHandle> oldHandle = oldHandlesByTokens.at(handle->getToken());
+ if (oldHandlesById.find(handle->getId()) != oldHandlesById.end()) {
+ const sp<InputWindowHandle>& oldHandle = oldHandlesById.at(handle->getId());
oldHandle->updateFrom(handle);
newHandles.push_back(oldHandle);
} else {
@@ -3328,7 +3382,7 @@
sp<InputWindowHandle> oldFocusedWindowHandle =
getValueByKey(mFocusedWindowHandlesByDisplay, displayId);
- if (oldFocusedWindowHandle != newFocusedWindowHandle) {
+ if (!haveSameToken(oldFocusedWindowHandle, newFocusedWindowHandle)) {
if (oldFocusedWindowHandle != nullptr) {
if (DEBUG_FOCUS) {
ALOGD("Focus left window: %s in display %" PRId32,
@@ -3739,12 +3793,10 @@
dump += StringPrintf(INDENT3 "%zu: name='%s', displayId=%d, "
"portalToDisplayId=%d, paused=%s, hasFocus=%s, "
- "hasWallpaper=%s, "
- "visible=%s, canReceiveKeys=%s, flags=0x%08x, "
- "type=0x%08x, layer=%d, "
+ "hasWallpaper=%s, visible=%s, canReceiveKeys=%s, "
+ "flags=0x%08x, type=0x%08x, "
"frame=[%d,%d][%d,%d], globalScale=%f, "
- "windowScale=(%f,%f), "
- "touchableRegion=",
+ "windowScale=(%f,%f), touchableRegion=",
i, windowInfo->name.c_str(), windowInfo->displayId,
windowInfo->portalToDisplayId,
toString(windowInfo->paused),
@@ -3753,11 +3805,10 @@
toString(windowInfo->visible),
toString(windowInfo->canReceiveKeys),
windowInfo->layoutParamsFlags,
- windowInfo->layoutParamsType, windowInfo->layer,
- windowInfo->frameLeft, windowInfo->frameTop,
- windowInfo->frameRight, windowInfo->frameBottom,
- windowInfo->globalScaleFactor, windowInfo->windowXScale,
- windowInfo->windowYScale);
+ windowInfo->layoutParamsType, windowInfo->frameLeft,
+ windowInfo->frameTop, windowInfo->frameRight,
+ windowInfo->frameBottom, windowInfo->globalScaleFactor,
+ windowInfo->windowXScale, windowInfo->windowYScale);
dumpRegion(dump, windowInfo->touchableRegion);
dump += StringPrintf(", inputFeatures=0x%08x", windowInfo->inputFeatures);
dump += StringPrintf(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n",
@@ -4073,7 +4124,9 @@
options.displayId = displayId;
for (const TouchedWindow& window : state.windows) {
sp<InputChannel> channel = getInputChannelLocked(window.windowHandle->getToken());
- synthesizeCancelationEventsForInputChannelLocked(channel, options);
+ if (channel != nullptr) {
+ synthesizeCancelationEventsForInputChannelLocked(channel, options);
+ }
}
// Then clear the current touch state so we stop dispatching to them as well.
state.filterNonMonitors();
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 38f8674..96a09e3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -82,8 +82,8 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
virtual bool waitForIdle() override;
-
- virtual void dispatchOnce() override;
+ virtual status_t start() override;
+ virtual status_t stop() override;
virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override;
virtual void notifyKey(const NotifyKeyArgs* args) override;
@@ -124,6 +124,9 @@
STALE,
};
+ class InputDispatcherThread;
+ sp<InputDispatcherThread> mThread;
+
sp<InputDispatcherPolicyInterface> mPolicy;
android::InputDispatcherConfiguration mConfig;
@@ -141,6 +144,11 @@
DropReason mLastDropReason GUARDED_BY(mLock);
+ // With each iteration, InputDispatcher nominally processes one queued event,
+ // a timeout, or a response from an input consumer.
+ // This method should only be called on the input dispatcher's own thread.
+ void dispatchOnce();
+
void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock);
// Enqueues an inbound event. Returns true if mLooper->wake() should be called.
diff --git a/services/inputflinger/dispatcher/InputDispatcherThread.cpp b/services/inputflinger/dispatcher/InputDispatcherThread.cpp
deleted file mode 100644
index 18b1b8c..0000000
--- a/services/inputflinger/dispatcher/InputDispatcherThread.cpp
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.
- */
-
-#include "InputDispatcherThread.h"
-
-#include "InputDispatcherInterface.h"
-
-namespace android {
-
-InputDispatcherThread::InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher)
- : Thread(/*canCallJava*/ true), mDispatcher(dispatcher) {}
-
-InputDispatcherThread::~InputDispatcherThread() {}
-
-bool InputDispatcherThread::threadLoop() {
- mDispatcher->dispatchOnce();
- return true;
-}
-
-} // namespace android
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index db9fba6..3424f4c 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -71,12 +71,15 @@
*/
virtual bool waitForIdle() = 0;
- /* Runs a single iteration of the dispatch loop.
- * Nominally processes one queued event, a timeout, or a response from an input consumer.
+ /* Make the dispatcher start processing events.
*
- * This method should only be called on the input dispatcher thread.
+ * The dispatcher will start consuming events from the InputListenerInterface
+ * in the order that they were received.
*/
- virtual void dispatchOnce() = 0;
+ virtual status_t start() = 0;
+
+ /* Makes the dispatcher stop processing events. */
+ virtual status_t stop() = 0;
/* Injects an input event and optionally waits for sync.
* The synchronization mode determines whether the method blocks while waiting for
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherThread.h b/services/inputflinger/dispatcher/include/InputDispatcherThread.h
deleted file mode 100644
index 2604959..0000000
--- a/services/inputflinger/dispatcher/include/InputDispatcherThread.h
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
-#define _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
-
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class InputDispatcherInterface;
-
-/* Enqueues and dispatches input events, endlessly. */
-class InputDispatcherThread : public Thread {
-public:
- explicit InputDispatcherThread(const sp<InputDispatcherInterface>& dispatcher);
- ~InputDispatcherThread();
-
-private:
- virtual bool threadLoop();
-
- sp<InputDispatcherInterface> mDispatcher;
-};
-
-} // namespace android
-
-#endif // _UI_INPUT_INPUTDISPATCHER_INPUTDISPATCHERTHREAD_H
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
index f51d4a0..348a7ad 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.cpp
@@ -183,6 +183,9 @@
mParameters.handlesKeyRepeat = false;
config.tryGetProperty(String8("keyboard.handlesKeyRepeat"), mParameters.handlesKeyRepeat);
+
+ mParameters.doNotWakeByDefault = false;
+ config.tryGetProperty(String8("keyboard.doNotWakeByDefault"), mParameters.doNotWakeByDefault);
}
void KeyboardInputMapper::dumpParameters(std::string& dump) {
@@ -331,10 +334,12 @@
// Key down on external an keyboard should wake the device.
// We don't do this for internal keyboards to prevent them from waking up in your pocket.
- // For internal keyboards, the key layout file should specify the policy flags for
- // each wake key individually.
+ // For internal keyboards and devices for which the default wake behavior is explicitly
+ // prevented (e.g. TV remotes), the key layout file should specify the policy flags for each
+ // wake key individually.
// TODO: Use the input device configuration to control this behavior more finely.
- if (down && getDevice()->isExternal() && !isMediaKey(keyCode)) {
+ if (down && getDevice()->isExternal() && !mParameters.doNotWakeByDefault &&
+ !isMediaKey(keyCode)) {
policyFlags |= POLICY_FLAG_WAKE;
}
diff --git a/services/inputflinger/reader/mapper/KeyboardInputMapper.h b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
index de2a377..badbcb2 100644
--- a/services/inputflinger/reader/mapper/KeyboardInputMapper.h
+++ b/services/inputflinger/reader/mapper/KeyboardInputMapper.h
@@ -73,6 +73,7 @@
struct Parameters {
bool orientationAware;
bool handlesKeyRepeat;
+ bool doNotWakeByDefault;
} mParameters;
void configureParameters();
diff --git a/services/inputflinger/tests/Android.bp b/services/inputflinger/tests/Android.bp
index ada2266..09ecb13 100644
--- a/services/inputflinger/tests/Android.bp
+++ b/services/inputflinger/tests/Android.bp
@@ -4,6 +4,7 @@
name: "inputflinger_tests",
srcs: [
"BlockingQueue_test.cpp",
+ "EventHub_test.cpp",
"TestInputListener.cpp",
"InputClassifier_test.cpp",
"InputClassifierConverter_test.cpp",
@@ -36,4 +37,5 @@
header_libs: [
"libinputreader_headers",
],
+ require_root: true,
}
diff --git a/services/inputflinger/tests/EventHub_test.cpp b/services/inputflinger/tests/EventHub_test.cpp
new file mode 100644
index 0000000..6504738
--- /dev/null
+++ b/services/inputflinger/tests/EventHub_test.cpp
@@ -0,0 +1,238 @@
+/*
+ * 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.
+ */
+
+#include "EventHub.h"
+
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+#include <inttypes.h>
+#include <linux/uinput.h>
+#include <log/log.h>
+#include <chrono>
+
+#define TAG "EventHub_test"
+
+using android::EventHub;
+using android::EventHubInterface;
+using android::InputDeviceIdentifier;
+using android::RawEvent;
+using android::sp;
+using android::base::StringPrintf;
+using std::chrono_literals::operator""ms;
+
+static constexpr bool DEBUG = false;
+static const char* DEVICE_NAME = "EventHub Test Device";
+
+static void dumpEvents(const std::vector<RawEvent>& events) {
+ for (const RawEvent& event : events) {
+ if (event.type >= EventHubInterface::FIRST_SYNTHETIC_EVENT) {
+ switch (event.type) {
+ case EventHubInterface::DEVICE_ADDED:
+ ALOGI("Device added: %i", event.deviceId);
+ break;
+ case EventHubInterface::DEVICE_REMOVED:
+ ALOGI("Device removed: %i", event.deviceId);
+ break;
+ case EventHubInterface::FINISHED_DEVICE_SCAN:
+ ALOGI("Finished device scan.");
+ break;
+ }
+ } else {
+ ALOGI("Device %" PRId32 " : time = %" PRId64 ", type %i, code %i, value %i",
+ event.deviceId, event.when, event.type, event.code, event.value);
+ }
+ }
+}
+
+// --- EventHubTest ---
+class EventHubTest : public testing::Test {
+protected:
+ std::unique_ptr<EventHubInterface> mEventHub;
+ // We are only going to emulate a single input device currently.
+ android::base::unique_fd mDeviceFd;
+ int32_t mDeviceId;
+ virtual void SetUp() override {
+ mEventHub = std::make_unique<EventHub>();
+ consumeInitialDeviceAddedEvents();
+ createDevice();
+ mDeviceId = waitForDeviceCreation();
+ }
+ virtual void TearDown() override {
+ mDeviceFd.reset();
+ waitForDeviceClose(mDeviceId);
+ }
+
+ void createDevice();
+ /**
+ * Return the device id of the created device.
+ */
+ int32_t waitForDeviceCreation();
+ void waitForDeviceClose(int32_t deviceId);
+ void consumeInitialDeviceAddedEvents();
+ void sendEvent(uint16_t type, uint16_t code, int32_t value);
+ std::vector<RawEvent> getEvents(std::chrono::milliseconds timeout = 5ms);
+};
+
+std::vector<RawEvent> EventHubTest::getEvents(std::chrono::milliseconds timeout) {
+ static constexpr size_t EVENT_BUFFER_SIZE = 256;
+ std::array<RawEvent, EVENT_BUFFER_SIZE> eventBuffer;
+ std::vector<RawEvent> events;
+
+ while (true) {
+ size_t count =
+ mEventHub->getEvents(timeout.count(), eventBuffer.data(), eventBuffer.size());
+ if (count == 0) {
+ break;
+ }
+ events.insert(events.end(), eventBuffer.begin(), eventBuffer.begin() + count);
+ }
+ if (DEBUG) {
+ dumpEvents(events);
+ }
+ return events;
+}
+
+void EventHubTest::createDevice() {
+ mDeviceFd = android::base::unique_fd(open("/dev/uinput", O_WRONLY | O_NONBLOCK));
+ if (mDeviceFd < 0) {
+ FAIL() << "Can't open /dev/uinput :" << strerror(errno);
+ }
+
+ /**
+ * Signal which type of events this input device supports.
+ * We will emulate a keyboard here.
+ */
+ // enable key press/release event
+ if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_KEY)) {
+ ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_KEY: " << strerror(errno);
+ }
+
+ // enable set of KEY events
+ if (ioctl(mDeviceFd, UI_SET_KEYBIT, KEY_HOME)) {
+ ADD_FAILURE() << "Error in ioctl : UI_SET_KEYBIT : KEY_HOME: " << strerror(errno);
+ }
+
+ // enable synchronization event
+ if (ioctl(mDeviceFd, UI_SET_EVBIT, EV_SYN)) {
+ ADD_FAILURE() << "Error in ioctl : UI_SET_EVBIT : EV_SYN: " << strerror(errno);
+ }
+
+ struct uinput_user_dev keyboard = {};
+ strlcpy(keyboard.name, DEVICE_NAME, UINPUT_MAX_NAME_SIZE);
+ keyboard.id.bustype = BUS_USB;
+ keyboard.id.vendor = 0x01;
+ keyboard.id.product = 0x01;
+ keyboard.id.version = 1;
+
+ if (write(mDeviceFd, &keyboard, sizeof(keyboard)) < 0) {
+ FAIL() << "Could not write uinput_user_dev struct into uinput file descriptor: "
+ << strerror(errno);
+ }
+
+ if (ioctl(mDeviceFd, UI_DEV_CREATE)) {
+ FAIL() << "Error in ioctl : UI_DEV_CREATE: " << strerror(errno);
+ }
+}
+
+/**
+ * Since the test runs on a real platform, there will be existing devices
+ * in addition to the test devices being added. Therefore, when EventHub is first created,
+ * it will return a lot of "device added" type of events.
+ */
+void EventHubTest::consumeInitialDeviceAddedEvents() {
+ std::vector<RawEvent> events = getEvents(0ms);
+ std::set<int32_t /*deviceId*/> existingDevices;
+ // All of the events should be DEVICE_ADDED type, except the last one.
+ for (size_t i = 0; i < events.size() - 1; i++) {
+ const RawEvent& event = events[i];
+ EXPECT_EQ(EventHubInterface::DEVICE_ADDED, event.type);
+ existingDevices.insert(event.deviceId);
+ }
+ // None of the existing system devices should be changing while this test is run.
+ // Check that the returned device ids are unique for all of the existing devices.
+ EXPECT_EQ(existingDevices.size(), events.size() - 1);
+ // The last event should be "finished device scan"
+ EXPECT_EQ(EventHubInterface::FINISHED_DEVICE_SCAN, events[events.size() - 1].type);
+}
+
+int32_t EventHubTest::waitForDeviceCreation() {
+ // Wait a little longer than usual, to ensure input device has time to be created
+ std::vector<RawEvent> events = getEvents(20ms);
+ EXPECT_EQ(2U, events.size()); // Using "expect" because the function is non-void.
+ const RawEvent& deviceAddedEvent = events[0];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_ADDED), deviceAddedEvent.type);
+ InputDeviceIdentifier identifier = mEventHub->getDeviceIdentifier(deviceAddedEvent.deviceId);
+ const int32_t deviceId = deviceAddedEvent.deviceId;
+ EXPECT_EQ(identifier.name, DEVICE_NAME);
+ const RawEvent& finishedDeviceScanEvent = events[1];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+ finishedDeviceScanEvent.type);
+ return deviceId;
+}
+
+void EventHubTest::waitForDeviceClose(int32_t deviceId) {
+ std::vector<RawEvent> events = getEvents(20ms);
+ ASSERT_EQ(2U, events.size());
+ const RawEvent& deviceRemovedEvent = events[0];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::DEVICE_REMOVED), deviceRemovedEvent.type);
+ EXPECT_EQ(deviceId, deviceRemovedEvent.deviceId);
+ const RawEvent& finishedDeviceScanEvent = events[1];
+ EXPECT_EQ(static_cast<int32_t>(EventHubInterface::FINISHED_DEVICE_SCAN),
+ finishedDeviceScanEvent.type);
+}
+
+void EventHubTest::sendEvent(uint16_t type, uint16_t code, int32_t value) {
+ struct input_event event = {};
+ event.type = type;
+ event.code = code;
+ event.value = value;
+ event.time = {}; // uinput ignores the timestamp
+
+ if (write(mDeviceFd, &event, sizeof(input_event)) < 0) {
+ std::string msg = StringPrintf("Could not write event %" PRIu16 " %" PRIu16
+ " with value %" PRId32 " : %s",
+ type, code, value, strerror(errno));
+ ALOGE("%s", msg.c_str());
+ ADD_FAILURE() << msg.c_str();
+ }
+}
+
+/**
+ * Ensure that input_events are generated with monotonic clock.
+ * That means input_event should receive a timestamp that is in the future of the time
+ * before the event was sent.
+ * Input system uses CLOCK_MONOTONIC everywhere in the code base.
+ */
+TEST_F(EventHubTest, InputEvent_TimestampIsMonotonic) {
+ nsecs_t lastEventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ // key press
+ sendEvent(EV_KEY, KEY_HOME, 1);
+ sendEvent(EV_SYN, SYN_REPORT, 0);
+
+ // key release
+ sendEvent(EV_KEY, KEY_HOME, 0);
+ sendEvent(EV_SYN, SYN_REPORT, 0);
+
+ std::vector<RawEvent> events = getEvents();
+ ASSERT_EQ(4U, events.size()) << "Expected to receive 2 keys and 2 syncs, total of 4 events";
+ for (const RawEvent& event : events) {
+ // Cannot use strict comparison because the events may happen too quickly
+ ASSERT_LE(lastEventTime, event.when) << "Event must have occurred after the key was sent";
+ ASSERT_LT(std::chrono::nanoseconds(event.when - lastEventTime), 100ms)
+ << "Event times are too far apart";
+ lastEventTime = event.when; // Ensure all returned events are monotonic
+ }
+}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index c8d39cf..5ffc89d 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,13 +16,12 @@
#include "../dispatcher/InputDispatcher.h"
-#include <InputDispatcherThread.h>
-
#include <binder/Binder.h>
#include <input/Input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
+#include <vector>
namespace android::inputdispatcher {
@@ -39,6 +38,10 @@
static const int32_t INJECTOR_PID = 999;
static const int32_t INJECTOR_UID = 1001;
+struct PointF {
+ float x;
+ float y;
+};
// --- FakeInputDispatcherPolicy ---
@@ -201,20 +204,17 @@
protected:
sp<FakeInputDispatcherPolicy> mFakePolicy;
sp<InputDispatcher> mDispatcher;
- sp<InputDispatcherThread> mDispatcherThread;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakePolicy = new FakeInputDispatcherPolicy();
mDispatcher = new InputDispatcher(mFakePolicy);
mDispatcher->setInputDispatchMode(/*enabled*/ true, /*frozen*/ false);
//Start InputDispatcher thread
- mDispatcherThread = new InputDispatcherThread(mDispatcher);
- mDispatcherThread->run("InputDispatcherTest", PRIORITY_URGENT_DISPLAY);
+ ASSERT_EQ(OK, mDispatcher->start());
}
- virtual void TearDown() {
- mDispatcherThread->requestExit();
- mDispatcherThread.clear();
+ virtual void TearDown() override {
+ ASSERT_EQ(OK, mDispatcher->stop());
mFakePolicy.clear();
mDispatcher.clear();
}
@@ -416,6 +416,11 @@
class FakeInputReceiver {
public:
+ explicit FakeInputReceiver(const sp<InputChannel>& clientChannel, const std::string name)
+ : mName(name) {
+ mConsumer = std::make_unique<InputConsumer>(clientChannel);
+ }
+
InputEvent* consume() {
uint32_t consumeSeq;
InputEvent* event;
@@ -423,7 +428,7 @@
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,
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq,
&event);
std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
if (elapsed > 100ms) {
@@ -445,7 +450,7 @@
return nullptr;
}
- status = mConsumer->sendFinishedSignal(consumeSeq, handled());
+ status = mConsumer->sendFinishedSignal(consumeSeq, true);
if (status != OK) {
ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
@@ -483,6 +488,89 @@
}
}
+ void assertNoEvents() {
+ InputEvent* event = consume();
+ ASSERT_EQ(nullptr, event)
+ << mName.c_str()
+ << ": should not have received any events, so consume() should return NULL";
+ }
+
+ sp<IBinder> getToken() { return mConsumer->getChannel()->getConnectionToken(); }
+
+protected:
+ std::unique_ptr<InputConsumer> mConsumer;
+ PreallocatedInputEventFactory mEventFactory;
+
+ std::string mName;
+};
+
+class FakeWindowHandle : public InputWindowHandle {
+public:
+ static const int32_t WIDTH = 600;
+ static const int32_t HEIGHT = 800;
+ const std::string mName;
+
+ FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
+ const sp<InputDispatcher>& dispatcher, const std::string name,
+ int32_t displayId, sp<IBinder> token = nullptr)
+ : mName(name) {
+ if (token == nullptr) {
+ sp<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+ dispatcher->registerInputChannel(serverChannel);
+ token = serverChannel->getConnectionToken();
+ }
+
+ inputApplicationHandle->updateInfo();
+ mInfo.applicationInfo = *inputApplicationHandle->getInfo();
+
+ mInfo.token = token;
+ mInfo.id = 0;
+ mInfo.name = name;
+ mInfo.layoutParamsFlags = 0;
+ mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
+ mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
+ mInfo.frameLeft = 0;
+ mInfo.frameTop = 0;
+ mInfo.frameRight = WIDTH;
+ mInfo.frameBottom = HEIGHT;
+ mInfo.globalScaleFactor = 1.0;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(Rect(0, 0, WIDTH, HEIGHT));
+ mInfo.visible = true;
+ mInfo.canReceiveKeys = true;
+ mInfo.hasFocus = false;
+ mInfo.hasWallpaper = false;
+ mInfo.paused = false;
+ mInfo.ownerPid = INJECTOR_PID;
+ mInfo.ownerUid = INJECTOR_UID;
+ mInfo.inputFeatures = 0;
+ mInfo.displayId = displayId;
+ }
+
+ virtual bool updateInfo() { return true; }
+
+ void setFocus() { mInfo.hasFocus = true; }
+
+ void setFrame(const Rect& frame) {
+ mInfo.frameLeft = frame.left;
+ mInfo.frameTop = frame.top;
+ mInfo.frameRight = frame.right;
+ mInfo.frameBottom = frame.bottom;
+ mInfo.touchableRegion.clear();
+ mInfo.addTouchableRegion(frame);
+ }
+
+ void setLayoutParamFlags(int32_t flags) { mInfo.layoutParamsFlags = flags; }
+
+ void setId(int32_t id) { mInfo.id = id; }
+
+ void setWindowScale(float xScale, float yScale) {
+ mInfo.windowXScale = xScale;
+ mInfo.windowYScale = yScale;
+ }
+
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN, expectedDisplayId,
expectedFlags);
@@ -493,102 +581,35 @@
expectedFlags);
}
+ void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP, expectedDisplayId,
+ expectedFlags);
+ }
+
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+ int32_t expectedFlags) {
+ ASSERT_NE(mInputReceiver, nullptr) << "Invalid consume event on window with no receiver";
+ mInputReceiver->consumeEvent(expectedEventType, expectedAction, expectedDisplayId,
+ expectedFlags);
+ }
+
+ InputEvent* consume() {
+ if (mInputReceiver == nullptr) {
+ return nullptr;
+ }
+ return mInputReceiver->consume();
+ }
+
void assertNoEvents() {
- InputEvent* event = consume();
- ASSERT_EQ(nullptr, event)
- << mName.c_str()
- << ": should not have received any events, so consume() should return NULL";
+ ASSERT_NE(mInputReceiver, nullptr)
+ << "Call 'assertNoEvents' on a window with an InputReceiver";
+ mInputReceiver->assertNoEvents();
}
-protected:
- explicit FakeInputReceiver(const sp<InputDispatcher>& dispatcher,
- const std::string name, int32_t displayId) :
- mDispatcher(dispatcher), mName(name), mDisplayId(displayId) {
- InputChannel::openInputChannelPair(name, mServerChannel, mClientChannel);
- mConsumer = new InputConsumer(mClientChannel);
- }
+ sp<IBinder> getToken() { return mInfo.token; }
- virtual ~FakeInputReceiver() {
- }
-
- // return true if the event has been handled.
- virtual bool handled() {
- return false;
- }
-
- sp<InputDispatcher> mDispatcher;
- sp<InputChannel> mServerChannel, mClientChannel;
- InputConsumer *mConsumer;
- PreallocatedInputEventFactory mEventFactory;
-
- std::string mName;
- int32_t mDisplayId;
-};
-
-class FakeWindowHandle : public InputWindowHandle, public FakeInputReceiver {
-public:
- static const int32_t WIDTH = 600;
- static const int32_t HEIGHT = 800;
-
- FakeWindowHandle(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<InputDispatcher>& dispatcher, const std::string name, int32_t displayId) :
- FakeInputReceiver(dispatcher, name, displayId),
- mFocused(false), mFrame(Rect(0, 0, WIDTH, HEIGHT)), mLayoutParamFlags(0) {
- mDispatcher->registerInputChannel(mServerChannel);
-
- inputApplicationHandle->updateInfo();
- mInfo.applicationInfo = *inputApplicationHandle->getInfo();
- }
-
- virtual bool updateInfo() {
- mInfo.token = mServerChannel ? mServerChannel->getConnectionToken() : nullptr;
- mInfo.name = mName;
- mInfo.layoutParamsFlags = mLayoutParamFlags;
- mInfo.layoutParamsType = InputWindowInfo::TYPE_APPLICATION;
- mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
- mInfo.frameLeft = mFrame.left;
- mInfo.frameTop = mFrame.top;
- mInfo.frameRight = mFrame.right;
- mInfo.frameBottom = mFrame.bottom;
- mInfo.globalScaleFactor = 1.0;
- mInfo.touchableRegion.clear();
- mInfo.addTouchableRegion(mFrame);
- mInfo.visible = true;
- mInfo.canReceiveKeys = true;
- mInfo.hasFocus = mFocused;
- mInfo.hasWallpaper = false;
- mInfo.paused = false;
- mInfo.layer = 0;
- mInfo.ownerPid = INJECTOR_PID;
- mInfo.ownerUid = INJECTOR_UID;
- mInfo.inputFeatures = 0;
- mInfo.displayId = mDisplayId;
-
- return true;
- }
-
- void setFocus() {
- mFocused = true;
- }
-
- void setFrame(const Rect& frame) {
- mFrame.set(frame);
- }
-
- void setLayoutParamFlags(int32_t flags) {
- mLayoutParamFlags = flags;
- }
-
- void releaseChannel() {
- mServerChannel.clear();
- InputWindowHandle::releaseChannel();
- }
-protected:
- virtual bool handled() override { return true; }
-
- bool mFocused;
- Rect mFrame;
- int32_t mLayoutParamFlags;
+private:
+ std::unique_ptr<FakeInputReceiver> mInputReceiver;
};
static int32_t injectKeyDown(const sp<InputDispatcher>& dispatcher,
@@ -644,6 +665,11 @@
return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_DOWN, source, displayId, x, y);
}
+static int32_t injectMotionUp(const sp<InputDispatcher>& dispatcher, int32_t source,
+ int32_t displayId, int32_t x = 100, int32_t y = 200) {
+ return injectMotionEvent(dispatcher, AMOTION_EVENT_ACTION_UP, source, displayId, x, y);
+}
+
static NotifyKeyArgs generateKeyArgs(int32_t action, int32_t displayId = ADISPLAY_ID_NONE) {
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid key event.
@@ -654,31 +680,43 @@
return args;
}
-static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
- PointerProperties pointerProperties[1];
- PointerCoords pointerCoords[1];
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId,
+ const std::vector<PointF>& points) {
+ size_t pointerCount = points.size();
+ if (action == AMOTION_EVENT_ACTION_DOWN || action == AMOTION_EVENT_ACTION_UP) {
+ EXPECT_EQ(1U, pointerCount) << "Actions DOWN and UP can only contain a single pointer";
+ }
- pointerProperties[0].clear();
- pointerProperties[0].id = 0;
- pointerProperties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ PointerProperties pointerProperties[pointerCount];
+ PointerCoords pointerCoords[pointerCount];
- pointerCoords[0].clear();
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 100);
- pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 200);
+ for (size_t i = 0; i < pointerCount; i++) {
+ pointerProperties[i].clear();
+ pointerProperties[i].id = i;
+ pointerProperties[i].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+
+ pointerCoords[i].clear();
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, points[i].x);
+ pointerCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, points[i].y);
+ }
nsecs_t currentTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Define a valid motion event.
NotifyMotionArgs args(/* sequenceNum */ 0, currentTime, DEVICE_ID, source, displayId,
POLICY_FLAG_PASS_TO_USER, action, /* actionButton */ 0, /* flags */ 0,
AMETA_NONE, /* buttonState */ 0, MotionClassification::NONE,
- AMOTION_EVENT_EDGE_FLAG_NONE, 1, pointerProperties, pointerCoords,
- /* xPrecision */ 0, /* yPrecision */ 0,
+ AMOTION_EVENT_EDGE_FLAG_NONE, pointerCount, pointerProperties,
+ pointerCoords, /* xPrecision */ 0, /* yPrecision */ 0,
AMOTION_EVENT_INVALID_CURSOR_POSITION,
AMOTION_EVENT_INVALID_CURSOR_POSITION, currentTime, /* videoFrames */ {});
return args;
}
+static NotifyMotionArgs generateMotionArgs(int32_t action, int32_t source, int32_t displayId) {
+ return generateMotionArgs(action, source, displayId, {PointF{100, 200}});
+}
+
TEST_F(InputDispatcherTest, SetInputWindow_SingleWindowTouch) {
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
@@ -852,11 +890,131 @@
0 /*expectedFlags*/);
}
+class FakeMonitorReceiver {
+public:
+ FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
+ int32_t displayId, bool isGestureMonitor = false) {
+ sp<InputChannel> serverChannel, clientChannel;
+ InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
+ mInputReceiver = std::make_unique<FakeInputReceiver>(clientChannel, name);
+ dispatcher->registerInputMonitor(serverChannel, displayId, isGestureMonitor);
+ }
+
+ sp<IBinder> getToken() { return mInputReceiver->getToken(); }
+
+ void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_DOWN,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void consumeMotionUp(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
+ mInputReceiver->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_UP,
+ expectedDisplayId, expectedFlags);
+ }
+
+ void assertNoEvents() { mInputReceiver->assertNoEvents(); }
+
+private:
+ std::unique_ptr<FakeInputReceiver> mInputReceiver;
+};
+
+// Tests for gesture monitors
+TEST_F(InputDispatcherTest, GestureMonitor_ReceivesMotionEvents) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, GestureMonitor_DoesNotReceiveKeyEvents) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
+ window->setFocus();
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
+ << "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+ monitor.assertNoEvents();
+}
+
+TEST_F(InputDispatcherTest, GestureMonitor_CanPilferAfterWindowIsRemovedMidStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ FakeMonitorReceiver monitor = FakeMonitorReceiver(mDispatcher, "GM_1", ADISPLAY_ID_DEFAULT,
+ true /*isGestureMonitor*/);
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitor.consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ window->releaseChannel();
+
+ mDispatcher->pilferPointers(monitor.getToken());
+
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
+ << "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
+ monitor.consumeMotionUp(ADISPLAY_ID_DEFAULT);
+}
+
+TEST_F(InputDispatcherTest, TestMoveEvent) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT);
+
+ mDispatcher->notifyMotion(&motionArgs);
+ // Window should receive motion down event.
+ window->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ motionArgs.action = AMOTION_EVENT_ACTION_MOVE;
+ motionArgs.sequenceNum += 1;
+ motionArgs.eventTime = systemTime(SYSTEM_TIME_MONOTONIC);
+ motionArgs.pointerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X,
+ motionArgs.pointerCoords[0].getX() - 10);
+
+ mDispatcher->notifyMotion(&motionArgs);
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_MOVE, ADISPLAY_ID_DEFAULT,
+ 0 /*expectedFlags*/);
+}
+
/* Test InputDispatcher for MultiDisplay */
class InputDispatcherFocusOnTwoDisplaysTest : public InputDispatcherTest {
public:
static constexpr int32_t SECOND_DISPLAY_ID = 1;
- virtual void SetUp() {
+ virtual void SetUp() override {
InputDispatcherTest::SetUp();
application1 = new FakeApplicationHandle();
@@ -880,7 +1038,7 @@
mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
InputDispatcherTest::TearDown();
application1.clear();
@@ -939,39 +1097,30 @@
windowInSecondary->assertNoEvents();
}
-class FakeMonitorReceiver : public FakeInputReceiver, public RefBase {
-public:
- FakeMonitorReceiver(const sp<InputDispatcher>& dispatcher, const std::string name,
- int32_t displayId, bool isGestureMonitor = false)
- : FakeInputReceiver(dispatcher, name, displayId) {
- mDispatcher->registerInputMonitor(mServerChannel, displayId, isGestureMonitor);
- }
-};
-
// Test per-display input monitors for motion event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorMotionEvent_MultiDisplay) {
- sp<FakeMonitorReceiver> monitorInPrimary =
- new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
- sp<FakeMonitorReceiver> monitorInSecondary =
- new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver monitorInPrimary =
+ FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitorInSecondary =
+ FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test touch down on primary display.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
- monitorInPrimary->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ monitorInPrimary.consumeMotionDown(ADISPLAY_ID_DEFAULT);
windowInSecondary->assertNoEvents();
- monitorInSecondary->assertNoEvents();
+ monitorInSecondary.assertNoEvents();
// Test touch down on second display.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, SECOND_DISPLAY_ID))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- monitorInPrimary->assertNoEvents();
+ monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
- monitorInSecondary->consumeMotionDown(SECOND_DISPLAY_ID);
+ monitorInSecondary.consumeMotionDown(SECOND_DISPLAY_ID);
// Test inject a non-pointer motion event.
// If specific a display, it will dispatch to the focused window of particular display,
@@ -980,26 +1129,26 @@
AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_NONE))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- monitorInPrimary->assertNoEvents();
+ monitorInPrimary.assertNoEvents();
windowInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
- monitorInSecondary->consumeMotionDown(ADISPLAY_ID_NONE);
+ monitorInSecondary.consumeMotionDown(ADISPLAY_ID_NONE);
}
// Test per-display input monitors for key event.
TEST_F(InputDispatcherFocusOnTwoDisplaysTest, MonitorKeyEvent_MultiDisplay) {
//Input monitor per display.
- sp<FakeMonitorReceiver> monitorInPrimary =
- new FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
- sp<FakeMonitorReceiver> monitorInSecondary =
- new FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
+ FakeMonitorReceiver monitorInPrimary =
+ FakeMonitorReceiver(mDispatcher, "M_1", ADISPLAY_ID_DEFAULT);
+ FakeMonitorReceiver monitorInSecondary =
+ FakeMonitorReceiver(mDispatcher, "M_2", SECOND_DISPLAY_ID);
// Test inject a key down.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
windowInPrimary->assertNoEvents();
- monitorInPrimary->assertNoEvents();
+ monitorInPrimary.assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
- monitorInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
+ monitorInSecondary.consumeKeyDown(ADISPLAY_ID_NONE);
}
class InputFilterTest : public InputDispatcherTest {
@@ -1076,7 +1225,7 @@
}
class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
- virtual void SetUp() {
+ virtual void SetUp() override {
InputDispatcherTest::SetUp();
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
@@ -1101,7 +1250,7 @@
mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
InputDispatcherTest::TearDown();
mUnfocusedWindow.clear();
@@ -1162,4 +1311,132 @@
mFakePolicy->assertOnPointerDownWasNotCalled();
}
+// These tests ensures we can send touch events to a single client when there are multiple input
+// windows that point to the same client token.
+class InputDispatcherMultiWindowSameTokenTests : public InputDispatcherTest {
+ virtual void SetUp() override {
+ InputDispatcherTest::SetUp();
+
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ mWindow1 = new FakeWindowHandle(application, mDispatcher, "Fake Window 1",
+ ADISPLAY_ID_DEFAULT);
+ // Adding FLAG_NOT_TOUCH_MODAL otherwise all taps will go to the top most window.
+ // We also need FLAG_SPLIT_TOUCH or we won't be able to get touches for both windows.
+ mWindow1->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow1->setId(0);
+ mWindow1->setFrame(Rect(0, 0, 100, 100));
+
+ mWindow2 = new FakeWindowHandle(application, mDispatcher, "Fake Window 2",
+ ADISPLAY_ID_DEFAULT, mWindow1->getToken());
+ mWindow2->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL |
+ InputWindowInfo::FLAG_SPLIT_TOUCH);
+ mWindow2->setId(1);
+ mWindow2->setFrame(Rect(100, 100, 200, 200));
+
+ mDispatcher->setInputWindows({mWindow1, mWindow2}, ADISPLAY_ID_DEFAULT);
+ }
+
+protected:
+ sp<FakeWindowHandle> mWindow1;
+ sp<FakeWindowHandle> mWindow2;
+
+ // Helper function to convert the point from screen coordinates into the window's space
+ static PointF getPointInWindow(const InputWindowInfo* windowInfo, const PointF& point) {
+ float x = windowInfo->windowXScale * (point.x - windowInfo->frameLeft);
+ float y = windowInfo->windowYScale * (point.y - windowInfo->frameTop);
+ return {x, y};
+ }
+
+ void consumeMotionEvent(const sp<FakeWindowHandle>& window, int32_t expectedAction,
+ const std::vector<PointF>& points) {
+ std::string name = window->mName;
+ InputEvent* event = window->consume();
+
+ ASSERT_NE(nullptr, event) << name.c_str()
+ << ": consumer should have returned non-NULL event.";
+
+ ASSERT_EQ(AINPUT_EVENT_TYPE_MOTION, event->getType())
+ << name.c_str() << "expected " << inputEventTypeToString(AINPUT_EVENT_TYPE_MOTION)
+ << " event, got " << inputEventTypeToString(event->getType()) << " event";
+
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*event);
+ EXPECT_EQ(expectedAction, motionEvent.getAction());
+
+ for (size_t i = 0; i < points.size(); i++) {
+ float expectedX = points[i].x;
+ float expectedY = points[i].y;
+
+ EXPECT_EQ(expectedX, motionEvent.getX(i))
+ << "expected " << expectedX << " for x[" << i << "] coord of " << name.c_str()
+ << ", got " << motionEvent.getX(i);
+ EXPECT_EQ(expectedY, motionEvent.getY(i))
+ << "expected " << expectedY << " for y[" << i << "] coord of " << name.c_str()
+ << ", got " << motionEvent.getY(i);
+ }
+ }
+};
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchSameScale) {
+ // Touch Window 1
+ PointF touchedPoint = {10, 10};
+ PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+ // Release touch on Window 1
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ // consume the UP event
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+ // Touch Window 2
+ touchedPoint = {150, 150};
+ expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ PointF touchedPoint = {10, 10};
+ PointF expectedPoint = getPointInWindow(mWindow1->getInfo(), touchedPoint);
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+
+ // Release touch on Window 1
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+ // consume the UP event
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_UP, {expectedPoint});
+
+ // Touch Window 2
+ touchedPoint = {150, 150};
+ expectedPoint = getPointInWindow(mWindow2->getInfo(), touchedPoint);
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {touchedPoint});
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index d6624c9..1fc8217 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -2580,6 +2580,84 @@
AKEYCODE_DPAD_LEFT, newDisplayId));
}
+TEST_F(KeyboardInputMapperTest, ExternalDevice_WakeBehavior) {
+ // For external devices, non-media keys will trigger wake on key down. Media keys need to be
+ // marked as WAKE in the keylayout file to trigger wake.
+ mDevice->setExternal(true);
+
+ mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, 0);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, 0);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_PLAYPAUSE, 0, AKEYCODE_MEDIA_PLAY_PAUSE, POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addMapperAndConfigure(mapper);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAYPAUSE, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAYPAUSE, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
+TEST_F(KeyboardInputMapperTest, ExternalDevice_DoNotWakeByDefaultBehavior) {
+ // Tv Remote key's wake behavior is prescribed by the keylayout file.
+ mDevice->setExternal(true);
+
+ mFakeEventHub->addKey(DEVICE_ID, KEY_HOME, 0, AKEYCODE_HOME, POLICY_FLAG_WAKE);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_DOWN, 0, AKEYCODE_DPAD_DOWN, 0);
+ mFakeEventHub->addKey(DEVICE_ID, KEY_PLAY, 0, AKEYCODE_MEDIA_PLAY, POLICY_FLAG_WAKE);
+
+ KeyboardInputMapper* mapper = new KeyboardInputMapper(mDevice, AINPUT_SOURCE_KEYBOARD,
+ AINPUT_KEYBOARD_TYPE_ALPHABETIC);
+ addConfigurationProperty("keyboard.doNotWakeByDefault", "1");
+ addMapperAndConfigure(mapper);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_HOME, 1);
+ NotifyKeyArgs args;
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_HOME, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_DOWN, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_DOWN, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(uint32_t(0), args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME, EV_KEY, KEY_PLAY, 1);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+
+ process(mapper, ARBITRARY_TIME + 1, EV_KEY, KEY_PLAY, 0);
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyKeyWasCalled(&args));
+ ASSERT_EQ(POLICY_FLAG_WAKE, args.policyFlags);
+}
+
// --- CursorInputMapperTest ---
class CursorInputMapperTest : public InputMapperTest {
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 054acc5..bdecdb7 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -770,17 +770,20 @@
// After buffer info is updated, the drawingState from the real layer needs to be copied into
// the cloned. This is because some properties of drawingState can change when latchBuffer is
- // called. However, copying the drawingState would also overwrite the cloned layer's relatives.
- // Therefore, temporarily store the relatives so they can be set in the cloned drawingState
- // again.
+ // called. However, copying the drawingState would also overwrite the cloned layer's relatives
+ // and touchableRegionCrop. Therefore, temporarily store the relatives so they can be set in
+ // the cloned drawingState again.
wp<Layer> tmpZOrderRelativeOf = mDrawingState.zOrderRelativeOf;
SortedVector<wp<Layer>> tmpZOrderRelatives = mDrawingState.zOrderRelatives;
+ wp<Layer> tmpTouchableRegionCrop = mDrawingState.touchableRegionCrop;
+ InputWindowInfo tmpInputInfo = mDrawingState.inputInfo;
+
mDrawingState = clonedFrom->mDrawingState;
- // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
- // InputWindows per client token yet.
- mDrawingState.inputInfo.token = nullptr;
+
+ mDrawingState.touchableRegionCrop = tmpTouchableRegionCrop;
mDrawingState.zOrderRelativeOf = tmpZOrderRelativeOf;
mDrawingState.zOrderRelatives = tmpZOrderRelatives;
+ mDrawingState.inputInfo = tmpInputInfo;
}
} // namespace android
diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h
index 16855d2..5f5532e 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -190,7 +190,7 @@
static bool getOpacityForFormat(uint32_t format);
- // from GLES
+ // from graphics API
const uint32_t mTextureName;
bool mRefreshPending{false};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
index 0e67acf..6559ed8 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/DisplaySurface.h
@@ -45,19 +45,19 @@
// prepareFrame is called after the composition configuration is known but
// before composition takes place. The DisplaySurface can use the
// composition type to decide how to manage the flow of buffers between
- // GLES and HWC for this frame.
+ // GPU and HWC for this frame.
enum CompositionType {
COMPOSITION_UNKNOWN = 0,
- COMPOSITION_GLES = 1,
+ COMPOSITION_GPU = 1,
COMPOSITION_HWC = 2,
- COMPOSITION_MIXED = COMPOSITION_GLES | COMPOSITION_HWC
+ COMPOSITION_MIXED = COMPOSITION_GPU | COMPOSITION_HWC
};
virtual status_t prepareFrame(CompositionType compositionType) = 0;
- // Inform the surface that GLES composition is complete for this frame, and
+ // Inform the surface that GPU composition is complete for this frame, and
// the surface should make sure that HWComposer has the correct buffer for
// this frame. Some implementations may only push a new buffer to
- // HWComposer if GLES composition took place, others need to push a new
+ // HWComposer if GPU composition took place, others need to push a new
// buffer on every frame.
//
// advanceFrame must be followed by a call to onFrameCommitted before
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index 0a70165..e432b1c 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -17,6 +17,7 @@
#pragma once
#include <optional>
+#include <ostream>
#include <unordered_set>
#include <renderengine/LayerSettings.h>
@@ -115,5 +116,29 @@
using LayerFESet = std::unordered_set<sp<LayerFE>, LayerFESpHash>;
+static inline bool operator==(const LayerFE::ClientCompositionTargetSettings& lhs,
+ const LayerFE::ClientCompositionTargetSettings& rhs) {
+ return lhs.clip.hasSameRects(rhs.clip) &&
+ lhs.useIdentityTransform == rhs.useIdentityTransform &&
+ lhs.needsFiltering == rhs.needsFiltering && lhs.isSecure == rhs.isSecure &&
+ lhs.supportsProtectedContent == rhs.supportsProtectedContent &&
+ lhs.clearRegion.hasSameRects(rhs.clearRegion);
+}
+
+// Defining PrintTo helps with Google Tests.
+static inline void PrintTo(const LayerFE::ClientCompositionTargetSettings& settings,
+ ::std::ostream* os) {
+ *os << "ClientCompositionTargetSettings{";
+ *os << "\n .clip = \n";
+ PrintTo(settings.clip, os);
+ *os << "\n .useIdentityTransform = " << settings.useIdentityTransform;
+ *os << "\n .needsFiltering = " << settings.needsFiltering;
+ *os << "\n .isSecure = " << settings.isSecure;
+ *os << "\n .supportsProtectedContent = " << settings.supportsProtectedContent;
+ *os << "\n .clearRegion = ";
+ PrintTo(settings.clearRegion, os);
+ *os << "\n}";
+}
+
} // namespace compositionengine
} // namespace android
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
index 2ba781d..a64fdbf 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -79,6 +79,9 @@
// The bounds of the layer in layer local coordinates
FloatRect geomLayerBounds;
+ // length of the shadow in screen space
+ float shadowRadius;
+
/*
* Geometry state
*/
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
index 11cfccc..00baa89 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h
@@ -52,6 +52,9 @@
// The visibleRegion transformed to output space
Region outputSpaceVisibleRegion;
+ // Region cast by the layer's shadow
+ Region shadowRegion;
+
// If true, client composition will be used on this output
bool forceClientComposition{false};
diff --git a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
index 1ca03dc..e740529 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerFECompositionState.cpp
@@ -51,6 +51,9 @@
out.append(" ");
dumpVal(out, "geomLayerBounds", geomLayerBounds);
+ out.append(" ");
+ dumpVal(out, "shadowRadius", shadowRadius);
+
out.append("\n ");
dumpVal(out, "blend", toString(blendMode), blendMode);
dumpVal(out, "alpha", alpha);
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 7e5a720..28d2653 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -408,6 +408,11 @@
*/
Region transparentRegion;
+ /*
+ * shadowRegion: Region cast by the layer's shadow.
+ */
+ Region shadowRegion;
+
// handle hidden surfaces by setting the visible region to empty
if (CC_UNLIKELY(!layerFEState.isVisible)) {
return;
@@ -418,7 +423,18 @@
// Get the visible region
// TODO(b/121291683): Is it worth creating helper methods on LayerFEState
// for computations like this?
- visibleRegion.set(Rect(tr.transform(layerFEState.geomLayerBounds)));
+ const Rect visibleRect(tr.transform(layerFEState.geomLayerBounds));
+ visibleRegion.set(visibleRect);
+
+ if (layerFEState.shadowRadius > 0.0f) {
+ // if the layer casts a shadow, offset the layers visible region and
+ // calculate the shadow region.
+ const int32_t inset = layerFEState.shadowRadius * -1.0f;
+ Rect visibleRectWithShadows(visibleRect);
+ visibleRectWithShadows.inset(inset, inset, inset, inset);
+ visibleRegion.set(visibleRectWithShadows);
+ shadowRegion = visibleRegion.subtract(visibleRect);
+ }
if (visibleRegion.isEmpty()) {
return;
@@ -444,7 +460,7 @@
// Otherwise we don't try and compute the opaque region since there may
// be errors at the edges, and we treat the entire layer as
// translucent.
- opaqueRegion = visibleRegion;
+ opaqueRegion.set(visibleRect);
}
// Clip the covered region to the visible region
@@ -510,7 +526,7 @@
// Compute the visible non-transparent region
Region visibleNonTransparentRegion = visibleRegion.subtract(transparentRegion);
- // Peform the final check to see if this layer is visible on this output
+ // Perform the final check to see if this layer is visible on this output
// TODO(b/121291683): Why does this not use visibleRegion? (see outputSpaceVisibleRegion below)
const auto& outputState = getState();
Region drawRegion(outputState.transform.transform(visibleNonTransparentRegion));
@@ -519,6 +535,8 @@
return;
}
+ Region visibleNonShadowRegion = visibleRegion.subtract(shadowRegion);
+
// The layer is visible. Either reuse the existing outputLayer if we have
// one, or create a new one if we do not.
auto result = ensureOutputLayer(prevOutputLayerIndex, layer, layerFE);
@@ -529,8 +547,9 @@
outputLayerState.visibleRegion = visibleRegion;
outputLayerState.visibleNonTransparentRegion = visibleNonTransparentRegion;
outputLayerState.coveredRegion = coveredRegion;
- outputLayerState.outputSpaceVisibleRegion = outputState.transform.transform(
- outputLayerState.visibleRegion.intersect(outputState.viewport));
+ outputLayerState.outputSpaceVisibleRegion =
+ outputState.transform.transform(visibleNonShadowRegion.intersect(outputState.viewport));
+ outputLayerState.shadowRegion = shadowRegion;
}
void Output::setReleasedLayers(const compositionengine::CompositionRefreshArgs&) {
@@ -884,7 +903,7 @@
continue;
}
- bool clientComposition = layer->requiresClientComposition();
+ const bool clientComposition = layer->requiresClientComposition();
// We clear the client target for non-client composed layers if
// requested by the HWC. We skip this if the layer is not an opaque
@@ -921,8 +940,15 @@
}
}
- layer->editState().clientCompositionTimestamp = systemTime();
- clientCompositionLayers.push_back(*result);
+ // If the layer casts a shadow but the content casting the shadow is occluded, skip
+ // composing the non-shadow content and only draw the shadows.
+ const bool skipNonShadowContentComposition = clientComposition &&
+ layerState.visibleRegion.subtract(layerState.shadowRegion).isEmpty();
+
+ if (!skipNonShadowContentComposition) {
+ layer->editState().clientCompositionTimestamp = systemTime();
+ clientCompositionLayers.push_back(*result);
+ }
}
}
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
index ce0222c..82d2422 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp
@@ -231,7 +231,7 @@
* (NOTE: the matrices are multiplied in reverse order)
*/
const ui::Transform& layerTransform = layerState.geomLayerTransform;
- const ui::Transform displayTransform{outputState.orientation};
+ const ui::Transform displayTransform{outputState.transform};
const ui::Transform bufferTransform{layerState.geomBufferTransform};
ui::Transform transform(displayTransform * layerTransform * bufferTransform);
diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
index ad668b6..cc3c54c 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -51,6 +51,9 @@
dumpVal(out, "output visibleRegion", outputSpaceVisibleRegion);
out.append(" ");
+ dumpVal(out, "shadowRegion", shadowRegion);
+
+ out.append(" ");
dumpVal(out, "forceClientComposition", forceClientComposition);
dumpVal(out, "clearClientTarget", clearClientTarget);
dumpVal(out, "displayFrame", displayFrame);
diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
index 5ed21fc..949feb4 100644
--- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp
@@ -117,12 +117,12 @@
if (usesClientComposition && usesDeviceComposition) {
compositionType = DisplaySurface::COMPOSITION_MIXED;
} else if (usesClientComposition) {
- compositionType = DisplaySurface::COMPOSITION_GLES;
+ compositionType = DisplaySurface::COMPOSITION_GPU;
} else if (usesDeviceComposition) {
compositionType = DisplaySurface::COMPOSITION_HWC;
} else {
// Nothing to do -- when turning the screen off we get a frame like
- // this. Call it a HWC frame since we won't be doing any GLES work but
+ // this. Call it a HWC frame since we won't be doing any GPU work but
// will do a prepare/set cycle.
compositionType = DisplaySurface::COMPOSITION_HWC;
}
diff --git a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h
deleted file mode 100644
index 6741cc9..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 <string>
-
-#include <android-base/stringprintf.h>
-#include <gmock/gmock.h>
-
-namespace {
-
-using android::base::StringAppendF;
-using FloatRect = android::FloatRect;
-
-void dumpFloatRect(const FloatRect& rect, std::string& result, const char* name) {
- StringAppendF(&result, "%s (%f %f %f %f) ", name, rect.left, rect.top, rect.right, rect.bottom);
-}
-
-// Checks for a region match
-MATCHER_P(FloatRectEq, expected, "") {
- std::string buf;
- buf.append("FloatRects are not equal\n");
- dumpFloatRect(expected, buf, "expected rect");
- dumpFloatRect(arg, buf, "actual rect");
- *result_listener << buf;
-
- const float TOLERANCE = 1e-3f;
- return (std::fabs(expected.left - arg.left) < TOLERANCE) &&
- (std::fabs(expected.top - arg.top) < TOLERANCE) &&
- (std::fabs(expected.right - arg.right) < TOLERANCE) &&
- (std::fabs(expected.bottom - arg.bottom) < TOLERANCE);
-}
-
-} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 364661b..9971791 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -86,6 +86,9 @@
MOCK_METHOD4(setActiveConfigWithConstraints,
status_t(DisplayId, size_t, const HWC2::VsyncPeriodChangeConstraints&,
HWC2::VsyncPeriodChangeTimeline*));
+ MOCK_METHOD2(setAutoLowLatencyMode, status_t(DisplayId, bool));
+ MOCK_METHOD2(getSupportedContentTypes, status_t(DisplayId, std::vector<HWC2::ContentType>*));
+ MOCK_METHOD2(setContentType, status_t(DisplayId, HWC2::ContentType));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
index 88acd04..0e579fa 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp
@@ -23,10 +23,8 @@
#include <compositionengine/mock/Output.h>
#include <gtest/gtest.h>
-#include "FloatRectMatcher.h"
#include "MockHWC2.h"
#include "MockHWComposer.h"
-#include "RectMatcher.h"
#include "RegionMatcher.h"
namespace android::compositionengine {
@@ -159,19 +157,19 @@
mLayerFEState.geomUsesSourceCrop = false;
const FloatRect expected{};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) {
const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) {
mLayerFEState.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f};
const FloatRect expected{0.f, 0.f, 1920.f, 1080.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) {
@@ -179,7 +177,7 @@
mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
const FloatRect expected{0.f, 0.f, 1080.f, 1080.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) {
@@ -218,7 +216,7 @@
mLayerFEState.geomBufferTransform = entry.buffer;
mOutputState.orientation = entry.display;
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(entry.expected)) << "entry " << i;
+ EXPECT_THAT(calculateOutputSourceCrop(), entry.expected) << "entry " << i;
}
}
@@ -226,14 +224,14 @@
mLayerFEState.geomContentCrop = Rect{0, 0, 960, 540};
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) {
mOutputState.viewport = Rect{0, 0, 960, 540};
const FloatRect expected{0.f, 0.f, 960.f, 540.f};
- EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected));
+ EXPECT_THAT(calculateOutputSourceCrop(), expected);
}
/*
@@ -265,50 +263,50 @@
TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) {
const Rect expected{0, 0, 1920, 1080};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) {
mLayerFEState.transparentRegionHint = Region{Rect{0, 0, 1920, 1080}};
const Rect expected{0, 0, 0, 0};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) {
mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
const Rect expected{100, 200, 300, 500};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) {
mLayerFEState.geomCrop = Rect{100, 200, 300, 500};
mLayerFEState.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080);
const Rect expected{1420, 100, 1720, 300};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) {
mLayerFEState.geomCrop = Rect{};
const Rect expected{0, 0, 1920, 1080};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) {
mLayerFEState.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f};
const Rect expected{0, 0, 960, 540};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) {
mOutputState.viewport = Rect{0, 0, 960, 540};
const Rect expected{0, 0, 960, 540};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) {
mOutputState.transform = ui::Transform{HAL_TRANSFORM_ROT_90};
const Rect expected{-1080, 0, 0, 1920};
- EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected));
+ EXPECT_THAT(calculateOutputDisplayFrame(), expected);
}
/*
@@ -366,6 +364,7 @@
mLayerFEState.geomLayerTransform.set(entry.layer, 1920, 1080);
mLayerFEState.geomBufferTransform = entry.buffer;
mOutputState.orientation = entry.display;
+ mOutputState.transform = ui::Transform{entry.display};
auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
EXPECT_EQ(entry.expected, actual) << "entry " << i;
@@ -424,6 +423,7 @@
mLayerFEState.geomLayerTransform = ui::Transform{entry.layer};
mLayerFEState.geomBufferTransform = entry.buffer;
mOutputState.orientation = entry.display;
+ mOutputState.transform = ui::Transform{entry.display};
auto actual = mOutputLayer.calculateOutputRelativeBufferTransform();
EXPECT_EQ(entry.expected, actual) << "entry " << i;
@@ -766,6 +766,21 @@
mOutputLayer.writeStateToHWC(true);
}
+TEST_F(OutputLayerTest, displayInstallOrientationBufferTransformSetTo90) {
+ mLayerFEState.geomBufferUsesDisplayInverseTransform = false;
+ mLayerFEState.geomLayerTransform = ui::Transform{TR_IDENT};
+ // This test simulates a scenario where displayInstallOrientation is set to
+ // ROT_90. This only has an effect on the transform; orientation stays 0 (see
+ // DisplayDevice::setProjection).
+ mOutputState.orientation = TR_IDENT;
+ mOutputState.transform = ui::Transform{TR_ROT_90};
+ // Buffers are pre-rotated based on the transform hint (ROT_90); their
+ // geomBufferTransform is set to the inverse transform.
+ mLayerFEState.geomBufferTransform = TR_ROT_270;
+
+ EXPECT_EQ(TR_IDENT, mOutputLayer.calculateOutputRelativeBufferTransform());
+}
+
TEST_F(OutputLayerWriteStateToHWCTest, canSetPerFrameStateForSolidColor) {
mLayerFEState.compositionType = Hwc2::IComposerClient::Composition::SOLID_COLOR;
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 37b62d8..cb6c50c 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -35,16 +35,19 @@
#include "CallOrderStateMachineHelper.h"
#include "MockHWC2.h"
#include "RegionMatcher.h"
-#include "TransformMatcher.h"
namespace android::compositionengine {
namespace {
using testing::_;
using testing::ByMove;
+using testing::ByRef;
using testing::DoAll;
+using testing::ElementsAreArray;
using testing::Eq;
using testing::InSequence;
+using testing::Invoke;
+using testing::IsEmpty;
using testing::Mock;
using testing::Property;
using testing::Ref;
@@ -244,7 +247,7 @@
mOutput->setProjection(transform, orientation, frame, viewport, scissor, needsFiltering);
- EXPECT_THAT(mOutput->getState().transform, TransformEq(transform));
+ EXPECT_THAT(mOutput->getState().transform, transform);
EXPECT_EQ(orientation, mOutput->getState().orientation);
EXPECT_EQ(frame, mOutput->getState().frame);
EXPECT_EQ(viewport, mOutput->getState().viewport);
@@ -1436,6 +1439,87 @@
EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kExpectedLayerVisibleRegion));
}
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesWithShadowsTest) {
+ ui::Transform translate;
+ translate.set(50, 50);
+ mLayerFEState.geomLayerTransform = translate;
+ mLayerFEState.shadowRadius = 10.0f;
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ // half of the layer including the casting shadow is covered and opaque
+ mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 100, 260));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 100, 260));
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ const Region kExpectedDirtyRegion = Region(Rect(0, 0, 500, 500));
+ const Region kExpectedAboveCoveredRegion = Region(Rect(40, 40, 160, 260));
+ // add starting opaque region to the opaque half of the casting layer bounds
+ const Region kExpectedAboveOpaqueRegion =
+ Region(Rect(40, 40, 100, 260)).orSelf(Rect(100, 50, 150, 250));
+ const Region kExpectedLayerVisibleRegion = Region(Rect(100, 40, 160, 260));
+ const Region kExpectedoutputSpaceLayerVisibleRegion = Region(Rect(100, 50, 150, 250));
+ const Region kExpectedLayerCoveredRegion = Region(Rect(40, 40, 100, 260));
+ const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(100, 40, 160, 260));
+ const Region kExpectedLayerShadowRegion =
+ Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kExpectedDirtyRegion));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kExpectedAboveCoveredRegion));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kExpectedAboveOpaqueRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ RegionEq(kExpectedLayerVisibleNonTransparentRegion));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kExpectedLayerCoveredRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion,
+ RegionEq(kExpectedoutputSpaceLayerVisibleRegion));
+ EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+ EXPECT_FALSE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, shadowRegionOnlyTest) {
+ ui::Transform translate;
+ translate.set(50, 50);
+ mLayerFEState.geomLayerTransform = translate;
+ mLayerFEState.shadowRadius = 10.0f;
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ // Casting layer is covered by an opaque region leaving only part of its shadow to be drawn
+ mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 150, 260));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 150, 260));
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ const Region kExpectedLayerVisibleRegion = Region(Rect(150, 40, 160, 260));
+ const Region kExpectedLayerShadowRegion =
+ Region(Rect(40, 40, 160, 260)).subtractSelf(Rect(50, 50, 150, 250));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kExpectedLayerVisibleRegion));
+ EXPECT_THAT(mOutputLayerState.shadowRegion, RegionEq(kExpectedLayerShadowRegion));
+ EXPECT_TRUE(kExpectedLayerVisibleRegion.subtract(kExpectedLayerShadowRegion).isEmpty());
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifLayerWithShadowIsCovered) {
+ ui::Transform translate;
+ translate.set(50, 50);
+ mLayerFEState.geomLayerTransform = translate;
+ mLayerFEState.shadowRadius = 10.0f;
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ // Casting layer and its shadows are covered by an opaque region
+ mCoverageState.aboveCoveredLayers = Region(Rect(40, 40, 160, 260));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(40, 40, 160, 260));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
/*
* Output::present()
*/
@@ -2439,8 +2523,6 @@
mOutput.devOptRepaintFlash(mRefreshArgs);
}
-// TODO(b/144060211) - Add coverage
-
/*
* Output::finishFrame()
*/
@@ -2668,13 +2750,7 @@
*/
struct OutputComposeSurfacesTest : public testing::Test {
- static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
- static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::DISPLAY_P3;
-
- static const Rect kDefaultOutputFrame;
- static const Rect kDefaultOutputViewport;
- static const Rect kDefaultOutputScissor;
- static const mat4 kDefaultColorTransformMat;
+ using TestType = OutputComposeSurfacesTest;
struct OutputPartialMock : public OutputPartialMockBase {
// Sets up the helper functions called by the function under test to use
@@ -2692,84 +2768,404 @@
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
- mOutput.editState().frame = kDefaultOutputFrame;
- mOutput.editState().viewport = kDefaultOutputViewport;
- mOutput.editState().scissor = kDefaultOutputScissor;
- mOutput.editState().transform = ui::Transform{kDefaultOutputOrientation};
- mOutput.editState().orientation = kDefaultOutputOrientation;
- mOutput.editState().dataspace = kDefaultOutputDataspace;
- mOutput.editState().colorTransformMatrix = kDefaultColorTransformMat;
- mOutput.editState().isSecure = true;
- mOutput.editState().needsFiltering = false;
- mOutput.editState().usesClientComposition = true;
- mOutput.editState().usesDeviceComposition = false;
+ mOutput.mState.frame = kDefaultOutputFrame;
+ mOutput.mState.viewport = kDefaultOutputViewport;
+ mOutput.mState.scissor = kDefaultOutputScissor;
+ mOutput.mState.transform = ui::Transform{kDefaultOutputOrientation};
+ mOutput.mState.orientation = kDefaultOutputOrientation;
+ mOutput.mState.dataspace = kDefaultOutputDataspace;
+ mOutput.mState.colorTransformMatrix = kDefaultColorTransformMat;
+ mOutput.mState.isSecure = false;
+ mOutput.mState.needsFiltering = false;
+ mOutput.mState.usesClientComposition = true;
+ mOutput.mState.usesDeviceComposition = false;
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&mOutputLayer1));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
- .WillRepeatedly(Return(&mOutputLayer2));
EXPECT_CALL(mOutput, getCompositionEngine()).WillRepeatedly(ReturnRef(mCompositionEngine));
EXPECT_CALL(mCompositionEngine, getRenderEngine()).WillRepeatedly(ReturnRef(mRenderEngine));
EXPECT_CALL(mCompositionEngine, getTimeStats())
.WillRepeatedly(ReturnRef(*mTimeStats.get()));
+ EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities())
+ .WillRepeatedly(ReturnRef(kHdrCapabilities));
}
+ struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+ auto execute() {
+ getInstance()->mReadyFence = getInstance()->mOutput.composeSurfaces(kDebugRegion);
+ return nextState<FenceCheckState>();
+ }
+ };
+
+ struct FenceCheckState : public CallOrderStateMachineHelper<TestType, FenceCheckState> {
+ void expectNoFenceWasReturned() { EXPECT_FALSE(getInstance()->mReadyFence); }
+
+ void expectAFenceWasReturned() { EXPECT_TRUE(getInstance()->mReadyFence); }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return ExecuteState::make(this); }
+
+ static constexpr uint32_t kDefaultOutputOrientation = TR_IDENT;
+ static constexpr ui::Dataspace kDefaultOutputDataspace = ui::Dataspace::UNKNOWN;
+ static constexpr ui::Dataspace kExpensiveOutputDataspace = ui::Dataspace::DISPLAY_P3;
+ static constexpr float kDefaultMaxLuminance = 0.9f;
+ static constexpr float kDefaultAvgLuminance = 0.7f;
+ static constexpr float kDefaultMinLuminance = 0.1f;
+
+ static const Rect kDefaultOutputFrame;
+ static const Rect kDefaultOutputViewport;
+ static const Rect kDefaultOutputScissor;
+ static const mat4 kDefaultColorTransformMat;
+
+ static const Region kDebugRegion;
+ static const HdrCapabilities kHdrCapabilities;
+
StrictMock<mock::CompositionEngine> mCompositionEngine;
StrictMock<renderengine::mock::RenderEngine> mRenderEngine;
// TODO: make this is a proper mock.
std::shared_ptr<TimeStats> mTimeStats = std::make_shared<android::impl::TimeStats>();
mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
- StrictMock<mock::OutputLayer> mOutputLayer1;
- StrictMock<mock::OutputLayer> mOutputLayer2;
StrictMock<OutputPartialMock> mOutput;
sp<GraphicBuffer> mOutputBuffer = new GraphicBuffer();
+
+ std::optional<base::unique_fd> mReadyFence;
};
const Rect OutputComposeSurfacesTest::kDefaultOutputFrame{1001, 1002, 1003, 1004};
const Rect OutputComposeSurfacesTest::kDefaultOutputViewport{1005, 1006, 1007, 1008};
const Rect OutputComposeSurfacesTest::kDefaultOutputScissor{1009, 1010, 1011, 1012};
const mat4 OutputComposeSurfacesTest::kDefaultColorTransformMat{mat4() * 0.5};
-
-// TODO(b/121291683): Expand unit test coverage for composeSurfaces beyond these
-// basic tests.
+const Region OutputComposeSurfacesTest::kDebugRegion{Rect{100, 101, 102, 103}};
+const HdrCapabilities OutputComposeSurfacesTest::
+ kHdrCapabilities{{},
+ OutputComposeSurfacesTest::kDefaultMaxLuminance,
+ OutputComposeSurfacesTest::kDefaultAvgLuminance,
+ OutputComposeSurfacesTest::kDefaultMinLuminance};
TEST_F(OutputComposeSurfacesTest, doesNothingIfNoClientComposition) {
- mOutput.editState().usesClientComposition = false;
+ mOutput.mState.usesClientComposition = false;
- Region debugRegion;
- std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(debugRegion);
- EXPECT_TRUE(readyFence);
+ verify().execute().expectAFenceWasReturned();
}
-TEST_F(OutputComposeSurfacesTest, worksIfNoClientLayersQueued) {
- const Region kDebugRegion{Rect{100, 101, 102, 103}};
+TEST_F(OutputComposeSurfacesTest, doesMinimalWorkIfDequeueBufferFails) {
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
- constexpr float kDefaultMaxLuminance = 1.0f;
- constexpr float kDefaultAvgLuminance = 0.7f;
- constexpr float kDefaultMinLuminance = 0.1f;
- HdrCapabilities HdrCapabilities{{},
- kDefaultMaxLuminance,
- kDefaultAvgLuminance,
- kDefaultMinLuminance};
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(nullptr));
- EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillOnce(Return(false));
- EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).Times(1);
+ verify().execute().expectNoFenceWasReturned();
+}
- EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillOnce(Return(true));
- EXPECT_CALL(*mDisplayColorProfile, getHdrCapabilities()).WillOnce(ReturnRef(HdrCapabilities));
+TEST_F(OutputComposeSurfacesTest, handlesZeroCompositionRequests) {
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
- EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, IsEmpty(), _, true, _, _))
+ .WillRepeatedly(Return(NO_ERROR));
- EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _, _)).Times(1);
- EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _)).Times(1);
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true)).Times(1);
- EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false)).Times(1);
+ verify().execute().expectAFenceWasReturned();
+}
- std::optional<base::unique_fd> readyFence = mOutput.composeSurfaces(kDebugRegion);
- EXPECT_TRUE(readyFence);
+TEST_F(OutputComposeSurfacesTest, buildsAndRendersRequestList) {
+ renderengine::LayerSettings r1;
+ renderengine::LayerSettings r2;
+
+ r1.geometry.boundaries = FloatRect{1, 2, 3, 4};
+ r2.geometry.boundaries = FloatRect{5, 6, 7, 8};
+
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{r1}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(
+ Invoke([&](const Region&,
+ std::vector<renderengine::LayerSettings>& clientCompositionLayers) {
+ clientCompositionLayers.emplace_back(r2);
+ }));
+
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, ElementsAreArray({r1, r2}), _, true, _, _))
+ .WillRepeatedly(Return(NO_ERROR));
+
+ verify().execute().expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_UsesExpectedDisplaySettings : public OutputComposeSurfacesTest {
+ OutputComposeSurfacesTest_UsesExpectedDisplaySettings() {
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kDefaultOutputDataspace))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ }
+
+ struct MixedCompositionState
+ : public CallOrderStateMachineHelper<TestType, MixedCompositionState> {
+ auto ifMixedCompositionIs(bool used) {
+ getInstance()->mOutput.mState.usesDeviceComposition = used;
+ return nextState<OutputUsesHdrState>();
+ }
+ };
+
+ struct OutputUsesHdrState : public CallOrderStateMachineHelper<TestType, OutputUsesHdrState> {
+ auto andIfUsesHdr(bool used) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasWideColorGamut())
+ .WillOnce(Return(used));
+ return nextState<SkipColorTransformState>();
+ }
+ };
+
+ struct SkipColorTransformState
+ : public CallOrderStateMachineHelper<TestType, SkipColorTransformState> {
+ auto andIfSkipColorTransform(bool skip) {
+ // May be called zero or one times.
+ EXPECT_CALL(getInstance()->mOutput, getSkipColorTransform())
+ .WillRepeatedly(Return(skip));
+ return nextState<ExpectDisplaySettingsState>();
+ }
+ };
+
+ struct ExpectDisplaySettingsState
+ : public CallOrderStateMachineHelper<TestType, ExpectDisplaySettingsState> {
+ auto thenExpectDisplaySettingsUsed(renderengine::DisplaySettings settings) {
+ EXPECT_CALL(getInstance()->mRenderEngine, drawLayers(settings, _, _, true, _, _))
+ .WillOnce(Return(NO_ERROR));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return MixedCompositionState::make(this); }
+};
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrMixedComposition) {
+ verify().ifMixedCompositionIs(true)
+ .andIfUsesHdr(true)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+ Region::INVALID_REGION, kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrMixedComposition) {
+ verify().ifMixedCompositionIs(true)
+ .andIfUsesHdr(false)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+ Region::INVALID_REGION, kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forHdrOnlyClientComposition) {
+ verify().ifMixedCompositionIs(false)
+ .andIfUsesHdr(true)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace,
+ kDefaultColorTransformMat, Region::INVALID_REGION,
+ kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings, forNonHdrOnlyClientComposition) {
+ verify().ifMixedCompositionIs(false)
+ .andIfUsesHdr(false)
+ .andIfSkipColorTransform(false)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace,
+ kDefaultColorTransformMat, Region::INVALID_REGION,
+ kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+TEST_F(OutputComposeSurfacesTest_UsesExpectedDisplaySettings,
+ usesExpectedDisplaySettingsForHdrOnlyClientCompositionWithSkipClientTransform) {
+ verify().ifMixedCompositionIs(false)
+ .andIfUsesHdr(true)
+ .andIfSkipColorTransform(true)
+ .thenExpectDisplaySettingsUsed({kDefaultOutputScissor, kDefaultOutputScissor, mat4(),
+ kDefaultMaxLuminance, kDefaultOutputDataspace, mat4(),
+ Region::INVALID_REGION, kDefaultOutputOrientation})
+ .execute()
+ .expectAFenceWasReturned();
+}
+
+struct OutputComposeSurfacesTest_HandlesProtectedContent : public OutputComposeSurfacesTest {
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
+ EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ }
+
+ StrictMock<mock::OutputLayer> mOutputLayer;
+ StrictMock<mock::Layer> mLayer;
+ LayerFECompositionState mLayerFEState;
+ };
+
+ OutputComposeSurfacesTest_HandlesProtectedContent() {
+ mLayer1.mLayerFEState.hasProtectedContent = false;
+ mLayer2.mLayerFEState.hasProtectedContent = false;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, _))
+ .WillRepeatedly(Return(std::vector<renderengine::LayerSettings>{}));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _))
+ .WillRepeatedly(Return(NO_ERROR));
+ }
+
+ Layer mLayer1;
+ Layer mLayer2;
+};
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifDisplayIsNotSecure) {
+ mOutput.mState.isSecure = false;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifRenderEngineDoesNotSupportIt) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNoProtectedContentLayers) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = false;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(false));
+ EXPECT_CALL(*mRenderSurface, setProtected(false));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifNotEnabled) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+
+ // For this test, we also check the call order of key functions.
+ InSequence seq;
+
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, setProtected(true));
+ // Must happen after setting the protected content state.
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledEverywhere) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifFailsToEnableInRenderEngine) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false)).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderEngine) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(true)).WillOnce(Return(true));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, setProtected(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+TEST_F(OutputComposeSurfacesTest_HandlesProtectedContent, ifAlreadyEnabledInRenderSurface) {
+ mOutput.mState.isSecure = true;
+ mLayer2.mLayerFEState.hasProtectedContent = true;
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, isProtected).WillOnce(Return(false));
+ EXPECT_CALL(*mRenderSurface, isProtected).WillOnce(Return(true));
+ EXPECT_CALL(mRenderEngine, useProtectedContext(true));
+
+ mOutput.composeSurfaces(kDebugRegion);
+}
+
+struct OutputComposeSurfacesTest_SetsExpensiveRendering : public OutputComposeSurfacesTest {
+ OutputComposeSurfacesTest_SetsExpensiveRendering() {
+ EXPECT_CALL(mOutput, getSkipColorTransform()).WillRepeatedly(Return(false));
+ EXPECT_CALL(*mDisplayColorProfile, hasWideColorGamut()).WillRepeatedly(Return(true));
+ EXPECT_CALL(mRenderEngine, supportsProtectedContent()).WillRepeatedly(Return(false));
+ EXPECT_CALL(mOutput, appendRegionFlashRequests(RegionEq(kDebugRegion), _))
+ .WillRepeatedly(Return());
+ EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillRepeatedly(Return(mOutputBuffer));
+ }
+};
+
+TEST_F(OutputComposeSurfacesTest_SetsExpensiveRendering, IfExepensiveOutputDataspaceIsUsed) {
+ mOutput.mState.dataspace = kExpensiveOutputDataspace;
+
+ EXPECT_CALL(mOutput, generateClientCompositionRequests(_, _, kExpensiveOutputDataspace))
+ .WillOnce(Return(std::vector<renderengine::LayerSettings>{}));
+
+ // For this test, we also check the call order of key functions.
+ InSequence seq;
+
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(true));
+ EXPECT_CALL(mRenderEngine, drawLayers(_, _, _, true, _, _)).WillOnce(Return(NO_ERROR));
+ EXPECT_CALL(mOutput, setExpensiveRenderingExpected(false));
+
+ mOutput.composeSurfaces(kDebugRegion);
}
/*
@@ -2787,7 +3183,26 @@
}
};
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ 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;
+ impl::OutputLayerCompositionState mOutputLayerState;
+ renderengine::LayerSettings mRELayerSettings;
+ };
+
GenerateClientCompositionRequestsTest() {
+ mOutput.mState.needsFiltering = false;
+
mOutput.setDisplayColorProfileForTest(
std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
@@ -2798,222 +3213,569 @@
StrictMock<OutputPartialMock> mOutput;
};
-// TODO(b/121291683): Add more unit test coverage for generateClientCompositionRequests
+struct GenerateClientCompositionRequestsTest_ThreeLayers
+ : public GenerateClientCompositionRequestsTest {
+ GenerateClientCompositionRequestsTest_ThreeLayers() {
+ mOutput.mState.frame = kDisplayFrame;
+ mOutput.mState.viewport = kDisplayViewport;
+ mOutput.mState.scissor = kDisplayScissor;
+ mOutput.mState.transform = ui::Transform{kDisplayOrientation};
+ mOutput.mState.orientation = kDisplayOrientation;
+ mOutput.mState.needsFiltering = false;
+ mOutput.mState.isSecure = false;
-TEST_F(GenerateClientCompositionRequestsTest, worksForLandscapeModeSplitScreen) {
- // In split-screen landscape mode, the screen is rotated 90 degrees, with
- // one layer on the left covering the left side of the output, and one layer
- // on the right covering that side of the output.
+ for (size_t i = 0; i < mLayers.size(); i++) {
+ mLayers[i].mOutputLayerState.clearClientTarget = false;
+ mLayers[i].mOutputLayerState.visibleRegion = Region(kDisplayFrame);
+ mLayers[i].mLayerFEState.isOpaque = true;
+ mLayers[i].mRELayerSettings.geometry.boundaries =
+ FloatRect{static_cast<float>(i + 1), 0.f, 0.f, 0.f};
+ mLayers[i].mRELayerSettings.source.solidColor = {1.0f, 1.0f, 1.0f};
+ mLayers[i].mRELayerSettings.alpha = 1.0f;
+ mLayers[i].mRELayerSettings.disableBlending = false;
- StrictMock<mock::OutputLayer> leftOutputLayer;
- StrictMock<mock::OutputLayer> rightOutputLayer;
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(i))
+ .WillRepeatedly(Return(&mLayers[i].mOutputLayer));
+ EXPECT_CALL(mLayers[i].mOutputLayer, requiresClientComposition())
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mLayers[i].mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ }
- StrictMock<mock::Layer> leftLayer;
- StrictMock<mock::LayerFE> leftLayerFE;
- StrictMock<mock::Layer> rightLayer;
- StrictMock<mock::LayerFE> rightLayerFE;
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(mLayers.size()));
+ }
- impl::OutputLayerCompositionState leftOutputLayerState;
- leftOutputLayerState.clearClientTarget = false;
- leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
+ static constexpr uint32_t kDisplayOrientation = TR_IDENT;
+ static constexpr ui::Dataspace kDisplayDataspace = ui::Dataspace::UNKNOWN;
- LayerFECompositionState leftLayerFEState;
- leftLayerFEState.isOpaque = true;
+ static const Rect kDisplayFrame;
+ static const Rect kDisplayViewport;
+ static const Rect kDisplayScissor;
- const half3 leftLayerColor{1.f, 0.f, 0.f};
- renderengine::LayerSettings leftLayerRESettings;
- leftLayerRESettings.source.solidColor = leftLayerColor;
+ std::array<Layer, 3> mLayers;
+};
- impl::OutputLayerCompositionState rightOutputLayerState;
- rightOutputLayerState.clearClientTarget = false;
- rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayFrame(0, 0, 100, 200);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayViewport(0, 0, 101, 201);
+const Rect GenerateClientCompositionRequestsTest_ThreeLayers::kDisplayScissor(0, 0, 102, 202);
- LayerFECompositionState rightLayerFEState;
- rightLayerFEState.isOpaque = true;
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, handlesNoClientCompostionLayers) {
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
- const half3 rightLayerColor{0.f, 1.f, 0.f};
- renderengine::LayerSettings rightLayerRESettings;
- rightLayerRESettings.source.solidColor = rightLayerColor;
-
- EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
- EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
- EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
- EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
- EXPECT_CALL(leftLayerFE, prepareClientComposition(_)).WillOnce(Return(leftLayerRESettings));
- EXPECT_CALL(leftLayerFE, prepareShadowClientComposition(_, _, _))
- .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
- EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
-
- EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
- EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
- EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
- EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
- EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
- EXPECT_CALL(rightLayerFE, prepareShadowClientComposition(_, _, _))
- .WillOnce(Return(std::optional<renderengine::LayerSettings>()));
- EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
-
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&leftOutputLayer));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
- .WillRepeatedly(Return(&rightOutputLayer));
-
- const Rect kPortraitFrame(0, 0, 1000, 2000);
- const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitScissor(0, 0, 1000, 2000);
- const uint32_t kPortraitOrientation = TR_ROT_90;
-
- mOutput.editState().frame = kPortraitFrame;
- mOutput.editState().viewport = kPortraitViewport;
- mOutput.editState().scissor = kPortraitScissor;
- mOutput.editState().transform = ui::Transform{kPortraitOrientation};
- mOutput.editState().orientation = kPortraitOrientation;
- mOutput.editState().needsFiltering = true;
- mOutput.editState().isSecure = false;
-
- constexpr bool supportsProtectedContent = false;
- Region clearRegion;
- auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
- mOutput.getState().targetDataspace);
-
- ASSERT_EQ(2u, requests.size());
- EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
- EXPECT_EQ(rightLayerColor, requests[1].source.solidColor);
-}
-
-TEST_F(GenerateClientCompositionRequestsTest, ignoresLayersThatDoNotIntersectWithViewport) {
- // Layers whose visible region does not intersect with the viewport will be
- // skipped when generating client composition request state.
-
- StrictMock<mock::OutputLayer> outputLayer;
- StrictMock<mock::Layer> layer;
- StrictMock<mock::LayerFE> layerFE;
-
- impl::OutputLayerCompositionState outputLayerState;
- outputLayerState.clearClientTarget = false;
- outputLayerState.visibleRegion = Region{Rect{3000, 0, 4000, 1000}};
-
- LayerFECompositionState layerFEState;
- layerFEState.isOpaque = true;
-
- EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
- EXPECT_CALL(outputLayer, getLayer()).WillRepeatedly(ReturnRef(layer));
- EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
- EXPECT_CALL(outputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
- EXPECT_CALL(outputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(layer, getFEState()).WillRepeatedly(ReturnRef(layerFEState));
- EXPECT_CALL(layerFE, prepareClientComposition(_)).Times(0);
- EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
-
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u)).WillRepeatedly(Return(&outputLayer));
-
- const Rect kPortraitFrame(0, 0, 1000, 2000);
- const Rect kPortraitViewport(0, 0, 2000, 1000);
- const Rect kPortraitScissor(0, 0, 1000, 2000);
- const uint32_t kPortraitOrientation = TR_ROT_90;
-
- mOutput.editState().frame = kPortraitFrame;
- mOutput.editState().viewport = kPortraitViewport;
- mOutput.editState().scissor = kPortraitScissor;
- mOutput.editState().transform = ui::Transform{kPortraitOrientation};
- mOutput.editState().orientation = kPortraitOrientation;
- mOutput.editState().needsFiltering = true;
- mOutput.editState().isSecure = false;
-
- constexpr bool supportsProtectedContent = false;
- Region clearRegion;
- auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
- mOutput.getState().targetDataspace);
-
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
EXPECT_EQ(0u, requests.size());
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
}
-TEST_F(GenerateClientCompositionRequestsTest, clearsDeviceLayesAfterFirst) {
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, requiresVisibleRegionAfterViewportClip) {
+ mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 10, 10));
+ mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(4000, 0, 4010, 10));
+ mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 0, 0));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ EXPECT_EQ(0u, requests.size());
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, gathersClientCompositionRequests) {
+ renderengine::LayerSettings mREShadowSettings;
+ mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(_)).WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[1].mRELayerSettings));
+ EXPECT_CALL(mLayers[1].mLayerFE,
+ prepareShadowClientComposition(mLayers[1].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE,
+ prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(mREShadowSettings));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(3u, requests.size());
+ EXPECT_EQ(mLayers[1].mRELayerSettings, requests[0]);
+ EXPECT_EQ(mREShadowSettings, requests[1]);
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[2]);
+
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+
+ // Check that a timestamp was set for the layers that generated requests
+ EXPECT_TRUE(0 == mLayers[0].mOutputLayerState.clientCompositionTimestamp);
+ EXPECT_TRUE(0 != mLayers[1].mOutputLayerState.clientCompositionTimestamp);
+ EXPECT_TRUE(0 != mLayers[2].mOutputLayerState.clientCompositionTimestamp);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ onlyClientComposesClientComposedLayersIfNoClearingNeeded) {
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+ mLayers[0].mOutputLayerState.clearClientTarget = false;
+ mLayers[1].mOutputLayerState.clearClientTarget = false;
+ mLayers[2].mOutputLayerState.clearClientTarget = false;
+
+ mLayers[0].mLayerFEState.isOpaque = true;
+ mLayers[1].mLayerFEState.isOpaque = true;
+ mLayers[2].mLayerFEState.isOpaque = true;
+
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::nullopt));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(1u, requests.size());
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[0]);
+
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ onlyClientComposesClientComposedLayersIfOthersAreNotOpaque) {
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
+
+ mLayers[0].mOutputLayerState.clearClientTarget = true;
+ mLayers[1].mOutputLayerState.clearClientTarget = true;
+ mLayers[2].mOutputLayerState.clearClientTarget = true;
+
+ mLayers[0].mLayerFEState.isOpaque = false;
+ mLayers[1].mLayerFEState.isOpaque = false;
+ mLayers[2].mLayerFEState.isOpaque = false;
+
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::nullopt));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(1u, requests.size());
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[0]);
+
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqueAndNotFirst) {
// If client composition is performed with some layers set to use device
// composition, device layers after the first layer (device or client) will
// clear the frame buffer if they are opaque and if that layer has a flag
// set to do so. The first layer is skipped as the frame buffer is already
// expected to be clear.
- StrictMock<mock::OutputLayer> leftOutputLayer;
- StrictMock<mock::OutputLayer> rightOutputLayer;
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mOutputLayer, requiresClientComposition()).WillOnce(Return(true));
- StrictMock<mock::Layer> leftLayer;
- StrictMock<mock::LayerFE> leftLayerFE;
- StrictMock<mock::Layer> rightLayer;
- StrictMock<mock::LayerFE> rightLayerFE;
+ mLayers[0].mOutputLayerState.clearClientTarget = true;
+ mLayers[1].mOutputLayerState.clearClientTarget = true;
+ mLayers[2].mOutputLayerState.clearClientTarget = true;
- impl::OutputLayerCompositionState leftOutputLayerState;
- leftOutputLayerState.clearClientTarget = true;
- leftOutputLayerState.visibleRegion = Region{Rect{0, 0, 1000, 1000}};
+ mLayers[0].mLayerFEState.isOpaque = true;
+ mLayers[1].mLayerFEState.isOpaque = true;
+ mLayers[2].mLayerFEState.isOpaque = true;
- LayerFECompositionState leftLayerFEState;
- leftLayerFEState.isOpaque = true;
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[1].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareShadowClientComposition(_, _, _))
+ .WillOnce(Return(std::nullopt));
- impl::OutputLayerCompositionState rightOutputLayerState;
- rightOutputLayerState.clearClientTarget = true;
- rightOutputLayerState.visibleRegion = Region{Rect{1000, 0, 2000, 1000}};
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(2u, requests.size());
- LayerFECompositionState rightLayerFEState;
- rightLayerFEState.isOpaque = true;
+ // The second layer is expected to be rendered as alpha=0 black with no blending
+ EXPECT_EQ(mLayers[1].mRELayerSettings.geometry.boundaries, requests[0].geometry.boundaries);
+ EXPECT_FALSE(requests[0].source.buffer.buffer);
+ EXPECT_EQ((half3{0.f, 0.f, 0.f}), requests[0].source.solidColor);
+ EXPECT_EQ(half{0.f}, requests[0].alpha);
+ EXPECT_EQ(true, requests[0].disableBlending);
- const half3 rightLayerColor{0.f, 1.f, 0.f};
- renderengine::LayerSettings rightLayerRESettings;
- rightLayerRESettings.geometry.boundaries = FloatRect{456, 0, 0, 0};
- rightLayerRESettings.source.solidColor = rightLayerColor;
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[1]);
- EXPECT_CALL(leftOutputLayer, getState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
- EXPECT_CALL(leftOutputLayer, getLayer()).WillRepeatedly(ReturnRef(leftLayer));
- EXPECT_CALL(leftOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(leftLayerFE));
- EXPECT_CALL(leftOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
- EXPECT_CALL(leftOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(leftLayer, getFEState()).WillRepeatedly(ReturnRef(leftLayerFEState));
- EXPECT_CALL(leftOutputLayer, editState()).WillRepeatedly(ReturnRef(leftOutputLayerState));
+ EXPECT_THAT(accumClearRegion, RegionEq(Region(Rect(10, 11, 12, 13))));
+}
- EXPECT_CALL(rightOutputLayer, getState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
- EXPECT_CALL(rightOutputLayer, getLayer()).WillRepeatedly(ReturnRef(rightLayer));
- EXPECT_CALL(rightOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(rightLayerFE));
- EXPECT_CALL(rightOutputLayer, requiresClientComposition()).WillRepeatedly(Return(false));
- EXPECT_CALL(rightOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
- EXPECT_CALL(rightLayer, getFEState()).WillRepeatedly(ReturnRef(rightLayerFEState));
- EXPECT_CALL(rightLayerFE, prepareClientComposition(_)).WillOnce(Return(rightLayerRESettings));
- EXPECT_CALL(rightOutputLayer, editState()).WillRepeatedly(ReturnRef(rightOutputLayerState));
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ clippedVisibleRegionUsedToGenerateRequest) {
+ mLayers[0].mOutputLayerState.visibleRegion = Region(Rect(10, 10, 20, 20));
+ mLayers[1].mOutputLayerState.visibleRegion = Region(Rect(-10, -10, 30, 30));
+ mLayers[2].mOutputLayerState.visibleRegion = Region(Rect(-10, 0, 40, 4000));
- EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
- .WillRepeatedly(Return(&leftOutputLayer));
- EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
- .WillRepeatedly(Return(&rightOutputLayer));
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(Rect(10, 10, 20, 20)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(Rect(0, 0, 30, 30)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(Rect(0, 0, 40, 201)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ perLayerNeedsFilteringUsedToGenerateRequests) {
+ mOutput.mState.needsFiltering = false;
+ EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ wholeOutputNeedsFilteringUsedToGenerateRequests) {
+ mOutput.mState.needsFiltering = true;
+ EXPECT_CALL(mLayers[0].mOutputLayer, needsFiltering()).WillRepeatedly(Return(true));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ true, /* needs filtering */
+ false, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ wholeOutputSecurityUsedToGenerateRequests) {
+ mOutput.mState.isSecure = true;
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ false, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(
+ mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ protectedContentSupportUsedToGenerateRequests) {
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer0TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+ compositionengine::LayerFE::ClientCompositionTargetSettings layer2TargetSettings{
+ Region(kDisplayFrame),
+ false, /* identity transform */
+ false, /* needs filtering */
+ false, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(mLayers[0].mLayerFE, prepareClientComposition(Eq(ByRef(layer0TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[1].mLayerFE, prepareClientComposition(Eq(ByRef(layer1TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(Eq(ByRef(layer2TargetSettings))))
+ .WillOnce(Return(std::nullopt));
+
+ static_cast<void>(mOutput.generateClientCompositionRequests(true /* supportsProtectedContent */,
+ accumClearRegion,
+ kDisplayDataspace));
+}
+
+TEST_F(GenerateClientCompositionRequestsTest, handlesLandscapeModeSplitScreenRequests) {
+ // In split-screen landscape mode, the screen is rotated 90 degrees, with
+ // one layer on the left covering the left side of the output, and one layer
+ // on the right covering that side of the output.
const Rect kPortraitFrame(0, 0, 1000, 2000);
const Rect kPortraitViewport(0, 0, 2000, 1000);
const Rect kPortraitScissor(0, 0, 1000, 2000);
const uint32_t kPortraitOrientation = TR_ROT_90;
+ constexpr ui::Dataspace kOutputDataspace = ui::Dataspace::DISPLAY_P3;
- mOutput.editState().frame = kPortraitFrame;
- mOutput.editState().viewport = kPortraitViewport;
- mOutput.editState().scissor = kPortraitScissor;
- mOutput.editState().transform = ui::Transform{kPortraitOrientation};
- mOutput.editState().orientation = kPortraitOrientation;
- mOutput.editState().needsFiltering = true;
- mOutput.editState().isSecure = false;
+ mOutput.mState.frame = kPortraitFrame;
+ mOutput.mState.viewport = kPortraitViewport;
+ mOutput.mState.scissor = kPortraitScissor;
+ mOutput.mState.transform = ui::Transform{kPortraitOrientation};
+ mOutput.mState.orientation = kPortraitOrientation;
+ mOutput.mState.needsFiltering = false;
+ mOutput.mState.isSecure = true;
- constexpr bool supportsProtectedContent = false;
- Region clearRegion;
- auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
- mOutput.getState().targetDataspace);
+ Layer leftLayer;
+ Layer rightLayer;
- const half3 clearColor{0.f, 0.f, 0.f};
+ leftLayer.mOutputLayerState.clearClientTarget = false;
+ leftLayer.mOutputLayerState.visibleRegion = Region(Rect(0, 0, 1000, 1000));
+ leftLayer.mLayerFEState.isOpaque = true;
+ leftLayer.mRELayerSettings.source.solidColor = {1.f, 0.f, 0.f};
+ rightLayer.mOutputLayerState.clearClientTarget = false;
+ rightLayer.mOutputLayerState.visibleRegion = Region(Rect(1000, 0, 2000, 1000));
+ rightLayer.mLayerFEState.isOpaque = true;
+ rightLayer.mRELayerSettings.source.solidColor = {0.f, 1.f, 0.f};
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&leftLayer.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&rightLayer.mOutputLayer));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings leftLayerSettings{
+ Region(Rect(0, 0, 1000, 1000)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(leftLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(leftLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(leftLayer.mLayerFE, prepareClientComposition(Eq(ByRef(leftLayerSettings))))
+ .WillOnce(Return(leftLayer.mRELayerSettings));
+ EXPECT_CALL(leftLayer.mLayerFE,
+ prepareShadowClientComposition(leftLayer.mRELayerSettings, kPortraitViewport,
+ kOutputDataspace))
+ .WillOnce(Return(std::nullopt));
+
+ compositionengine::LayerFE::ClientCompositionTargetSettings rightLayerSettings{
+ Region(Rect(1000, 0, 2000, 1000)),
+ false, /* identity transform */
+ false, /* needs filtering */
+ true, /* secure */
+ true, /* supports protected content */
+ accumClearRegion,
+ };
+
+ EXPECT_CALL(rightLayer.mOutputLayer, requiresClientComposition()).WillRepeatedly(Return(true));
+ EXPECT_CALL(rightLayer.mOutputLayer, needsFiltering()).WillRepeatedly(Return(false));
+ EXPECT_CALL(rightLayer.mLayerFE, prepareClientComposition(Eq(ByRef(rightLayerSettings))))
+ .WillOnce(Return(rightLayer.mRELayerSettings));
+ EXPECT_CALL(rightLayer.mLayerFE,
+ prepareShadowClientComposition(rightLayer.mRELayerSettings, kPortraitViewport,
+ kOutputDataspace))
+ .WillOnce(Return(std::nullopt));
+
+ constexpr bool supportsProtectedContent = true;
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent,
+ accumClearRegion, kOutputDataspace);
+ ASSERT_EQ(2u, requests.size());
+ EXPECT_EQ(leftLayer.mRELayerSettings, requests[0]);
+ EXPECT_EQ(rightLayer.mRELayerSettings, requests[1]);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ shadowRegionOnlyVisibleSkipsContentComposition) {
+ const Rect kContentWithShadow(40, 40, 70, 90);
+ const Rect kContent(50, 50, 60, 80);
+ const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+ const Region kPartialShadowRegion = Region(kContentWithShadow).subtract(Rect(40, 40, 60, 80));
+
+ renderengine::LayerSettings mREShadowSettings;
+ mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+ mLayers[2].mOutputLayerState.visibleRegion = kPartialShadowRegion;
+ mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE,
+ prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(mREShadowSettings));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
ASSERT_EQ(1u, requests.size());
- EXPECT_EQ(456.f, requests[0].geometry.boundaries.left);
- EXPECT_EQ(clearColor, requests[0].source.solidColor);
+
+ EXPECT_EQ(mREShadowSettings, requests[0]);
+}
+
+TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers,
+ shadowRegionWithContentVisibleRequestsContentAndShadowComposition) {
+ const Rect kContentWithShadow(40, 40, 70, 90);
+ const Rect kContent(50, 50, 60, 80);
+ const Region kShadowRegion = Region(kContentWithShadow).subtract(kContent);
+ const Region kPartialContentWithPartialShadowRegion =
+ Region(kContentWithShadow).subtract(Rect(40, 40, 50, 80));
+
+ renderengine::LayerSettings mREShadowSettings;
+ mREShadowSettings.source.solidColor = {0.1f, 0.1f, 0.1f};
+
+ mLayers[2].mOutputLayerState.visibleRegion = kPartialContentWithPartialShadowRegion;
+ mLayers[2].mOutputLayerState.shadowRegion = kShadowRegion;
+
+ EXPECT_CALL(mLayers[0].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[1].mOutputLayer, requiresClientComposition()).WillOnce(Return(false));
+ EXPECT_CALL(mLayers[2].mLayerFE, prepareClientComposition(_))
+ .WillOnce(Return(mLayers[2].mRELayerSettings));
+ EXPECT_CALL(mLayers[2].mLayerFE,
+ prepareShadowClientComposition(mLayers[2].mRELayerSettings, kDisplayViewport,
+ kDisplayDataspace))
+ .WillOnce(Return(mREShadowSettings));
+
+ Region accumClearRegion(Rect(10, 11, 12, 13));
+ auto requests = mOutput.generateClientCompositionRequests(false /* supportsProtectedContent */,
+ accumClearRegion, kDisplayDataspace);
+ ASSERT_EQ(2u, requests.size());
+
+ EXPECT_EQ(mREShadowSettings, requests[0]);
+ EXPECT_EQ(mLayers[2].mRELayerSettings, requests[1]);
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
deleted file mode 100644
index d4c76bc..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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 <string>
-
-#include <android-base/stringprintf.h>
-#include <gmock/gmock.h>
-
-namespace {
-
-using android::base::StringAppendF;
-using Rect = android::Rect;
-
-void dumpRect(const Rect& rect, std::string& result, const char* name) {
- StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom);
-}
-
-// Checks for a region match
-MATCHER_P(RectEq, expected, "") {
- std::string buf;
- buf.append("Rects are not equal\n");
- dumpRect(expected, buf, "expected rect");
- dumpRect(arg, buf, "actual rect");
- *result_listener << buf;
-
- return (expected.left == arg.left) && (expected.top == arg.top) &&
- (expected.right == arg.right) && (expected.bottom == arg.bottom);
-}
-
-} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
index 5a4efa9..2adde23 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
+++ b/services/surfaceflinger/CompositionEngine/tests/RegionMatcher.h
@@ -20,24 +20,22 @@
namespace {
-// Checks for a region match
-MATCHER_P(RegionEq, expected, "") {
- std::string buf;
- buf.append("Regions are not equal\n");
- expected.dump(buf, "expected region");
- arg.dump(buf, "actual region");
- *result_listener << buf;
+using Region = android::Region;
- size_t expectedRectCount = 0;
- android::Rect const* expectedRects = expected.getArray(&expectedRectCount);
- size_t actualRectCount = 0;
- android::Rect const* actualRects = arg.getArray(&actualRectCount);
+struct RegionMatcher : public testing::MatcherInterface<const Region&> {
+ const Region expected;
- if (expectedRectCount != actualRectCount) return false;
- for (size_t i = 0; i < expectedRectCount; i++) {
- if (expectedRects[i] != actualRects[i]) return false;
+ explicit RegionMatcher(const Region& expectedValue) : expected(expectedValue) {}
+
+ bool MatchAndExplain(const Region& actual, testing::MatchResultListener*) const override {
+ return expected.hasSameRects(actual);
}
- return true;
+
+ void DescribeTo(::std::ostream* os) const override { PrintTo(expected, os); }
+};
+
+testing::Matcher<const Region&> RegionEq(const Region& expected) {
+ return MakeMatcher(new RegionMatcher(expected));
}
} // namespace
diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
index da3f4fb..fd47e45 100644
--- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp
@@ -192,8 +192,8 @@
mSurface.prepareFrame(true, true);
}
-TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGlesComposition) {
- EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GLES))
+TEST_F(RenderSurfaceTest, prepareFrameHandlesOnlyGpuComposition) {
+ EXPECT_CALL(*mDisplaySurface, prepareFrame(DisplaySurface::COMPOSITION_GPU))
.WillOnce(Return(NO_ERROR));
mSurface.prepareFrame(true, false);
diff --git a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h b/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
deleted file mode 100644
index ea07bed..0000000
--- a/services/surfaceflinger/CompositionEngine/tests/TransformMatcher.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 <string>
-
-#include <gmock/gmock.h>
-
-namespace {
-
-// Check for a transform match
-MATCHER_P(TransformEq, expected, "") {
- std::string buf;
- buf.append("Transforms are not equal\n");
- expected.dump(buf, "expected transform");
- arg.dump(buf, "actual transform");
- *result_listener << buf;
-
- const float TOLERANCE = 1e-3f;
-
- for (int i = 0; i < 3; i++) {
- for (int j = 0; j < 3; j++) {
- if (std::fabs(expected[i][j] - arg[i][j]) > TOLERANCE) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-} // namespace
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index dc71128..eb032f3 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -1281,6 +1281,45 @@
return error;
}
+V2_4::Error Composer::setAutoLowLatencyMode(Display display, bool on) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setAutoLowLatencyMode(display, on);
+}
+
+V2_4::Error Composer::getSupportedContentTypes(
+ Display displayId, std::vector<IComposerClient::ContentType>* outSupportedContentTypes) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getSupportedContentTypes(displayId,
+ [&](const auto& tmpError,
+ const auto& tmpSupportedContentTypes) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outSupportedContentTypes = tmpSupportedContentTypes;
+ });
+ return error;
+}
+
+V2_4::Error Composer::setContentType(Display display, IComposerClient::ContentType contentType) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ return mClient_2_4->setContentType(display, contentType);
+}
+
CommandReader::~CommandReader()
{
resetData();
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index 336fdd8..301f54f 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -221,6 +221,13 @@
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
+
+ virtual V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) = 0;
+ virtual V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) = 0;
+ virtual V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) = 0;
};
namespace impl {
@@ -442,6 +449,12 @@
Display display, Config config,
const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display displayId, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display displayId,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display displayId,
+ IComposerClient::ContentType contentType) 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 34254e0..12b0ddd 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -42,8 +42,6 @@
using android::Rect;
using android::Region;
using android::sp;
-using android::hardware::Return;
-using android::hardware::Void;
namespace HWC2 {
@@ -60,175 +58,8 @@
return keys.find(key) != keys.end();
}
-class ComposerCallbackBridge : public Hwc2::IComposerCallback {
-public:
- ComposerCallbackBridge(ComposerCallback* callback, int32_t sequenceId)
- : mCallback(callback), mSequenceId(sequenceId) {}
-
- Return<void> onHotplug(Hwc2::Display display,
- IComposerCallback::Connection conn) override
- {
- HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
- mCallback->onHotplugReceived(mSequenceId, display, connection);
- return Void();
- }
-
- Return<void> onRefresh(Hwc2::Display display) override
- {
- mCallback->onRefreshReceived(mSequenceId, display);
- return Void();
- }
-
- Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
- {
- 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();
- }
-
-private:
- ComposerCallback* mCallback;
- int32_t mSequenceId;
-};
-
} // namespace anonymous
-
-// Device methods
-
-Device::Device(std::unique_ptr<android::Hwc2::Composer> composer) : mComposer(std::move(composer)) {
- loadCapabilities();
-}
-
-void Device::registerCallback(ComposerCallback* callback, int32_t sequenceId) {
- if (mRegisteredCallback) {
- ALOGW("Callback already registered. Ignored extra registration "
- "attempt.");
- return;
- }
- mRegisteredCallback = true;
- sp<ComposerCallbackBridge> callbackBridge(
- new ComposerCallbackBridge(callback, sequenceId));
- mComposer->registerCallback(callbackBridge);
-}
-
-// Required by HWC2 device
-
-std::string Device::dump() const
-{
- return mComposer->dumpDebugInfo();
-}
-
-uint32_t Device::getMaxVirtualDisplayCount() const
-{
- return mComposer->getMaxVirtualDisplayCount();
-}
-
-Error Device::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
- std::vector<uint8_t>* outData) const {
- auto intError = mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
- return static_cast<Error>(intError);
-}
-
-Error Device::createVirtualDisplay(uint32_t width, uint32_t height,
- PixelFormat* format, Display** outDisplay)
-{
- ALOGI("Creating virtual display");
-
- hwc2_display_t displayId = 0;
- auto intError = mComposer->createVirtualDisplay(width, height,
- format, &displayId);
- auto error = static_cast<Error>(intError);
- if (error != Error::None) {
- return error;
- }
-
- auto display = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, displayId,
- DisplayType::Virtual);
- display->setConnected(true);
- *outDisplay = display.get();
- mDisplays.emplace(displayId, std::move(display));
- ALOGI("Created virtual display");
- return Error::None;
-}
-
-void Device::destroyDisplay(hwc2_display_t displayId)
-{
- ALOGI("Destroying display %" PRIu64, displayId);
- mDisplays.erase(displayId);
-}
-
-void Device::onHotplug(hwc2_display_t displayId, Connection connection) {
- if (connection == Connection::Connected) {
- // If we get a hotplug connected event for a display we already have,
- // destroy the display and recreate it. This will force us to requery
- // the display params and recreate all layers on that display.
- auto oldDisplay = getDisplayById(displayId);
- if (oldDisplay != nullptr && oldDisplay->isConnected()) {
- ALOGI("Hotplug connecting an already connected display."
- " Clearing old display state.");
- }
- mDisplays.erase(displayId);
-
- auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities,
- displayId, DisplayType::Physical);
- newDisplay->setConnected(true);
- mDisplays.emplace(displayId, std::move(newDisplay));
- } else if (connection == Connection::Disconnected) {
- // The display will later be destroyed by a call to
- // destroyDisplay(). For now we just mark it disconnected.
- auto display = getDisplayById(displayId);
- if (display) {
- display->setConnected(false);
- } else {
- ALOGW("Attempted to disconnect unknown display %" PRIu64,
- displayId);
- }
- }
-}
-
-// Other Device methods
-
-Display* Device::getDisplayById(hwc2_display_t id) {
- auto iter = mDisplays.find(id);
- return iter == mDisplays.end() ? nullptr : iter->second.get();
-}
-
-// Device initialization methods
-
-void Device::loadCapabilities()
-{
- static_assert(sizeof(Capability) == sizeof(int32_t),
- "Capability size has changed");
- auto capabilities = mComposer->getCapabilities();
- for (auto capability : capabilities) {
- mCapabilities.emplace(static_cast<Capability>(capability));
- }
-}
-
-Error Device::flushCommands()
-{
- return static_cast<Error>(mComposer->executeCommands());
-}
-
// Display methods
Display::~Display() = default;
@@ -799,6 +630,26 @@
return static_cast<Error>(intError);
}
+Error Display::setAutoLowLatencyMode(bool on) const {
+ auto intError = mComposer.setAutoLowLatencyMode(mId, on);
+ return static_cast<Error>(intError);
+}
+
+Error Display::getSupportedContentTypes(std::vector<ContentType>* outSupportedContentTypes) const {
+ std::vector<Hwc2::IComposerClient::ContentType> tmpSupportedContentTypes;
+ auto intError = mComposer.getSupportedContentTypes(mId, &tmpSupportedContentTypes);
+ for (Hwc2::IComposerClient::ContentType contentType : tmpSupportedContentTypes) {
+ outSupportedContentTypes->push_back(static_cast<ContentType>(contentType));
+ }
+ return static_cast<Error>(intError);
+}
+
+Error Display::setContentType(ContentType contentType) const {
+ using Hwc2_ContentType = Hwc2::IComposerClient::ContentType;
+ auto intError = mComposer.setContentType(mId, static_cast<Hwc2_ContentType>(contentType));
+ return static_cast<Error>(intError);
+}
+
// For use by Device
void Display::setConnected(bool connected) {
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index 81ae3b6..8b532e3 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,8 +38,6 @@
#include <unordered_set>
#include <vector>
-#include "../Scheduler/StrongTyping.h"
-
namespace android {
struct DisplayedFrameStats;
class Fence;
@@ -54,7 +52,6 @@
namespace HWC2 {
-class Display;
class Layer;
using VsyncPeriodChangeConstraints = hwc_vsync_period_change_constraints_t;
using VsyncPeriodChangeTimeline = hwc_vsync_period_change_timeline_t;
@@ -83,56 +80,6 @@
virtual ~ComposerCallback() = default;
};
-// C++ Wrapper around hwc2_device_t. Load all functions pointers
-// and handle callback registration.
-class Device
-{
-public:
- explicit Device(std::unique_ptr<android::Hwc2::Composer> composer);
-
- void registerCallback(ComposerCallback* callback, int32_t sequenceId);
-
- // Required by HWC2
-
- std::string dump() const;
-
- const std::unordered_set<Capability>& getCapabilities() const {
- return mCapabilities;
- };
-
- uint32_t getMaxVirtualDisplayCount() const;
- Error getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
- std::vector<uint8_t>* outData) const;
-
- Error createVirtualDisplay(uint32_t width, uint32_t height,
- android::ui::PixelFormat* format, Display** outDisplay);
- void destroyDisplay(hwc2_display_t displayId);
-
- void onHotplug(hwc2_display_t displayId, Connection connection);
-
- // Other Device methods
-
- Display* getDisplayById(hwc2_display_t id);
-
- android::Hwc2::Composer* getComposer() { return mComposer.get(); }
-
- // We buffer most state changes and flush them implicitly with
- // Display::validate, Display::present, and Display::presentOrValidate.
- // This method provides an explicit way to flush state changes to HWC.
- Error flushCommands();
-
-private:
- // Initialization methods
-
- void loadCapabilities();
-
- // Member variables
- std::unique_ptr<android::Hwc2::Composer> mComposer;
- std::unordered_set<Capability> mCapabilities;
- std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays;
- bool mRegisteredCallback = false;
-};
-
// Convenience C++ class to access hwc2_device_t Display functions directly.
class Display {
public:
@@ -285,6 +232,10 @@
const std::shared_ptr<const HWC2::Display::Config>& config,
const VsyncPeriodChangeConstraints& constraints,
VsyncPeriodChangeTimeline* outTimeline) = 0;
+ [[clang::warn_unused_result]] virtual Error setAutoLowLatencyMode(bool on) const = 0;
+ [[clang::warn_unused_result]] virtual Error getSupportedContentTypes(
+ std::vector<HWC2::ContentType>*) const = 0;
+ [[clang::warn_unused_result]] virtual Error setContentType(HWC2::ContentType) const = 0;
};
namespace impl {
@@ -348,7 +299,10 @@
Error setActiveConfigWithConstraints(const std::shared_ptr<const HWC2::Display::Config>& config,
const VsyncPeriodChangeConstraints& constraints,
VsyncPeriodChangeTimeline* outTimeline) override;
-
+ Error setAutoLowLatencyMode(bool on) const override;
+ Error getSupportedContentTypes(
+ std::vector<HWC2::ContentType>* outSupportedContentTypes) const override;
+ Error setContentType(HWC2::ContentType contentType) const override;
// Other Display methods
hwc2_display_t getId() const override { return mId; }
bool isConnected() const override { return mIsConnected; }
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
index d480f7c..0a7009b 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -65,14 +65,88 @@
#define RETURN_IF_HWC_ERROR(error, displayId, ...) \
RETURN_IF_HWC_ERROR_FOR(__FUNCTION__, error, displayId, __VA_ARGS__)
+namespace {
+
+using android::hardware::Return;
+using android::hardware::Void;
+
+class ComposerCallbackBridge : public android::Hwc2::IComposerCallback {
+public:
+ ComposerCallbackBridge(HWC2::ComposerCallback* callback, int32_t sequenceId,
+ bool vsyncSwitchingSupported)
+ : mCallback(callback),
+ mSequenceId(sequenceId),
+ mVsyncSwitchingSupported(vsyncSwitchingSupported) {}
+
+ android::hardware::Return<void> onHotplug(
+ android::Hwc2::Display display,
+ android::Hwc2::IComposerCallback::Connection conn) override {
+ HWC2::Connection connection = static_cast<HWC2::Connection>(conn);
+ mCallback->onHotplugReceived(mSequenceId, display, connection);
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onRefresh(android::Hwc2::Display display) override {
+ mCallback->onRefreshReceived(mSequenceId, display);
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onVsync(android::Hwc2::Display display,
+ int64_t timestamp) override {
+ if (!mVsyncSwitchingSupported) {
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+ } else {
+ ALOGW("Unexpected onVsync callback on composer >= 2.4, ignoring.");
+ }
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onVsync_2_4(
+ android::Hwc2::Display display, int64_t timestamp,
+ android::Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
+ if (mVsyncSwitchingSupported) {
+ // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp,
+ std::make_optional(vsyncPeriodNanos));
+ } else {
+ ALOGW("Unexpected onVsync_2_4 callback on composer <= 2.3, ignoring.");
+ }
+ return android::hardware::Void();
+ }
+
+ android::hardware::Return<void> onVsyncPeriodTimingChanged(
+ android::Hwc2::Display display,
+ const android::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 android::hardware::Void();
+ }
+
+private:
+ HWC2::ComposerCallback* mCallback;
+ const int32_t mSequenceId;
+ const bool mVsyncSwitchingSupported;
+};
+
+} // namespace
+
namespace android {
HWComposer::~HWComposer() = default;
namespace impl {
-HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer)
- : mHwcDevice(std::make_unique<HWC2::Device>(std::move(composer))) {}
+HWComposer::HWComposer(std::unique_ptr<Hwc2::Composer> composer) : mComposer(std::move(composer)) {
+ loadCapabilities();
+}
+
+HWComposer::HWComposer(const std::string& composerServiceName)
+ : mComposer(std::make_unique<Hwc2::impl::Composer>(composerServiceName)) {
+ loadCapabilities();
+}
HWComposer::~HWComposer() {
mDisplayData.clear();
@@ -80,12 +154,21 @@
void HWComposer::registerCallback(HWC2::ComposerCallback* callback,
int32_t sequenceId) {
- mHwcDevice->registerCallback(callback, sequenceId);
+ if (mRegisteredCallback) {
+ ALOGW("Callback already registered. Ignored extra registration attempt.");
+ return;
+ }
+ mRegisteredCallback = true;
+ sp<ComposerCallbackBridge> callbackBridge(
+ new ComposerCallbackBridge(callback, sequenceId,
+ mComposer->isVsyncPeriodSwitchSupported()));
+ mComposer->registerCallback(callbackBridge);
}
bool HWComposer::getDisplayIdentificationData(hwc2_display_t hwcDisplayId, uint8_t* outPort,
DisplayIdentificationData* outData) const {
- const auto error = mHwcDevice->getDisplayIdentificationData(hwcDisplayId, outPort, outData);
+ const auto error = static_cast<HWC2::Error>(
+ mComposer->getDisplayIdentificationData(hwcDisplayId, outPort, outData));
if (error != HWC2::Error::None) {
if (error != HWC2::Error::Unsupported) {
LOG_HWC_DISPLAY_ERROR(hwcDisplayId, to_string(error).c_str());
@@ -95,9 +178,8 @@
return true;
}
-bool HWComposer::hasCapability(HWC2::Capability capability) const
-{
- return mHwcDevice->getCapabilities().count(capability) > 0;
+bool HWComposer::hasCapability(HWC2::Capability capability) const {
+ return mCapabilities.count(capability) > 0;
}
bool HWComposer::hasDisplayCapability(const std::optional<DisplayId>& displayId,
@@ -133,13 +215,33 @@
hwcDisplayId == mInternalHwcDisplayId ? "internal" : "external",
to_string(info->id).c_str(), hwcDisplayId);
- mHwcDevice->onHotplug(hwcDisplayId, connection);
-
- // Disconnect is handled through HWComposer::disconnectDisplay via
- // SurfaceFlinger's onHotplugReceived callback handling
if (connection == HWC2::Connection::Connected) {
- mDisplayData[info->id].hwcDisplay = mHwcDevice->getDisplayById(hwcDisplayId);
+ auto& displayData = mDisplayData[info->id];
+ // If we get a hotplug connected event for a display we already have,
+ // destroy the display and recreate it. This will force us to requery
+ // the display params and recreate all layers on that display.
+ if (displayData.hwcDisplay != nullptr && displayData.hwcDisplay->isConnected()) {
+ ALOGI("Hotplug connecting an already connected display."
+ " Clearing old display state.");
+ }
+ displayData.hwcDisplay.reset();
+ auto newDisplay =
+ std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities, hwcDisplayId,
+ HWC2::DisplayType::Physical);
+ newDisplay->setConnected(true);
+ displayData.hwcDisplay = std::move(newDisplay);
mPhysicalDisplayIdMap[hwcDisplayId] = info->id;
+ } else if (connection == HWC2::Connection::Disconnected) {
+ // The display will later be destroyed by a call to
+ // destroyDisplay(). For now we just mark it disconnected.
+ auto& displayData = mDisplayData[info->id];
+ if (displayData.hwcDisplay) {
+ displayData.hwcDisplay->setConnected(false);
+ } else {
+ ALOGW("Attempted to disconnect unknown display %" PRIu64, hwcDisplayId);
+ }
+ // The cleanup of Disconnect is handled through HWComposer::disconnectDisplay
+ // via SurfaceFlinger's onHotplugReceived callback handling
}
return info;
@@ -197,14 +299,18 @@
height, SurfaceFlinger::maxVirtualDisplaySize);
return {};
}
- HWC2::Display* display;
- auto error = mHwcDevice->createVirtualDisplay(width, height, format,
- &display);
+ hwc2_display_t hwcDisplayId = 0;
+ const auto error = static_cast<HWC2::Error>(
+ mComposer->createVirtualDisplay(width, height, format, &hwcDisplayId));
if (error != HWC2::Error::None) {
ALOGE("%s: Failed to create HWC virtual display", __FUNCTION__);
return {};
}
+ auto display = std::make_unique<HWC2::impl::Display>(*mComposer.get(), mCapabilities,
+ hwcDisplayId, HWC2::DisplayType::Virtual);
+ display->setConnected(true);
+
DisplayId displayId;
if (mFreeVirtualDisplayIds.empty()) {
displayId = getVirtualDisplayId(mNextVirtualDisplayId++);
@@ -214,7 +320,7 @@
}
auto& displayData = mDisplayData[displayId];
- displayData.hwcDisplay = display;
+ displayData.hwcDisplay = std::move(display);
displayData.isVirtual = true;
--mRemainingHwcVirtualDisplays;
@@ -224,9 +330,8 @@
HWC2::Layer* HWComposer::createLayer(DisplayId displayId) {
RETURN_IF_INVALID_DISPLAY(displayId, nullptr);
- auto display = mDisplayData[displayId].hwcDisplay;
HWC2::Layer* layer;
- auto error = display->createLayer(&layer);
+ auto error = mDisplayData[displayId].hwcDisplay->createLayer(&layer);
RETURN_IF_HWC_ERROR(error, displayId, nullptr);
return layer;
}
@@ -234,8 +339,7 @@
void HWComposer::destroyLayer(DisplayId displayId, HWC2::Layer* layer) {
RETURN_IF_INVALID_DISPLAY(displayId);
- auto display = mDisplayData[displayId].hwcDisplay;
- auto error = display->destroyLayer(layer);
+ auto error = mDisplayData[displayId].hwcDisplay->destroyLayer(layer);
RETURN_IF_HWC_ERROR(error, displayId);
}
@@ -247,8 +351,8 @@
// the refresh period and whatever closest timestamp we have.
std::lock_guard lock(displayData.lastHwVsyncLock);
nsecs_t now = systemTime(CLOCK_MONOTONIC);
- auto vsyncPeriod = getActiveConfig(displayId)->getVsyncPeriod();
- return now - ((now - displayData.lastHwVsync) % vsyncPeriod);
+ auto vsyncPeriodNanos = getDisplayVsyncPeriod(displayId);
+ return now - ((now - displayData.lastHwVsync) % vsyncPeriodNanos);
}
bool HWComposer::isConnected(DisplayId displayId) const {
@@ -291,6 +395,23 @@
return config;
}
+// Composer 2.4
+
+bool HWComposer::isVsyncPeriodSwitchSupported(DisplayId displayId) const {
+ return mDisplayData.at(displayId).hwcDisplay->isVsyncPeriodSwitchSupported();
+}
+
+nsecs_t HWComposer::getDisplayVsyncPeriod(DisplayId displayId) const {
+ nsecs_t vsyncPeriodNanos;
+ auto error = mDisplayData.at(displayId).hwcDisplay->getDisplayVsyncPeriod(&vsyncPeriodNanos);
+ if (error != HWC2::Error::None) {
+ LOG_DISPLAY_ERROR(displayId, "Failed to get Vsync Period");
+ return 0;
+ }
+
+ return vsyncPeriodNanos;
+}
+
int HWComposer::getActiveConfigIndex(DisplayId displayId) const {
RETURN_IF_INVALID_DISPLAY(displayId, -1);
@@ -467,8 +588,8 @@
if (displayData.validateWasSkipped) {
// explicitly flush all pending commands
- auto error = mHwcDevice->flushCommands();
- RETURN_IF_HWC_ERROR_FOR("flushCommands", error, displayId, UNKNOWN_ERROR);
+ auto error = static_cast<HWC2::Error>(mComposer->executeCommands());
+ RETURN_IF_HWC_ERROR_FOR("executeCommands", error, displayId, UNKNOWN_ERROR);
RETURN_IF_HWC_ERROR_FOR("present", displayData.presentError, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
@@ -541,7 +662,9 @@
return NO_ERROR;
}
-status_t HWComposer::setActiveConfig(DisplayId displayId, size_t configId) {
+status_t HWComposer::setActiveConfigWithConstraints(
+ DisplayId displayId, size_t configId, const HWC2::VsyncPeriodChangeConstraints& constraints,
+ HWC2::VsyncPeriodChangeTimeline* outTimeline) {
RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
auto& displayData = mDisplayData[displayId];
@@ -550,7 +673,9 @@
return BAD_INDEX;
}
- auto error = displayData.hwcDisplay->setActiveConfig(displayData.configMap[configId]);
+ auto error =
+ displayData.hwcDisplay->setActiveConfigWithConstraints(displayData.configMap[configId],
+ constraints, outTimeline);
RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
return NO_ERROR;
}
@@ -579,8 +704,6 @@
}
const auto hwcDisplayId = displayData.hwcDisplay->getId();
- mPhysicalDisplayIdMap.erase(hwcDisplayId);
- mDisplayData.erase(displayId);
// TODO(b/74619554): Select internal/external display from remaining displays.
if (hwcDisplayId == mInternalHwcDisplayId) {
@@ -588,8 +711,8 @@
} else if (hwcDisplayId == mExternalHwcDisplayId) {
mExternalHwcDisplayId.reset();
}
-
- mHwcDevice->destroyDisplay(hwcDisplayId);
+ mPhysicalDisplayIdMap.erase(hwcDisplayId);
+ mDisplayData.erase(displayId);
}
status_t HWComposer::setOutputBuffer(DisplayId displayId, const sp<Fence>& acquireFence,
@@ -701,11 +824,46 @@
return getComposer()->isUsingVrComposer();
}
+status_t HWComposer::setAutoLowLatencyMode(DisplayId displayId, bool on) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setAutoLowLatencyMode(on);
+ if (error == HWC2::Error::Unsupported) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == HWC2::Error::BadParameter) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+ return NO_ERROR;
+}
+
+status_t HWComposer::getSupportedContentTypes(
+ DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error =
+ mDisplayData[displayId].hwcDisplay->getSupportedContentTypes(outSupportedContentTypes);
+
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+ return NO_ERROR;
+}
+
+status_t HWComposer::setContentType(DisplayId displayId, HWC2::ContentType contentType) {
+ RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX);
+ const auto error = mDisplayData[displayId].hwcDisplay->setContentType(contentType);
+ if (error == HWC2::Error::Unsupported) {
+ RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION);
+ }
+ if (error == HWC2::Error::BadParameter) {
+ RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE);
+ }
+ RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR);
+
+ return NO_ERROR;
+}
+
void HWComposer::dump(std::string& result) const {
- // TODO: In order to provide a dump equivalent to HWC1, we need to shadow
- // all the state going into the layers. This is probably better done in
- // Layer itself, but it's going to take a bit of work to get there.
- result.append(mHwcDevice->dump());
+ result.append(mComposer->dumpDebugInfo());
}
std::optional<DisplayId> HWComposer::toPhysicalDisplayId(hwc2_display_t hwcDisplayId) const {
@@ -771,5 +929,17 @@
: "External display"};
}
+void HWComposer::loadCapabilities() {
+ static_assert(sizeof(HWC2::Capability) == sizeof(int32_t), "Capability size has changed");
+ auto capabilities = mComposer->getCapabilities();
+ for (auto capability : capabilities) {
+ mCapabilities.emplace(static_cast<HWC2::Capability>(capability));
+ }
+}
+
+uint32_t HWComposer::getMaxVirtualDisplayCount() const {
+ return mComposer->getMaxVirtualDisplayCount();
+}
+
} // namespace impl
} // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
index e87c5c3..a0dabb4 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -102,9 +102,6 @@
// set power mode
virtual status_t setPowerMode(DisplayId displayId, int mode) = 0;
- // set active config
- virtual status_t setActiveConfig(DisplayId displayId, size_t configId) = 0;
-
// Sets a color transform to be applied to the result of composition
virtual status_t setColorTransform(DisplayId displayId, const mat4& transform) = 0;
@@ -180,6 +177,18 @@
virtual bool isUsingVrComposer() const = 0;
+ // Composer 2.4
+ virtual bool isVsyncPeriodSwitchSupported(DisplayId displayId) const = 0;
+ virtual nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const = 0;
+ virtual status_t setActiveConfigWithConstraints(
+ DisplayId displayId, size_t configId,
+ const HWC2::VsyncPeriodChangeConstraints& constraints,
+ HWC2::VsyncPeriodChangeTimeline* outTimeline) = 0;
+ virtual status_t setAutoLowLatencyMode(DisplayId displayId, bool on) = 0;
+ virtual status_t getSupportedContentTypes(
+ DisplayId displayId, std::vector<HWC2::ContentType>* outSupportedContentTypes) = 0;
+ virtual status_t setContentType(DisplayId displayId, HWC2::ContentType contentType) = 0;
+
// for debugging ----------------------------------------------------------
virtual void dump(std::string& out) const = 0;
@@ -198,6 +207,7 @@
class HWComposer final : public android::HWComposer {
public:
explicit HWComposer(std::unique_ptr<Hwc2::Composer> composer);
+ explicit HWComposer(const std::string& composerServiceName);
~HWComposer() override;
@@ -232,9 +242,6 @@
// set power mode
status_t setPowerMode(DisplayId displayId, int mode) override;
- // set active config
- status_t setActiveConfig(DisplayId displayId, size_t configId) override;
-
// Sets a color transform to be applied to the result of composition
status_t setColorTransform(DisplayId displayId, const mat4& transform) override;
@@ -305,10 +312,21 @@
bool isUsingVrComposer() const override;
+ // Composer 2.4
+ bool isVsyncPeriodSwitchSupported(DisplayId displayId) const override;
+ nsecs_t getDisplayVsyncPeriod(DisplayId displayId) const override;
+ status_t setActiveConfigWithConstraints(DisplayId displayId, size_t configId,
+ const HWC2::VsyncPeriodChangeConstraints& constraints,
+ HWC2::VsyncPeriodChangeTimeline* outTimeline) override;
+ status_t setAutoLowLatencyMode(DisplayId displayId, bool) override;
+ status_t getSupportedContentTypes(DisplayId displayId,
+ std::vector<HWC2::ContentType>*) override;
+ status_t setContentType(DisplayId displayId, HWC2::ContentType) override;
+
// for debugging ----------------------------------------------------------
void dump(std::string& out) const override;
- Hwc2::Composer* getComposer() const override { return mHwcDevice->getComposer(); }
+ Hwc2::Composer* getComposer() const override { return mComposer.get(); }
// TODO(b/74619554): Remove special cases for internal/external display.
std::optional<hwc2_display_t> getInternalHwcDisplayId() const override {
@@ -326,11 +344,12 @@
friend TestableSurfaceFlinger;
std::optional<DisplayIdentificationInfo> onHotplugConnect(hwc2_display_t hwcDisplayId);
+ void loadCapabilities();
+ uint32_t getMaxVirtualDisplayCount() const;
struct DisplayData {
bool isVirtual = false;
-
- HWC2::Display* hwcDisplay = nullptr;
+ std::unique_ptr<HWC2::Display> hwcDisplay;
sp<Fence> lastPresentFence = Fence::NO_FENCE; // signals when the last set op retires
std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences;
buffer_handle_t outbufHandle = nullptr;
@@ -352,9 +371,9 @@
std::unordered_map<DisplayId, DisplayData> mDisplayData;
- // This must be destroyed before mDisplayData, because destructor may call back into HWComposer
- // and look up DisplayData.
- std::unique_ptr<HWC2::Device> mHwcDevice;
+ std::unique_ptr<android::Hwc2::Composer> mComposer;
+ std::unordered_set<HWC2::Capability> mCapabilities;
+ bool mRegisteredCallback = false;
std::unordered_map<hwc2_display_t, DisplayId> mPhysicalDisplayIdMap;
std::optional<hwc2_display_t> mInternalHwcDisplayId;
@@ -363,7 +382,7 @@
std::unordered_set<DisplayId> mFreeVirtualDisplayIds;
uint32_t mNextVirtualDisplayId = 0;
- uint32_t mRemainingHwcVirtualDisplays{mHwcDevice->getMaxVirtualDisplayCount()};
+ uint32_t mRemainingHwcVirtualDisplays{getMaxVirtualDisplayCount()};
};
} // namespace impl
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
index 4e0e4df..56b0ea6 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp
@@ -42,13 +42,14 @@
switch (type) {
case compositionengine::DisplaySurface::COMPOSITION_UNKNOWN:
return "UNKNOWN";
- case compositionengine::DisplaySurface::COMPOSITION_GLES:
- return "GLES";
+ case compositionengine::DisplaySurface::COMPOSITION_GPU:
+ return "GPU";
case compositionengine::DisplaySurface::COMPOSITION_HWC:
return "HWC";
case compositionengine::DisplaySurface::COMPOSITION_MIXED:
return "MIXED";
- default: return "<INVALID>";
+ default:
+ return "<INVALID>";
}
}
@@ -92,7 +93,7 @@
mSinkBufferHeight = sinkHeight;
// Pick the buffer format to request from the sink when not rendering to it
- // with GLES. If the consumer needs CPU access, use the default format
+ // with GPU. If the consumer needs CPU access, use the default format
// set by the consumer. Otherwise allow gralloc to decide the format based
// on usage bits.
int sinkUsage;
@@ -143,10 +144,10 @@
mDbgState = DBG_STATE_PREPARED;
mCompositionType = compositionType;
- if (mForceHwcCopy && mCompositionType == COMPOSITION_GLES) {
+ if (mForceHwcCopy && mCompositionType == COMPOSITION_GPU) {
// Some hardware can do RGB->YUV conversion more efficiently in hardware
// controlled by HWC than in hardware controlled by the video encoder.
- // Forcing GLES-composed frames to go through an extra copy by the HWC
+ // Forcing GPU-composed frames to go through an extra copy by the HWC
// allows the format conversion to happen there, rather than passing RGB
// directly to the consumer.
//
@@ -161,18 +162,17 @@
mDbgLastCompositionType = mCompositionType;
}
- if (mCompositionType != COMPOSITION_GLES &&
- (mOutputFormat != mDefaultOutputFormat ||
- mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
- // We must have just switched from GLES-only to MIXED or HWC
- // composition. Stop using the format and usage requested by the GLES
+ if (mCompositionType != COMPOSITION_GPU &&
+ (mOutputFormat != mDefaultOutputFormat || mOutputUsage != GRALLOC_USAGE_HW_COMPOSER)) {
+ // We must have just switched from GPU-only to MIXED or HWC
+ // composition. Stop using the format and usage requested by the GPU
// driver; they may be suboptimal when HWC is writing to the output
// buffer. For example, if the output is going to a video encoder, and
// HWC can write directly to YUV, some hardware can skip a
// memory-to-memory RGB-to-YUV conversion step.
//
- // If we just switched *to* GLES-only mode, we'll change the
- // format/usage and get a new buffer when the GLES driver calls
+ // If we just switched *to* GPU-only mode, we'll change the
+ // format/usage and get a new buffer when the GPU driver calls
// dequeueBuffer().
mOutputFormat = mDefaultOutputFormat;
mOutputUsage = GRALLOC_USAGE_HW_COMPOSER;
@@ -192,17 +192,16 @@
"Unexpected advanceFrame() in %s state on HWC frame",
dbgStateStr());
} else {
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES_DONE,
- "Unexpected advanceFrame() in %s state on GLES/MIXED frame",
- dbgStateStr());
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU_DONE,
+ "Unexpected advanceFrame() in %s state on GPU/MIXED frame", dbgStateStr());
}
mDbgState = DBG_STATE_HWC;
if (mOutputProducerSlot < 0 ||
(mCompositionType != COMPOSITION_HWC && mFbProducerSlot < 0)) {
// Last chance bailout if something bad happened earlier. For example,
- // in a GLES configuration, if the sink disappears then dequeueBuffer
- // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+ // in a graphics API configuration, if the sink disappears then dequeueBuffer
+ // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
VDS_LOGE("advanceFrame: no buffer, bailing out");
@@ -302,9 +301,8 @@
return mSource[SOURCE_SINK]->requestBuffer(pslot, outBuf);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
- "Unexpected requestBuffer pslot=%d in %s state",
- pslot, dbgStateStr());
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected requestBuffer pslot=%d in %s state", pslot,
+ dbgStateStr());
*outBuf = mProducerBuffers[pslot];
return NO_ERROR;
@@ -374,7 +372,7 @@
VDS_LOGW_IF(mDbgState != DBG_STATE_PREPARED,
"Unexpected dequeueBuffer() in %s state", dbgStateStr());
- mDbgState = DBG_STATE_GLES;
+ mDbgState = DBG_STATE_GPU;
VDS_LOGV("dequeueBuffer %dx%d fmt=%d usage=%#" PRIx64, w, h, format, usage);
@@ -385,18 +383,18 @@
if (mOutputProducerSlot < 0) {
// Last chance bailout if something bad happened earlier. For example,
- // in a GLES configuration, if the sink disappears then dequeueBuffer
- // will fail, the GLES driver won't queue a buffer, but SurfaceFlinger
+ // in a graphics API configuration, if the sink disappears then dequeueBuffer
+ // will fail, the GPU driver won't queue a buffer, but SurfaceFlinger
// will soldier on. So we end up here without a buffer. There should
// be lots of scary messages in the log just before this.
VDS_LOGE("dequeueBuffer: no buffer, bailing out");
return NO_MEMORY;
}
- // We already dequeued the output buffer. If the GLES driver wants
+ // We already dequeued the output buffer. If the GPU driver wants
// something incompatible, we have to cancel and get a new one. This
// will mean that HWC will see a different output buffer between
- // prepare and set, but since we're in GLES-only mode already it
+ // prepare and set, but since we're in GPU-only mode already it
// shouldn't matter.
usage |= GRALLOC_USAGE_HW_COMPOSER;
@@ -458,10 +456,9 @@
return mSource[SOURCE_SINK]->queueBuffer(pslot, input, output);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
- "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
- mDbgState = DBG_STATE_GLES_DONE;
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected queueBuffer(pslot=%d) in %s state", pslot,
+ dbgStateStr());
+ mDbgState = DBG_STATE_GPU_DONE;
VDS_LOGV("queueBuffer pslot=%d", pslot);
@@ -488,11 +485,11 @@
mFbFence = mSlots[item.mSlot].mFence;
} else {
- LOG_FATAL_IF(mCompositionType != COMPOSITION_GLES,
- "Unexpected queueBuffer in state %s for compositionType %s",
- dbgStateStr(), dbgCompositionTypeStr(mCompositionType));
+ LOG_FATAL_IF(mCompositionType != COMPOSITION_GPU,
+ "Unexpected queueBuffer in state %s for compositionType %s", dbgStateStr(),
+ dbgCompositionTypeStr(mCompositionType));
- // Extract the GLES release fence for HWC to acquire
+ // Extract the GPU release fence for HWC to acquire
int64_t timestamp;
bool isAutoTimestamp;
android_dataspace dataSpace;
@@ -517,9 +514,8 @@
return mSource[SOURCE_SINK]->cancelBuffer(mapProducer2SourceSlot(SOURCE_SINK, pslot), fence);
}
- VDS_LOGW_IF(mDbgState != DBG_STATE_GLES,
- "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
- dbgStateStr());
+ VDS_LOGW_IF(mDbgState != DBG_STATE_GPU, "Unexpected cancelBuffer(pslot=%d) in %s state", pslot,
+ dbgStateStr());
VDS_LOGV("cancelBuffer pslot=%d", pslot);
Source source = fbSourceForCompositionType(mCompositionType);
return mSource[source]->cancelBuffer(
@@ -641,8 +637,8 @@
return result;
mOutputProducerSlot = mapSource2ProducerSlot(SOURCE_SINK, sslot);
- // On GLES-only frames, we don't have the right output buffer acquire fence
- // until after GLES calls queueBuffer(). So here we just set the buffer
+ // On GPU-only frames, we don't have the right output buffer acquire fence
+ // until after GPU calls queueBuffer(). So here we just set the buffer
// (for use in HWC prepare) but not the fence; we'll call this again with
// the proper fence once we have it.
result = mHwc.setOutputBuffer(*mDisplayId, Fence::NO_FENCE,
@@ -672,12 +668,18 @@
const char* VirtualDisplaySurface::dbgStateStr() const {
switch (mDbgState) {
- case DBG_STATE_IDLE: return "IDLE";
- case DBG_STATE_PREPARED: return "PREPARED";
- case DBG_STATE_GLES: return "GLES";
- case DBG_STATE_GLES_DONE: return "GLES_DONE";
- case DBG_STATE_HWC: return "HWC";
- default: return "INVALID";
+ case DBG_STATE_IDLE:
+ return "IDLE";
+ case DBG_STATE_PREPARED:
+ return "PREPARED";
+ case DBG_STATE_GPU:
+ return "GPU";
+ case DBG_STATE_GPU_DONE:
+ return "GPU_DONE";
+ case DBG_STATE_HWC:
+ return "HWC";
+ default:
+ return "INVALID";
}
}
diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
index d6543d1..3cbad8f 100644
--- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
+++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.h
@@ -34,23 +34,23 @@
class HWComposer;
class IProducerListener;
-/* This DisplaySurface implementation supports virtual displays, where GLES
+/* This DisplaySurface implementation supports virtual displays, where GPU
* and/or HWC compose into a buffer that is then passed to an arbitrary
* consumer (the sink) running in another process.
*
* The simplest case is when the virtual display will never use the h/w
* composer -- either the h/w composer doesn't support writing to buffers, or
* there are more virtual displays than it supports simultaneously. In this
- * case, the GLES driver works directly with the output buffer queue, and
+ * case, the GPU driver works directly with the output buffer queue, and
* calls to the VirtualDisplay from SurfaceFlinger and DisplayHardware do
* nothing.
*
* If h/w composer might be used, then each frame will fall into one of three
- * configurations: GLES-only, HWC-only, and MIXED composition. In all of these,
+ * configurations: GPU-only, HWC-only, and MIXED composition. In all of these,
* we must provide a FB target buffer and output buffer for the HWC set() call.
*
- * In GLES-only composition, the GLES driver is given a buffer from the sink to
- * render into. When the GLES driver queues the buffer to the
+ * In GPU-only composition, the GPU driver is given a buffer from the sink to
+ * render into. When the GPU driver queues the buffer to the
* VirtualDisplaySurface, the VirtualDisplaySurface holds onto it instead of
* immediately queueing it to the sink. The buffer is used as both the FB
* target and output buffer for HWC, though on these frames the HWC doesn't
@@ -65,7 +65,7 @@
*
* On MIXED frames, things become more complicated, since some h/w composer
* implementations can't read from and write to the same buffer. This class has
- * an internal BufferQueue that it uses as a scratch buffer pool. The GLES
+ * an internal BufferQueue that it uses as a scratch buffer pool. The GPU
* driver is given a scratch buffer to render into. When it finishes rendering,
* the buffer is queued and then immediately acquired by the
* VirtualDisplaySurface. The scratch buffer is then used as the FB target
@@ -99,7 +99,7 @@
virtual ~VirtualDisplaySurface();
//
- // IGraphicBufferProducer interface, used by the GLES driver.
+ // IGraphicBufferProducer interface, used by the GPU driver.
//
virtual status_t requestBuffer(int pslot, sp<GraphicBuffer>* outBuf);
virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers);
@@ -144,7 +144,7 @@
// Both the sink and scratch buffer pools have their own set of slots
// ("source slots", or "sslot"). We have to merge these into the single
- // set of slots used by the GLES producer ("producer slots" or "pslot") and
+ // set of slots used by the graphics producer ("producer slots" or "pslot") and
// internally in the VirtualDisplaySurface. To minimize the number of times
// a producer slot switches which source it comes from, we map source slot
// numbers to producer slot numbers differently for each source.
@@ -166,12 +166,12 @@
// To avoid buffer reallocations, we track the buffer usage and format
// we used on the previous frame and use it again on the new frame. If
- // the composition type changes or the GLES driver starts requesting
+ // the composition type changes or the GPU driver starts requesting
// different usage/format, we'll get a new buffer.
uint32_t mOutputFormat;
uint64_t mOutputUsage;
- // Since we present a single producer interface to the GLES driver, but
+ // Since we present a single producer interface to the GPU driver, but
// are internally muxing between the sink and scratch producers, we have
// to keep track of which source last returned each producer slot from
// dequeueBuffer. Each bit in mProducerSlotSource corresponds to a producer
@@ -181,7 +181,7 @@
sp<GraphicBuffer> mProducerBuffers[BufferQueueDefs::NUM_BUFFER_SLOTS];
// The QueueBufferOutput with the latest info from the sink, and with the
- // transform hint cleared. Since we defer queueBuffer from the GLES driver
+ // transform hint cleared. Since we defer queueBuffer from the GPU driver
// to the sink, we have to return the previous version.
// Moves instead of copies are performed to avoid duplicate
// FrameEventHistoryDeltas.
@@ -195,7 +195,7 @@
// Intra-frame state
//
- // Composition type and GLES buffer source for the current frame.
+ // Composition type and graphics buffer source for the current frame.
// Valid after prepareFrame(), cleared in onFrameCommitted.
CompositionType mCompositionType;
@@ -221,13 +221,13 @@
// +-----------+-------------------+-------------+
// | IDLE | beginFrame || BEGUN |
// | BEGUN | prepareFrame || PREPARED |
- // | PREPARED | dequeueBuffer [1] || GLES |
+ // | PREPARED | dequeueBuffer [1] || GPU |
// | PREPARED | advanceFrame [2] || HWC |
- // | GLES | queueBuffer || GLES_DONE |
- // | GLES_DONE | advanceFrame || HWC |
+ // | GPU | queueBuffer || GPU_DONE |
+ // | GPU_DONE | advanceFrame || HWC |
// | HWC | onFrameCommitted || IDLE |
// +-----------+-------------------++------------+
- // [1] COMPOSITION_GLES and COMPOSITION_MIXED frames.
+ // [1] COMPOSITION_GPU and COMPOSITION_MIXED frames.
// [2] COMPOSITION_HWC frames.
//
enum DbgState {
@@ -236,12 +236,12 @@
// output buffer dequeued, framebuffer source not yet known
DBG_STATE_BEGUN,
// output buffer dequeued, framebuffer source known but not provided
- // to GLES yet.
+ // to GPU yet.
DBG_STATE_PREPARED,
- // GLES driver has a buffer dequeued
- DBG_STATE_GLES,
- // GLES driver has queued the buffer, we haven't sent it to HWC yet
- DBG_STATE_GLES_DONE,
+ // GPU driver has a buffer dequeued
+ DBG_STATE_GPU,
+ // GPU driver has queued the buffer, we haven't sent it to HWC yet
+ DBG_STATE_GPU_DONE,
// HWC has the buffer for this frame
DBG_STATE_HWC,
};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 35fc4be..c7eb9c3 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -430,6 +430,7 @@
compositionState.internalOnly = getPrimaryDisplayOnly();
compositionState.isVisible = isVisible();
compositionState.isOpaque = opaque && !usesRoundedCorners && alpha == 1.f;
+ compositionState.shadowRadius = mEffectiveShadowRadius;
compositionState.contentDirty = contentDirty;
contentDirty = false;
@@ -585,6 +586,7 @@
renderengine::LayerSettings shadowLayer = casterLayerSettings;
shadowLayer.shadow = shadow;
+ shadowLayer.geometry.boundaries = mBounds; // ignore transparent region
// If the casting layer is translucent, we need to fill in the shadow underneath the layer.
// Otherwise the generated shadow will only be shown around the casting layer.
@@ -1935,6 +1937,9 @@
[&]() { return layerInfo->mutable_source_bounds(); });
LayerProtoHelper::writeToProto(mScreenBounds,
[&]() { return layerInfo->mutable_screen_bounds(); });
+ LayerProtoHelper::writeToProto(getRoundedCornerState().cropRect,
+ [&]() { return layerInfo->mutable_corner_radius_crop(); });
+ layerInfo->set_shadow_radius(mEffectiveShadowRadius);
}
}
@@ -2025,6 +2030,7 @@
InputWindowInfo Layer::fillInputInfo() {
InputWindowInfo info = mDrawingState.inputInfo;
+ info.id = sequence;
if (info.displayId == ADISPLAY_ID_NONE) {
info.displayId = getLayerStack();
@@ -2081,9 +2087,29 @@
info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds});
}
+ // If the layer is a clone, we need to crop the input region to cloned root to prevent
+ // touches from going outside the cloned area.
+ if (isClone()) {
+ sp<Layer> clonedRoot = getClonedRoot();
+ if (clonedRoot != nullptr) {
+ Rect rect(clonedRoot->mScreenBounds);
+ info.touchableRegion = info.touchableRegion.intersect(rect);
+ }
+ }
+
return info;
}
+sp<Layer> Layer::getClonedRoot() {
+ if (mClonedChild != nullptr) {
+ return this;
+ }
+ if (mDrawingParent == nullptr || mDrawingParent.promote() == nullptr) {
+ return nullptr;
+ }
+ return mDrawingParent.promote()->getClonedRoot();
+}
+
bool Layer::hasInput() const {
return mDrawingState.inputInfo.token != nullptr;
}
@@ -2119,10 +2145,6 @@
// copy drawing state from cloned layer
mDrawingState = clonedFrom->mDrawingState;
mClonedFrom = clonedFrom;
-
- // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
- // InputWindows per client token yet.
- mDrawingState.inputInfo.token = nullptr;
}
void Layer::updateMirrorInfo() {
@@ -2157,9 +2179,6 @@
if (isClonedFromAlive()) {
sp<Layer> clonedFrom = getClonedFrom();
mDrawingState = clonedFrom->mDrawingState;
- // TODO: (b/140756730) Ignore input for now since InputDispatcher doesn't support multiple
- // InputWindows per client token yet.
- mDrawingState.inputInfo.token = nullptr;
clonedLayersMap.emplace(clonedFrom, this);
}
@@ -2198,7 +2217,24 @@
}
}
-void Layer::updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap) {
+void Layer::updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
+ auto cropLayer = mDrawingState.touchableRegionCrop.promote();
+ if (cropLayer != nullptr) {
+ if (clonedLayersMap.count(cropLayer) == 0) {
+ // Real layer had a crop layer but it's not in the cloned hierarchy. Just set to
+ // self as crop layer to avoid going outside bounds.
+ mDrawingState.touchableRegionCrop = this;
+ } else {
+ const sp<Layer>& clonedCropLayer = clonedLayersMap.at(cropLayer);
+ mDrawingState.touchableRegionCrop = clonedCropLayer;
+ }
+ }
+ // Cloned layers shouldn't handle watch outside since their z order is not determined by
+ // WM or the client.
+ mDrawingState.inputInfo.layoutParamsFlags &= ~InputWindowInfo::FLAG_WATCH_OUTSIDE_TOUCH;
+}
+
+void Layer::updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap) {
mDrawingState.zOrderRelativeOf = nullptr;
mDrawingState.zOrderRelatives.clear();
@@ -2206,11 +2242,11 @@
return;
}
- sp<Layer> clonedFrom = getClonedFrom();
+ const sp<Layer>& clonedFrom = getClonedFrom();
for (wp<Layer>& relativeWeak : clonedFrom->mDrawingState.zOrderRelatives) {
- sp<Layer> relative = relativeWeak.promote();
- auto clonedRelative = clonedLayersMap[relative];
- if (clonedRelative != nullptr) {
+ const sp<Layer>& relative = relativeWeak.promote();
+ if (clonedLayersMap.count(relative) > 0) {
+ auto& clonedRelative = clonedLayersMap.at(relative);
mDrawingState.zOrderRelatives.add(clonedRelative);
}
}
@@ -2220,12 +2256,14 @@
// In that case, we treat the layer as if the relativeOf has been removed. This way, it will
// still traverse the children, but the layer with the missing relativeOf will not be shown
// on screen.
- sp<Layer> relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
- sp<Layer> clonedRelativeOf = clonedLayersMap[relativeOf];
- if (clonedRelativeOf != nullptr) {
+ const sp<Layer>& relativeOf = clonedFrom->mDrawingState.zOrderRelativeOf.promote();
+ if (clonedLayersMap.count(relativeOf) > 0) {
+ const sp<Layer>& clonedRelativeOf = clonedLayersMap.at(relativeOf);
mDrawingState.zOrderRelativeOf = clonedRelativeOf;
}
+ updateClonedInputInfo(clonedLayersMap);
+
for (sp<Layer>& child : mDrawingChildren) {
child->updateClonedRelatives(clonedLayersMap);
}
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 843d3ae..d697a6a 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -497,8 +497,9 @@
void updateClonedDrawingState(std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void updateClonedChildren(const sp<Layer>& mirrorRoot,
std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
- void updateClonedRelatives(std::map<sp<Layer>, sp<Layer>> clonedLayersMap);
+ void updateClonedRelatives(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
void addChildToDrawing(const sp<Layer>& layer);
+ void updateClonedInputInfo(const std::map<sp<Layer>, sp<Layer>>& clonedLayersMap);
public:
/*
@@ -972,6 +973,10 @@
// Returns true if the layer can draw shadows on its border.
virtual bool canDrawShadows() const { return true; }
+
+ // Find the root of the cloned hierarchy, this means the first non cloned parent.
+ // This will return null if first non cloned parent is not found.
+ sp<Layer> getClonedRoot();
};
} // namespace android
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 38a80a7..6598bd8 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -18,26 +18,139 @@
#include "Client.h"
#include "Layer.h"
+#include <gui/IProducerListener.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RefreshRateOverlay"
+
namespace android {
+void RefreshRateOverlay::SevenSegmentDrawer::drawRect(const Rect& r, const half4& color,
+ const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels) {
+ for (int32_t j = r.top; j < r.bottom; j++) {
+ if (j >= buffer->getHeight()) {
+ break;
+ }
+
+ for (int32_t i = r.left; i < r.right; i++) {
+ if (i >= buffer->getWidth()) {
+ break;
+ }
+
+ uint8_t* iter = pixels + 4 * (i + (buffer->getStride() * j));
+ iter[0] = uint8_t(color.r * 255);
+ iter[1] = uint8_t(color.g * 255);
+ iter[2] = uint8_t(color.b * 255);
+ iter[3] = uint8_t(color.a * 255);
+ }
+ }
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawSegment(Segment segment, int left,
+ const half4& color,
+ const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels) {
+ const Rect rect = [&]() {
+ switch (segment) {
+ case Segment::Upper:
+ return Rect(left, 0, left + DIGIT_WIDTH, DIGIT_SPACE);
+ case Segment::UpperLeft:
+ return Rect(left, 0, left + DIGIT_SPACE, DIGIT_HEIGHT / 2);
+ case Segment::UpperRight:
+ return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, 0, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT / 2);
+ case Segment::Middle:
+ return Rect(left, DIGIT_HEIGHT / 2 - DIGIT_SPACE / 2, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT / 2 + DIGIT_SPACE / 2);
+ case Segment::LowerLeft:
+ return Rect(left, DIGIT_HEIGHT / 2, left + DIGIT_SPACE, DIGIT_HEIGHT);
+ case Segment::LowerRight:
+ return Rect(left + DIGIT_WIDTH - DIGIT_SPACE, DIGIT_HEIGHT / 2, left + DIGIT_WIDTH,
+ DIGIT_HEIGHT);
+ case Segment::Buttom:
+ return Rect(left, DIGIT_HEIGHT - DIGIT_SPACE, left + DIGIT_WIDTH, DIGIT_HEIGHT);
+ }
+ }();
+
+ drawRect(rect, color, buffer, pixels);
+}
+
+void RefreshRateOverlay::SevenSegmentDrawer::drawDigit(int digit, int left, const half4& color,
+ const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels) {
+ if (digit < 0 || digit > 9) return;
+
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::Upper, left, color, buffer, pixels);
+ if (digit == 0 || digit == 4 || digit == 5 || digit == 6 || digit == 8 || digit == 9)
+ drawSegment(Segment::UpperLeft, left, color, buffer, pixels);
+ if (digit == 0 || digit == 1 || digit == 2 || digit == 3 || digit == 4 || digit == 7 ||
+ digit == 8 || digit == 9)
+ drawSegment(Segment::UpperRight, left, color, buffer, pixels);
+ if (digit == 2 || digit == 3 || digit == 4 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Middle, left, color, buffer, pixels);
+ if (digit == 0 || digit == 2 || digit == 6 || digit == 8)
+ drawSegment(Segment::LowerLeft, left, color, buffer, pixels);
+ if (digit == 0 || digit == 1 || digit == 3 || digit == 4 || digit == 5 || digit == 6 ||
+ digit == 7 || digit == 8 || digit == 9)
+ drawSegment(Segment::LowerRight, left, color, buffer, pixels);
+ if (digit == 0 || digit == 2 || digit == 3 || digit == 5 || digit == 6 || digit == 8 ||
+ digit == 9)
+ drawSegment(Segment::Buttom, left, color, buffer, pixels);
+}
+
+sp<GraphicBuffer> RefreshRateOverlay::SevenSegmentDrawer::drawNumber(int number,
+ const half4& color) {
+ if (number < 0 || number > 1000) return nullptr;
+
+ const auto hundreds = number / 100;
+ const auto tens = (number / 10) % 10;
+ const auto ones = number % 10;
+
+ sp<GraphicBuffer> buffer =
+ new GraphicBuffer(BUFFER_WIDTH, BUFFER_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1,
+ GRALLOC_USAGE_SW_WRITE_RARELY, "RefreshRateOverlayBuffer");
+ uint8_t* pixels;
+ buffer->lock(GRALLOC_USAGE_SW_WRITE_RARELY, reinterpret_cast<void**>(&pixels));
+ int left = 0;
+ if (hundreds != 0) {
+ drawDigit(hundreds, left, color, buffer, pixels);
+ left += DIGIT_WIDTH + DIGIT_SPACE;
+ }
+
+ if (tens != 0) {
+ drawDigit(tens, left, color, buffer, pixels);
+ left += DIGIT_WIDTH + DIGIT_SPACE;
+ }
+
+ drawDigit(ones, left, color, buffer, pixels);
+ buffer->unlock();
+ return buffer;
+}
+
RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
: mFlinger(flinger), mClient(new Client(&mFlinger)) {
createLayer();
+ primeCache();
}
bool RefreshRateOverlay::createLayer() {
const status_t ret =
- mFlinger.createLayer(String8("RefreshRateOverlay"), mClient, 0, 0,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor,
- LayerMetadata(), &mIBinder, &mGbp, nullptr);
+ mFlinger.createLayer(String8("RefreshRateOverlay"), mClient,
+ SevenSegmentDrawer::getWidth(), SevenSegmentDrawer::getHeight(),
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceBufferState, LayerMetadata(),
+ &mIBinder, &mGbp, nullptr);
if (ret) {
- ALOGE("failed to create color layer");
+ ALOGE("failed to create buffer state layer");
return false;
}
Mutex::Autolock _l(mFlinger.mStateLock);
mLayer = mClient->getLayerUser(mIBinder);
- mLayer->setCrop_legacy(Rect(50, 70, 200, 100));
// setting Layer's Z requires resorting layersSortedByZ
ssize_t idx = mFlinger.mCurrentState.layersSortedByZ.indexOf(mLayer);
@@ -49,9 +162,41 @@
return true;
}
+void RefreshRateOverlay::primeCache() {
+ auto allRefreshRates = mFlinger.mRefreshRateConfigs->getAllRefreshRates();
+ if (allRefreshRates.size() == 1) {
+ auto fps = allRefreshRates.begin()->second.fps;
+ half4 color = {LOW_FPS_COLOR, ALPHA};
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ return;
+ }
+
+ std::vector<uint32_t> supportedFps;
+ supportedFps.reserve(allRefreshRates.size());
+ for (auto [ignored, refreshRate] : allRefreshRates) {
+ supportedFps.push_back(refreshRate.fps);
+ }
+
+ std::sort(supportedFps.begin(), supportedFps.end());
+ const auto mLowFps = supportedFps[0];
+ const auto mHighFps = supportedFps[supportedFps.size() - 1];
+ for (auto fps : supportedFps) {
+ const auto fpsScale = float(fps - mLowFps) / (mHighFps - mLowFps);
+ half4 color;
+ color.r = HIGH_FPS_COLOR.r * fpsScale + LOW_FPS_COLOR.r * (1 - fpsScale);
+ color.g = HIGH_FPS_COLOR.g * fpsScale + LOW_FPS_COLOR.g * (1 - fpsScale);
+ color.b = HIGH_FPS_COLOR.b * fpsScale + LOW_FPS_COLOR.b * (1 - fpsScale);
+ color.a = ALPHA;
+ mBufferCache.emplace(fps, SevenSegmentDrawer::drawNumber(fps, color));
+ }
+}
+
void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
- const half3& color = (refreshRate.fps > 65.0f) ? GREEN : RED;
- mLayer->setColor(color);
+ auto buffer = mBufferCache[refreshRate.fps];
+ mLayer->setBuffer(buffer, 0, 0, {});
+ mLayer->setFrame(Rect(20, 120, 20 + SevenSegmentDrawer::getWidth(),
+ 120 + SevenSegmentDrawer::getHeight()));
+
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index 414bc47..6d34df2 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -28,7 +28,32 @@
void changeRefreshRate(const RefreshRate& refreshRate);
private:
+ class SevenSegmentDrawer {
+ public:
+ static sp<GraphicBuffer> drawNumber(int number, const half4& color);
+ static uint32_t getHeight() { return BUFFER_HEIGHT; }
+ static uint32_t getWidth() { return BUFFER_WIDTH; }
+
+ private:
+ enum class Segment { Upper, UpperLeft, UpperRight, Middle, LowerLeft, LowerRight, Buttom };
+
+ static void drawRect(const Rect& r, const half4& color, const sp<GraphicBuffer>& buffer,
+ uint8_t* pixels);
+ static void drawSegment(Segment segment, int left, const half4& color,
+ const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+ static void drawDigit(int digit, int left, const half4& color,
+ const sp<GraphicBuffer>& buffer, uint8_t* pixels);
+
+ static constexpr uint32_t DIGIT_HEIGHT = 100;
+ static constexpr uint32_t DIGIT_WIDTH = 64;
+ static constexpr uint32_t DIGIT_SPACE = 16;
+ static constexpr uint32_t BUFFER_HEIGHT = DIGIT_HEIGHT;
+ static constexpr uint32_t BUFFER_WIDTH =
+ 3 * DIGIT_WIDTH + 2 * DIGIT_SPACE; // Digit|Space|Digit|Space|Digit
+ };
+
bool createLayer();
+ void primeCache();
SurfaceFlinger& mFlinger;
sp<Client> mClient;
@@ -36,8 +61,11 @@
sp<IBinder> mIBinder;
sp<IGraphicBufferProducer> mGbp;
- const half3 RED = half3(1.0f, 0.0f, 0.0f);
- const half3 GREEN = half3(0.0f, 1.0f, 0.0f);
+ std::unordered_map<int, sp<GraphicBuffer>> mBufferCache;
+
+ static constexpr float ALPHA = 0.8f;
+ const half3 LOW_FPS_COLOR = half3(1.0f, 0.0f, 0.0f);
+ const half3 HIGH_FPS_COLOR = half3(0.0f, 1.0f, 0.0f);
};
}; // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index 571c9ca..bd4b0ec 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -27,16 +27,14 @@
namespace android {
-DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset,
- nsecs_t offsetThresholdForNextVsync, bool traceVsync,
+DispSyncSource::DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync,
const char* name)
: mName(name),
mValue(base::StringPrintf("VSYNC-%s", name), 0),
mTraceVsync(traceVsync),
mVsyncOnLabel(base::StringPrintf("VsyncOn-%s", name)),
mDispSync(dispSync),
- mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset),
- mOffsetThresholdForNextVsync(offsetThresholdForNextVsync) {}
+ mPhaseOffset(base::StringPrintf("VsyncOffset-%s", name), phaseOffset) {}
void DispSyncSource::setVSyncEnabled(bool enable) {
std::lock_guard lock(mVsyncMutex);
@@ -67,10 +65,6 @@
void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) {
std::lock_guard lock(mVsyncMutex);
const nsecs_t period = mDispSync->getPeriod();
- // Check if offset should be handled as negative
- if (phaseOffset >= mOffsetThresholdForNextVsync) {
- phaseOffset -= period;
- }
// Normalize phaseOffset to [-period, period)
const int numPeriods = phaseOffset / period;
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h
index 536464e..328b8c1 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -26,8 +26,7 @@
class DispSyncSource final : public VSyncSource, private DispSync::Callback {
public:
- DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, nsecs_t offsetThresholdForNextVsync,
- bool traceVsync, const char* name);
+ DispSyncSource(DispSync* dispSync, nsecs_t phaseOffset, bool traceVsync, const char* name);
~DispSyncSource() override = default;
@@ -55,7 +54,6 @@
std::mutex mVsyncMutex;
TracedOrdinal<nsecs_t> mPhaseOffset GUARDED_BY(mVsyncMutex);
- const nsecs_t mOffsetThresholdForNextVsync;
bool mEnabled GUARDED_BY(mVsyncMutex) = false;
};
diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
index 6c502e6..fa46e6f 100644
--- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h
+++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h
@@ -45,7 +45,6 @@
const char* getName() const override { return "inject"; }
void setVSyncEnabled(bool) override {}
void setPhaseOffset(nsecs_t) override {}
- void pauseVsyncCallback(bool) {}
private:
std::mutex mCallbackMutex;
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 12832a6..4330742 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -35,45 +35,48 @@
namespace android::scheduler {
-PhaseOffsets::~PhaseOffsets() = default;
+PhaseConfiguration::~PhaseConfiguration() = default;
namespace impl {
-PhaseOffsets::PhaseOffsets() {
- // Below defines the threshold when an offset is considered to be negative, i.e. targeting
- // for the N+2 vsync instead of N+1. This means that:
- // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
- // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
- const nsecs_t thresholdForNextVsync =
- getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
- .value_or(std::numeric_limits<nsecs_t>::max());
-
- mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync);
- mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
-}
-
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(float fps) const {
- // TODO(145561086): Once offsets are common for all refresh rates we can remove the magic
- // number for refresh rate
- if (fps > 65.0f) {
- return mHighFpsOffsets;
- } else {
- return mDefaultOffsets;
- }
-}
+PhaseOffsets::PhaseOffsets(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : // Below defines the threshold when an offset is considered to be negative, i.e. targeting
+ // for the N+2 vsync instead of N+1. This means that:
+ // For offset < threshold, SF wake up (vsync_duration - offset) before HW vsync.
+ // For offset >= threshold, SF wake up (2 * vsync_duration - offset) before HW vsync.
+ mThresholdForNextVsync(getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
+ .value_or(std::numeric_limits<nsecs_t>::max())),
+ mOffsets(initializeOffsets(refreshRateConfigs)),
+ mRefreshRateFps(refreshRateConfigs.getCurrentRefreshRate().fps) {}
void PhaseOffsets::dump(std::string& result) const {
- const auto [early, earlyGl, late, threshold] = getCurrentOffsets();
+ const auto [early, earlyGl, late] = getCurrentOffsets();
using base::StringAppendF;
StringAppendF(&result,
" app phase: %9" PRId64 " ns\t SF phase: %9" PRId64 " ns\n"
" early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64 " ns\n"
" GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64 " ns\n"
"next VSYNC threshold: %9" PRId64 " ns\n",
- late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf, threshold);
+ late.app, late.sf, early.app, early.sf, earlyGl.app, earlyGl.sf,
+ mThresholdForNextVsync);
}
-PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t thresholdForNextVsync) {
+std::unordered_map<float, PhaseDurations::Offsets> PhaseOffsets::initializeOffsets(
+ const scheduler::RefreshRateConfigs& refreshRateConfigs) const {
+ std::unordered_map<float, PhaseDurations::Offsets> offsets;
+
+ for (const auto& [ignored, refreshRate] : refreshRateConfigs.getAllRefreshRates()) {
+ const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / refreshRate.fps);
+ if (refreshRate.fps > 65.0f) {
+ offsets.emplace(refreshRate.fps, getHighFpsOffsets(vsyncDuration));
+ } else {
+ offsets.emplace(refreshRate.fps, getDefaultOffsets(vsyncDuration));
+ }
+ }
+ return offsets;
+}
+
+PhaseOffsets::Offsets PhaseOffsets::getDefaultOffsets(nsecs_t vsyncDuration) const {
const int64_t vsyncPhaseOffsetNs = sysprop::vsync_event_phase_offset_ns(1000000);
const int64_t sfVsyncPhaseOffsetNs = sysprop::vsync_sf_event_phase_offset_ns(1000000);
@@ -82,19 +85,32 @@
const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
- return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
- earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+ return {
+ {
+ earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+ : earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
- {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
- earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
+ earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+ },
+ {
+ earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) < mThresholdForNextVsync
+ ? earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs)
+ : earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs) - vsyncDuration,
- {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
+ earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs),
+ },
+ {
+ sfVsyncPhaseOffsetNs < mThresholdForNextVsync
+ ? sfVsyncPhaseOffsetNs
+ : sfVsyncPhaseOffsetNs - vsyncDuration,
- thresholdForNextVsync};
+ vsyncPhaseOffsetNs,
+ },
+ };
}
-PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t thresholdForNextVsync) {
- // TODO(b/122905996): Define these in device.mk.
+PhaseOffsets::Offsets PhaseOffsets::getHighFpsOffsets(nsecs_t vsyncDuration) const {
const int highFpsLateAppOffsetNs =
getProperty("debug.sf.high_fps_late_app_phase_offset_ns").value_or(2000000);
const int highFpsLateSfOffsetNs =
@@ -106,15 +122,195 @@
const auto highFpsEarlyGlAppOffsetNs =
getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
- return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
- highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+ return {
+ {
+ highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) < mThresholdForNextVsync
+ ? highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs)
+ : highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+ vsyncDuration,
- {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
- highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
+ highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+ },
+ {
+ highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) <
+ mThresholdForNextVsync
+ ? highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs)
+ : highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs) -
+ vsyncDuration,
- {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+ highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs),
+ },
+ {
+ highFpsLateSfOffsetNs < mThresholdForNextVsync
+ ? highFpsLateSfOffsetNs
+ : highFpsLateSfOffsetNs - vsyncDuration,
- thresholdForNextVsync};
+ highFpsLateAppOffsetNs,
+ },
+ };
+}
+
+static void validateSysprops() {
+ const auto validatePropertyBool = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(!property_get_bool(prop, false), "%s is false", prop);
+ };
+
+ validatePropertyBool("debug.sf.use_phase_offsets_as_durations");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ LOG_ALWAYS_FATAL_IF(sysprop::vsync_sf_event_phase_offset_ns(-1) != -1,
+ "ro.surface_flinger.vsync_sf_event_phase_offset_ns is set but expecting "
+ "duration");
+
+ const auto validateProperty = [](const char* prop) {
+ LOG_ALWAYS_FATAL_IF(getProperty(prop).has_value(),
+ "%s is set to %" PRId64 " but expecting duration", prop,
+ getProperty(prop).value_or(-1));
+ };
+
+ validateProperty("debug.sf.early_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_phase_offset_ns");
+ validateProperty("debug.sf.early_app_phase_offset_ns");
+ validateProperty("debug.sf.early_gl_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_late_sf_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_app_phase_offset_ns");
+ validateProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
+}
+
+static nsecs_t sfDurationToOffset(nsecs_t sfDuration, nsecs_t vsyncDuration) {
+ return sfDuration == -1 ? 1'000'000 : vsyncDuration - sfDuration % vsyncDuration;
+}
+
+static nsecs_t appDurationToOffset(nsecs_t appDuration, nsecs_t sfDuration, nsecs_t vsyncDuration) {
+ return sfDuration == -1 ? 1'000'000
+ : vsyncDuration - (appDuration + sfDuration) % vsyncDuration;
+}
+
+static std::vector<float> getRefreshRatesFromConfigs(
+ const android::scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ const auto& allRefreshRates = refreshRateConfigs.getAllRefreshRates();
+ std::vector<float> refreshRates;
+ refreshRates.reserve(allRefreshRates.size());
+
+ for (const auto& [ignored, refreshRate] : allRefreshRates) {
+ refreshRates.emplace_back(refreshRate.fps);
+ }
+
+ return refreshRates;
+}
+
+std::unordered_map<float, PhaseDurations::Offsets> PhaseDurations::initializeOffsets(
+ const std::vector<float>& refreshRates) const {
+ std::unordered_map<float, PhaseDurations::Offsets> offsets;
+
+ for (const auto fps : refreshRates) {
+ const nsecs_t vsyncDuration = static_cast<nsecs_t>(1e9f / fps);
+ offsets.emplace(fps,
+ Offsets{
+ {
+ mSfEarlyDuration < vsyncDuration
+ ? sfDurationToOffset(mSfEarlyDuration,
+ vsyncDuration)
+ : sfDurationToOffset(mSfEarlyDuration,
+ vsyncDuration) -
+ vsyncDuration,
+
+ appDurationToOffset(mAppEarlyDuration, mSfEarlyDuration,
+ vsyncDuration),
+ },
+ {
+ mSfEarlyGlDuration < vsyncDuration
+ ? sfDurationToOffset(mSfEarlyGlDuration,
+ vsyncDuration)
+ : sfDurationToOffset(mSfEarlyGlDuration,
+ vsyncDuration) -
+ vsyncDuration,
+
+ appDurationToOffset(mAppEarlyGlDuration, mSfEarlyGlDuration,
+ vsyncDuration),
+ },
+ {
+ mSfDuration < vsyncDuration
+ ? sfDurationToOffset(mSfDuration, vsyncDuration)
+ : sfDurationToOffset(mSfDuration, vsyncDuration) -
+ vsyncDuration,
+
+ appDurationToOffset(mAppDuration, mSfDuration,
+ vsyncDuration),
+ },
+ });
+ }
+ return offsets;
+}
+
+PhaseDurations::PhaseDurations(const scheduler::RefreshRateConfigs& refreshRateConfigs)
+ : PhaseDurations(getRefreshRatesFromConfigs(refreshRateConfigs),
+ refreshRateConfigs.getCurrentRefreshRate().fps,
+ getProperty("debug.sf.late.sf.duration").value_or(-1),
+ getProperty("debug.sf.late.app.duration").value_or(-1),
+ getProperty("debug.sf.early.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.early.app.duration").value_or(mAppDuration),
+ getProperty("debug.sf.earlyGl.sf.duration").value_or(mSfDuration),
+ getProperty("debug.sf.earlyGl.app.duration").value_or(mAppDuration)) {
+ validateSysprops();
+}
+
+PhaseDurations::PhaseDurations(const std::vector<float>& refreshRates, float currentFps,
+ nsecs_t sfDuration, nsecs_t appDuration, nsecs_t sfEarlyDuration,
+ nsecs_t appEarlyDuration, nsecs_t sfEarlyGlDuration,
+ nsecs_t appEarlyGlDuration)
+ : mSfDuration(sfDuration),
+ mAppDuration(appDuration),
+ mSfEarlyDuration(sfEarlyDuration),
+ mAppEarlyDuration(appEarlyDuration),
+ mSfEarlyGlDuration(sfEarlyGlDuration),
+ mAppEarlyGlDuration(appEarlyGlDuration),
+ mOffsets(initializeOffsets(refreshRates)),
+ mRefreshRateFps(currentFps) {}
+
+PhaseOffsets::Offsets PhaseDurations::getOffsetsForRefreshRate(float fps) const {
+ const auto iter = mOffsets.find(fps);
+ LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+ return iter->second;
+}
+
+void PhaseDurations::dump(std::string& result) const {
+ const auto [early, earlyGl, late] = getCurrentOffsets();
+ using base::StringAppendF;
+ StringAppendF(&result,
+ " app phase: %9" PRId64 " ns\t SF phase: %9" PRId64
+ " ns\n"
+ " app duration: %9" PRId64 " ns\t SF duration: %9" PRId64
+ " ns\n"
+ " early app phase: %9" PRId64 " ns\t early SF phase: %9" PRId64
+ " ns\n"
+ " early app duration: %9" PRId64 " ns\t early SF duration: %9" PRId64
+ " ns\n"
+ " GL early app phase: %9" PRId64 " ns\tGL early SF phase: %9" PRId64
+ " ns\n"
+ " GL early app duration: %9" PRId64 " ns\tGL early SF duration: %9" PRId64
+ " ns\n",
+ late.app,
+
+ late.sf,
+
+ mAppDuration, mSfDuration,
+
+ early.app, early.sf,
+
+ mAppEarlyDuration, mSfEarlyDuration,
+
+ earlyGl.app,
+
+ earlyGl.sf,
+
+ mAppEarlyGlDuration, mSfEarlyGlDuration);
}
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 7747f0c..c10efde 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -29,17 +29,11 @@
* different offsets will help us with latency. This class keeps track of
* which mode the device is on, and returns approprate offsets when needed.
*/
-class PhaseOffsets {
+class PhaseConfiguration {
public:
using Offsets = VSyncModulator::OffsetsConfig;
- virtual ~PhaseOffsets();
-
- nsecs_t getCurrentAppOffset() const { return getCurrentOffsets().late.app; }
- nsecs_t getCurrentSfOffset() const { return getCurrentOffsets().late.sf; }
- nsecs_t getOffsetThresholdForNextVsync() const {
- return getCurrentOffsets().thresholdForNextVsync;
- }
+ virtual ~PhaseConfiguration();
virtual Offsets getCurrentOffsets() const = 0;
virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
@@ -51,9 +45,51 @@
namespace impl {
-class PhaseOffsets : public scheduler::PhaseOffsets {
+/*
+ * This is the old implementation of phase offsets and considered as deprecated.
+ * PhaseDurations is the new implementation.
+ */
+class PhaseOffsets : public scheduler::PhaseConfiguration {
public:
- PhaseOffsets();
+ PhaseOffsets(const scheduler::RefreshRateConfigs&);
+
+ // Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
+ Offsets getOffsetsForRefreshRate(float fps) const override {
+ const auto iter = mOffsets.find(fps);
+ LOG_ALWAYS_FATAL_IF(iter == mOffsets.end());
+ return iter->second;
+ }
+
+ // Returns early, early GL, and late offsets for Apps and SF.
+ Offsets getCurrentOffsets() const override { return getOffsetsForRefreshRate(mRefreshRateFps); }
+
+ // This function should be called when the device is switching between different
+ // refresh rates, to properly update the offsets.
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
+
+ // Returns current offsets in human friendly format.
+ void dump(std::string& result) const override;
+
+private:
+ std::unordered_map<float, PhaseOffsets::Offsets> initializeOffsets(
+ const scheduler::RefreshRateConfigs&) const;
+ Offsets getDefaultOffsets(nsecs_t vsyncDuration) const;
+ Offsets getHighFpsOffsets(nsecs_t vsyncDuration) const;
+
+ const nsecs_t mThresholdForNextVsync;
+ const std::unordered_map<float, Offsets> mOffsets;
+
+ std::atomic<float> mRefreshRateFps;
+};
+
+/*
+ * Class that encapsulates the phase offsets for SurfaceFlinger and App.
+ * The offsets are calculated from durations for each one of the (late, early, earlyGL)
+ * offset types.
+ */
+class PhaseDurations : public scheduler::PhaseConfiguration {
+public:
+ PhaseDurations(const scheduler::RefreshRateConfigs&);
// Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
Offsets getOffsetsForRefreshRate(float fps) const override;
@@ -68,14 +104,28 @@
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
+protected:
+ // Used for unit tests
+ PhaseDurations(const std::vector<float>& refreshRates, float currentFps, nsecs_t sfDuration,
+ nsecs_t appDuration, nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration);
+
private:
- static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync);
- static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync);
+ std::unordered_map<float, PhaseDurations::Offsets> initializeOffsets(
+ const std::vector<float>&) const;
- std::atomic<float> mRefreshRateFps = 0;
+ const nsecs_t mSfDuration;
+ const nsecs_t mAppDuration;
- Offsets mDefaultOffsets;
- Offsets mHighFpsOffsets;
+ const nsecs_t mSfEarlyDuration;
+ const nsecs_t mAppEarlyDuration;
+
+ const nsecs_t mSfEarlyGlDuration;
+ const nsecs_t mAppEarlyGlDuration;
+
+ const std::unordered_map<float, Offsets> mOffsets;
+
+ std::atomic<float> mRefreshRateFps;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
index 23fb96a..847e20c 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -110,13 +110,48 @@
init(inputConfigs, currentConfigId);
}
-void RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
- float maxRefreshRate) {
+status_t RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+ float maxRefreshRate, bool* outPolicyChanged) {
std::lock_guard lock(mLock);
- mCurrentGroupId = mRefreshRates.at(defaultConfigId).configGroup;
+ bool policyChanged = defaultConfigId != mDefaultConfig ||
+ minRefreshRate != mMinRefreshRateFps || maxRefreshRate != mMaxRefreshRateFps;
+ if (outPolicyChanged) {
+ *outPolicyChanged = policyChanged;
+ }
+ if (!policyChanged) {
+ return NO_ERROR;
+ }
+ // defaultConfigId must be a valid config ID, and within the given refresh rate range.
+ if (mRefreshRates.count(defaultConfigId) == 0) {
+ return BAD_VALUE;
+ }
+ const RefreshRate& refreshRate = mRefreshRates.at(defaultConfigId);
+ if (refreshRate.fps < minRefreshRate || refreshRate.fps > maxRefreshRate) {
+ return BAD_VALUE;
+ }
+ mDefaultConfig = defaultConfigId;
mMinRefreshRateFps = minRefreshRate;
mMaxRefreshRateFps = maxRefreshRate;
constructAvailableRefreshRates();
+ return NO_ERROR;
+}
+
+void RefreshRateConfigs::getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
+ float* maxRefreshRate) const {
+ std::lock_guard lock(mLock);
+ *defaultConfigId = mDefaultConfig;
+ *minRefreshRate = mMinRefreshRateFps;
+ *maxRefreshRate = mMaxRefreshRateFps;
+}
+
+bool RefreshRateConfigs::isConfigAllowed(HwcConfigIndexType config) const {
+ std::lock_guard lock(mLock);
+ for (const RefreshRate* refreshRate : mAvailableRefreshRates) {
+ if (refreshRate->configId == config) {
+ return true;
+ }
+ }
+ return false;
}
void RefreshRateConfigs::getSortedRefreshRateList(
@@ -140,15 +175,18 @@
void RefreshRateConfigs::constructAvailableRefreshRates() {
// Filter configs based on current policy and sort based on vsync period
- ALOGV("constructRefreshRateMap: group %d min %.2f max %.2f", mCurrentGroupId.value(),
- mMinRefreshRateFps, mMaxRefreshRateFps);
+ HwcConfigGroupType group = mRefreshRates.at(mDefaultConfig).configGroup;
+ ALOGV("constructRefreshRateMap: default %d group %d min %.2f max %.2f", mDefaultConfig.value(),
+ group.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
getSortedRefreshRateList(
- [this](const RefreshRate& refreshRate) REQUIRES(mLock) {
- return refreshRate.configGroup == mCurrentGroupId &&
- refreshRate.fps >= mMinRefreshRateFps &&
+ [&](const RefreshRate& refreshRate) REQUIRES(mLock) {
+ return refreshRate.configGroup == group && refreshRate.fps >= mMinRefreshRateFps &&
refreshRate.fps <= mMaxRefreshRateFps;
},
&mAvailableRefreshRates);
+ LOG_ALWAYS_FATAL_IF(mAvailableRefreshRates.empty(),
+ "No compatible display configs for default=%d min=%.0f max=%.0f",
+ mDefaultConfig.value(), mMinRefreshRateFps, mMaxRefreshRateFps);
}
// NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
@@ -167,12 +205,12 @@
mRefreshRates.emplace(config.configId, buildRefreshRate(config));
if (config.configId == currentHwcConfig) {
mCurrentRefreshRate = &mRefreshRates.at(config.configId);
- mCurrentGroupId = config.configGroup;
}
}
std::vector<const RefreshRate*> sortedConfigs;
getSortedRefreshRateList([](const RefreshRate&) { return true; }, &sortedConfigs);
+ mDefaultConfig = currentHwcConfig;
mMinSupportedRefreshRate = sortedConfigs.front();
mMaxSupportedRefreshRate = sortedConfigs.back();
constructAvailableRefreshRates();
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index fb14dc7..1e740ca 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -73,9 +73,17 @@
using AllRefreshRatesMapType = std::unordered_map<HwcConfigIndexType, const RefreshRate>;
- // Sets the current policy to choose refresh rates.
- void setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate, float maxRefreshRate)
- EXCLUDES(mLock);
+ // Sets the current policy to choose refresh rates. Returns NO_ERROR if the requested policy is
+ // valid, or a negative error value otherwise. policyChanged, if non-null, will be set to true
+ // if the new policy is different from the old policy.
+ status_t setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+ float maxRefreshRate, bool* policyChanged) EXCLUDES(mLock);
+ // Gets the current policy.
+ void getPolicy(HwcConfigIndexType* defaultConfigId, float* minRefreshRate,
+ float* maxRefreshRate) const EXCLUDES(mLock);
+
+ // Returns true if config is allowed by the current policy.
+ bool isConfigAllowed(HwcConfigIndexType config) const EXCLUDES(mLock);
// Returns true if this device is doing refresh rate switching. This won't change at runtime.
bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
@@ -143,9 +151,9 @@
// the main thread, and read by the Scheduler (and other objects) on other threads.
const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
- // The current config group. This will change at runtime. This is set by SurfaceFlinger on
+ // The default config. This will change at runtime. This is set by SurfaceFlinger on
// the main thread, and read by the Scheduler (and other objects) on other threads.
- HwcConfigGroupType mCurrentGroupId GUARDED_BY(mLock);
+ HwcConfigIndexType mDefaultConfig GUARDED_BY(mLock);
// The min and max FPS allowed by the policy. This will change at runtime and set by
// SurfaceFlinger on the main thread.
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 1d50fe1..71ebfc3 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -59,11 +59,13 @@
namespace android {
Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function,
- const scheduler::RefreshRateConfigs& refreshRateConfig)
+ const scheduler::RefreshRateConfigs& refreshRateConfig,
+ ISchedulerCallback& schedulerCallback)
: mPrimaryDispSync(new impl::DispSync("SchedulerDispSync",
sysprop::running_without_sync_framework(true))),
mEventControlThread(new impl::EventControlThread(std::move(function))),
mSupportKernelTimer(sysprop::support_kernel_idle_timer(false)),
+ mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(refreshRateConfig) {
using namespace sysprop;
@@ -104,10 +106,12 @@
Scheduler::Scheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
- const scheduler::RefreshRateConfigs& configs)
+ const scheduler::RefreshRateConfigs& configs,
+ ISchedulerCallback& schedulerCallback)
: mPrimaryDispSync(std::move(primaryDispSync)),
mEventControlThread(std::move(eventControlThread)),
mSupportKernelTimer(false),
+ mSchedulerCallback(schedulerCallback),
mRefreshRateConfigs(configs) {}
Scheduler::~Scheduler() {
@@ -121,18 +125,16 @@
return *mPrimaryDispSync;
}
-std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(
- const char* name, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync) {
+std::unique_ptr<VSyncSource> Scheduler::makePrimaryDispSyncSource(const char* name,
+ nsecs_t phaseOffsetNs) {
return std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), phaseOffsetNs,
- offsetThresholdForNextVsync, true /* traceVsync */,
- name);
+ true /* traceVsync */, name);
}
Scheduler::ConnectionHandle Scheduler::createConnection(
- const char* connectionName, nsecs_t phaseOffsetNs, nsecs_t offsetThresholdForNextVsync,
+ const char* connectionName, nsecs_t phaseOffsetNs,
impl::EventThread::InterceptVSyncsCallback interceptCallback) {
- auto vsyncSource =
- makePrimaryDispSyncSource(connectionName, phaseOffsetNs, offsetThresholdForNextVsync);
+ auto vsyncSource = makePrimaryDispSyncSource(connectionName, phaseOffsetNs);
auto eventThread = std::make_unique<impl::EventThread>(std::move(vsyncSource),
std::move(interceptCallback));
return createConnection(std::move(eventThread));
@@ -368,12 +370,7 @@
mFeatures.configId = newConfigId;
};
auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
-}
-
-void Scheduler::setSchedulerCallback(android::Scheduler::ISchedulerCallback* callback) {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- mSchedulerCallback = callback;
+ mSchedulerCallback.changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
void Scheduler::resetIdleTimer() {
@@ -486,7 +483,7 @@
}
}
const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
- changeRefreshRate(newRefreshRate, event);
+ mSchedulerCallback.changeRefreshRate(newRefreshRate, event);
}
HwcConfigIndexType Scheduler::calculateRefreshRateType() {
@@ -526,10 +523,36 @@
return mFeatures.configId;
}
-void Scheduler::changeRefreshRate(const RefreshRate& refreshRate, ConfigEvent configEvent) {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mSchedulerCallback) {
- mSchedulerCallback->changeRefreshRate(refreshRate, configEvent);
+void Scheduler::onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline) {
+ if (timeline.refreshRequired) {
+ mSchedulerCallback.repaintEverythingForHWC();
+ }
+
+ std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+ mLastVsyncPeriodChangeTimeline = std::make_optional(timeline);
+
+ const auto maxAppliedTime = systemTime() + MAX_VSYNC_APPLIED_TIME.count();
+ if (timeline.newVsyncAppliedTimeNanos > maxAppliedTime) {
+ mLastVsyncPeriodChangeTimeline->newVsyncAppliedTimeNanos = maxAppliedTime;
+ }
+}
+
+void Scheduler::onDisplayRefreshed(nsecs_t timestamp) {
+ bool callRepaint = false;
+ {
+ std::lock_guard<std::mutex> lock(mVsyncTimelineLock);
+ if (mLastVsyncPeriodChangeTimeline && mLastVsyncPeriodChangeTimeline->refreshRequired) {
+ if (mLastVsyncPeriodChangeTimeline->refreshTimeNanos < timestamp) {
+ mLastVsyncPeriodChangeTimeline->refreshRequired = false;
+ } else {
+ // We need to send another refresh as refreshTimeNanos is still in the future
+ callRepaint = true;
+ }
+ }
+ }
+
+ if (callRepaint) {
+ mSchedulerCallback.repaintEverythingForHWC();
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 04a8390..2fa8b3f 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -41,22 +41,24 @@
class InjectVSyncSource;
struct DisplayStateInfo;
+class ISchedulerCallback {
+public:
+ virtual ~ISchedulerCallback() = default;
+ virtual void changeRefreshRate(const scheduler::RefreshRateConfigs::RefreshRate&,
+ scheduler::RefreshRateConfigEvent) = 0;
+ virtual void repaintEverythingForHWC() = 0;
+};
+
class Scheduler {
public:
using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- class ISchedulerCallback {
- public:
- virtual ~ISchedulerCallback() = default;
- virtual void changeRefreshRate(const RefreshRate&, ConfigEvent) = 0;
- };
-
// Indicates whether to start the transaction early, or at vsync time.
enum class TransactionStart { EARLY, NORMAL };
Scheduler(impl::EventControlThread::SetVSyncEnabledFunction,
- const scheduler::RefreshRateConfigs&);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
virtual ~Scheduler();
@@ -64,7 +66,6 @@
using ConnectionHandle = scheduler::ConnectionHandle;
ConnectionHandle createConnection(const char* connectionName, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync,
impl::EventThread::InterceptVSyncsCallback);
sp<IDisplayEventConnection> createDisplayEventConnection(ConnectionHandle,
@@ -114,9 +115,6 @@
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
- // Called by Scheduler to control SurfaceFlinger operations.
- void setSchedulerCallback(ISchedulerCallback*);
-
bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
void resetIdleTimer();
@@ -131,6 +129,12 @@
// Get the appropriate refresh for current conditions.
std::optional<HwcConfigIndexType> getPreferredConfigId();
+ // Notifies the scheduler about a refresh rate timeline change.
+ void onNewVsyncPeriodChangeTimeline(const HWC2::VsyncPeriodChangeTimeline& timeline);
+
+ // Notifies the scheduler when the display was refreshed
+ void onDisplayRefreshed(nsecs_t timestamp);
+
private:
friend class TestableScheduler;
@@ -142,10 +146,9 @@
// Used by tests to inject mocks.
Scheduler(std::unique_ptr<DispSync>, std::unique_ptr<EventControlThread>,
- const scheduler::RefreshRateConfigs&);
+ const scheduler::RefreshRateConfigs&, ISchedulerCallback& schedulerCallback);
- std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs,
- nsecs_t offsetThresholdForNextVsync);
+ std::unique_ptr<VSyncSource> makePrimaryDispSyncSource(const char* name, nsecs_t phaseOffsetNs);
// Create a connection on the given EventThread.
ConnectionHandle createConnection(std::unique_ptr<EventThread>);
@@ -165,8 +168,6 @@
void setVsyncPeriod(nsecs_t period);
HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
- // Acquires a lock and calls the ChangeRefreshRateCallback with given parameters.
- void changeRefreshRate(const RefreshRate&, ConfigEvent);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -203,8 +204,7 @@
// Timer used to monitor display power mode.
std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
- std::mutex mCallbackLock;
- ISchedulerCallback* mSchedulerCallback GUARDED_BY(mCallbackLock) = nullptr;
+ ISchedulerCallback& mSchedulerCallback;
// In order to make sure that the features don't override themselves, we need a state machine
// to keep track which feature requested the config change.
@@ -223,6 +223,11 @@
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
+
+ std::mutex mVsyncTimelineLock;
+ std::optional<HWC2::VsyncPeriodChangeTimeline> mLastVsyncPeriodChangeTimeline
+ GUARDED_BY(mVsyncTimelineLock);
+ static constexpr std::chrono::nanoseconds MAX_VSYNC_APPLIED_TIME = 200ms;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Timer.cpp b/services/surfaceflinger/Scheduler/Timer.cpp
index 2394ed2..fb4f315 100644
--- a/services/surfaceflinger/Scheduler/Timer.cpp
+++ b/services/surfaceflinger/Scheduler/Timer.cpp
@@ -85,7 +85,7 @@
};
if (timerfd_settime(mTimerFd, 0, &new_timer, &old_timer)) {
- ALOGW("Failed to set timerfd");
+ ALOGW("Failed to set timerfd %s (%i)", strerror(errno), errno);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index e001080..56b3252 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -26,7 +26,7 @@
class TimeKeeper;
class VSyncTracker;
-enum class ScheduleResult { Scheduled, ReScheduled, CannotSchedule, Error };
+enum class ScheduleResult { Scheduled, CannotSchedule, Error };
enum class CancelResult { Cancelled, TooLate, Error };
/*
@@ -83,7 +83,6 @@
* \param [in] earliestVsync The targeted display time. This will be snapped to the closest
* predicted vsync time after earliestVsync.
* \return A ScheduleResult::Scheduled if callback was scheduled.
- * A ScheduleResult::ReScheduled if callback was rescheduled.
* A ScheduleResult::CannotSchedule
* if (workDuration - earliestVsync) is in the past, or
* if a callback was dispatched for the predictedVsync already.
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index a79fe98..48f2abb 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -29,8 +29,13 @@
TimeKeeper::~TimeKeeper() = default;
VSyncDispatchTimerQueueEntry::VSyncDispatchTimerQueueEntry(std::string const& name,
- std::function<void(nsecs_t)> const& cb)
- : mName(name), mCallback(cb), mWorkDuration(0), mEarliestVsync(0) {}
+ std::function<void(nsecs_t)> const& cb,
+ nsecs_t minVsyncDistance)
+ : mName(name),
+ mCallback(cb),
+ mWorkDuration(0),
+ mEarliestVsync(0),
+ mMinVsyncDistance(minVsyncDistance) {}
std::optional<nsecs_t> VSyncDispatchTimerQueueEntry::lastExecutedVsyncTarget() const {
return mLastDispatchTime;
@@ -49,18 +54,28 @@
ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
VSyncTracker& tracker, nsecs_t now) {
- auto const nextVsyncTime =
+ auto nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
- if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
- return ScheduleResult::CannotSchedule;
+
+ bool const wouldSkipAVsyncTarget =
+ mArmedInfo && (nextVsyncTime > (mArmedInfo->mActualVsyncTime + mMinVsyncDistance));
+ if (wouldSkipAVsyncTarget) {
+ return ScheduleResult::Scheduled;
+ }
+
+ bool const alreadyDispatchedForVsync = mLastDispatchTime &&
+ ((*mLastDispatchTime + mMinVsyncDistance) >= nextVsyncTime &&
+ (*mLastDispatchTime - mMinVsyncDistance) <= nextVsyncTime);
+ if (alreadyDispatchedForVsync) {
+ nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(*mLastDispatchTime + mMinVsyncDistance);
}
auto const nextWakeupTime = nextVsyncTime - workDuration;
- auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
mWorkDuration = workDuration;
mEarliestVsync = earliestVsync;
mArmedInfo = {nextWakeupTime, nextVsyncTime};
- return result;
+ return ScheduleResult::Scheduled;
}
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
@@ -101,8 +116,12 @@
}
VSyncDispatchTimerQueue::VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk,
- VSyncTracker& tracker, nsecs_t timerSlack)
- : mTimeKeeper(std::move(tk)), mTracker(tracker), mTimerSlack(timerSlack) {}
+ VSyncTracker& tracker, nsecs_t timerSlack,
+ nsecs_t minVsyncDistance)
+ : mTimeKeeper(std::move(tk)),
+ mTracker(tracker),
+ mTimerSlack(timerSlack),
+ mMinVsyncDistance(minVsyncDistance) {}
VSyncDispatchTimerQueue::~VSyncDispatchTimerQueue() {
std::lock_guard<decltype(mMutex)> lk(mMutex);
@@ -187,7 +206,8 @@
mCallbacks
.emplace(++mCallbackToken,
std::make_shared<VSyncDispatchTimerQueueEntry>(callbackName,
- callbackFn))
+ callbackFn,
+ mMinVsyncDistance))
.first->first};
}
@@ -277,12 +297,16 @@
}
ScheduleResult VSyncCallbackRegistration::schedule(nsecs_t workDuration, nsecs_t earliestVsync) {
- if (!mValidToken) return ScheduleResult::Error;
+ if (!mValidToken) {
+ return ScheduleResult::Error;
+ }
return mDispatch.get().schedule(mToken, workDuration, earliestVsync);
}
CancelResult VSyncCallbackRegistration::cancel() {
- if (!mValidToken) return CancelResult::Error;
+ if (!mValidToken) {
+ return CancelResult::Error;
+ }
return mDispatch.get().cancel(mToken);
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index fc78da3..0c9b4fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -36,7 +36,8 @@
// Valid transition: disarmed -> armed ( when scheduled )
// Valid transition: armed -> running -> disarmed ( when timer is called)
// Valid transition: armed -> disarmed ( when cancelled )
- VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn);
+ VSyncDispatchTimerQueueEntry(std::string const& name, std::function<void(nsecs_t)> const& fn,
+ nsecs_t minVsyncDistance);
std::string_view name() const;
// Start: functions that are not threadsafe.
@@ -72,6 +73,7 @@
nsecs_t mWorkDuration;
nsecs_t mEarliestVsync;
+ nsecs_t const mMinVsyncDistance;
struct ArmingInfo {
nsecs_t mActualWakeupTime;
@@ -91,8 +93,15 @@
*/
class VSyncDispatchTimerQueue : public VSyncDispatch {
public:
+ // Constructs a VSyncDispatchTimerQueue.
+ // \param[in] tk A timekeeper.
+ // \param[in] tracker A tracker.
+ // \param[in] timerSlack The threshold at which different similarly timed callbacks
+ // should be grouped into one wakeup.
+ // \param[in] minVsyncDistance The minimum distance between two vsync estimates before the
+ // vsyncs are considered the same vsync event.
explicit VSyncDispatchTimerQueue(std::unique_ptr<TimeKeeper> tk, VSyncTracker& tracker,
- nsecs_t timerSlack);
+ nsecs_t timerSlack, nsecs_t minVsyncDistance);
~VSyncDispatchTimerQueue();
CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
@@ -119,6 +128,7 @@
std::unique_ptr<TimeKeeper> const mTimeKeeper;
VSyncTracker& mTracker;
nsecs_t const mTimerSlack;
+ nsecs_t const mMinVsyncDistance;
std::mutex mutable mMutex;
size_t mCallbackToken GUARDED_BY(mMutex) = 0;
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 63c0feb..704a5d5 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -43,6 +43,10 @@
struct Offsets {
nsecs_t sf;
nsecs_t app;
+
+ bool operator==(const Offsets& other) const { return sf == other.sf && app == other.app; }
+
+ bool operator!=(const Offsets& other) const { return !(*this == other); }
};
struct OffsetsConfig {
@@ -50,7 +54,11 @@
Offsets earlyGl; // As above but while compositing with GL.
Offsets late; // Default.
- nsecs_t thresholdForNextVsync;
+ bool operator==(const OffsetsConfig& other) const {
+ return early == other.early && earlyGl == other.earlyGl && late == other.late;
+ }
+
+ bool operator!=(const OffsetsConfig& other) const { return !(*this == other); }
};
VSyncModulator(Scheduler&, ConnectionHandle appConnectionHandle,
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 3894992..3b99a58 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -23,6 +23,7 @@
#include <utils/Trace.h>
#include <algorithm>
#include <chrono>
+#include <sstream>
#include "SchedulerUtils.h"
namespace android::scheduler {
@@ -153,12 +154,24 @@
}
auto const oldest = *std::min_element(timestamps.begin(), timestamps.end());
- auto const ordinalRequest = (timePoint - oldest + slope) / slope;
+
+ // See b/145667109, the ordinal calculation must take into account the intercept.
+ auto const zeroPoint = oldest + intercept;
+ auto const ordinalRequest = (timePoint - zeroPoint + slope) / slope;
auto const prediction = (ordinalRequest * slope) + intercept + oldest;
- ALOGV("prediction made from: %" PRId64 " prediction: %" PRId64 " (+%" PRId64 ") slope: %" PRId64
- " intercept: %" PRId64,
- timePoint, prediction, prediction - timePoint, slope, intercept);
+ auto const printer = [&, slope = slope, intercept = intercept] {
+ std::stringstream str;
+ str << "prediction made from: " << timePoint << "prediction: " << prediction << " (+"
+ << prediction - timePoint << ") slope: " << slope << " intercept: " << intercept
+ << "oldestTS: " << oldest << " ordinal: " << ordinalRequest;
+ return str.str();
+ };
+
+ ALOGV("%s", printer().c_str());
+ LOG_ALWAYS_FATAL_IF(prediction < timePoint, "VSyncPredictor: model miscalculation: %s",
+ printer().c_str());
+
return prediction;
}
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
index f2a7791..47e3f4f 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -14,7 +14,11 @@
* limitations under the License.
*/
+#undef LOG_TAG
+#define LOG_TAG "VSyncReactor"
+//#define LOG_NDEBUG 0
#include "VSyncReactor.h"
+#include <log/log.h>
#include "TimeKeeper.h"
#include "VSyncDispatch.h"
#include "VSyncTracker.h"
@@ -30,6 +34,87 @@
mTracker(std::move(tracker)),
mPendingLimit(pendingFenceLimit) {}
+VSyncReactor::~VSyncReactor() = default;
+
+// The DispSync interface has a 'repeat this callback at rate' semantic. This object adapts
+// VSyncDispatch's individually-scheduled callbacks so as to meet DispSync's existing semantic
+// for now.
+class CallbackRepeater {
+public:
+ CallbackRepeater(VSyncDispatch& dispatch, DispSync::Callback* cb, const char* name,
+ nsecs_t period, nsecs_t offset, nsecs_t notBefore)
+ : mCallback(cb),
+ mRegistration(dispatch,
+ std::bind(&CallbackRepeater::callback, this, std::placeholders::_1),
+ std::string(name)),
+ mPeriod(period),
+ mOffset(offset),
+ mLastCallTime(notBefore) {}
+
+ ~CallbackRepeater() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mRegistration.cancel();
+ }
+
+ void start(nsecs_t offset) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mStopped = false;
+ mOffset = offset;
+
+ auto const schedule_result = mRegistration.schedule(calculateWorkload(), mLastCallTime);
+ LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+ "Error scheduling callback: rc %X", schedule_result);
+ }
+
+ void setPeriod(nsecs_t period) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ if (period == mPeriod) {
+ return;
+ }
+ mPeriod = period;
+ }
+
+ void stop() {
+ std::lock_guard<std::mutex> lk(mMutex);
+ LOG_ALWAYS_FATAL_IF(mStopped, "DispSyncInterface misuse: callback already stopped");
+ mStopped = true;
+ mRegistration.cancel();
+ }
+
+private:
+ void callback(nsecs_t vsynctime) {
+ nsecs_t period = 0;
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ period = mPeriod;
+ mLastCallTime = vsynctime;
+ }
+
+ mCallback->onDispSyncEvent(vsynctime - period);
+
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const schedule_result = mRegistration.schedule(calculateWorkload(), vsynctime);
+ LOG_ALWAYS_FATAL_IF((schedule_result != ScheduleResult::Scheduled),
+ "Error rescheduling callback: rc %X", schedule_result);
+ }
+ }
+
+ // DispSync offsets are defined as time after the vsync before presentation.
+ // VSyncReactor workloads are defined as time before the intended presentation vsync.
+ // Note change in sign between the two defnitions.
+ nsecs_t calculateWorkload() REQUIRES(mMutex) { return mPeriod - mOffset; }
+
+ DispSync::Callback* const mCallback;
+
+ std::mutex mutable mMutex;
+ VSyncCallbackRegistration mRegistration GUARDED_BY(mMutex);
+ bool mStopped GUARDED_BY(mMutex) = false;
+ nsecs_t mPeriod GUARDED_BY(mMutex);
+ nsecs_t mOffset GUARDED_BY(mMutex);
+ nsecs_t mLastCallTime GUARDED_BY(mMutex);
+};
+
bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
if (!fence) {
return false;
@@ -92,6 +177,9 @@
{
std::lock_guard<std::mutex> lk(mMutex);
mPeriodChangeInProgress = true;
+ for (auto& entry : mCallbacks) {
+ entry.second->setPeriod(period);
+ }
}
}
@@ -114,4 +202,47 @@
return false;
}
+status_t VSyncReactor::addEventListener(const char* name, nsecs_t phase,
+ DispSync::Callback* callback,
+ nsecs_t /* lastCallbackTime */) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto it = mCallbacks.find(callback);
+ if (it == mCallbacks.end()) {
+ // TODO (b/146557561): resolve lastCallbackTime semantics in DispSync i/f.
+ static auto constexpr maxListeners = 3;
+ if (mCallbacks.size() >= maxListeners) {
+ ALOGE("callback %s not added, exceeded callback limit of %i (currently %zu)", name,
+ maxListeners, mCallbacks.size());
+ return NO_MEMORY;
+ }
+
+ auto const period = mTracker->currentPeriod();
+ auto repeater = std::make_unique<CallbackRepeater>(*mDispatch, callback, name, period,
+ phase, mClock->now());
+ it = mCallbacks.emplace(std::pair(callback, std::move(repeater))).first;
+ }
+
+ it->second->start(phase);
+ return NO_ERROR;
+}
+
+status_t VSyncReactor::removeEventListener(DispSync::Callback* callback,
+ nsecs_t* /* outLastCallback */) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const it = mCallbacks.find(callback);
+ LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback %p not registered", callback);
+
+ it->second->stop();
+ return NO_ERROR;
+}
+
+status_t VSyncReactor::changePhaseOffset(DispSync::Callback* callback, nsecs_t phase) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ auto const it = mCallbacks.find(callback);
+ LOG_ALWAYS_FATAL_IF(it == mCallbacks.end(), "callback was %p not registered", callback);
+
+ it->second->start(phase);
+ return NO_ERROR;
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
index 786ee98..837eb75 100644
--- a/services/surfaceflinger/Scheduler/VSyncReactor.h
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -20,19 +20,23 @@
#include <ui/FenceTime.h>
#include <memory>
#include <mutex>
+#include <unordered_map>
#include <vector>
+#include "DispSync.h"
namespace android::scheduler {
class Clock;
class VSyncDispatch;
class VSyncTracker;
+class CallbackRepeater;
// TODO (b/145217110): consider renaming.
class VSyncReactor /* TODO (b/140201379): : public android::DispSync */ {
public:
VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit);
+ ~VSyncReactor();
bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
void setIgnorePresentFences(bool ignoration);
@@ -48,6 +52,11 @@
bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
void endResync();
+ status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback,
+ nsecs_t lastCallbackTime);
+ status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback);
+ status_t changePhaseOffset(DispSync::Callback* callback, nsecs_t phase);
+
private:
std::unique_ptr<Clock> const mClock;
std::unique_ptr<VSyncDispatch> const mDispatch;
@@ -58,6 +67,8 @@
bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
+ std::unordered_map<DispSync::Callback*, std::unique_ptr<CallbackRepeater>> mCallbacks
+ GUARDED_BY(mMutex);
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 9f4dd2b..d02d783 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -260,7 +260,6 @@
mFrameTracer(std::make_unique<FrameTracer>()),
mEventQueue(mFactory.createMessageQueue()),
mCompositionEngine(mFactory.createCompositionEngine()),
- mPhaseOffsets(mFactory.createPhaseOffsets()),
mPendingSyncInputWindows(false) {}
SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipInitialization) {
@@ -582,8 +581,6 @@
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
- ALOGI("Phase offset: %" PRId64 " ns", mPhaseOffsets->getCurrentAppOffset());
-
Mutex::Autolock _l(mStateLock);
// Get a RenderEngine for the given display / config (can't fail)
@@ -817,7 +814,7 @@
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(info.fps);
+ const auto offset = mPhaseConfiguration->getOffsetsForRefreshRate(info.fps);
info.appVsyncOffset = offset.late.app;
// This is how far in advance a buffer must be queued for
@@ -897,8 +894,8 @@
// DispSync model is locked.
mVSyncModulator->onRefreshRateChangeInitiated();
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
mDesiredActiveConfigChanged = true;
@@ -910,10 +907,30 @@
status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) {
ATRACE_CALL();
- std::vector<int32_t> allowedConfig;
- allowedConfig.push_back(mode);
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
- return setAllowedDisplayConfigs(displayToken, allowedConfig);
+ status_t result = NO_ERROR;
+
+ postMessageSync(new LambdaMessage([&]() {
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set allowed display configs for invalid display token %p",
+ displayToken.get());
+ result = BAD_VALUE;
+ } else if (display->isVirtual()) {
+ ALOGW("Attempt to set allowed display configs for virtual display");
+ result = BAD_VALUE;
+ } else {
+ HwcConfigIndexType config(mode);
+ const auto& refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(config);
+ result = setDesiredDisplayConfigSpecsInternal(display, config, refreshRate.fps,
+ refreshRate.fps);
+ }
+ }));
+
+ return result;
}
void SurfaceFlinger::setActiveConfigInternal() {
@@ -931,8 +948,8 @@
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
@@ -949,8 +966,8 @@
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
auto refreshRate =
mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
- mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
- mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->setRefreshRateFps(refreshRate.fps);
+ mVSyncModulator->setPhaseOffsets(mPhaseConfiguration->getCurrentOffsets());
}
bool SurfaceFlinger::performSetActiveConfig() {
@@ -1004,11 +1021,25 @@
LOG_ALWAYS_FATAL_IF(!displayId);
ATRACE_INT("ActiveConfigFPS_HWC", refreshRate.fps);
- getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId.value());
- // we need to submit an empty frame to HWC to start the process
+ // TODO(b/142753666) use constrains
+ HWC2::VsyncPeriodChangeConstraints constraints;
+ constraints.desiredTimeNanos = systemTime();
+ constraints.seamlessRequired = false;
+
+ HWC2::VsyncPeriodChangeTimeline outTimeline;
+ auto status =
+ getHwComposer().setActiveConfigWithConstraints(*displayId,
+ mUpcomingActiveConfig.configId.value(),
+ constraints, &outTimeline);
+ if (status != NO_ERROR) {
+ LOG_ALWAYS_FATAL("setActiveConfigWithConstraints failed: %d", status);
+ return false;
+ }
+
+ mScheduler->onNewVsyncPeriodChangeTimeline(outTimeline);
+ // Scheduler will submit an empty frame to HWC if needed.
mCheckPendingFence = true;
- mEventQueue->invalidate();
return false;
}
@@ -1097,6 +1128,86 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ Mutex::Autolock _l(mStateLock);
+
+ if (!displayToken) {
+ ALOGE("getAutoLowLatencyModeSupport() failed. Missing display token.");
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("getAutoLowLatencyModeSupport() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+ *outSupport = getHwComposer().hasDisplayCapability(displayId,
+ HWC2::DisplayCapability::AutoLowLatencyMode);
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) {
+ postMessageAsync(new LambdaMessage([=] { setAutoLowLatencyModeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on) {
+ if (!displayToken) {
+ ALOGE("setAutoLowLatencyMode() failed. Missing display token.");
+ return;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setAutoLowLatencyMode() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return;
+ }
+
+ getHwComposer().setAutoLowLatencyMode(*displayId, on);
+}
+
+status_t SurfaceFlinger::getGameContentTypeSupport(const sp<IBinder>& displayToken,
+ bool* outSupport) const {
+ Mutex::Autolock _l(mStateLock);
+
+ if (!displayToken) {
+ ALOGE("getGameContentTypeSupport() failed. Missing display token.");
+ return BAD_VALUE;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("getGameContentTypeSupport() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return NAME_NOT_FOUND;
+ }
+
+ std::vector<HWC2::ContentType> outSupportedContentTypes;
+ getHwComposer().getSupportedContentTypes(*displayId, &outSupportedContentTypes);
+ *outSupport = std::find(outSupportedContentTypes.begin(), outSupportedContentTypes.end(),
+ HWC2::ContentType::Game) != outSupportedContentTypes.end();
+ return NO_ERROR;
+}
+
+void SurfaceFlinger::setGameContentType(const sp<IBinder>& displayToken, bool on) {
+ postMessageAsync(new LambdaMessage([=] { setGameContentTypeInternal(displayToken, on); }));
+}
+
+void SurfaceFlinger::setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on) {
+ if (!displayToken) {
+ ALOGE("setGameContentType() failed. Missing display token.");
+ return;
+ }
+ const auto displayId = getPhysicalDisplayIdLocked(displayToken);
+ if (!displayId) {
+ ALOGE("setGameContentType() failed. Display id for display token %p not found.",
+ displayToken.get());
+ return;
+ }
+
+ const HWC2::ContentType type = on ? HWC2::ContentType::Game : HWC2::ContentType::None;
+ getHwComposer().setContentType(*displayId, type);
+}
+
status_t SurfaceFlinger::clearAnimationFrameStats() {
Mutex::Autolock _l(mStateLock);
mAnimFrameTracker.clearStats();
@@ -1361,8 +1472,7 @@
return 0;
}
- const auto config = getHwComposer().getActiveConfig(*displayId);
- return config ? config->getVsyncPeriod() : 0;
+ return getHwComposer().getDisplayVsyncPeriod(*displayId);
}
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1400,7 +1510,7 @@
}
bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
- return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId);
+ return mRefreshRateConfigs->isConfigAllowed(configId);
}
void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
@@ -1448,9 +1558,13 @@
}
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
+ int32_t sequenceId, hwc2_display_t /*display*/,
+ const hwc_vsync_period_change_timeline_t& updatedTimeline) {
+ Mutex::Autolock lock(mStateLock);
+ if (sequenceId != getBE().mComposerSequenceId) {
+ return;
+ }
+ mScheduler->onNewVsyncPeriodChangeTimeline(updatedTimeline);
}
void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
@@ -1581,10 +1695,8 @@
// We are storing the last 2 present fences. If sf's phase offset is to be
// woken up before the actual vsync but targeting the next vsync, we need to check
// fence N-2
- const sp<Fence>& fence =
- mVSyncModulator->getOffsets().sf < mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? mPreviousPresentFences[0]
- : mPreviousPresentFences[1];
+ const sp<Fence>& fence = mVSyncModulator->getOffsets().sf > 0 ? mPreviousPresentFences[0]
+ : mPreviousPresentFences[1];
if (fence == Fence::NO_FENCE) {
return false;
@@ -1602,10 +1714,8 @@
mScheduler->getDisplayStatInfo(&stats);
const nsecs_t presentTime = mScheduler->getDispSyncExpectedPresentTime();
// Inflate the expected present time if we're targetting the next vsync.
- mExpectedPresentTime.store(mVSyncModulator->getOffsets().sf <
- mPhaseOffsets->getOffsetThresholdForNextVsync()
- ? presentTime
- : presentTime + stats.vsyncPeriod);
+ mExpectedPresentTime.store(
+ mVSyncModulator->getOffsets().sf > 0 ? presentTime : presentTime + stats.vsyncPeriod);
}
void SurfaceFlinger::onMessageReceived(int32_t what) NO_THREAD_SAFETY_ANALYSIS {
@@ -1761,11 +1871,17 @@
mGeometryInvalid = false;
+ // Store the present time just before calling to the composition engine so we could notify
+ // the scheduler.
+ const auto presentTime = systemTime();
+
mCompositionEngine->present(refreshArgs);
mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
// Reset the frame start time now that we've recorded this frame.
mFrameStartTime = 0;
+ mScheduler->onDisplayRefreshed(presentTime);
+
postFrame();
postComposition();
@@ -1843,11 +1959,12 @@
nsecs_t compositeToPresentLatency) {
// Integer division and modulo round toward 0 not -inf, so we need to
// treat negative and positive offsets differently.
- nsecs_t idealLatency = (mPhaseOffsets->getCurrentSfOffset() > 0)
- ? (stats.vsyncPeriod - (mPhaseOffsets->getCurrentSfOffset() % stats.vsyncPeriod))
- : ((-mPhaseOffsets->getCurrentSfOffset()) % stats.vsyncPeriod);
+ nsecs_t idealLatency = (mPhaseConfiguration->getCurrentOffsets().late.sf > 0)
+ ? (stats.vsyncPeriod -
+ (mPhaseConfiguration->getCurrentOffsets().late.sf % stats.vsyncPeriod))
+ : ((-mPhaseConfiguration->getCurrentOffsets().late.sf) % stats.vsyncPeriod);
- // Just in case mPhaseOffsets->getCurrentSfOffset() == -vsyncInterval.
+ // Just in case mPhaseConfiguration->getCurrentOffsets().late.sf == -vsyncInterval.
if (idealLatency <= 0) {
idealLatency = stats.vsyncPeriod;
}
@@ -1856,8 +1973,8 @@
// composition and present times, which often have >1ms of jitter.
// Reducing jitter is important if an app attempts to extrapolate
// something (such as user input) to an accurate diasplay time.
- // Snapping also allows an app to precisely calculate mPhaseOffsets->getCurrentSfOffset()
- // with (presentLatency % interval).
+ // Snapping also allows an app to precisely calculate
+ // mPhaseConfiguration->getCurrentOffsets().late.sf with (presentLatency % interval).
nsecs_t bias = stats.vsyncPeriod / 2;
int64_t extraVsyncs = (compositeToPresentLatency - idealLatency + bias) / stats.vsyncPeriod;
nsecs_t snappedCompositeToPresentLatency =
@@ -2006,6 +2123,7 @@
// Even though ATRACE_INT64 already checks if tracing is enabled, it doesn't prevent the
// side-effect of getTotalSize(), so we check that again here
if (ATRACE_ENABLED()) {
+ // getTotalSize returns the total number of buffers that were allocated by SurfaceFlinger
ATRACE_INT64("Total Buffer Size", GraphicBufferAllocator::get().getTotalSize());
}
}
@@ -2548,30 +2666,28 @@
currentConfig, HWC_POWER_MODE_OFF);
mRefreshRateStats->setConfigMode(currentConfig);
+ mPhaseConfiguration = getFactory().createPhaseConfiguration(*mRefreshRateConfigs);
+
// start the EventThread
mScheduler =
getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); },
- *mRefreshRateConfigs);
+ *mRefreshRateConfigs, *this);
mAppConnectionHandle =
- mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("app", mPhaseConfiguration->getCurrentOffsets().late.app,
impl::EventThread::InterceptVSyncsCallback());
mSfConnectionHandle =
- mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(),
- mPhaseOffsets->getOffsetThresholdForNextVsync(),
+ mScheduler->createConnection("sf", mPhaseConfiguration->getCurrentOffsets().late.sf,
[this](nsecs_t timestamp) {
mInterceptor->saveVSyncEvent(timestamp);
});
mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle));
mVSyncModulator.emplace(*mScheduler, mAppConnectionHandle, mSfConnectionHandle,
- mPhaseOffsets->getCurrentOffsets());
+ mPhaseConfiguration->getCurrentOffsets());
mRegionSamplingThread =
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
-
- mScheduler->setSchedulerCallback(this);
}
void SurfaceFlinger::commitTransaction()
@@ -3968,23 +4084,18 @@
mRefreshRateStats->dump(result);
result.append("\n");
- mPhaseOffsets->dump(result);
+ mPhaseConfiguration->dump(result);
StringAppendF(&result,
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriod());
- StringAppendF(&result, "Allowed Display Configs: ");
- for (auto configId : mAllowedDisplayConfigs) {
- StringAppendF(&result, "%" PRIu32 " Hz, ",
- static_cast<int32_t>(
- mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps));
- }
+ HwcConfigIndexType defaultConfig;
+ float minFps, maxFps;
+ mRefreshRateConfigs->getPolicy(&defaultConfig, &minFps, &maxFps);
StringAppendF(&result,
- "DesiredDisplayConfigSpecs: default config ID: %" PRIu32
+ "DesiredDisplayConfigSpecs: default config ID: %d"
", min: %.2f Hz, max: %.2f Hz",
- mDesiredDisplayConfigSpecs.defaultModeId,
- mDesiredDisplayConfigSpecs.minRefreshRate,
- mDesiredDisplayConfigSpecs.maxRefreshRate);
+ defaultConfig.value(), minFps, maxFps);
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
@@ -4316,8 +4427,8 @@
" refresh-rate : %f fps\n"
" x-dpi : %f\n"
" y-dpi : %f\n",
- 1e9 / activeConfig->getVsyncPeriod(), activeConfig->getDpiX(),
- activeConfig->getDpiY());
+ 1e9 / getHwComposer().getDisplayVsyncPeriod(*displayId),
+ activeConfig->getDpiX(), activeConfig->getDpiY());
}
StringAppendF(&result, " transaction time: %f us\n", inTransactionDuration / 1000.0);
@@ -4412,11 +4523,13 @@
case GET_ANIMATION_FRAME_STATS:
case GET_HDR_CAPABILITIES:
case SET_ACTIVE_CONFIG:
- case SET_ALLOWED_DISPLAY_CONFIGS:
- case GET_ALLOWED_DISPLAY_CONFIGS:
case SET_DESIRED_DISPLAY_CONFIG_SPECS:
case GET_DESIRED_DISPLAY_CONFIG_SPECS:
case SET_ACTIVE_COLOR_MODE:
+ case GET_AUTO_LOW_LATENCY_MODE_SUPPORT:
+ case SET_AUTO_LOW_LATENCY_MODE:
+ case GET_GAME_CONTENT_TYPE_SUPPORT:
+ case SET_GAME_CONTENT_TYPE:
case INJECT_VSYNC:
case SET_POWER_MODE:
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
@@ -4822,15 +4935,15 @@
return NO_ERROR;
}
case 1034: {
- // TODO(b/129297325): expose this via developer menu option
n = data.readInt32();
- if (n && !mRefreshRateOverlay &&
- mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ if (n == 1 && !mRefreshRateOverlay) {
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
auto current = mRefreshRateConfigs->getCurrentRefreshRate();
mRefreshRateOverlay->changeRefreshRate(current);
- } else if (!n) {
+ } else if (n == 0) {
mRefreshRateOverlay.reset();
+ } else {
+ reply->writeBool(mRefreshRateOverlay != nullptr);
}
return NO_ERROR;
}
@@ -4839,7 +4952,7 @@
mDebugDisplayConfigSetByBackdoor = false;
if (n >= 0) {
const auto displayToken = getInternalDisplayToken();
- status_t result = setAllowedDisplayConfigs(displayToken, {n});
+ status_t result = setActiveConfig(displayToken, n);
if (result != NO_ERROR) {
return result;
}
@@ -5435,20 +5548,55 @@
}
}
-void SurfaceFlinger::setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
- const std::vector<int32_t>& allowedConfigs) {
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
+ HwcConfigIndexType defaultConfig,
+ float minRefreshRate,
+ float maxRefreshRate) {
+ Mutex::Autolock lock(mStateLock);
+
if (!display->isPrimary()) {
- return;
+ // TODO(b/144711714): For non-primary displays we should be able to set an active config
+ // as well. For now, just call directly to setActiveConfigWithConstraints but ideally
+ // it should go thru setDesiredActiveConfig, similar to primary display.
+ ALOGV("setAllowedDisplayConfigsInternal for non-primary display");
+ const auto displayId = display->getId();
+ LOG_ALWAYS_FATAL_IF(!displayId);
+
+ HWC2::VsyncPeriodChangeConstraints constraints;
+ constraints.desiredTimeNanos = systemTime();
+ constraints.seamlessRequired = false;
+
+ HWC2::VsyncPeriodChangeTimeline timeline = {0, 0, 0};
+ if (getHwComposer().setActiveConfigWithConstraints(*displayId, defaultConfig.value(),
+ constraints, &timeline) < 0) {
+ return BAD_VALUE;
+ }
+ if (timeline.refreshRequired) {
+ repaintEverythingForHWC();
+ }
+
+ auto configId = HwcConfigIndexType(defaultConfig);
+ display->setActiveConfig(configId);
+ mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value, configId);
+ return NO_ERROR;
}
- const auto allowedDisplayConfigs = DisplayConfigs(allowedConfigs.begin(),
- allowedConfigs.end());
- if (allowedDisplayConfigs == mAllowedDisplayConfigs) {
- return;
+ if (mDebugDisplayConfigSetByBackdoor) {
+ // ignore this request as config is overridden by backdoor
+ return NO_ERROR;
}
- ALOGV("Updating allowed configs");
- mAllowedDisplayConfigs = std::move(allowedDisplayConfigs);
+ bool policyChanged;
+ if (mRefreshRateConfigs->setPolicy(defaultConfig, minRefreshRate, maxRefreshRate,
+ &policyChanged) < 0) {
+ return BAD_VALUE;
+ }
+ if (!policyChanged) {
+ return NO_ERROR;
+ }
+
+ ALOGV("Setting desired display config specs: defaultConfig: %d min: %.f max: %.f",
+ defaultConfig.value(), minRefreshRate, maxRefreshRate);
// TODO(b/140204874): This hack triggers a notification that something has changed, so
// that listeners that care about a change in allowed configs can get the notification.
@@ -5456,25 +5604,6 @@
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
display->getActiveConfig());
- // Prepare the parameters needed for RefreshRateConfigs::setPolicy. This will change to just
- // passthrough once DisplayManager provide these parameters directly.
- const auto refreshRate =
- mRefreshRateConfigs->getRefreshRateFromConfigId(HwcConfigIndexType(allowedConfigs[0]));
- const auto defaultModeId = refreshRate.configId;
- auto minRefreshRateFps = refreshRate.fps;
- auto maxRefreshRateFps = minRefreshRateFps;
-
- for (auto config : allowedConfigs) {
- const auto configRefreshRate =
- mRefreshRateConfigs->getRefreshRateFromConfigId(HwcConfigIndexType(config));
- if (configRefreshRate.fps < minRefreshRateFps) {
- minRefreshRateFps = configRefreshRate.fps;
- } else if (configRefreshRate.fps > maxRefreshRateFps) {
- maxRefreshRateFps = configRefreshRate.fps;
- }
- }
- mRefreshRateConfigs->setPolicy(defaultModeId, minRefreshRateFps, maxRefreshRateFps);
-
if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
auto configId = mScheduler->getPreferredConfigId();
auto preferredRefreshRate = configId
@@ -5493,70 +5622,15 @@
Scheduler::ConfigEvent::Changed});
}
} else {
- if (!allowedConfigs.empty()) {
- ALOGV("switching to config %d", allowedConfigs[0]);
- auto configId = HwcConfigIndexType(allowedConfigs[0]);
- setDesiredActiveConfig({configId, Scheduler::ConfigEvent::Changed});
- }
- }
-}
-
-status_t SurfaceFlinger::setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs) {
- ATRACE_CALL();
-
- if (!displayToken || allowedConfigs.empty()) {
- return BAD_VALUE;
- }
-
- if (mDebugDisplayConfigSetByBackdoor) {
- // ignore this request as config is overridden by backdoor
- return NO_ERROR;
- }
-
- postMessageSync(new LambdaMessage([&]() {
- const auto display = getDisplayDeviceLocked(displayToken);
- if (!display) {
- ALOGE("Attempt to set allowed display configs for invalid display token %p",
- displayToken.get());
- } else if (display->isVirtual()) {
- ALOGW("Attempt to set allowed display configs for virtual display");
- } else {
- Mutex::Autolock lock(mStateLock);
- setAllowedDisplayConfigsInternal(display, allowedConfigs);
- }
- }));
-
- return NO_ERROR;
-}
-
-status_t SurfaceFlinger::getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) {
- ATRACE_CALL();
-
- if (!displayToken || !outAllowedConfigs) {
- return BAD_VALUE;
- }
-
- Mutex::Autolock lock(mStateLock);
-
- const auto display = getDisplayDeviceLocked(displayToken);
- if (!display) {
- return NAME_NOT_FOUND;
- }
-
- if (display->isPrimary()) {
- outAllowedConfigs->reserve(mAllowedDisplayConfigs.size());
- for (auto configId : mAllowedDisplayConfigs) {
- outAllowedConfigs->push_back(configId.value());
- }
+ ALOGV("switching to config %d", defaultConfig.value());
+ setDesiredActiveConfig({defaultConfig, Scheduler::ConfigEvent::Changed});
}
return NO_ERROR;
}
status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t defaultModeId, float minRefreshRate,
+ int32_t defaultConfig, float minRefreshRate,
float maxRefreshRate) {
ATRACE_CALL();
@@ -5564,39 +5638,34 @@
return BAD_VALUE;
}
+ status_t result = NO_ERROR;
+
postMessageSync(new LambdaMessage([&]() {
const auto display = getDisplayDeviceLocked(displayToken);
if (!display) {
+ result = BAD_VALUE;
ALOGE("Attempt to set desired display configs for invalid display token %p",
displayToken.get());
} else if (display->isVirtual()) {
+ result = BAD_VALUE;
ALOGW("Attempt to set desired display configs for virtual display");
} else {
- // TODO(b/142507213): Plug through to HWC once the interface is ready.
- Mutex::Autolock lock(mStateLock);
- const DesiredDisplayConfigSpecs desiredDisplayConfigSpecs = {defaultModeId,
- minRefreshRate,
- maxRefreshRate};
- if (desiredDisplayConfigSpecs == mDesiredDisplayConfigSpecs) {
- return;
- }
- ALOGV("Updating desired display configs");
- ALOGD("desiredDisplayConfigSpecs: defaultId: %d min: %.f max: %.f decisions: ",
- desiredDisplayConfigSpecs.defaultModeId, desiredDisplayConfigSpecs.minRefreshRate,
- desiredDisplayConfigSpecs.maxRefreshRate);
- mDesiredDisplayConfigSpecs = desiredDisplayConfigSpecs;
+ result =
+ setDesiredDisplayConfigSpecsInternal(display, HwcConfigIndexType(defaultConfig),
+ minRefreshRate, maxRefreshRate);
}
}));
- return NO_ERROR;
+
+ return result;
}
status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultModeId,
+ int32_t* outDefaultConfig,
float* outMinRefreshRate,
float* outMaxRefreshRate) {
ATRACE_CALL();
- if (!displayToken || !outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) {
+ if (!displayToken || !outDefaultConfig || !outMinRefreshRate || !outMaxRefreshRate) {
return BAD_VALUE;
}
@@ -5607,12 +5676,23 @@
}
if (display->isPrimary()) {
- *outDefaultModeId = mDesiredDisplayConfigSpecs.defaultModeId;
- *outMinRefreshRate = mDesiredDisplayConfigSpecs.minRefreshRate;
- *outMaxRefreshRate = mDesiredDisplayConfigSpecs.maxRefreshRate;
+ HwcConfigIndexType defaultConfig;
+ mRefreshRateConfigs->getPolicy(&defaultConfig, outMinRefreshRate, outMaxRefreshRate);
+ *outDefaultConfig = defaultConfig.value();
+ return NO_ERROR;
+ } else if (display->isVirtual()) {
+ return BAD_VALUE;
+ } else {
+ const auto displayId = display->getId();
+ if (!displayId) {
+ return BAD_VALUE;
+ }
+ *outDefaultConfig = getHwComposer().getActiveConfigIndex(*displayId);
+ auto vsyncPeriod = getHwComposer().getActiveConfig(*displayId)->getVsyncPeriod();
+ *outMinRefreshRate = 1e9f / vsyncPeriod;
+ *outMaxRefreshRate = 1e9f / vsyncPeriod;
+ return NO_ERROR;
}
-
- return NO_ERROR;
}
void SurfaceFlinger::SetInputWindowsListener::onSetInputWindowsFinished() {
@@ -5620,7 +5700,10 @@
}
sp<Layer> SurfaceFlinger::fromHandle(const sp<IBinder>& handle) {
- BBinder *b = handle->localBinder();
+ BBinder* b = nullptr;
+ if (handle) {
+ b = handle->localBinder();
+ }
if (b == nullptr) {
return nullptr;
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index ae3f2d7..2f84b13 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -176,7 +176,7 @@
public ClientCache::ErasedRecipient,
private IBinder::DeathRecipient,
private HWC2::ComposerCallback,
- private Scheduler::ISchedulerCallback {
+ private ISchedulerCallback {
public:
SurfaceFlingerBE& getBE() { return mBE; }
const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -273,9 +273,6 @@
// force full composition on all displays
void repaintEverything();
- // force full composition on all displays without resetting the scheduler idle timer.
- void repaintEverythingForHWC();
-
surfaceflinger::Factory& getFactory() { return mFactory; }
// The CompositionEngine encapsulates all composition related interfaces and actions.
@@ -387,17 +384,6 @@
void traverseInReverseZOrder(const LayerVector::Visitor& visitor) const;
};
- struct DesiredDisplayConfigSpecs {
- int32_t defaultModeId;
- float minRefreshRate;
- float maxRefreshRate;
-
- bool operator==(const DesiredDisplayConfigSpecs& other) const {
- return defaultModeId == other.defaultModeId && minRefreshRate == other.minRefreshRate &&
- maxRefreshRate == other.maxRefreshRate;
- }
- };
-
/* ------------------------------------------------------------------------
* IBinder interface
*/
@@ -452,6 +438,12 @@
ui::DisplayPrimaries &primaries);
ui::ColorMode getActiveColorMode(const sp<IBinder>& displayToken) override;
status_t setActiveColorMode(const sp<IBinder>& displayToken, ui::ColorMode colorMode) override;
+ status_t getAutoLowLatencyModeSupport(const sp<IBinder>& displayToken,
+ bool* outSupported) const override;
+ void setAutoLowLatencyMode(const sp<IBinder>& displayToken, bool on) override;
+ status_t getGameContentTypeSupport(const sp<IBinder>& displayToken,
+ bool* outSupported) const override;
+ void setGameContentType(const sp<IBinder>& displayToken, bool on) override;
void setPowerMode(const sp<IBinder>& displayToken, int mode) override;
status_t setActiveConfig(const sp<IBinder>& displayToken, int id) override;
status_t clearAnimationFrameStats() override;
@@ -481,14 +473,10 @@
status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
const sp<IRegionSamplingListener>& listener) override;
status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override;
- status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- const std::vector<int32_t>& allowedConfigs) override;
- status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken,
- std::vector<int32_t>* outAllowedConfigs) override;
status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken, int32_t displayModeId,
float minRefreshRate, float maxRefreshRate) override;
status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
- int32_t* outDefaultModeId, float* outMinRefreshRate,
+ int32_t* outDefaultConfig, float* outMinRefreshRate,
float* outMaxRefreshRate) override;
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const override;
@@ -519,9 +507,11 @@
const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
/* ------------------------------------------------------------------------
- * Scheduler::ISchedulerCallback
+ * ISchedulerCallback
*/
void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+ // force full composition on all displays without resetting the scheduler idle timer.
+ void repaintEverythingForHWC() override;
/* ------------------------------------------------------------------------
* Message handling
*/
@@ -560,10 +550,16 @@
// called on the main thread in response to setPowerMode()
void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock);
- // called on the main thread in response to setAllowedDisplayConfigs()
- void setAllowedDisplayConfigsInternal(const sp<DisplayDevice>& display,
- const std::vector<int32_t>& allowedConfigs)
- REQUIRES(mStateLock);
+ // Sets the desired display configs.
+ status_t setDesiredDisplayConfigSpecsInternal(const sp<DisplayDevice>& display,
+ HwcConfigIndexType defaultConfig,
+ float minRefreshRate, float maxRefreshRate)
+ EXCLUDES(mStateLock);
+
+ // called on the main thread in response to setAutoLowLatencyMode()
+ void setAutoLowLatencyModeInternal(const sp<IBinder>& displayToken, bool on);
+ // called on the main thread in response to setGameContentType()
+ void setGameContentTypeInternal(const sp<IBinder>& displayToken, bool on);
// Returns whether the transaction actually modified any state
bool handleMessageTransaction();
@@ -1131,7 +1127,7 @@
scheduler::ConnectionHandle mSfConnectionHandle;
// Stores phase offsets configured per refresh rate.
- const std::unique_ptr<scheduler::PhaseOffsets> mPhaseOffsets;
+ std::unique_ptr<scheduler::PhaseConfiguration> mPhaseConfiguration;
// Optional to defer construction until scheduler connections are created.
std::optional<scheduler::VSyncModulator> mVSyncModulator;
@@ -1141,11 +1137,6 @@
std::atomic<nsecs_t> mExpectedPresentTime = 0;
- // All configs are allowed if the set is empty.
- using DisplayConfigs = std::set<HwcConfigIndexType>;
- DisplayConfigs mAllowedDisplayConfigs GUARDED_BY(mStateLock);
- DesiredDisplayConfigSpecs mDesiredDisplayConfigSpecs GUARDED_BY(mStateLock);
-
std::mutex mActiveConfigLock;
// This bit is set once we start setting the config. We read from this bit during the
// process. If at the end, this bit is different than mDesiredActiveConfig, we restart
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
index 4f439da..d5c2306 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.cpp
@@ -15,6 +15,7 @@
*/
#include <compositionengine/impl/CompositionEngine.h>
+#include <cutils/properties.h>
#include <ui/GraphicBuffer.h>
#include "BufferLayerConsumer.h"
@@ -51,21 +52,26 @@
}
std::unique_ptr<HWComposer> DefaultFactory::createHWComposer(const std::string& serviceName) {
- return std::make_unique<android::impl::HWComposer>(
- std::make_unique<Hwc2::impl::Composer>(serviceName));
+ return std::make_unique<android::impl::HWComposer>(serviceName);
}
std::unique_ptr<MessageQueue> DefaultFactory::createMessageQueue() {
return std::make_unique<android::impl::MessageQueue>();
}
-std::unique_ptr<scheduler::PhaseOffsets> DefaultFactory::createPhaseOffsets() {
- return std::make_unique<scheduler::impl::PhaseOffsets>();
+std::unique_ptr<scheduler::PhaseConfiguration> DefaultFactory::createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs& refreshRateConfigs) {
+ if (property_get_bool("debug.sf.use_phase_offsets_as_durations", false)) {
+ return std::make_unique<scheduler::impl::PhaseDurations>(refreshRateConfigs);
+ } else {
+ return std::make_unique<scheduler::impl::PhaseOffsets>(refreshRateConfigs);
+ }
}
std::unique_ptr<Scheduler> DefaultFactory::createScheduler(
- SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs) {
- return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs);
+ SetVSyncEnabled setVSyncEnabled, const scheduler::RefreshRateConfigs& configs,
+ ISchedulerCallback& schedulerCallback) {
+ return std::make_unique<Scheduler>(std::move(setVSyncEnabled), configs, schedulerCallback);
}
std::unique_ptr<SurfaceInterceptor> DefaultFactory::createSurfaceInterceptor(
diff --git a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
index 42bb177..36fae21 100644
--- a/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerDefaultFactory.h
@@ -30,9 +30,11 @@
std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) override;
std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) override;
std::unique_ptr<MessageQueue> createMessageQueue() override;
- std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override;
+ std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs&) override;
std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&) override;
+ const scheduler::RefreshRateConfigs&,
+ ISchedulerCallback&) override;
std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) override;
sp<StartPropertySetThread> createStartPropertySetThread(bool timestampPropertyValue) override;
sp<DisplayDevice> createDisplayDevice(DisplayDeviceCreationArgs&&) override;
diff --git a/services/surfaceflinger/SurfaceFlingerFactory.h b/services/surfaceflinger/SurfaceFlingerFactory.h
index 20784d2..951bd09 100644
--- a/services/surfaceflinger/SurfaceFlingerFactory.h
+++ b/services/surfaceflinger/SurfaceFlingerFactory.h
@@ -40,6 +40,7 @@
class HWComposer;
class IGraphicBufferConsumer;
class IGraphicBufferProducer;
+class ISchedulerCallback;
class Layer;
class MessageQueue;
class Scheduler;
@@ -55,7 +56,7 @@
} // namespace compositionengine
namespace scheduler {
-class PhaseOffsets;
+class PhaseConfiguration;
class RefreshRateConfigs;
} // namespace scheduler
@@ -73,9 +74,11 @@
virtual std::unique_ptr<EventControlThread> createEventControlThread(SetVSyncEnabled) = 0;
virtual std::unique_ptr<HWComposer> createHWComposer(const std::string& serviceName) = 0;
virtual std::unique_ptr<MessageQueue> createMessageQueue() = 0;
- virtual std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() = 0;
+ virtual std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs&) = 0;
virtual std::unique_ptr<Scheduler> createScheduler(SetVSyncEnabled,
- const scheduler::RefreshRateConfigs&) = 0;
+ const scheduler::RefreshRateConfigs&,
+ ISchedulerCallback&) = 0;
virtual std::unique_ptr<SurfaceInterceptor> createSurfaceInterceptor(SurfaceFlinger*) = 0;
virtual sp<StartPropertySetThread> createStartPropertySetThread(
diff --git a/services/surfaceflinger/SurfaceInterceptor.cpp b/services/surfaceflinger/SurfaceInterceptor.cpp
index 7e6c472..5e8910a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.cpp
+++ b/services/surfaceflinger/SurfaceInterceptor.cpp
@@ -124,6 +124,7 @@
addRelativeParentLocked(transaction, layerId,
getLayerIdFromWeakRef(layer->mCurrentState.zOrderRelativeOf),
layer->mCurrentState.z);
+ addShadowRadiusLocked(transaction, layerId, layer->mCurrentState.shadowRadius);
}
void SurfaceInterceptor::addInitialDisplayStateLocked(Increment* increment,
@@ -368,6 +369,13 @@
overrideChange->set_z(z);
}
+void SurfaceInterceptor::addShadowRadiusLocked(Transaction* transaction, int32_t layerId,
+ float shadowRadius) {
+ SurfaceChange* change(createSurfaceChangeLocked(transaction, layerId));
+ ShadowRadiusChange* overrideChange(change->mutable_shadow_radius());
+ overrideChange->set_radius(shadowRadius);
+}
+
void SurfaceInterceptor::addSurfaceChangesLocked(Transaction* transaction,
const layer_state_t& state)
{
@@ -441,6 +449,9 @@
addRelativeParentLocked(transaction, layerId,
getLayerIdFromHandle(state.relativeLayerHandle), state.z);
}
+ if (state.what & layer_state_t::eShadowRadiusChanged) {
+ addShadowRadiusLocked(transaction, layerId, state.shadowRadius);
+ }
}
void SurfaceInterceptor::addDisplayChangesLocked(Transaction* transaction,
diff --git a/services/surfaceflinger/SurfaceInterceptor.h b/services/surfaceflinger/SurfaceInterceptor.h
index 72b734b..c6f9e8a 100644
--- a/services/surfaceflinger/SurfaceInterceptor.h
+++ b/services/surfaceflinger/SurfaceInterceptor.h
@@ -165,6 +165,7 @@
void addDetachChildrenLocked(Transaction* transaction, int32_t layerId, bool detached);
void addRelativeParentLocked(Transaction* transaction, int32_t layerId, int32_t parentId,
int z);
+ void addShadowRadiusLocked(Transaction* transaction, int32_t layerId, float shadowRadius);
// Add display transactions to the trace
DisplayChange* createDisplayChangeLocked(Transaction* transaction, int32_t sequenceId);
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index 8db03db..4c1baaf 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -311,8 +311,16 @@
interface_cast<ITransactionCompletedListener>(listenerStats.listener)
->onTransactionCompleted(listenerStats);
listener->unlinkToDeath(mDeathRecipient);
+ if (transactionStatsDeque.empty()) {
+ completedTransactionsItr =
+ mCompletedTransactions.erase(completedTransactionsItr);
+ } else {
+ completedTransactionsItr++;
+ }
+ } else {
+ completedTransactionsItr =
+ mCompletedTransactions.erase(completedTransactionsItr);
}
- completedTransactionsItr = mCompletedTransactions.erase(completedTransactionsItr);
} else {
completedTransactionsItr++;
}
diff --git a/services/surfaceflinger/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
index ef488bd..ef27847 100644
--- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp
+++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp
@@ -112,7 +112,8 @@
outData.resize(dataStr.size());
memcpy(outData.data(), dataStr.data(), dataStr.size());
}
-
+ layer.cornerRadiusCrop = generateFloatRect(layerProto.corner_radius_crop());
+ layer.shadowRadius = layerProto.shadow_radius();
return layer;
}
@@ -307,8 +308,9 @@
first = false;
result.append(metadata.itemToString(entry.first, ":"));
}
- result.append("}");
-
+ result.append("},");
+ StringAppendF(&result, " cornerRadiusCrop=%s, ", cornerRadiusCrop.to_string().c_str());
+ StringAppendF(&result, " shadowRadius=%.3f, ", shadowRadius);
return result;
}
diff --git a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
index 54e02ca..774b0e1 100644
--- a/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
+++ b/services/surfaceflinger/layerproto/include/layerproto/LayerProtoParser.h
@@ -111,6 +111,8 @@
bool isProtected;
float cornerRadius;
LayerMetadata metadata;
+ LayerProtoParser::FloatRect cornerRadiusCrop;
+ float shadowRadius;
std::string to_string() const;
};
diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto
index c7fbff3..9ad9b91 100644
--- a/services/surfaceflinger/layerproto/layers.proto
+++ b/services/surfaceflinger/layerproto/layers.proto
@@ -94,6 +94,13 @@
FloatRectProto screen_bounds = 46;
InputWindowInfoProto input_window_info = 47;
+
+ // Crop used to draw the rounded corner.
+ FloatRectProto corner_radius_crop = 48;
+
+ // length of the shadow to draw around the layer, it may be set on the
+ // layer or set by a parent layer.
+ float shadow_radius = 49;
}
message PositionProto {
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index ca7e18d..dcfc077 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -21,7 +21,6 @@
"CommonTypes_test.cpp",
"Credentials_test.cpp",
"DereferenceSurfaceControl_test.cpp",
- "DisplayActiveConfig_test.cpp",
"DisplayConfigs_test.cpp",
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
index a3e16f9..ab4af09 100644
--- a/services/surfaceflinger/tests/CommonTypes_test.cpp
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -100,21 +100,21 @@
static_cast<uint32_t>(HidlDataspace::RANGE_LIMITED));
static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_EXTENDED) ==
static_cast<uint32_t>(HidlDataspace::RANGE_EXTENDED));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SRGB_LINEAR) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
static_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB_LINEAR) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB_LINEAR) ==
static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SRGB) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
static_cast<uint32_t>(HidlDataspace::V0_SRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::SCRGB) ==
static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_JFIF) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
static_cast<uint32_t>(HidlDataspace::V0_JFIF));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_625) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
static_cast<uint32_t>(HidlDataspace::V0_BT601_625));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_525) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
static_cast<uint32_t>(HidlDataspace::V0_BT601_525));
-static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT709) ==
+static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
static_cast<uint32_t>(HidlDataspace::V0_BT709));
static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3_LINEAR) ==
static_cast<uint32_t>(HidlDataspace::DCI_P3_LINEAR));
@@ -152,19 +152,3 @@
static_cast<uint32_t>(HidlDataspace::JPEG_APP_SEGMENTS));
static_assert(static_cast<uint32_t>(AidlDataspace::HEIF) ==
static_cast<uint32_t>(HidlDataspace::HEIF));
-
-// Below are the dataspaces that have been deprecated for sometime. They are required to behave
-// the same as their V0_* counterparts. We redefined them in AIDL to be the same as the
-// their V0_* counterparts.
-static_assert(static_cast<uint32_t>(AidlDataspace::SRGB_LINEAR) ==
- static_cast<uint32_t>(AidlDataspace::V0_SRGB_LINEAR));
-static_assert(static_cast<uint32_t>(AidlDataspace::SRGB) ==
- static_cast<uint32_t>(AidlDataspace::V0_SRGB));
-static_assert(static_cast<uint32_t>(AidlDataspace::JFIF) ==
- static_cast<uint32_t>(AidlDataspace::V0_JFIF));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT601_625) ==
- static_cast<uint32_t>(AidlDataspace::V0_BT601_625));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT601_525) ==
- static_cast<uint32_t>(AidlDataspace::V0_BT601_525));
-static_assert(static_cast<uint32_t>(AidlDataspace::BT709) ==
- static_cast<uint32_t>(AidlDataspace::V0_BT709));
diff --git a/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp b/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp
deleted file mode 100644
index 2e3b760..0000000
--- a/services/surfaceflinger/tests/DisplayActiveConfig_test.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * 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.
- */
-
-#include <thread>
-#include "LayerTransactionTest.h"
-namespace android {
-
-using android::hardware::graphics::common::V1_1::BufferUsage;
-
-::testing::Environment* const binderEnv =
- ::testing::AddGlobalTestEnvironment(new BinderEnvironment());
-
-class DisplayActiveConfigTest : public ::testing::Test {
-protected:
- void SetUp() override {
- mDisplayToken = SurfaceComposerClient::getInternalDisplayToken();
- SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs);
- EXPECT_GT(mDisplayconfigs.size(), 0);
-
- // set display power to on to make sure config can be changed
- SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL);
- }
-
- sp<IBinder> mDisplayToken;
- Vector<DisplayInfo> mDisplayconfigs;
-};
-
-TEST_F(DisplayActiveConfigTest, allConfigsAllowed) {
- std::vector<int32_t> allowedConfigs;
-
- // Add all configs to the allowed configs
- for (int i = 0; i < mDisplayconfigs.size(); i++) {
- allowedConfigs.push_back(i);
- }
-
- status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
- EXPECT_EQ(res, NO_ERROR);
-
- std::vector<int32_t> outConfigs;
- res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs);
- EXPECT_EQ(res, NO_ERROR);
- EXPECT_EQ(allowedConfigs, outConfigs);
-}
-
-TEST_F(DisplayActiveConfigTest, changeAllowedConfig) {
- // we need at least 2 configs available for this test
- if (mDisplayconfigs.size() <= 1) return;
-
- int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
-
- // We want to set the allowed config to everything but the active config
- std::vector<int32_t> allowedConfigs;
- for (int i = 0; i < mDisplayconfigs.size(); i++) {
- if (i != activeConfig) {
- allowedConfigs.push_back(i);
- }
- }
-
- status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs);
- EXPECT_EQ(res, NO_ERROR);
-
- // Allow some time for the config change
- std::this_thread::sleep_for(200ms);
-
- int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken);
- EXPECT_NE(activeConfig, newActiveConfig);
-
- // Make sure the new config is part of allowed config
- EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) !=
- allowedConfigs.end());
-}
-} // namespace android
diff --git a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
index 999e82d..627de7a 100644
--- a/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
+++ b/services/surfaceflinger/tests/LayerRenderTypeTransaction_test.cpp
@@ -72,8 +72,8 @@
ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32));
ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32));
- // GLES requires only 4 bits of subpixel precision during rasterization
- // XXX GLES composition does not match HWC composition due to precision
+ // GPU composition requires only 4 bits of subpixel precision during rasterization
+ // XXX GPU composition does not match HWC composition due to precision
// loss (b/69315223)
const float epsilon = 1.0f / 16.0f;
Transaction().setPosition(layer, 0.5f - epsilon, 0.5f - epsilon).apply();
diff --git a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
index 59e9c00..1fa426d 100644
--- a/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
+++ b/services/surfaceflinger/tests/SurfaceInterceptor_test.cpp
@@ -51,6 +51,7 @@
constexpr float CORNER_RADIUS_UPDATE = 0.2f;
constexpr float POSITION_UPDATE = 121;
const Rect CROP_UPDATE(16, 16, 32, 32);
+const float SHADOW_RADIUS_UPDATE = 35.0f;
const String8 DISPLAY_NAME("SurfaceInterceptor Display Test");
constexpr auto TEST_BG_SURFACE_NAME = "BG Interceptor Test Surface";
@@ -190,6 +191,7 @@
bool relativeParentUpdateFound(const SurfaceChange& change, bool found);
bool detachChildrenUpdateFound(const SurfaceChange& change, bool found);
bool reparentChildrenUpdateFound(const SurfaceChange& change, bool found);
+ bool shadowRadiusUpdateFound(const SurfaceChange& change, bool found);
bool surfaceUpdateFound(const Trace& trace, SurfaceChange::SurfaceChangeCase changeCase);
// Find all of the updates in the single trace
@@ -226,6 +228,7 @@
void relativeParentUpdate(Transaction&);
void detachChildrenUpdate(Transaction&);
void reparentChildrenUpdate(Transaction&);
+ void shadowRadiusUpdate(Transaction&);
void surfaceCreation(Transaction&);
void displayCreation(Transaction&);
void displayDeletion(Transaction&);
@@ -406,6 +409,10 @@
t.reparentChildren(mBGSurfaceControl, mFGSurfaceControl->getHandle());
}
+void SurfaceInterceptorTest::shadowRadiusUpdate(Transaction& t) {
+ t.setShadowRadius(mBGSurfaceControl, SHADOW_RADIUS_UPDATE);
+}
+
void SurfaceInterceptorTest::displayCreation(Transaction&) {
sp<IBinder> testDisplay = SurfaceComposerClient::createDisplay(DISPLAY_NAME, true);
SurfaceComposerClient::destroyDisplay(testDisplay);
@@ -435,6 +442,7 @@
runInTransaction(&SurfaceInterceptorTest::reparentChildrenUpdate);
runInTransaction(&SurfaceInterceptorTest::detachChildrenUpdate);
runInTransaction(&SurfaceInterceptorTest::relativeParentUpdate);
+ runInTransaction(&SurfaceInterceptorTest::shadowRadiusUpdate);
}
void SurfaceInterceptorTest::surfaceCreation(Transaction&) {
@@ -655,6 +663,17 @@
return found;
}
+bool SurfaceInterceptorTest::shadowRadiusUpdateFound(const SurfaceChange& change,
+ bool foundShadowRadius) {
+ bool hasShadowRadius(change.shadow_radius().radius() == SHADOW_RADIUS_UPDATE);
+ if (hasShadowRadius && !foundShadowRadius) {
+ foundShadowRadius = true;
+ } else if (hasShadowRadius && foundShadowRadius) {
+ []() { FAIL(); }();
+ }
+ return foundShadowRadius;
+}
+
bool SurfaceInterceptorTest::surfaceUpdateFound(const Trace& trace,
SurfaceChange::SurfaceChangeCase changeCase) {
bool foundUpdate = false;
@@ -718,6 +737,9 @@
case SurfaceChange::SurfaceChangeCase::kDetachChildren:
foundUpdate = detachChildrenUpdateFound(change, foundUpdate);
break;
+ case SurfaceChange::SurfaceChangeCase::kShadowRadius:
+ foundUpdate = shadowRadiusUpdateFound(change, foundUpdate);
+ break;
case SurfaceChange::SurfaceChangeCase::SURFACECHANGE_NOT_SET:
break;
}
@@ -920,6 +942,11 @@
SurfaceChange::SurfaceChangeCase::kDetachChildren);
}
+TEST_F(SurfaceInterceptorTest, InterceptShadowRadiusUpdateWorks) {
+ captureTest(&SurfaceInterceptorTest::shadowRadiusUpdate,
+ SurfaceChange::SurfaceChangeCase::kShadowRadius);
+}
+
TEST_F(SurfaceInterceptorTest, InterceptAllUpdatesWorks) {
captureTest(&SurfaceInterceptorTest::runAllUpdates,
&SurfaceInterceptorTest::assertAllUpdatesFound);
diff --git a/services/surfaceflinger/tests/fakehwc/Android.bp b/services/surfaceflinger/tests/fakehwc/Android.bp
index 9d74761..31837a9 100644
--- a/services/surfaceflinger/tests/fakehwc/Android.bp
+++ b/services/surfaceflinger/tests/fakehwc/Android.bp
@@ -10,6 +10,9 @@
],
shared_libs: [
"android.hardware.graphics.composer@2.1",
+ "android.hardware.graphics.composer@2.2",
+ "android.hardware.graphics.composer@2.3",
+ "android.hardware.graphics.composer@2.4",
"android.hardware.graphics.composer@2.1-resources",
"android.hardware.graphics.mapper@2.0",
"android.hardware.graphics.mapper@3.0",
@@ -39,8 +42,8 @@
"libtrace_proto",
],
header_libs: [
- "android.hardware.graphics.composer@2.1-command-buffer",
- "android.hardware.graphics.composer@2.1-hal",
+ "android.hardware.graphics.composer@2.4-command-buffer",
+ "android.hardware.graphics.composer@2.4-hal",
"libsurfaceflinger_headers",
],
}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
index eeb6efe..0c370a6 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp
@@ -146,6 +146,7 @@
FakeComposerClient::FakeComposerClient()
: mEventCallback(nullptr),
+ mEventCallback_2_4(nullptr),
mCurrentConfig(NULL_DISPLAY_CONFIG),
mVsyncEnabled(false),
mLayers(),
@@ -165,6 +166,9 @@
void FakeComposerClient::registerEventCallback(EventCallback* callback) {
ALOGV("registerEventCallback");
+ LOG_FATAL_IF(mEventCallback_2_4 != nullptr,
+ "already registered using registerEventCallback_2_4");
+
mEventCallback = callback;
if (mEventCallback) {
mEventCallback->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
@@ -179,12 +183,16 @@
void FakeComposerClient::hotplugDisplay(Display display, IComposerCallback::Connection state) {
if (mEventCallback) {
mEventCallback->onHotplug(display, state);
+ } else if (mEventCallback_2_4) {
+ mEventCallback_2_4->onHotplug(display, state);
}
}
void FakeComposerClient::refreshDisplay(Display display) {
if (mEventCallback) {
mEventCallback->onRefresh(display);
+ } else if (mEventCallback_2_4) {
+ mEventCallback_2_4->onRefresh(display);
}
}
@@ -193,33 +201,37 @@
return 1;
}
-Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
- PixelFormat* /*format*/, Display* /*outDisplay*/) {
+V2_1::Error FakeComposerClient::createVirtualDisplay(uint32_t /*width*/, uint32_t /*height*/,
+ V1_0::PixelFormat* /*format*/,
+ Display* /*outDisplay*/) {
ALOGV("createVirtualDisplay");
- return Error::NONE;
+ return V2_1::Error::NONE;
}
-Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
+V2_1::Error FakeComposerClient::destroyVirtualDisplay(Display /*display*/) {
ALOGV("destroyVirtualDisplay");
- return Error::NONE;
+ return V2_1::Error::NONE;
}
-Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
+V2_1::Error FakeComposerClient::createLayer(Display /*display*/, Layer* outLayer) {
ALOGV("createLayer");
*outLayer = mLayers.size();
auto newLayer = std::make_unique<LayerImpl>();
mLayers.push_back(std::move(newLayer));
- return Error::NONE;
+ return V2_1::Error::NONE;
}
-Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
+V2_1::Error FakeComposerClient::destroyLayer(Display /*display*/, Layer layer) {
ALOGV("destroyLayer");
mLayers[layer]->mValid = false;
- return Error::NONE;
+ return V2_1::Error::NONE;
}
-Error FakeComposerClient::getActiveConfig(Display /*display*/, Config* outConfig) {
+V2_1::Error FakeComposerClient::getActiveConfig(Display display, Config* outConfig) {
ALOGV("getActiveConfig");
+ if (mMockHal) {
+ return mMockHal->getActiveConfig(display, outConfig);
+ }
// TODO Assert outConfig != nullptr
@@ -227,30 +239,480 @@
// IComposerClient::getActiveConfig, but returning BAD_CONFIG
// seems to not fit SurfaceFlinger plans. See version 2 below.
// if (mCurrentConfig == NULL_DISPLAY_CONFIG) {
- // return Error::BAD_CONFIG;
+ // return V2_1::Error::BAD_CONFIG;
// }
//*outConfig = mCurrentConfig;
*outConfig = 1; // Very special config for you my friend
- return Error::NONE;
+ return V2_1::Error::NONE;
}
-Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
- uint32_t /*height*/, PixelFormat /*format*/,
- Dataspace /*dataspace*/) {
+V2_1::Error FakeComposerClient::getClientTargetSupport(Display /*display*/, uint32_t /*width*/,
+ uint32_t /*height*/,
+ V1_0::PixelFormat /*format*/,
+ V1_0::Dataspace /*dataspace*/) {
ALOGV("getClientTargetSupport");
- return Error::NONE;
+ return V2_1::Error::NONE;
}
-Error FakeComposerClient::getColorModes(Display /*display*/, hidl_vec<ColorMode>* /*outModes*/) {
+V2_1::Error FakeComposerClient::getColorModes(Display /*display*/,
+ hidl_vec<V1_0::ColorMode>* /*outModes*/) {
ALOGV("getColorModes");
- return Error::NONE;
+ return V2_1::Error::NONE;
}
-Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
- IComposerClient::Attribute attribute,
- int32_t* outValue) {
+V2_1::Error FakeComposerClient::getDisplayAttribute(Display display, Config config,
+ V2_1::IComposerClient::Attribute attribute,
+ int32_t* outValue) {
+ auto tmpError =
+ getDisplayAttribute_2_4(display, config,
+ static_cast<IComposerClient::Attribute>(attribute), outValue);
+ return static_cast<V2_1::Error>(tmpError);
+}
+
+V2_1::Error FakeComposerClient::getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) {
+ ALOGV("getDisplayConfigs");
+ if (mMockHal) {
+ return mMockHal->getDisplayConfigs(display, outConfigs);
+ }
+
+ // TODO assert display == 1, outConfigs != nullptr
+
+ outConfigs->resize(1);
+ (*outConfigs)[0] = 1;
+
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
+ ALOGV("getDisplayName");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDisplayType(Display /*display*/,
+ IComposerClient::DisplayType* outType) {
+ ALOGV("getDisplayType");
+ // TODO: This setting nothing on the output had no effect on initial trials. Is first display
+ // assumed to be physical?
+ *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
+ ALOGV("getDozeSupport");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::getHdrCapabilities(Display /*display*/,
+ hidl_vec<V1_0::Hdr>* /*outTypes*/,
+ float* /*outMaxLuminance*/,
+ float* /*outMaxAverageLuminance*/,
+ float* /*outMinLuminance*/) {
+ ALOGV("getHdrCapabilities");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setActiveConfig(Display display, Config config) {
+ ALOGV("setActiveConfig");
+ if (mMockHal) {
+ return mMockHal->setActiveConfig(display, config);
+ }
+ mCurrentConfig = config;
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setColorMode(Display /*display*/, V1_0::ColorMode /*mode*/) {
+ ALOGV("setColorMode");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setPowerMode(Display /*display*/,
+ V2_1::IComposerClient::PowerMode /*mode*/) {
+ ALOGV("setPowerMode");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setVsyncEnabled(Display /*display*/,
+ IComposerClient::Vsync enabled) {
+ mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
+ ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
+ int32_t /*hint*/) {
+ ALOGV("setColorTransform");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
+ int32_t /*acquireFence*/, int32_t /*dataspace*/,
+ const std::vector<hwc_rect_t>& /*damage*/) {
+ ALOGV("setClientTarget");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
+ int32_t /*releaseFence*/) {
+ ALOGV("setOutputBuffer");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::validateDisplay(
+ Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
+ std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
+ uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
+ std::vector<uint32_t>* /*outRequestMasks*/) {
+ ALOGV("validateDisplay");
+ // TODO: Assume touching nothing means All Korrekt!
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
+ ALOGV("acceptDisplayChanges");
+ // Didn't ask for changes because software is omnipotent.
+ return V2_1::Error::NONE;
+}
+
+bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
+ return a->z <= b->z;
+}
+
+V2_1::Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
+ std::vector<Layer>* /*outLayers*/,
+ std::vector<int32_t>* /*outReleaseFences*/) {
+ ALOGV("presentDisplay");
+ // TODO Leaving layers and their fences out for now. Doing so
+ // means that we've already processed everything. Important to
+ // test that the fences are respected, though. (How?)
+
+ std::unique_ptr<Frame> newFrame(new Frame);
+ for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
+ const LayerImpl& layerImpl = *mLayers[layer];
+
+ if (!layerImpl.mValid) continue;
+
+ auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
+ newFrame->rectangles.push_back(std::move(rect));
+ }
+ std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
+ {
+ Mutex::Autolock _l(mStateMutex);
+ mFrames.push_back(std::move(newFrame));
+ mFramesAvailable.broadcast();
+ }
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
+ int32_t /*x*/, int32_t /*y*/) {
+ ALOGV("setLayerCursorPosition");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer,
+ buffer_handle_t buffer, int32_t acquireFence) {
+ ALOGV("setLayerBuffer");
+ LayerImpl& l = getLayerImpl(layer);
+ if (buffer != l.mRenderState.mBuffer) {
+ l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
+ }
+ l.mRenderState.mBuffer = buffer;
+ l.mRenderState.mAcquireFence = acquireFence;
+
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
+ const std::vector<hwc_rect_t>& /*damage*/) {
+ ALOGV("setLayerSurfaceDamage");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
+ ALOGV("setLayerBlendMode");
+ getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
+ IComposerClient::Color color) {
+ ALOGV("setLayerColor");
+ getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
+ getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
+ getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
+ getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
+ int32_t /*type*/) {
+ ALOGV("setLayerCompositionType");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
+ int32_t /*dataspace*/) {
+ ALOGV("setLayerDataspace");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
+ const hwc_rect_t& frame) {
+ ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
+ frame.bottom);
+ getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
+ ALOGV("setLayerPlaneAlpha");
+ getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
+ buffer_handle_t /*stream*/) {
+ ALOGV("setLayerSidebandStream");
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
+ const hwc_frect_t& crop) {
+ ALOGV("setLayerSourceCrop");
+ getLayerImpl(layer).mRenderState.mSourceCrop = crop;
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer,
+ int32_t transform) {
+ ALOGV("setLayerTransform");
+ getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
+ const std::vector<hwc_rect_t>& visible) {
+ ALOGV("setLayerVisibleRegion");
+ getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
+ return V2_1::Error::NONE;
+}
+
+V2_1::Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
+ ALOGV("setLayerZOrder");
+ getLayerImpl(layer).mZ = z;
+ return V2_1::Error::NONE;
+}
+
+// Composer 2.2
+V2_1::Error FakeComposerClient::getPerFrameMetadataKeys(
+ Display /*display*/, std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadata(
+ Display /*display*/, Layer /*layer*/,
+ const std::vector<V2_2::IComposerClient::PerFrameMetadata>& /*metadata*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferAttributes(
+ Display /*display*/, graphics::common::V1_1::PixelFormat* /*outFormat*/,
+ graphics::common::V1_1::Dataspace* /*outDataspace*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setReadbackBuffer(Display /*display*/,
+ const native_handle_t* /*bufferHandle*/,
+ android::base::unique_fd /*fenceFd*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferFence(Display /*display*/,
+ android::base::unique_fd* /*outFenceFd*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::createVirtualDisplay_2_2(
+ uint32_t /*width*/, uint32_t /*height*/, graphics::common::V1_1::PixelFormat* /*format*/,
+ Display* /*outDisplay*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+V2_1::Error FakeComposerClient::getClientTargetSupport_2_2(
+ Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
+ graphics::common::V1_1::PixelFormat /*format*/,
+ graphics::common::V1_1::Dataspace /*dataspace*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setPowerMode_2_2(Display /*display*/,
+ V2_2::IComposerClient::PowerMode /*mode*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerFloatColor(Display /*display*/, Layer /*layer*/,
+ V2_2::IComposerClient::FloatColor /*color*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getColorModes_2_2(
+ Display /*display*/, hidl_vec<graphics::common::V1_1::ColorMode>* /*outModes*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getRenderIntents(
+ Display /*display*/, graphics::common::V1_1::ColorMode /*mode*/,
+ std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setColorMode_2_2(Display /*display*/,
+ graphics::common::V1_1::ColorMode /*mode*/,
+ graphics::common::V1_1::RenderIntent /*intent*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+std::array<float, 16> FakeComposerClient::getDataspaceSaturationMatrix(
+ graphics::common::V1_1::Dataspace /*dataspace*/) {
+ return {};
+}
+
+// Composer 2.3
+V2_1::Error FakeComposerClient::getPerFrameMetadataKeys_2_3(
+ Display /*display*/, std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* /*outKeys*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setColorMode_2_3(Display /*display*/,
+ graphics::common::V1_2::ColorMode /*mode*/,
+ graphics::common::V1_1::RenderIntent /*intent*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getRenderIntents_2_3(
+ Display /*display*/, graphics::common::V1_2::ColorMode /*mode*/,
+ std::vector<graphics::common::V1_1::RenderIntent>* /*outIntents*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getColorModes_2_3(
+ Display /*display*/, hidl_vec<graphics::common::V1_2::ColorMode>* /*outModes*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getClientTargetSupport_2_3(
+ Display /*display*/, uint32_t /*width*/, uint32_t /*height*/,
+ graphics::common::V1_2::PixelFormat /*format*/,
+ graphics::common::V1_2::Dataspace /*dataspace*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getReadbackBufferAttributes_2_3(
+ Display /*display*/, graphics::common::V1_2::PixelFormat* /*outFormat*/,
+ graphics::common::V1_2::Dataspace* /*outDataspace*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getHdrCapabilities_2_3(
+ Display /*display*/, hidl_vec<graphics::common::V1_2::Hdr>* /*outTypes*/,
+ float* /*outMaxLuminance*/, float* /*outMaxAverageLuminance*/, float* /*outMinLuminance*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadata_2_3(
+ Display /*display*/, Layer /*layer*/,
+ const std::vector<V2_3::IComposerClient::PerFrameMetadata>& /*metadata*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayIdentificationData(Display /*display*/,
+ uint8_t* /*outPort*/,
+ std::vector<uint8_t>* /*outData*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerColorTransform(Display /*display*/, Layer /*layer*/,
+ const float* /*matrix*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayedContentSamplingAttributes(
+ uint64_t /*display*/, graphics::common::V1_2::PixelFormat& /*format*/,
+ graphics::common::V1_2::Dataspace& /*dataspace*/,
+ hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& /*componentMask*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setDisplayedContentSamplingEnabled(
+ uint64_t /*display*/, V2_3::IComposerClient::DisplayedContentSampling /*enable*/,
+ hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> /*componentMask*/,
+ uint64_t /*maxFrames*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayedContentSample(
+ uint64_t /*display*/, uint64_t /*maxFrames*/, uint64_t /*timestamp*/,
+ uint64_t& /*frameCount*/, hidl_vec<uint64_t>& /*sampleComponent0*/,
+ hidl_vec<uint64_t>& /*sampleComponent1*/, hidl_vec<uint64_t>& /*sampleComponent2*/,
+ hidl_vec<uint64_t>& /*sampleComponent3*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayCapabilities(
+ Display /*display*/,
+ std::vector<V2_3::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setLayerPerFrameMetadataBlobs(
+ Display /*display*/, Layer /*layer*/,
+ std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& /*blobs*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::getDisplayBrightnessSupport(Display /*display*/,
+ bool* /*outSupport*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+V2_1::Error FakeComposerClient::setDisplayBrightness(Display /*display*/, float /*brightness*/) {
+ return V2_1::Error::UNSUPPORTED;
+}
+
+// Composer 2.4
+void FakeComposerClient::registerEventCallback_2_4(EventCallback_2_4* callback) {
+ ALOGV("registerEventCallback_2_4");
+ LOG_FATAL_IF(mEventCallback != nullptr, "already registered using registerEventCallback");
+
+ mEventCallback_2_4 = callback;
+ if (mEventCallback_2_4) {
+ mEventCallback_2_4->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+ }
+}
+
+void FakeComposerClient::unregisterEventCallback_2_4() {
+ ALOGV("unregisterEventCallback_2_4");
+ mEventCallback_2_4 = nullptr;
+}
+
+V2_4::Error FakeComposerClient::getDisplayCapabilities_2_4(
+ Display /*display*/,
+ std::vector<V2_4::IComposerClient::DisplayCapability>* /*outCapabilities*/) {
+ return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getDisplayConnectionType(
+ Display /*display*/, V2_4::IComposerClient::DisplayConnectionType* /*outType*/) {
+ return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::getDisplayAttribute_2_4(Display display, Config config,
+ IComposerClient::Attribute attribute,
+ int32_t* outValue) {
ALOGV("getDisplayAttribute (%d, %d, %d, %p)", static_cast<int>(display),
static_cast<int>(config), static_cast<int>(attribute), outValue);
+ if (mMockHal) {
+ return mMockHal->getDisplayAttribute_2_4(display, config, attribute, outValue);
+ }
// TODO: SOOO much fun to be had with these alone
switch (attribute) {
@@ -276,233 +738,48 @@
return Error::NONE;
}
-Error FakeComposerClient::getDisplayConfigs(Display /*display*/, hidl_vec<Config>* outConfigs) {
- ALOGV("getDisplayConfigs");
- // TODO assert display == 1, outConfigs != nullptr
-
- outConfigs->resize(1);
- (*outConfigs)[0] = 1;
-
- return Error::NONE;
-}
-
-Error FakeComposerClient::getDisplayName(Display /*display*/, hidl_string* /*outName*/) {
- ALOGV("getDisplayName");
- return Error::NONE;
-}
-
-Error FakeComposerClient::getDisplayType(Display /*display*/,
- IComposerClient::DisplayType* outType) {
- ALOGV("getDisplayType");
- // TODO: This setting nothing on the output had no effect on initial trials. Is first display
- // assumed to be physical?
- *outType = static_cast<IComposerClient::DisplayType>(HWC2_DISPLAY_TYPE_PHYSICAL);
- return Error::NONE;
-}
-
-Error FakeComposerClient::getDozeSupport(Display /*display*/, bool* /*outSupport*/) {
- ALOGV("getDozeSupport");
- return Error::NONE;
-}
-
-Error FakeComposerClient::getHdrCapabilities(Display /*display*/, hidl_vec<Hdr>* /*outTypes*/,
- float* /*outMaxLuminance*/,
- float* /*outMaxAverageLuminance*/,
- float* /*outMinLuminance*/) {
- ALOGV("getHdrCapabilities");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setActiveConfig(Display /*display*/, Config config) {
- ALOGV("setActiveConfig");
- mCurrentConfig = config;
- return Error::NONE;
-}
-
-Error FakeComposerClient::setColorMode(Display /*display*/, ColorMode /*mode*/) {
- ALOGV("setColorMode");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setPowerMode(Display /*display*/, IComposerClient::PowerMode /*mode*/) {
- ALOGV("setPowerMode");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setVsyncEnabled(Display /*display*/, IComposerClient::Vsync enabled) {
- mVsyncEnabled = (enabled == IComposerClient::Vsync::ENABLE);
- ALOGV("setVsyncEnabled(%s)", mVsyncEnabled ? "ENABLE" : "DISABLE");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setColorTransform(Display /*display*/, const float* /*matrix*/,
- int32_t /*hint*/) {
- ALOGV("setColorTransform");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setClientTarget(Display /*display*/, buffer_handle_t /*target*/,
- int32_t /*acquireFence*/, int32_t /*dataspace*/,
- const std::vector<hwc_rect_t>& /*damage*/) {
- ALOGV("setClientTarget");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setOutputBuffer(Display /*display*/, buffer_handle_t /*buffer*/,
- int32_t /*releaseFence*/) {
- ALOGV("setOutputBuffer");
- return Error::NONE;
-}
-
-Error FakeComposerClient::validateDisplay(
- Display /*display*/, std::vector<Layer>* /*outChangedLayers*/,
- std::vector<IComposerClient::Composition>* /*outCompositionTypes*/,
- uint32_t* /*outDisplayRequestMask*/, std::vector<Layer>* /*outRequestedLayers*/,
- std::vector<uint32_t>* /*outRequestMasks*/) {
- ALOGV("validateDisplay");
- // TODO: Assume touching nothing means All Korrekt!
- return Error::NONE;
-}
-
-Error FakeComposerClient::acceptDisplayChanges(Display /*display*/) {
- ALOGV("acceptDisplayChanges");
- // Didn't ask for changes because software is omnipotent.
- return Error::NONE;
-}
-
-bool layerZOrdering(const std::unique_ptr<FrameRect>& a, const std::unique_ptr<FrameRect>& b) {
- return a->z <= b->z;
-}
-
-Error FakeComposerClient::presentDisplay(Display /*display*/, int32_t* /*outPresentFence*/,
- std::vector<Layer>* /*outLayers*/,
- std::vector<int32_t>* /*outReleaseFences*/) {
- ALOGV("presentDisplay");
- // TODO Leaving layers and their fences out for now. Doing so
- // means that we've already processed everything. Important to
- // test that the fences are respected, though. (How?)
-
- std::unique_ptr<Frame> newFrame(new Frame);
- for (uint64_t layer = 0; layer < mLayers.size(); layer++) {
- const LayerImpl& layerImpl = *mLayers[layer];
-
- if (!layerImpl.mValid) continue;
-
- auto rect = std::make_unique<FrameRect>(layer, layerImpl.mRenderState, layerImpl.mZ);
- newFrame->rectangles.push_back(std::move(rect));
+V2_4::Error FakeComposerClient::getDisplayVsyncPeriod(Display display,
+ V2_4::VsyncPeriodNanos* outVsyncPeriod) {
+ ALOGV("getDisplayVsyncPeriod");
+ if (mMockHal) {
+ return mMockHal->getDisplayVsyncPeriod(display, outVsyncPeriod);
}
- std::sort(newFrame->rectangles.begin(), newFrame->rectangles.end(), layerZOrdering);
- {
- Mutex::Autolock _l(mStateMutex);
- mFrames.push_back(std::move(newFrame));
- mFramesAvailable.broadcast();
+
+ return V2_4::Error::UNSUPPORTED;
+}
+
+V2_4::Error FakeComposerClient::setActiveConfigWithConstraints(
+ Display display, Config config,
+ const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* timeline) {
+ ALOGV("setActiveConfigWithConstraints");
+ if (mMockHal) {
+ return mMockHal->setActiveConfigWithConstraints(display, config,
+ vsyncPeriodChangeConstraints, timeline);
}
- return Error::NONE;
+ return V2_4::Error::UNSUPPORTED;
}
-Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
- int32_t /*x*/, int32_t /*y*/) {
- ALOGV("setLayerCursorPosition");
- return Error::NONE;
+V2_4::Error FakeComposerClient::setAutoLowLatencyMode(Display, bool) {
+ ALOGV("setAutoLowLatencyMode");
+ return V2_4::Error::UNSUPPORTED;
}
-Error FakeComposerClient::setLayerBuffer(Display /*display*/, Layer layer, buffer_handle_t buffer,
- int32_t acquireFence) {
- ALOGV("setLayerBuffer");
- LayerImpl& l = getLayerImpl(layer);
- if (buffer != l.mRenderState.mBuffer) {
- l.mRenderState.mSwapCount++; // TODO: Is setting to same value a swap or not?
- }
- l.mRenderState.mBuffer = buffer;
- l.mRenderState.mAcquireFence = acquireFence;
-
- return Error::NONE;
+V2_4::Error FakeComposerClient::getSupportedContentTypes(
+ Display, std::vector<IComposerClient::ContentType>*) {
+ ALOGV("getSupportedContentTypes");
+ return V2_4::Error::UNSUPPORTED;
}
-Error FakeComposerClient::setLayerSurfaceDamage(Display /*display*/, Layer /*layer*/,
- const std::vector<hwc_rect_t>& /*damage*/) {
- ALOGV("setLayerSurfaceDamage");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerBlendMode(Display /*display*/, Layer layer, int32_t mode) {
- ALOGV("setLayerBlendMode");
- getLayerImpl(layer).mRenderState.mBlendMode = static_cast<hwc2_blend_mode_t>(mode);
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerColor(Display /*display*/, Layer layer,
- IComposerClient::Color color) {
- ALOGV("setLayerColor");
- getLayerImpl(layer).mRenderState.mLayerColor.r = color.r;
- getLayerImpl(layer).mRenderState.mLayerColor.g = color.g;
- getLayerImpl(layer).mRenderState.mLayerColor.b = color.b;
- getLayerImpl(layer).mRenderState.mLayerColor.a = color.a;
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerCompositionType(Display /*display*/, Layer /*layer*/,
- int32_t /*type*/) {
- ALOGV("setLayerCompositionType");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerDataspace(Display /*display*/, Layer /*layer*/,
- int32_t /*dataspace*/) {
- ALOGV("setLayerDataspace");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerDisplayFrame(Display /*display*/, Layer layer,
- const hwc_rect_t& frame) {
- ALOGV("setLayerDisplayFrame (%d, %d, %d, %d)", frame.left, frame.top, frame.right,
- frame.bottom);
- getLayerImpl(layer).mRenderState.mDisplayFrame = frame;
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerPlaneAlpha(Display /*display*/, Layer layer, float alpha) {
- ALOGV("setLayerPlaneAlpha");
- getLayerImpl(layer).mRenderState.mPlaneAlpha = alpha;
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSidebandStream(Display /*display*/, Layer /*layer*/,
- buffer_handle_t /*stream*/) {
- ALOGV("setLayerSidebandStream");
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerSourceCrop(Display /*display*/, Layer layer,
- const hwc_frect_t& crop) {
- ALOGV("setLayerSourceCrop");
- getLayerImpl(layer).mRenderState.mSourceCrop = crop;
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerTransform(Display /*display*/, Layer layer, int32_t transform) {
- ALOGV("setLayerTransform");
- getLayerImpl(layer).mRenderState.mTransform = static_cast<hwc_transform_t>(transform);
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerVisibleRegion(Display /*display*/, Layer layer,
- const std::vector<hwc_rect_t>& visible) {
- ALOGV("setLayerVisibleRegion");
- getLayerImpl(layer).mRenderState.mVisibleRegion = visible;
- return Error::NONE;
-}
-
-Error FakeComposerClient::setLayerZOrder(Display /*display*/, Layer layer, uint32_t z) {
- ALOGV("setLayerZOrder");
- getLayerImpl(layer).mZ = z;
- return Error::NONE;
+V2_4::Error FakeComposerClient::setContentType(Display, IComposerClient::ContentType) {
+ ALOGV("setContentType");
+ return V2_4::Error::UNSUPPORTED;
}
//////////////////////////////////////////////////////////////////
void FakeComposerClient::requestVSync(uint64_t vsyncTime) {
- if (mEventCallback) {
+ if (mEventCallback || mEventCallback_2_4) {
uint64_t timestamp = vsyncTime;
ALOGV("Vsync");
if (timestamp == 0) {
@@ -512,8 +789,10 @@
}
if (mSurfaceComposer != nullptr) {
mSurfaceComposer->injectVSync(timestamp);
- } else {
+ } else if (mEventCallback) {
mEventCallback->onVsync(PRIMARY_DISPLAY, timestamp);
+ } else {
+ mEventCallback_2_4->onVsync_2_4(PRIMARY_DISPLAY, timestamp, 16'666'666);
}
}
}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
index d115d79..f9ff2bf 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h
@@ -19,10 +19,15 @@
#define HWC2_USE_CPP11
#define HWC2_INCLUDE_STRINGIFICATION
#include <composer-hal/2.1/ComposerClient.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
+#include <composer-hal/2.4/ComposerClient.h>
#undef HWC2_USE_CPP11
#undef HWC2_INCLUDE_STRINGIFICATION
#include "RenderState.h"
+#include "MockComposerHal.h"
+
// Needed for display type/ID enums
#include <hardware/hwcomposer_defs.h>
@@ -30,8 +35,10 @@
#include <chrono>
-using namespace android::hardware::graphics::composer::V2_1;
-using namespace android::hardware::graphics::composer::V2_1::hal;
+using namespace android::hardware::graphics::common;
+using namespace android::hardware::graphics::composer;
+using namespace android::hardware::graphics::composer::V2_4;
+using namespace android::hardware::graphics::composer::V2_4::hal;
using namespace android::hardware;
using namespace std::chrono_literals;
@@ -46,7 +53,6 @@
} // namespace android
namespace sftest {
-
// NOTE: The ID's need to be exactly these. VR composer and parts of
// the SurfaceFlinger assume the display IDs to have these values
// despite the enum being documented as a display type.
@@ -59,6 +65,8 @@
FakeComposerClient();
virtual ~FakeComposerClient();
+ void setMockHal(MockComposerHal* mockHal) { mMockHal = mockHal; }
+
bool hasCapability(hwc2_capability_t capability) override;
std::string dumpDebugInfo() override;
@@ -66,59 +74,183 @@
void unregisterEventCallback() override;
uint32_t getMaxVirtualDisplayCount() override;
- Error createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat* format,
- Display* outDisplay) override;
- Error destroyVirtualDisplay(Display display) override;
- Error createLayer(Display display, Layer* outLayer) override;
- Error destroyLayer(Display display, Layer layer) override;
+ V2_1::Error createVirtualDisplay(uint32_t width, uint32_t height, V1_0::PixelFormat* format,
+ Display* outDisplay) override;
+ V2_1::Error destroyVirtualDisplay(Display display) override;
+ V2_1::Error createLayer(Display display, Layer* outLayer) override;
+ V2_1::Error destroyLayer(Display display, Layer layer) override;
- Error getActiveConfig(Display display, Config* outConfig) override;
- Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
- PixelFormat format, Dataspace dataspace) override;
- Error getColorModes(Display display, hidl_vec<ColorMode>* outModes) override;
- Error getDisplayAttribute(Display display, Config config, IComposerClient::Attribute attribute,
- int32_t* outValue) override;
- Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
- Error getDisplayName(Display display, hidl_string* outName) override;
- Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
- Error getDozeSupport(Display display, bool* outSupport) override;
- Error getHdrCapabilities(Display display, hidl_vec<Hdr>* outTypes, float* outMaxLuminance,
- float* outMaxAverageLuminance, float* outMinLuminance) override;
+ V2_1::Error getActiveConfig(Display display, Config* outConfig) override;
+ V2_1::Error getClientTargetSupport(Display display, uint32_t width, uint32_t height,
+ V1_0::PixelFormat format,
+ V1_0::Dataspace dataspace) override;
+ V2_1::Error getColorModes(Display display, hidl_vec<V1_0::ColorMode>* outModes) override;
+ V2_1::Error getDisplayAttribute(Display display, Config config,
+ V2_1::IComposerClient::Attribute attribute,
+ int32_t* outValue) override;
+ V2_1::Error getDisplayConfigs(Display display, hidl_vec<Config>* outConfigs) override;
+ V2_1::Error getDisplayName(Display display, hidl_string* outName) override;
+ V2_1::Error getDisplayType(Display display, IComposerClient::DisplayType* outType) override;
+ V2_1::Error getDozeSupport(Display display, bool* outSupport) override;
+ V2_1::Error getHdrCapabilities(Display display, hidl_vec<V1_0::Hdr>* outTypes,
+ float* outMaxLuminance, float* outMaxAverageLuminance,
+ float* outMinLuminance) override;
- Error setActiveConfig(Display display, Config config) override;
- Error setColorMode(Display display, ColorMode mode) override;
- Error setPowerMode(Display display, IComposerClient::PowerMode mode) override;
- Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
+ V2_1::Error setActiveConfig(Display display, Config config) override;
+ V2_1::Error setColorMode(Display display, V1_0::ColorMode mode) override;
+ V2_1::Error setPowerMode(Display display, V2_1::IComposerClient::PowerMode mode) override;
+ V2_1::Error setVsyncEnabled(Display display, IComposerClient::Vsync enabled) override;
- Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
- Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
- int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
- Error setOutputBuffer(Display display, buffer_handle_t buffer, int32_t releaseFence) override;
- Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
- std::vector<IComposerClient::Composition>* outCompositionTypes,
- uint32_t* outDisplayRequestMask, std::vector<Layer>* outRequestedLayers,
- std::vector<uint32_t>* outRequestMasks) override;
- Error acceptDisplayChanges(Display display) override;
- Error presentDisplay(Display display, int32_t* outPresentFence, std::vector<Layer>* outLayers,
- std::vector<int32_t>* outReleaseFences) override;
+ V2_1::Error setColorTransform(Display display, const float* matrix, int32_t hint) override;
+ V2_1::Error setClientTarget(Display display, buffer_handle_t target, int32_t acquireFence,
+ int32_t dataspace, const std::vector<hwc_rect_t>& damage) override;
+ V2_1::Error setOutputBuffer(Display display, buffer_handle_t buffer,
+ int32_t releaseFence) override;
+ V2_1::Error validateDisplay(Display display, std::vector<Layer>* outChangedLayers,
+ std::vector<IComposerClient::Composition>* outCompositionTypes,
+ uint32_t* outDisplayRequestMask,
+ std::vector<Layer>* outRequestedLayers,
+ std::vector<uint32_t>* outRequestMasks) override;
+ V2_1::Error acceptDisplayChanges(Display display) override;
+ V2_1::Error presentDisplay(Display display, int32_t* outPresentFence,
+ std::vector<Layer>* outLayers,
+ std::vector<int32_t>* outReleaseFences) override;
- Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
- Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
- int32_t acquireFence) override;
- Error setLayerSurfaceDamage(Display display, Layer layer,
- const std::vector<hwc_rect_t>& damage) override;
- Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
- Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
- Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
- Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
- Error setLayerDisplayFrame(Display display, Layer layer, const hwc_rect_t& frame) override;
- Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
- Error setLayerSidebandStream(Display display, Layer layer, buffer_handle_t stream) override;
- Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
- Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
- Error setLayerVisibleRegion(Display display, Layer layer,
- const std::vector<hwc_rect_t>& visible) override;
- Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+ V2_1::Error setLayerCursorPosition(Display display, Layer layer, int32_t x, int32_t y) override;
+ V2_1::Error setLayerBuffer(Display display, Layer layer, buffer_handle_t buffer,
+ int32_t acquireFence) override;
+ V2_1::Error setLayerSurfaceDamage(Display display, Layer layer,
+ const std::vector<hwc_rect_t>& damage) override;
+ V2_1::Error setLayerBlendMode(Display display, Layer layer, int32_t mode) override;
+ V2_1::Error setLayerColor(Display display, Layer layer, IComposerClient::Color color) override;
+ V2_1::Error setLayerCompositionType(Display display, Layer layer, int32_t type) override;
+ V2_1::Error setLayerDataspace(Display display, Layer layer, int32_t dataspace) override;
+ V2_1::Error setLayerDisplayFrame(Display display, Layer layer,
+ const hwc_rect_t& frame) override;
+ V2_1::Error setLayerPlaneAlpha(Display display, Layer layer, float alpha) override;
+ V2_1::Error setLayerSidebandStream(Display display, Layer layer,
+ buffer_handle_t stream) override;
+ V2_1::Error setLayerSourceCrop(Display display, Layer layer, const hwc_frect_t& crop) override;
+ V2_1::Error setLayerTransform(Display display, Layer layer, int32_t transform) override;
+ V2_1::Error setLayerVisibleRegion(Display display, Layer layer,
+ const std::vector<hwc_rect_t>& visible) override;
+ V2_1::Error setLayerZOrder(Display display, Layer layer, uint32_t z) override;
+
+ // Composer 2.2
+ V2_1::Error getPerFrameMetadataKeys(
+ Display display,
+ std::vector<V2_2::IComposerClient::PerFrameMetadataKey>* outKeys) override;
+ V2_1::Error setLayerPerFrameMetadata(
+ Display display, Layer layer,
+ const std::vector<V2_2::IComposerClient::PerFrameMetadata>& metadata) override;
+
+ V2_1::Error getReadbackBufferAttributes(
+ Display display, graphics::common::V1_1::PixelFormat* outFormat,
+ graphics::common::V1_1::Dataspace* outDataspace) override;
+ V2_1::Error setReadbackBuffer(Display display, const native_handle_t* bufferHandle,
+ android::base::unique_fd fenceFd) override;
+ V2_1::Error getReadbackBufferFence(Display display,
+ android::base::unique_fd* outFenceFd) override;
+ V2_1::Error createVirtualDisplay_2_2(uint32_t width, uint32_t height,
+ graphics::common::V1_1::PixelFormat* format,
+ Display* outDisplay) override;
+ V2_1::Error getClientTargetSupport_2_2(Display display, uint32_t width, uint32_t height,
+ graphics::common::V1_1::PixelFormat format,
+ graphics::common::V1_1::Dataspace dataspace) override;
+ V2_1::Error setPowerMode_2_2(Display display, V2_2::IComposerClient::PowerMode mode) override;
+
+ V2_1::Error setLayerFloatColor(Display display, Layer layer,
+ V2_2::IComposerClient::FloatColor color) override;
+
+ V2_1::Error getColorModes_2_2(Display display,
+ hidl_vec<graphics::common::V1_1::ColorMode>* outModes) override;
+ V2_1::Error getRenderIntents(
+ Display display, graphics::common::V1_1::ColorMode mode,
+ std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
+ V2_1::Error setColorMode_2_2(Display display, graphics::common::V1_1::ColorMode mode,
+ graphics::common::V1_1::RenderIntent intent) override;
+
+ std::array<float, 16> getDataspaceSaturationMatrix(
+ graphics::common::V1_1::Dataspace dataspace) override;
+
+ // Composer 2.3
+ V2_1::Error getPerFrameMetadataKeys_2_3(
+ Display display,
+ std::vector<V2_3::IComposerClient::PerFrameMetadataKey>* outKeys) override;
+
+ V2_1::Error setColorMode_2_3(Display display, graphics::common::V1_2::ColorMode mode,
+ graphics::common::V1_1::RenderIntent intent) override;
+
+ V2_1::Error getRenderIntents_2_3(
+ Display display, graphics::common::V1_2::ColorMode mode,
+ std::vector<graphics::common::V1_1::RenderIntent>* outIntents) override;
+
+ V2_1::Error getColorModes_2_3(Display display,
+ hidl_vec<graphics::common::V1_2::ColorMode>* outModes) override;
+
+ V2_1::Error getClientTargetSupport_2_3(Display display, uint32_t width, uint32_t height,
+ graphics::common::V1_2::PixelFormat format,
+ graphics::common::V1_2::Dataspace dataspace) override;
+ V2_1::Error getReadbackBufferAttributes_2_3(
+ Display display, graphics::common::V1_2::PixelFormat* outFormat,
+ graphics::common::V1_2::Dataspace* outDataspace) override;
+ V2_1::Error getHdrCapabilities_2_3(Display display,
+ hidl_vec<graphics::common::V1_2::Hdr>* outTypes,
+ float* outMaxLuminance, float* outMaxAverageLuminance,
+ float* outMinLuminance) override;
+ V2_1::Error setLayerPerFrameMetadata_2_3(
+ Display display, Layer layer,
+ const std::vector<V2_3::IComposerClient::PerFrameMetadata>& metadata) override;
+ V2_1::Error getDisplayIdentificationData(Display display, uint8_t* outPort,
+ std::vector<uint8_t>* outData) override;
+ V2_1::Error setLayerColorTransform(Display display, Layer layer, const float* matrix) override;
+ V2_1::Error getDisplayedContentSamplingAttributes(
+ uint64_t display, graphics::common::V1_2::PixelFormat& format,
+ graphics::common::V1_2::Dataspace& dataspace,
+ hidl_bitfield<V2_3::IComposerClient::FormatColorComponent>& componentMask) override;
+ V2_1::Error setDisplayedContentSamplingEnabled(
+ uint64_t display, V2_3::IComposerClient::DisplayedContentSampling enable,
+ hidl_bitfield<V2_3::IComposerClient::FormatColorComponent> componentMask,
+ uint64_t maxFrames) override;
+ V2_1::Error getDisplayedContentSample(uint64_t display, uint64_t maxFrames, uint64_t timestamp,
+ uint64_t& frameCount,
+ hidl_vec<uint64_t>& sampleComponent0,
+ hidl_vec<uint64_t>& sampleComponent1,
+ hidl_vec<uint64_t>& sampleComponent2,
+ hidl_vec<uint64_t>& sampleComponent3) override;
+ V2_1::Error getDisplayCapabilities(
+ Display display,
+ std::vector<V2_3::IComposerClient::DisplayCapability>* outCapabilities) override;
+ V2_1::Error setLayerPerFrameMetadataBlobs(
+ Display display, Layer layer,
+ std::vector<V2_3::IComposerClient::PerFrameMetadataBlob>& blobs) override;
+ V2_1::Error getDisplayBrightnessSupport(Display display, bool* outSupport) override;
+ V2_1::Error setDisplayBrightness(Display display, float brightness) override;
+
+ // Composer 2.4
+ void registerEventCallback_2_4(EventCallback_2_4* callback) override;
+
+ void unregisterEventCallback_2_4() override;
+
+ V2_4::Error getDisplayCapabilities_2_4(
+ Display display,
+ std::vector<V2_4::IComposerClient::DisplayCapability>* outCapabilities) override;
+ V2_4::Error getDisplayConnectionType(
+ Display display, V2_4::IComposerClient::DisplayConnectionType* outType) override;
+ V2_4::Error getDisplayAttribute_2_4(Display display, Config config,
+ IComposerClient::Attribute attribute,
+ int32_t* outValue) override;
+ V2_4::Error getDisplayVsyncPeriod(Display display,
+ V2_4::VsyncPeriodNanos* outVsyncPeriod) override;
+ V2_4::Error setActiveConfigWithConstraints(
+ Display display, Config config,
+ const V2_4::IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) override;
+ V2_4::Error setAutoLowLatencyMode(Display display, bool on) override;
+ V2_4::Error getSupportedContentTypes(
+ Display display,
+ std::vector<IComposerClient::ContentType>* outSupportedContentTypes) override;
+ V2_4::Error setContentType(Display display, IComposerClient::ContentType type) override;
void setClient(ComposerClient* client);
@@ -150,6 +282,7 @@
LayerImpl& getLayerImpl(Layer handle);
EventCallback* mEventCallback;
+ EventCallback_2_4* mEventCallback_2_4;
Config mCurrentConfig;
bool mVsyncEnabled;
std::vector<std::unique_ptr<LayerImpl>> mLayers;
@@ -159,6 +292,8 @@
android::sp<android::SurfaceComposerClient> mSurfaceComposer; // For VSync injections
mutable android::Mutex mStateMutex;
mutable android::Condition mFramesAvailable;
+
+ MockComposerHal* mMockHal = nullptr;
};
} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
index f70cbdb..f727bc4 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.cpp
@@ -22,34 +22,150 @@
#include "FakeComposerService.h"
using namespace android::hardware;
+using namespace android::hardware::graphics::composer;
namespace sftest {
-FakeComposerService::FakeComposerService(android::sp<ComposerClient>& client) : mClient(client) {}
+FakeComposerService_2_1::FakeComposerService_2_1(android::sp<ComposerClient>& client)
+ : mClient(client) {}
-FakeComposerService::~FakeComposerService() {
+FakeComposerService_2_1::~FakeComposerService_2_1() {
ALOGI("Maybe killing client %p", mClient.get());
// Rely on sp to kill the client.
}
-Return<void> FakeComposerService::getCapabilities(getCapabilities_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::getCapabilities(getCapabilities_cb hidl_cb) {
ALOGI("FakeComposerService::getCapabilities");
hidl_cb(hidl_vec<Capability>());
return Void();
}
-Return<void> FakeComposerService::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
ALOGI("FakeComposerService::dumpDebugInfo");
hidl_cb(hidl_string());
return Void();
}
-Return<void> FakeComposerService::createClient(createClient_cb hidl_cb) {
+Return<void> FakeComposerService_2_1::createClient(createClient_cb hidl_cb) {
ALOGI("FakeComposerService::createClient %p", mClient.get());
if (!mClient->init()) {
LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
}
- hidl_cb(Error::NONE, mClient);
+ hidl_cb(V2_1::Error::NONE, mClient);
+ return Void();
+}
+
+FakeComposerService_2_2::FakeComposerService_2_2(android::sp<ComposerClient>& client)
+ : mClient(client) {}
+
+FakeComposerService_2_2::~FakeComposerService_2_2() {
+ ALOGI("Maybe killing client %p", mClient.get());
+ // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_2::getCapabilities(getCapabilities_cb hidl_cb) {
+ ALOGI("FakeComposerService::getCapabilities");
+ hidl_cb(hidl_vec<Capability>());
+ return Void();
+}
+
+Return<void> FakeComposerService_2_2::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+ ALOGI("FakeComposerService::dumpDebugInfo");
+ hidl_cb(hidl_string());
+ return Void();
+}
+
+Return<void> FakeComposerService_2_2::createClient(createClient_cb hidl_cb) {
+ ALOGI("FakeComposerService::createClient %p", mClient.get());
+ if (!mClient->init()) {
+ LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+ }
+ hidl_cb(V2_1::Error::NONE, mClient);
+ return Void();
+}
+
+FakeComposerService_2_3::FakeComposerService_2_3(android::sp<ComposerClient>& client)
+ : mClient(client) {}
+
+FakeComposerService_2_3::~FakeComposerService_2_3() {
+ ALOGI("Maybe killing client %p", mClient.get());
+ // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_3::getCapabilities(getCapabilities_cb hidl_cb) {
+ ALOGI("FakeComposerService::getCapabilities");
+ hidl_cb(hidl_vec<Capability>());
+ return Void();
+}
+
+Return<void> FakeComposerService_2_3::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+ ALOGI("FakeComposerService::dumpDebugInfo");
+ hidl_cb(hidl_string());
+ return Void();
+}
+
+Return<void> FakeComposerService_2_3::createClient(createClient_cb hidl_cb) {
+ LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_3");
+ if (!mClient->init()) {
+ LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+ }
+ hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+ return Void();
+}
+
+Return<void> FakeComposerService_2_3::createClient_2_3(createClient_2_3_cb hidl_cb) {
+ ALOGI("FakeComposerService_2_3::createClient_2_3 %p", mClient.get());
+ if (!mClient->init()) {
+ LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+ }
+ hidl_cb(V2_1::Error::NONE, mClient);
+ return Void();
+}
+
+FakeComposerService_2_4::FakeComposerService_2_4(android::sp<ComposerClient>& client)
+ : mClient(client) {}
+
+FakeComposerService_2_4::~FakeComposerService_2_4() {
+ ALOGI("Maybe killing client %p", mClient.get());
+ // Rely on sp to kill the client.
+}
+
+Return<void> FakeComposerService_2_4::getCapabilities(getCapabilities_cb hidl_cb) {
+ ALOGI("FakeComposerService::getCapabilities");
+ hidl_cb(hidl_vec<Capability>());
+ return Void();
+}
+
+Return<void> FakeComposerService_2_4::dumpDebugInfo(dumpDebugInfo_cb hidl_cb) {
+ ALOGI("FakeComposerService::dumpDebugInfo");
+ hidl_cb(hidl_string());
+ return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient(createClient_cb hidl_cb) {
+ LOG_ALWAYS_FATAL("createClient called on FakeComposerService_2_4");
+ if (!mClient->init()) {
+ LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+ }
+ hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+ return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient_2_3(createClient_2_3_cb hidl_cb) {
+ LOG_ALWAYS_FATAL("createClient_2_3 called on FakeComposerService_2_4");
+ if (!mClient->init()) {
+ LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+ }
+ hidl_cb(V2_1::Error::UNSUPPORTED, nullptr);
+ return Void();
+}
+
+Return<void> FakeComposerService_2_4::createClient_2_4(createClient_2_4_cb hidl_cb) {
+ ALOGI("FakeComposerService_2_4::createClient_2_4 %p", mClient.get());
+ if (!mClient->init()) {
+ LOG_ALWAYS_FATAL("failed to initialize ComposerClient");
+ }
+ hidl_cb(V2_4::Error::NONE, mClient);
return Void();
}
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
index a3fb8a6..47f970f 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerService.h
@@ -16,18 +16,24 @@
#pragma once
+#include <android/hardware/graphics/composer/2.4/IComposer.h>
#include <composer-hal/2.1/ComposerClient.h>
+#include <composer-hal/2.2/ComposerClient.h>
+#include <composer-hal/2.3/ComposerClient.h>
+#include <composer-hal/2.4/ComposerClient.h>
-using namespace android::hardware::graphics::composer::V2_1;
-using namespace android::hardware::graphics::composer::V2_1::hal;
using android::hardware::Return;
+using ComposerClient = android::hardware::graphics::composer::V2_4::hal::ComposerClient;
+
namespace sftest {
-class FakeComposerService : public IComposer {
+using IComposer_2_1 = android::hardware::graphics::composer::V2_1::IComposer;
+
+class FakeComposerService_2_1 : public IComposer_2_1 {
public:
- explicit FakeComposerService(android::sp<ComposerClient>& client);
- virtual ~FakeComposerService();
+ explicit FakeComposerService_2_1(android::sp<ComposerClient>& client);
+ virtual ~FakeComposerService_2_1();
Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
@@ -37,4 +43,50 @@
android::sp<ComposerClient> mClient;
};
+using IComposer_2_2 = android::hardware::graphics::composer::V2_2::IComposer;
+class FakeComposerService_2_2 : public IComposer_2_2 {
+public:
+ explicit FakeComposerService_2_2(android::sp<ComposerClient>& client);
+ virtual ~FakeComposerService_2_2();
+
+ Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+ Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+ Return<void> createClient(createClient_cb hidl_cb) override;
+
+private:
+ android::sp<ComposerClient> mClient;
+};
+
+using IComposer_2_3 = android::hardware::graphics::composer::V2_3::IComposer;
+class FakeComposerService_2_3 : public IComposer_2_3 {
+public:
+ explicit FakeComposerService_2_3(android::sp<ComposerClient>& client);
+ virtual ~FakeComposerService_2_3();
+
+ Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+ Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+ Return<void> createClient(createClient_cb hidl_cb) override;
+ Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
+
+private:
+ android::sp<ComposerClient> mClient;
+};
+
+using IComposer_2_4 = android::hardware::graphics::composer::V2_4::IComposer;
+
+class FakeComposerService_2_4 : public IComposer_2_4 {
+public:
+ explicit FakeComposerService_2_4(android::sp<ComposerClient>& client);
+ virtual ~FakeComposerService_2_4();
+
+ Return<void> getCapabilities(getCapabilities_cb hidl_cb) override;
+ Return<void> dumpDebugInfo(dumpDebugInfo_cb hidl_cb) override;
+ Return<void> createClient(createClient_cb hidl_cb) override;
+ Return<void> createClient_2_3(createClient_2_3_cb hidl_cb) override;
+ Return<void> createClient_2_4(createClient_2_4_cb hidl_cb) override;
+
+private:
+ android::sp<ComposerClient> mClient;
+};
+
} // namespace sftest
diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
index 7d20d3c..09a2a89 100644
--- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
+++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.h
@@ -108,9 +108,9 @@
LOG_ALWAYS_FATAL_IF(android::NO_ERROR != apply());
// Make sure that exactly one frame has been rendered.
mComposer.waitUntilFrame(frameCount + 1);
- LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
- "Unexpected frame advance. Delta: %d",
- mComposer.getFrameCount() - frameCount);
+ // LOG_ALWAYS_FATAL_IF(frameCount + 1 != mComposer.getFrameCount(),
+ // "Unexpected frame advance. Delta: %d",
+ // mComposer.getFrameCount() - frameCount);
}
FakeComposerClient& mComposer;
diff --git a/services/surfaceflinger/tests/fakehwc/MockComposerHal.h b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
new file mode 100644
index 0000000..5dc3778
--- /dev/null
+++ b/services/surfaceflinger/tests/fakehwc/MockComposerHal.h
@@ -0,0 +1,47 @@
+/*
+ * 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 <composer-hal/2.4/ComposerClient.h>
+
+#include <gmock/gmock.h>
+
+using namespace android::hardware::graphics::common;
+using namespace android::hardware::graphics::composer;
+using namespace android::hardware::graphics::composer::V2_4;
+using namespace android::hardware::graphics::composer::V2_4::hal;
+using namespace android::hardware;
+using namespace std::chrono_literals;
+
+namespace sftest {
+
+// Mock class for ComposerHal. Implements only the functions used in the test.
+class MockComposerHal {
+public:
+ MOCK_METHOD2(getActiveConfig, V2_1::Error(Display, Config*));
+ MOCK_METHOD4(getDisplayAttribute_2_4,
+ V2_4::Error(Display, Config, V2_4::IComposerClient::Attribute, int32_t*));
+ MOCK_METHOD2(getDisplayConfigs, V2_1::Error(Display, hidl_vec<Config>*));
+ MOCK_METHOD2(setActiveConfig, V2_1::Error(Display, Config));
+ MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, V2_4::VsyncPeriodNanos*));
+ MOCK_METHOD4(setActiveConfigWithConstraints,
+ V2_4::Error(Display, Config,
+ const V2_4::IComposerClient::VsyncPeriodChangeConstraints&,
+ VsyncPeriodChangeTimeline*));
+};
+
+} // namespace sftest
\ No newline at end of file
diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
index 67faa57..09fdbdf 100644
--- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
+++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp
@@ -21,6 +21,7 @@
#include "FakeComposerClient.h"
#include "FakeComposerService.h"
#include "FakeComposerUtils.h"
+#include "MockComposerHal.h"
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
@@ -43,6 +44,7 @@
#include <gtest/gtest.h>
#include <limits>
+#include <thread>
using namespace std::chrono_literals;
@@ -55,12 +57,14 @@
// Mock test helpers
using ::testing::_;
+using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::SetArgPointee;
using Transaction = SurfaceComposerClient::Transaction;
+using Attribute = V2_4::IComposerClient::Attribute;
///////////////////////////////////////////////
@@ -121,307 +125,1229 @@
static_cast<int>(bottom));
}
-////////////////////////////////////////////////
-
+///////////////////////////////////////////////
+template <typename FakeComposerService>
class DisplayTest : public ::testing::Test {
-public:
- class MockComposerClient : public FakeComposerClient {
- public:
- MOCK_METHOD4(getDisplayAttribute,
- Error(Display display, Config config, IComposerClient::Attribute attribute,
- int32_t* outValue));
-
- // Re-routing to basic fake implementation
- Error getDisplayAttributeFake(Display display, Config config,
- IComposerClient::Attribute attribute, int32_t* outValue) {
- return FakeComposerClient::getDisplayAttribute(display, config, attribute, outValue);
- }
+protected:
+ struct TestConfig {
+ int32_t id;
+ int32_t w;
+ int32_t h;
+ int32_t vsyncPeriod;
+ int32_t group;
};
-protected:
- static int processDisplayEvents(int fd, int events, void* data);
+ static int processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
+ auto self = static_cast<DisplayTest*>(data);
- void SetUp() override;
- void TearDown() override;
+ ssize_t n;
+ DisplayEventReceiver::Event buffer[1];
- void waitForDisplayTransaction();
- bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected);
-
- sp<IComposer> mFakeService;
- sp<SurfaceComposerClient> mComposerClient;
-
- MockComposerClient* mMockComposer;
-
- std::unique_ptr<DisplayEventReceiver> mReceiver;
- sp<Looper> mLooper;;
- std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
-};
-
-void DisplayTest::SetUp() {
- // TODO: The mMockComposer should be a unique_ptr, but it needs to
- // outlive the test class. Currently ComposerClient only dies
- // when the service is replaced. The Mock deletes itself when
- // removeClient is called on it, which is ugly. This can be
- // changed if HIDL ServiceManager allows removing services or
- // ComposerClient starts taking the ownership of the contained
- // implementation class. Moving the fake class to the HWC2
- // interface instead of the current Composer interface might also
- // change the situation.
- mMockComposer = new MockComposerClient;
- sp<ComposerClient> client = new ComposerClient(mMockComposer);
- mFakeService = new FakeComposerService(client);
- ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
-
- android::hardware::ProcessState::self()->startThreadPool();
- android::ProcessState::self()->startThreadPool();
-
- // Primary display will be queried twice for all 5 attributes. One
- // set of queries comes from the SurfaceFlinger proper an the
- // other set from the VR composer.
- // TODO: Is VR composer always present? Change to atLeast(5)?
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
- .Times(2 * 5)
- .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
-
- startSurfaceFlinger();
-
- // Fake composer wants to enable VSync injection
- mMockComposer->onSurfaceFlingerStart();
-
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- mReceiver.reset(new DisplayEventReceiver());
- mLooper = new Looper(false);
- mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
-}
-
-void DisplayTest::TearDown() {
- mLooper = nullptr;
- mReceiver = nullptr;
-
- mComposerClient->dispose();
- mComposerClient = nullptr;
-
- // Fake composer needs to release SurfaceComposerClient before the stop.
- mMockComposer->onSurfaceFlingerStop();
- stopSurfaceFlinger();
-
- mFakeService = nullptr;
- // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
- // management.
- mMockComposer = nullptr;
-}
-
-
-int DisplayTest::processDisplayEvents(int /*fd*/, int /*events*/, void* data) {
- auto self = static_cast<DisplayTest*>(data);
-
- ssize_t n;
- DisplayEventReceiver::Event buffer[1];
-
- while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
- for (int i=0 ; i<n ; i++) {
- self->mReceivedDisplayEvents.push_back(buffer[i]);
+ while ((n = self->mReceiver->getEvents(buffer, 1)) > 0) {
+ for (int i = 0; i < n; i++) {
+ self->mReceivedDisplayEvents.push_back(buffer[i]);
+ }
}
+ ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
+ return 1;
}
- ALOGD_IF(n < 0, "Error reading events (%s)\n", strerror(-n));
- return 1;
-}
-void DisplayTest::waitForDisplayTransaction() {
- // Both a refresh and a vsync event are needed to apply pending display
- // transactions.
- mMockComposer->refreshDisplay(EXTERNAL_DISPLAY);
- mMockComposer->runVSyncAndWait();
+ Error getDisplayAttributeNoMock(Display display, Config config,
+ V2_4::IComposerClient::Attribute attribute, int32_t* outValue) {
+ mFakeComposerClient->setMockHal(nullptr);
+ auto ret =
+ mFakeComposerClient->getDisplayAttribute_2_4(display, config, attribute, outValue);
+ mFakeComposerClient->setMockHal(mMockComposer.get());
+ return ret;
+ }
- // Extra vsync and wait to avoid a 10% flake due to a race.
- mMockComposer->runVSyncAndWait();
-}
+ void setExpectationsForConfigs(Display display, std::vector<TestConfig> testConfigs,
+ Config activeConfig, V2_4::VsyncPeriodNanos defaultVsyncPeriod) {
+ std::vector<Config> configIds;
+ for (int i = 0; i < testConfigs.size(); i++) {
+ configIds.push_back(testConfigs[i].id);
-bool DisplayTest::waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
- int waitCount = 20;
- while (waitCount--) {
- while (!mReceivedDisplayEvents.empty()) {
- auto event = mReceivedDisplayEvents.front();
- mReceivedDisplayEvents.pop_front();
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::WIDTH, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].w), Return(Error::NONE)));
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::HEIGHT, _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].h), Return(Error::NONE)));
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::VSYNC_PERIOD,
+ _))
+ .WillRepeatedly(DoAll(SetArgPointee<3>(testConfigs[i].vsyncPeriod),
+ Return(Error::NONE)));
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::CONFIG_GROUP,
+ _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<3>(testConfigs[i].group), Return(Error::NONE)));
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_X, _))
+ .WillRepeatedly(Return(Error::UNSUPPORTED));
+ EXPECT_CALL(*mMockComposer,
+ getDisplayAttribute_2_4(display, testConfigs[i].id, Attribute::DPI_Y, _))
+ .WillRepeatedly(Return(Error::UNSUPPORTED));
+ }
- ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
- "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
- ", connected %d\t",
- event.header.displayId, event.hotplug.connected);
+ EXPECT_CALL(*mMockComposer, getDisplayConfigs(display, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(hidl_vec<Config>(configIds)),
+ Return(V2_1::Error::NONE)));
- if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
- event.header.displayId == displayId && event.hotplug.connected == connected) {
- return true;
+ EXPECT_CALL(*mMockComposer, getActiveConfig(display, _))
+ .WillRepeatedly(DoAll(SetArgPointee<1>(activeConfig), Return(V2_1::Error::NONE)));
+
+ EXPECT_CALL(*mMockComposer, getDisplayVsyncPeriod(display, _))
+ .WillRepeatedly(
+ DoAll(SetArgPointee<1>(defaultVsyncPeriod), Return(V2_4::Error::NONE)));
+ }
+
+ void SetUp() override {
+ mMockComposer = std::make_unique<MockComposerHal>();
+ mFakeComposerClient = new FakeComposerClient();
+ mFakeComposerClient->setMockHal(mMockComposer.get());
+
+ sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(mFakeComposerClient);
+ mFakeService = new FakeComposerService(client);
+ ASSERT_EQ(android::OK, mFakeService->registerAsService("mock"));
+
+ android::hardware::ProcessState::self()->startThreadPool();
+ android::ProcessState::self()->startThreadPool();
+
+ setExpectationsForConfigs(PRIMARY_DISPLAY,
+ {{
+ .id = 1,
+ .w = 1920,
+ .h = 1024,
+ .vsyncPeriod = 16'666'666,
+ .group = 0,
+ }},
+ 1, 16'666'666);
+
+ startSurfaceFlinger();
+
+ // Fake composer wants to enable VSync injection
+ mFakeComposerClient->onSurfaceFlingerStart();
+
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ mReceiver.reset(new DisplayEventReceiver(ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::eConfigChangedDispatch));
+ mLooper = new Looper(false);
+ mLooper->addFd(mReceiver->getFd(), 0, ALOOPER_EVENT_INPUT, processDisplayEvents, this);
+ }
+
+ void TearDown() override {
+ mLooper = nullptr;
+ mReceiver = nullptr;
+
+ mComposerClient->dispose();
+ mComposerClient = nullptr;
+
+ // Fake composer needs to release SurfaceComposerClient before the stop.
+ mFakeComposerClient->onSurfaceFlingerStop();
+ stopSurfaceFlinger();
+
+ mFakeComposerClient->setMockHal(nullptr);
+
+ mFakeService = nullptr;
+ // TODO: Currently deleted in FakeComposerClient::removeClient(). Devise better lifetime
+ // management.
+ mMockComposer = nullptr;
+ }
+
+ void waitForDisplayTransaction() {
+ // Both a refresh and a vsync event are needed to apply pending display
+ // transactions.
+ mFakeComposerClient->refreshDisplay(EXTERNAL_DISPLAY);
+ mFakeComposerClient->runVSyncAndWait();
+
+ // Extra vsync and wait to avoid a 10% flake due to a race.
+ mFakeComposerClient->runVSyncAndWait();
+ }
+
+ bool waitForHotplugEvent(PhysicalDisplayId displayId, bool connected) {
+ int waitCount = 20;
+ while (waitCount--) {
+ while (!mReceivedDisplayEvents.empty()) {
+ auto event = mReceivedDisplayEvents.front();
+ mReceivedDisplayEvents.pop_front();
+
+ ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG,
+ "event hotplug: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", connected %d\t",
+ event.header.displayId, event.hotplug.connected);
+
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG &&
+ event.header.displayId == displayId && event.hotplug.connected == connected) {
+ return true;
+ }
+ }
+
+ mLooper->pollOnce(1);
+ }
+ return false;
+ }
+
+ bool waitForConfigChangedEvent(PhysicalDisplayId displayId, int32_t configId) {
+ int waitCount = 20;
+ while (waitCount--) {
+ while (!mReceivedDisplayEvents.empty()) {
+ auto event = mReceivedDisplayEvents.front();
+ mReceivedDisplayEvents.pop_front();
+
+ ALOGV_IF(event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED,
+ "event config: displayId %" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT
+ ", configId %d\t",
+ event.header.displayId, event.config.configId);
+
+ if (event.header.type == DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED &&
+ event.header.displayId == displayId && event.config.configId == configId) {
+ return true;
+ }
+ }
+
+ mLooper->pollOnce(1);
+ }
+ return false;
+ }
+
+ void Test_HotplugOneConfig() {
+ ALOGD("DisplayTest::Test_Hotplug_oneConfig");
+
+ setExpectationsForConfigs(EXTERNAL_DISPLAY,
+ {{.id = 1,
+ .w = 200,
+ .h = 400,
+ .vsyncPeriod = 16'666'666,
+ .group = 0}},
+ 1, 16'666'666);
+
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+ {
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
+
+ DisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(200u, info.w);
+ EXPECT_EQ(400u, info.h);
+ EXPECT_EQ(1e9f / 16'666'666, info.fps);
+
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
}
}
- mLooper->pollOnce(1);
- }
-
- return false;
-}
-
-TEST_F(DisplayTest, Hotplug) {
- ALOGD("DisplayTest::Hotplug");
-
- // The attribute queries will get done twice. This is for defaults
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _))
- .Times(2 * 3)
- .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
- // ... and then special handling for dimensions. Specifying these
- // rules later means that gmock will try them first, i.e.,
- // ordering of width/height vs. the default implementation for
- // other queries is significant.
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
- .Times(2)
- .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
-
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
- .Times(2)
- .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
-
- mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
-
- waitForDisplayTransaction();
-
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
- {
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
- ASSERT_FALSE(display == nullptr);
-
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- ASSERT_EQ(400u, info.w);
- ASSERT_EQ(200u, info.h);
-
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w, info.h,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(surfaceControl != nullptr);
- ASSERT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::DISCONNECTED);
+ waitForDisplayTransaction();
+ mFakeComposerClient->clearFrames();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
{
- TransactionScope ts(*mMockComposer);
- ts.setDisplayLayerStack(display, 0);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ EXPECT_TRUE(display == nullptr);
- ts.setLayer(surfaceControl, INT32_MAX - 2)
- .show(surfaceControl);
+ DisplayInfo info;
+ EXPECT_NE(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
}
}
- mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+ void Test_HotplugTwoSeparateConfigs() {
+ ALOGD("DisplayTest::Test_HotplugTwoSeparateConfigs");
- mMockComposer->clearFrames();
+ setExpectationsForConfigs(EXTERNAL_DISPLAY,
+ {{.id = 1,
+ .w = 200,
+ .h = 400,
+ .vsyncPeriod = 16'666'666,
+ .group = 0},
+ {.id = 2,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 11'111'111,
+ .group = 1}},
+ 1, 16'666'666);
- mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED);
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
- waitForDisplayTransaction();
-
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
- EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
-
- {
const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
- ASSERT_FALSE(display == nullptr);
+ EXPECT_FALSE(display == nullptr);
DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
- ASSERT_EQ(400u, info.w);
- ASSERT_EQ(200u, info.h);
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(200u, info.w);
+ EXPECT_EQ(400u, info.h);
+ EXPECT_EQ(1e9f / 16'666'666, info.fps);
- auto surfaceControl =
- mComposerClient->createSurface(String8("Display Test Surface Bar"), info.w, info.h,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(surfaceControl != nullptr);
- ASSERT_TRUE(surfaceControl->isValid());
- fillSurfaceRGBA8(surfaceControl, BLUE);
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ Vector<DisplayInfo> configs;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+ EXPECT_EQ(configs.size(), 2);
+
+ // change active config
+
+ if (mIs2_4Client) {
+ EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 2, _, _))
+ .WillOnce(Return(V2_4::Error::NONE));
+ } else {
+ EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 2))
+ .WillOnce(Return(V2_1::Error::NONE));
+ }
+
+ for (int i = 0; i < configs.size(); i++) {
+ if (configs[i].w == 800u) {
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::setActiveConfig(display, i));
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ break;
+ }
+ }
+
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(800u, info.w);
+ EXPECT_EQ(1600u, info.h);
+ EXPECT_EQ(1e9f / 11'111'111, info.fps);
+
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::DISCONNECTED);
+ waitForDisplayTransaction();
+ mFakeComposerClient->clearFrames();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+ }
+
+ void Test_HotplugTwoConfigsSameGroup() {
+ ALOGD("DisplayTest::Test_HotplugTwoConfigsSameGroup");
+
+ setExpectationsForConfigs(EXTERNAL_DISPLAY,
+ {{.id = 2,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 16'666'666,
+ .group = 31},
+ {.id = 3,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 11'111'111,
+ .group = 31}},
+ 2, 16'666'666);
+
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
+
+ DisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(800u, info.w);
+ EXPECT_EQ(1600u, info.h);
+ EXPECT_EQ(1e9f / 16'666'666, info.fps);
+
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ Vector<DisplayInfo> configs;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+ EXPECT_EQ(configs.size(), 2);
+
+ // change active config
+ if (mIs2_4Client) {
+ EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
+ .WillOnce(Return(V2_4::Error::NONE));
+ } else {
+ EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
+ .WillOnce(Return(V2_1::Error::NONE));
+ }
+
+ for (int i = 0; i < configs.size(); i++) {
+ if (configs[i].fps == 1e9f / 11'111'111) {
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::setActiveConfig(display, i));
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ break;
+ }
+ }
+
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(800u, info.w);
+ EXPECT_EQ(1600u, info.h);
+ EXPECT_EQ(1e9f / 11'111'111, info.fps);
+
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::DISCONNECTED);
+ waitForDisplayTransaction();
+ mFakeComposerClient->clearFrames();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+ }
+
+ void Test_HotplugThreeConfigsMixedGroups() {
+ ALOGD("DisplayTest::Test_HotplugThreeConfigsMixedGroups");
+
+ setExpectationsForConfigs(EXTERNAL_DISPLAY,
+ {{.id = 2,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 16'666'666,
+ .group = 0},
+ {.id = 3,
+ .w = 800,
+ .h = 1600,
+ .vsyncPeriod = 11'111'111,
+ .group = 0},
+ {.id = 4,
+ .w = 1600,
+ .h = 3200,
+ .vsyncPeriod = 8'333'333,
+ .group = 1},
+ {.id = 5,
+ .w = 1600,
+ .h = 3200,
+ .vsyncPeriod = 11'111'111,
+ .group = 1}},
+ 2, 16'666'666);
+
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::CONNECTED);
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, true));
+
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(EXTERNAL_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
+
+ DisplayInfo info;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(800u, info.w);
+ EXPECT_EQ(1600u, info.h);
+ EXPECT_EQ(1e9f / 16'666'666, info.fps);
+
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ Vector<DisplayInfo> configs;
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayConfigs(display, &configs));
+ EXPECT_EQ(configs.size(), 4);
+
+ // change active config to 800x1600@90Hz
+ if (mIs2_4Client) {
+ EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 3, _, _))
+ .WillOnce(Return(V2_4::Error::NONE));
+ } else {
+ EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 3))
+ .WillOnce(Return(V2_1::Error::NONE));
+ }
+
+ for (int i = 0; i < configs.size(); i++) {
+ if (configs[i].w == 800u && configs[i].fps == 1e9f / 11'111'111) {
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::setActiveConfig(display, i));
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ break;
+ }
+ }
+
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(800u, info.w);
+ EXPECT_EQ(1600u, info.h);
+ EXPECT_EQ(1e9f / 11'111'111, info.fps);
+
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ // change active config to 1600x3200@120Hz
+ if (mIs2_4Client) {
+ EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 4, _, _))
+ .WillOnce(Return(V2_4::Error::NONE));
+ } else {
+ EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 4))
+ .WillOnce(Return(V2_1::Error::NONE));
+ }
+
+ for (int i = 0; i < configs.size(); i++) {
+ if (configs[i].fps == 1e9f / 8'333'333) {
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::setActiveConfig(display, i));
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ break;
+ }
+ }
+
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(1600u, info.w);
+ EXPECT_EQ(3200u, info.h);
+ EXPECT_EQ(1e9f / 8'333'333, info.fps);
+
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ // change active config to 1600x3200@90Hz
+ if (mIs2_4Client) {
+ EXPECT_CALL(*mMockComposer, setActiveConfigWithConstraints(EXTERNAL_DISPLAY, 5, _, _))
+ .WillOnce(Return(V2_4::Error::NONE));
+ } else {
+ EXPECT_CALL(*mMockComposer, setActiveConfig(EXTERNAL_DISPLAY, 5))
+ .WillOnce(Return(V2_1::Error::NONE));
+ }
+
+ for (int i = 0; i < configs.size(); i++) {
+ if (configs[i].w == 1600 && configs[i].fps == 1e9f / 11'111'111) {
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::setActiveConfig(display, i));
+ waitForDisplayTransaction();
+ EXPECT_TRUE(waitForConfigChangedEvent(EXTERNAL_DISPLAY, i));
+ break;
+ }
+ }
+
+ EXPECT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+ EXPECT_EQ(1600u, info.w);
+ EXPECT_EQ(3200u, info.h);
+ EXPECT_EQ(1e9f / 11'111'111, info.fps);
+
+ mFakeComposerClient->clearFrames();
+ {
+ auto surfaceControl =
+ mComposerClient->createSurface(String8("Display Test Surface Foo"), info.w,
+ info.h, PIXEL_FORMAT_RGBA_8888, 0);
+ EXPECT_TRUE(surfaceControl != nullptr);
+ EXPECT_TRUE(surfaceControl->isValid());
+ fillSurfaceRGBA8(surfaceControl, BLUE);
+
+ {
+ TransactionScope ts(*mFakeComposerClient);
+ ts.setDisplayLayerStack(display, 0);
+
+ ts.setLayer(surfaceControl, INT32_MAX - 2).show(surfaceControl);
+ }
+ }
+
+ mFakeComposerClient->hotplugDisplay(EXTERNAL_DISPLAY,
+ V2_1::IComposerCallback::Connection::DISCONNECTED);
+ waitForDisplayTransaction();
+ mFakeComposerClient->clearFrames();
+ EXPECT_TRUE(waitForHotplugEvent(EXTERNAL_DISPLAY, false));
+ }
+
+ void Test_HotplugPrimaryDisplay() {
+ ALOGD("DisplayTest::HotplugPrimaryDisplay");
+
+ mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
+ V2_1::IComposerCallback::Connection::DISCONNECTED);
+
+ waitForDisplayTransaction();
+
+ EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
+ {
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ EXPECT_TRUE(display == nullptr);
+
+ DisplayInfo info;
+ auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+ EXPECT_NE(NO_ERROR, result);
+ }
+
+ mFakeComposerClient->clearFrames();
+
+ setExpectationsForConfigs(PRIMARY_DISPLAY,
+ {{.id = 1,
+ .w = 400,
+ .h = 200,
+ .vsyncPeriod = 16'666'666,
+ .group = 0}},
+ 1, 16'666'666);
+
+ mFakeComposerClient->hotplugDisplay(PRIMARY_DISPLAY,
+ V2_1::IComposerCallback::Connection::CONNECTED);
+
+ waitForDisplayTransaction();
+
+ EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
{
- TransactionScope ts(*mMockComposer);
- ts.setDisplayLayerStack(display, 0);
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ EXPECT_FALSE(display == nullptr);
- ts.setLayer(surfaceControl, INT32_MAX - 2)
- .show(surfaceControl);
+ DisplayInfo info;
+ auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
+ EXPECT_EQ(NO_ERROR, result);
+ ASSERT_EQ(400u, info.w);
+ ASSERT_EQ(200u, info.h);
+ EXPECT_EQ(1e9f / 16'666'666, info.fps);
}
}
- mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+
+ sp<V2_1::IComposer> mFakeService;
+ sp<SurfaceComposerClient> mComposerClient;
+
+ std::unique_ptr<MockComposerHal> mMockComposer;
+ FakeComposerClient* mFakeComposerClient;
+
+ std::unique_ptr<DisplayEventReceiver> mReceiver;
+ sp<Looper> mLooper;
+ std::deque<DisplayEventReceiver::Event> mReceivedDisplayEvents;
+
+ static constexpr bool mIs2_4Client =
+ std::is_same<FakeComposerService, FakeComposerService_2_4>::value;
+};
+
+using DisplayTest_2_1 = DisplayTest<FakeComposerService_2_1>;
+
+TEST_F(DisplayTest_2_1, HotplugOneConfig) {
+ Test_HotplugOneConfig();
}
-TEST_F(DisplayTest, HotplugPrimaryDisplay) {
- ALOGD("DisplayTest::HotplugPrimaryDisplay");
+TEST_F(DisplayTest_2_1, HotplugTwoSeparateConfigs) {
+ Test_HotplugTwoSeparateConfigs();
+}
- mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::DISCONNECTED);
+TEST_F(DisplayTest_2_1, HotplugTwoConfigsSameGroup) {
+ Test_HotplugTwoConfigsSameGroup();
+}
- waitForDisplayTransaction();
+TEST_F(DisplayTest_2_1, HotplugThreeConfigsMixedGroups) {
+ Test_HotplugThreeConfigsMixedGroups();
+}
- EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, false));
+TEST_F(DisplayTest_2_1, HotplugPrimaryOneConfig) {
+ Test_HotplugPrimaryDisplay();
+}
- {
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
- EXPECT_FALSE(display == nullptr);
+using DisplayTest_2_2 = DisplayTest<FakeComposerService_2_2>;
- DisplayInfo info;
- auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
- EXPECT_NE(NO_ERROR, result);
- }
+TEST_F(DisplayTest_2_2, HotplugOneConfig) {
+ Test_HotplugOneConfig();
+}
- mMockComposer->clearFrames();
+TEST_F(DisplayTest_2_2, HotplugTwoSeparateConfigs) {
+ Test_HotplugTwoSeparateConfigs();
+}
- // The attribute queries will get done twice. This is for defaults
- EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _))
- .Times(2 * 3)
- .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake));
- // ... and then special handling for dimensions. Specifying these
- // rules later means that gmock will try them first, i.e.,
- // ordering of width/height vs. the default implementation for
- // other queries is significant.
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _))
- .Times(2)
- .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE)));
+TEST_F(DisplayTest_2_2, HotplugTwoConfigsSameGroup) {
+ Test_HotplugTwoConfigsSameGroup();
+}
- EXPECT_CALL(*mMockComposer,
- getDisplayAttribute(PRIMARY_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _))
- .Times(2)
- .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE)));
+TEST_F(DisplayTest_2_2, HotplugThreeConfigsMixedGroups) {
+ Test_HotplugThreeConfigsMixedGroups();
+}
- mMockComposer->hotplugDisplay(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED);
+TEST_F(DisplayTest_2_2, HotplugPrimaryOneConfig) {
+ Test_HotplugPrimaryDisplay();
+}
- waitForDisplayTransaction();
+using DisplayTest_2_3 = DisplayTest<FakeComposerService_2_3>;
- EXPECT_TRUE(waitForHotplugEvent(PRIMARY_DISPLAY, true));
+TEST_F(DisplayTest_2_3, HotplugOneConfig) {
+ Test_HotplugOneConfig();
+}
- {
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
- EXPECT_FALSE(display == nullptr);
+TEST_F(DisplayTest_2_3, HotplugTwoSeparateConfigs) {
+ Test_HotplugTwoSeparateConfigs();
+}
- DisplayInfo info;
- auto result = SurfaceComposerClient::getDisplayInfo(display, &info);
- EXPECT_EQ(NO_ERROR, result);
- ASSERT_EQ(400u, info.w);
- ASSERT_EQ(200u, info.h);
- }
+TEST_F(DisplayTest_2_3, HotplugTwoConfigsSameGroup) {
+ Test_HotplugTwoConfigsSameGroup();
+}
+
+TEST_F(DisplayTest_2_3, HotplugThreeConfigsMixedGroups) {
+ Test_HotplugThreeConfigsMixedGroups();
+}
+
+TEST_F(DisplayTest_2_3, HotplugPrimaryOneConfig) {
+ Test_HotplugPrimaryDisplay();
+}
+
+using DisplayTest_2_4 = DisplayTest<FakeComposerService_2_4>;
+
+TEST_F(DisplayTest_2_4, HotplugOneConfig) {
+ Test_HotplugOneConfig();
+}
+
+TEST_F(DisplayTest_2_4, HotplugTwoSeparateConfigs) {
+ Test_HotplugTwoSeparateConfigs();
+}
+
+TEST_F(DisplayTest_2_4, HotplugTwoConfigsSameGroup) {
+ Test_HotplugTwoConfigsSameGroup();
+}
+
+TEST_F(DisplayTest_2_4, HotplugThreeConfigsMixedGroups) {
+ Test_HotplugThreeConfigsMixedGroups();
+}
+
+TEST_F(DisplayTest_2_4, HotplugPrimaryOneConfig) {
+ Test_HotplugPrimaryDisplay();
}
////////////////////////////////////////////////
+template <typename FakeComposerService>
class TransactionTest : public ::testing::Test {
protected:
// Layer array indexing constants.
constexpr static int BG_LAYER = 0;
constexpr static int FG_LAYER = 1;
- static void SetUpTestCase();
- static void TearDownTestCase();
+ static void SetUpTestCase() {
+ // TODO: See TODO comment at DisplayTest::SetUp for background on
+ // the lifetime of the FakeComposerClient.
+ sFakeComposer = new FakeComposerClient;
+ sp<V2_4::hal::ComposerClient> client = new V2_4::hal::ComposerClient(sFakeComposer);
+ sp<V2_1::IComposer> fakeService = new FakeComposerService(client);
+ (void)fakeService->registerAsService("mock");
- void SetUp() override;
- void TearDown() override;
+ android::hardware::ProcessState::self()->startThreadPool();
+ android::ProcessState::self()->startThreadPool();
+
+ startSurfaceFlinger();
+
+ // Fake composer wants to enable VSync injection
+ sFakeComposer->onSurfaceFlingerStart();
+ }
+
+ static void TearDownTestCase() {
+ // Fake composer needs to release SurfaceComposerClient before the stop.
+ sFakeComposer->onSurfaceFlingerStop();
+ stopSurfaceFlinger();
+ // TODO: This is deleted when the ComposerClient calls
+ // removeClient. Devise better lifetime control.
+ sFakeComposer = nullptr;
+ }
+
+ void SetUp() override {
+ ALOGI("TransactionTest::SetUp");
+ mComposerClient = new SurfaceComposerClient;
+ ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
+
+ ALOGI("TransactionTest::SetUp - display");
+ const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
+ ASSERT_FALSE(display == nullptr);
+
+ DisplayInfo info;
+ ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
+
+ mDisplayWidth = info.w;
+ mDisplayHeight = info.h;
+
+ // Background surface
+ mBGSurfaceControl =
+ mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
+ mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mBGSurfaceControl != nullptr);
+ ASSERT_TRUE(mBGSurfaceControl->isValid());
+ fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
+
+ // Foreground surface
+ mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(mFGSurfaceControl != nullptr);
+ ASSERT_TRUE(mFGSurfaceControl->isValid());
+
+ fillSurfaceRGBA8(mFGSurfaceControl, RED);
+
+ Transaction t;
+ t.setDisplayLayerStack(display, 0);
+
+ t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
+ t.show(mBGSurfaceControl);
+
+ t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
+ t.setPosition(mFGSurfaceControl, 64, 64);
+ t.show(mFGSurfaceControl);
+
+ // Synchronous transaction will stop this thread, so we set up a
+ // delayed, off-thread vsync request before closing the
+ // transaction. In the test code this is usually done with
+ // TransactionScope. Leaving here in the 'vanilla' form for
+ // reference.
+ ASSERT_EQ(0, sFakeComposer->getFrameCount());
+ sFakeComposer->runVSyncAfter(1ms);
+ t.apply();
+ sFakeComposer->waitUntilFrame(1);
+
+ // Reference data. This is what the HWC should see.
+ static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
+ mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
+ mBaseFrame[BG_LAYER].mSwapCount = 1;
+ mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+ mBaseFrame[FG_LAYER].mSwapCount = 1;
+
+ auto frame = sFakeComposer->getFrameRects(0);
+ ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+ }
+
+ void TearDown() override {
+ ALOGD("TransactionTest::TearDown");
+
+ mComposerClient->dispose();
+ mBGSurfaceControl = 0;
+ mFGSurfaceControl = 0;
+ mComposerClient = 0;
+
+ sFakeComposer->runVSyncAndWait();
+ mBaseFrame.clear();
+ sFakeComposer->clearFrames();
+ ASSERT_EQ(0, sFakeComposer->getFrameCount());
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ std::vector<LayerDebugInfo> layers;
+ status_t result = sf->getLayerDebugInfo(&layers);
+ if (result != NO_ERROR) {
+ ALOGE("Failed to get layers %s %d", strerror(-result), result);
+ } else {
+ // If this fails, the test being torn down leaked layers.
+ EXPECT_EQ(0u, layers.size());
+ if (layers.size() > 0) {
+ for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
+ std::cout << to_string(*layer).c_str();
+ }
+ // To ensure the next test has clean slate, will run the class
+ // tear down and setup here.
+ TearDownTestCase();
+ SetUpTestCase();
+ }
+ }
+ ALOGD("TransactionTest::TearDown - complete");
+ }
+
+ void Test_LayerMove() {
+ ALOGD("TransactionTest::LayerMove");
+
+ // The scope opens and closes a global transaction and, at the
+ // same time, makes sure the SurfaceFlinger progresses one frame
+ // after the transaction closes. The results of the transaction
+ // should be available in the latest frame stored by the fake
+ // composer.
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setPosition(mFGSurfaceControl, 128, 128);
+ // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
+ // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
+ //
+ // sFakeComposer->runVSyncAndWait();
+ }
+
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+
+ ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
+ // there's no extra frames.
+
+ // NOTE: Frame 0 is produced in the SetUp.
+ auto frame1Ref = mBaseFrame;
+ frame1Ref[FG_LAYER].mDisplayFrame =
+ hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
+ EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+ auto frame2Ref = frame1Ref;
+ frame2Ref[FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+ }
+
+ void Test_LayerResize() {
+ ALOGD("TransactionTest::LayerResize");
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setSize(mFGSurfaceControl, 128, 128);
+ }
+
+ fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
+ sFakeComposer->runVSyncAndWait();
+
+ ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and
+ // there's no extra frames.
+
+ auto frame1Ref = mBaseFrame;
+ // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size
+ // posted.
+ EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
+
+ auto frame2Ref = frame1Ref;
+ frame2Ref[FG_LAYER].mSwapCount++;
+ frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
+ frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
+ EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+ }
+
+ void Test_LayerCrop() {
+ // TODO: Add scaling to confirm that crop happens in buffer space?
+ {
+ TransactionScope ts(*sFakeComposer);
+ Rect cropRect(16, 16, 32, 32);
+ ts.setCrop_legacy(mFGSurfaceControl, cropRect);
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerSetLayer() {
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // The layers will switch order, but both are rendered because the background layer is
+ // transparent (RGBA8888).
+ std::vector<RenderState> referenceFrame(2);
+ referenceFrame[0] = mBaseFrame[FG_LAYER];
+ referenceFrame[1] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerSetLayerOpaque() {
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
+ ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
+ layer_state_t::eLayerOpaque);
+ }
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+
+ // The former foreground layer is now covered with opaque layer - it should have disappeared
+ std::vector<RenderState> referenceFrame(1);
+ referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_SetLayerStack() {
+ ALOGD("TransactionTest::SetLayerStack");
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setLayerStack(mFGSurfaceControl, 1);
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerShowHide() {
+ ALOGD("TransactionTest::LayerShowHide");
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.hide(mFGSurfaceControl);
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.show(mFGSurfaceControl);
+ }
+
+ // Foreground layer should be back
+ ASSERT_EQ(3, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerSetAlpha() {
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setAlpha(mFGSurfaceControl, 0.75f);
+ }
+
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerSetFlags() {
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
+ layer_state_t::eLayerHidden);
+ }
+
+ // Foreground layer should have disappeared.
+ ASSERT_EQ(2, sFakeComposer->getFrameCount());
+ std::vector<RenderState> refFrame(1);
+ refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
+ EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerSetMatrix() {
+ struct matrixTestData {
+ float matrix[4];
+ hwc_transform_t expectedTransform;
+ hwc_rect_t expectedDisplayFrame;
+ };
+
+ // The matrix operates on the display frame and is applied before
+ // the position is added. So, the foreground layer rect is (0, 0,
+ // 64, 64) is first transformed, potentially yielding negative
+ // coordinates and then the position (64, 64) is added yielding
+ // the final on-screen rectangles given.
+
+ const matrixTestData MATRIX_TESTS[7] = // clang-format off
+ {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}},
+ {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}},
+ {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}},
+ {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}},
+ {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}},
+ {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}},
+ {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}};
+ // clang-format on
+ constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
+
+ for (int i = 0; i < TEST_COUNT; i++) {
+ // TODO: How to leverage the HWC2 stringifiers?
+ const matrixTestData& xform = MATRIX_TESTS[i];
+ SCOPED_TRACE(i);
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1], xform.matrix[2],
+ xform.matrix[3]);
+ }
+
+ auto referenceFrame = mBaseFrame;
+ referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
+ referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
+
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+ }
+
+ void Test_DeferredTransaction() {
+ // Synchronization surface
+ constexpr static int SYNC_LAYER = 2;
+ auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
+ PIXEL_FORMAT_RGBA_8888, 0);
+ ASSERT_TRUE(syncSurfaceControl != nullptr);
+ ASSERT_TRUE(syncSurfaceControl->isValid());
+
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
+ ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
+ ts.show(syncSurfaceControl);
+ }
+ auto referenceFrame = mBaseFrame;
+ referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
+ mDisplayWidth - 1, mDisplayHeight - 1));
+ referenceFrame[SYNC_LAYER].mSwapCount = 1;
+ EXPECT_EQ(2, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // set up two deferred transactions on different frames - these should not yield composited
+ // frames
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setAlpha(mFGSurfaceControl, 0.75);
+ ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber());
+ }
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setPosition(mFGSurfaceControl, 128, 128);
+ ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
+ syncSurfaceControl->getSurface()->getNextFrameNumber() +
+ 1);
+ }
+ EXPECT_EQ(4, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // should trigger the first deferred transaction, but not the second one
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+ sFakeComposer->runVSyncAndWait();
+ EXPECT_EQ(5, sFakeComposer->getFrameCount());
+
+ referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
+ referenceFrame[SYNC_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // should show up immediately since it's not deferred
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setAlpha(mFGSurfaceControl, 1.0);
+ }
+ referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
+ EXPECT_EQ(6, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // trigger the second deferred transaction
+ fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
+ sFakeComposer->runVSyncAndWait();
+ // TODO: Compute from layer size?
+ referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
+ referenceFrame[SYNC_LAYER].mSwapCount++;
+ EXPECT_EQ(7, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_SetRelativeLayer() {
+ constexpr int RELATIVE_LAYER = 2;
+ auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64,
+ 64, PIXEL_FORMAT_RGBA_8888, 0);
+ fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
+
+ // Now we stack the surface above the foreground surface and make sure it is visible.
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setPosition(relativeSurfaceControl, 64, 64);
+ ts.show(relativeSurfaceControl);
+ ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
+ }
+ auto referenceFrame = mBaseFrame;
+ // NOTE: All three layers will be visible as the surfaces are
+ // transparent because of the RGBA format.
+ referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
+ referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
+ EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+
+ // A call to setLayer will override a call to setRelativeLayer
+ {
+ TransactionScope ts(*sFakeComposer);
+ ts.setLayer(relativeSurfaceControl, 0);
+ }
+
+ // Previous top layer will now appear at the bottom.
+ auto referenceFrame2 = mBaseFrame;
+ referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
+ EXPECT_EQ(3, sFakeComposer->getFrameCount());
+ EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+ }
sp<SurfaceComposerClient> mComposerClient;
sp<SurfaceControl> mBGSurfaceControl;
@@ -430,926 +1356,608 @@
uint32_t mDisplayWidth;
uint32_t mDisplayHeight;
- static FakeComposerClient* sFakeComposer;
+ static inline FakeComposerClient* sFakeComposer;
};
-FakeComposerClient* TransactionTest::sFakeComposer;
+using TransactionTest_2_1 = TransactionTest<FakeComposerService_2_1>;
-void TransactionTest::SetUpTestCase() {
- // TODO: See TODO comment at DisplayTest::SetUp for background on
- // the lifetime of the FakeComposerClient.
- sFakeComposer = new FakeComposerClient;
- sp<ComposerClient> client = new ComposerClient(sFakeComposer);
- sp<IComposer> fakeService = new FakeComposerService(client);
- (void)fakeService->registerAsService("mock");
-
- android::hardware::ProcessState::self()->startThreadPool();
- android::ProcessState::self()->startThreadPool();
-
- startSurfaceFlinger();
-
- // Fake composer wants to enable VSync injection
- sFakeComposer->onSurfaceFlingerStart();
+TEST_F(TransactionTest_2_1, DISABLED_LayerMove) {
+ Test_LayerMove();
}
-void TransactionTest::TearDownTestCase() {
- // Fake composer needs to release SurfaceComposerClient before the stop.
- sFakeComposer->onSurfaceFlingerStop();
- stopSurfaceFlinger();
- // TODO: This is deleted when the ComposerClient calls
- // removeClient. Devise better lifetime control.
- sFakeComposer = nullptr;
+TEST_F(TransactionTest_2_1, DISABLED_LayerResize) {
+ Test_LayerResize();
}
-void TransactionTest::SetUp() {
- ALOGI("TransactionTest::SetUp");
- mComposerClient = new SurfaceComposerClient;
- ASSERT_EQ(NO_ERROR, mComposerClient->initCheck());
-
- ALOGI("TransactionTest::SetUp - display");
- const auto display = SurfaceComposerClient::getPhysicalDisplayToken(PRIMARY_DISPLAY);
- ASSERT_FALSE(display == nullptr);
-
- DisplayInfo info;
- ASSERT_EQ(NO_ERROR, SurfaceComposerClient::getDisplayInfo(display, &info));
-
- mDisplayWidth = info.w;
- mDisplayHeight = info.h;
-
- // Background surface
- mBGSurfaceControl = mComposerClient->createSurface(String8("BG Test Surface"), mDisplayWidth,
- mDisplayHeight, PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mBGSurfaceControl != nullptr);
- ASSERT_TRUE(mBGSurfaceControl->isValid());
- fillSurfaceRGBA8(mBGSurfaceControl, BLUE);
-
- // Foreground surface
- mFGSurfaceControl = mComposerClient->createSurface(String8("FG Test Surface"), 64, 64,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(mFGSurfaceControl != nullptr);
- ASSERT_TRUE(mFGSurfaceControl->isValid());
-
- fillSurfaceRGBA8(mFGSurfaceControl, RED);
-
- Transaction t;
- t.setDisplayLayerStack(display, 0);
-
- t.setLayer(mBGSurfaceControl, INT32_MAX - 2);
- t.show(mBGSurfaceControl);
-
- t.setLayer(mFGSurfaceControl, INT32_MAX - 1);
- t.setPosition(mFGSurfaceControl, 64, 64);
- t.show(mFGSurfaceControl);
-
- // Synchronous transaction will stop this thread, so we set up a
- // delayed, off-thread vsync request before closing the
- // transaction. In the test code this is usually done with
- // TransactionScope. Leaving here in the 'vanilla' form for
- // reference.
- ASSERT_EQ(0, sFakeComposer->getFrameCount());
- sFakeComposer->runVSyncAfter(1ms);
- t.apply();
- sFakeComposer->waitUntilFrame(1);
-
- // Reference data. This is what the HWC should see.
- static_assert(BG_LAYER == 0 && FG_LAYER == 1, "Unexpected enum values for array indexing");
- mBaseFrame.push_back(makeSimpleRect(0u, 0u, mDisplayWidth, mDisplayHeight));
- mBaseFrame[BG_LAYER].mSwapCount = 1;
- mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
- mBaseFrame[FG_LAYER].mSwapCount = 1;
-
- auto frame = sFakeComposer->getFrameRects(0);
- ASSERT_TRUE(framesAreSame(mBaseFrame, frame));
+TEST_F(TransactionTest_2_1, DISABLED_LayerCrop) {
+ Test_LayerCrop();
}
-void TransactionTest::TearDown() {
- ALOGD("TransactionTest::TearDown");
-
- mComposerClient->dispose();
- mBGSurfaceControl = 0;
- mFGSurfaceControl = 0;
- mComposerClient = 0;
-
- sFakeComposer->runVSyncAndWait();
- mBaseFrame.clear();
- sFakeComposer->clearFrames();
- ASSERT_EQ(0, sFakeComposer->getFrameCount());
-
- sp<ISurfaceComposer> sf(ComposerService::getComposerService());
- std::vector<LayerDebugInfo> layers;
- status_t result = sf->getLayerDebugInfo(&layers);
- if (result != NO_ERROR) {
- ALOGE("Failed to get layers %s %d", strerror(-result), result);
- } else {
- // If this fails, the test being torn down leaked layers.
- EXPECT_EQ(0u, layers.size());
- if (layers.size() > 0) {
- for (auto layer = layers.begin(); layer != layers.end(); ++layer) {
- std::cout << to_string(*layer).c_str();
- }
- // To ensure the next test has clean slate, will run the class
- // tear down and setup here.
- TearDownTestCase();
- SetUpTestCase();
- }
- }
- ALOGD("TransactionTest::TearDown - complete");
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayer) {
+ Test_LayerSetLayer();
}
-TEST_F(TransactionTest, LayerMove) {
- ALOGD("TransactionTest::LayerMove");
-
- // The scope opens and closes a global transaction and, at the
- // same time, makes sure the SurfaceFlinger progresses one frame
- // after the transaction closes. The results of the transaction
- // should be available in the latest frame stored by the fake
- // composer.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 128, 128);
- // NOTE: No changes yet, so vsync will do nothing, HWC does not get any calls.
- // (How to verify that? Throw in vsync and wait a 2x frame time? Separate test?)
- //
- // sFakeComposer->runVSyncAndWait();
- }
-
- fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
- sFakeComposer->runVSyncAndWait();
-
- ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
- // no extra frames.
-
- // NOTE: Frame 0 is produced in the SetUp.
- auto frame1Ref = mBaseFrame;
- frame1Ref[FG_LAYER].mDisplayFrame =
- hwc_rect_t{128, 128, 128 + 64, 128 + 64}; // Top-most layer moves.
- EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
- auto frame2Ref = frame1Ref;
- frame2Ref[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetLayerOpaque) {
+ Test_LayerSetLayerOpaque();
}
-TEST_F(TransactionTest, LayerResize) {
- ALOGD("TransactionTest::LayerResize");
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- }
-
- fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
- sFakeComposer->runVSyncAndWait();
-
- ASSERT_EQ(3, sFakeComposer->getFrameCount()); // Make sure the waits didn't time out and there's
- // no extra frames.
-
- auto frame1Ref = mBaseFrame;
- // NOTE: The resize should not be visible for frame 1 as there's no buffer with new size posted.
- EXPECT_TRUE(framesAreSame(frame1Ref, sFakeComposer->getFrameRects(1)));
-
- auto frame2Ref = frame1Ref;
- frame2Ref[FG_LAYER].mSwapCount++;
- frame2Ref[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 128, 64 + 128};
- frame2Ref[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 128.f, 128.f};
- EXPECT_TRUE(framesAreSame(frame2Ref, sFakeComposer->getFrameRects(2)));
+TEST_F(TransactionTest_2_1, DISABLED_SetLayerStack) {
+ Test_SetLayerStack();
}
-TEST_F(TransactionTest, LayerCrop) {
- // TODO: Add scaling to confirm that crop happens in buffer space?
- {
- TransactionScope ts(*sFakeComposer);
- Rect cropRect(16, 16, 32, 32);
- ts.setCrop_legacy(mFGSurfaceControl, cropRect);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{16.f, 16.f, 32.f, 32.f};
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64 + 16, 64 + 16, 64 + 32, 64 + 32};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerShowHide) {
+ Test_LayerShowHide();
}
-TEST_F(TransactionTest, LayerSetLayer) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- // The layers will switch order, but both are rendered because the background layer is
- // transparent (RGBA8888).
- std::vector<RenderState> referenceFrame(2);
- referenceFrame[0] = mBaseFrame[FG_LAYER];
- referenceFrame[1] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetAlpha) {
+ Test_LayerSetAlpha();
}
-TEST_F(TransactionTest, LayerSetLayerOpaque) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(mFGSurfaceControl, INT_MAX - 3);
- ts.setFlags(mBGSurfaceControl, layer_state_t::eLayerOpaque,
- layer_state_t::eLayerOpaque);
- }
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
-
- // The former foreground layer is now covered with opaque layer - it should have disappeared
- std::vector<RenderState> referenceFrame(1);
- referenceFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetFlags) {
+ Test_LayerSetFlags();
}
-TEST_F(TransactionTest, SetLayerStack) {
- ALOGD("TransactionTest::SetLayerStack");
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayerStack(mFGSurfaceControl, 1);
- }
-
- // Foreground layer should have disappeared.
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- std::vector<RenderState> refFrame(1);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_LayerSetMatrix) {
+ Test_LayerSetMatrix();
}
-TEST_F(TransactionTest, LayerShowHide) {
- ALOGD("TransactionTest::LayerShowHide");
- {
- TransactionScope ts(*sFakeComposer);
- ts.hide(mFGSurfaceControl);
- }
-
- // Foreground layer should have disappeared.
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- std::vector<RenderState> refFrame(1);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mFGSurfaceControl);
- }
-
- // Foreground layer should be back
- ASSERT_EQ(3, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_DeferredTransaction) {
+ Test_DeferredTransaction();
}
-TEST_F(TransactionTest, LayerSetAlpha) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 0.75f);
- }
-
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(TransactionTest_2_1, DISABLED_SetRelativeLayer) {
+ Test_SetRelativeLayer();
}
-TEST_F(TransactionTest, LayerSetFlags) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setFlags(mFGSurfaceControl, layer_state_t::eLayerHidden,
- layer_state_t::eLayerHidden);
- }
+template <typename FakeComposerService>
+class ChildLayerTest : public TransactionTest<FakeComposerService> {
+ using Base = TransactionTest<FakeComposerService>;
- // Foreground layer should have disappeared.
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- std::vector<RenderState> refFrame(1);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, LayerSetMatrix) {
- struct matrixTestData {
- float matrix[4];
- hwc_transform_t expectedTransform;
- hwc_rect_t expectedDisplayFrame;
- };
-
- // The matrix operates on the display frame and is applied before
- // the position is added. So, the foreground layer rect is (0, 0,
- // 64, 64) is first transformed, potentially yielding negative
- // coordinates and then the position (64, 64) is added yielding
- // the final on-screen rectangles given.
-
- const matrixTestData MATRIX_TESTS[7] = // clang-format off
- {{{-1.f, 0.f, 0.f, 1.f}, HWC_TRANSFORM_FLIP_H, {0, 64, 64, 128}},
- {{1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_FLIP_V, {64, 0, 128, 64}},
- {{0.f, 1.f, -1.f, 0.f}, HWC_TRANSFORM_ROT_90, {0, 64, 64, 128}},
- {{-1.f, 0.f, 0.f, -1.f}, HWC_TRANSFORM_ROT_180, {0, 0, 64, 64}},
- {{0.f, -1.f, 1.f, 0.f}, HWC_TRANSFORM_ROT_270, {64, 0, 128, 64}},
- {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}},
- {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}};
- // clang-format on
- constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData);
-
- for (int i = 0; i < TEST_COUNT; i++) {
- // TODO: How to leverage the HWC2 stringifiers?
- const matrixTestData& xform = MATRIX_TESTS[i];
- SCOPED_TRACE(i);
- {
- TransactionScope ts(*sFakeComposer);
- ts.setMatrix(mFGSurfaceControl, xform.matrix[0], xform.matrix[1],
- xform.matrix[2], xform.matrix[3]);
- }
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mTransform = xform.expectedTransform;
- referenceFrame[FG_LAYER].mDisplayFrame = xform.expectedDisplayFrame;
-
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
- }
-}
-
-#if 0
-TEST_F(TransactionTest, LayerSetMatrix2) {
- {
- TransactionScope ts(*sFakeComposer);
- // TODO: PLEASE SPEC THE FUNCTION!
- ts.setMatrix(mFGSurfaceControl, 0.11f, 0.123f,
- -2.33f, 0.22f);
- }
- auto referenceFrame = mBaseFrame;
- // TODO: Is this correct for sure?
- //referenceFrame[FG_LAYER].mTransform = HWC_TRANSFORM_FLIP_V & HWC_TRANSFORM_ROT_90;
-
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-#endif
-
-TEST_F(TransactionTest, DeferredTransaction) {
- // Synchronization surface
- constexpr static int SYNC_LAYER = 2;
- auto syncSurfaceControl = mComposerClient->createSurface(String8("Sync Test Surface"), 1, 1,
- PIXEL_FORMAT_RGBA_8888, 0);
- ASSERT_TRUE(syncSurfaceControl != nullptr);
- ASSERT_TRUE(syncSurfaceControl->isValid());
-
- fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(syncSurfaceControl, INT32_MAX - 1);
- ts.setPosition(syncSurfaceControl, mDisplayWidth - 2, mDisplayHeight - 2);
- ts.show(syncSurfaceControl);
- }
- auto referenceFrame = mBaseFrame;
- referenceFrame.push_back(makeSimpleRect(mDisplayWidth - 2, mDisplayHeight - 2,
- mDisplayWidth - 1, mDisplayHeight - 1));
- referenceFrame[SYNC_LAYER].mSwapCount = 1;
- EXPECT_EQ(2, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // set up two deferred transactions on different frames - these should not yield composited
- // frames
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 0.75);
- ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
- syncSurfaceControl->getSurface()->getNextFrameNumber());
- }
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 128, 128);
- ts.deferTransactionUntil_legacy(mFGSurfaceControl, syncSurfaceControl->getHandle(),
- syncSurfaceControl->getSurface()->getNextFrameNumber() + 1);
- }
- EXPECT_EQ(4, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // should trigger the first deferred transaction, but not the second one
- fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
- sFakeComposer->runVSyncAndWait();
- EXPECT_EQ(5, sFakeComposer->getFrameCount());
-
- referenceFrame[FG_LAYER].mPlaneAlpha = 0.75f;
- referenceFrame[SYNC_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // should show up immediately since it's not deferred
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 1.0);
- }
- referenceFrame[FG_LAYER].mPlaneAlpha = 1.f;
- EXPECT_EQ(6, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // trigger the second deferred transaction
- fillSurfaceRGBA8(syncSurfaceControl, DARK_GRAY);
- sFakeComposer->runVSyncAndWait();
- // TODO: Compute from layer size?
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{128, 128, 128 + 64, 128 + 64};
- referenceFrame[SYNC_LAYER].mSwapCount++;
- EXPECT_EQ(7, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-}
-
-TEST_F(TransactionTest, SetRelativeLayer) {
- constexpr int RELATIVE_LAYER = 2;
- auto relativeSurfaceControl = mComposerClient->createSurface(String8("Test Surface"), 64, 64,
- PIXEL_FORMAT_RGBA_8888, 0);
- fillSurfaceRGBA8(relativeSurfaceControl, LIGHT_RED);
-
- // Now we stack the surface above the foreground surface and make sure it is visible.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(relativeSurfaceControl, 64, 64);
- ts.show(relativeSurfaceControl);
- ts.setRelativeLayer(relativeSurfaceControl, mFGSurfaceControl->getHandle(), 1);
- }
- auto referenceFrame = mBaseFrame;
- // NOTE: All three layers will be visible as the surfaces are
- // transparent because of the RGBA format.
- referenceFrame.push_back(makeSimpleRect(64, 64, 64 + 64, 64 + 64));
- referenceFrame[RELATIVE_LAYER].mSwapCount = 1;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- // A call to setLayer will override a call to setRelativeLayer
- {
- TransactionScope ts(*sFakeComposer);
- ts.setLayer(relativeSurfaceControl, 0);
- }
-
- // Previous top layer will now appear at the bottom.
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2.insert(referenceFrame2.begin(), referenceFrame[RELATIVE_LAYER]);
- EXPECT_EQ(3, sFakeComposer->getFrameCount());
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
-}
-
-class ChildLayerTest : public TransactionTest {
protected:
constexpr static int CHILD_LAYER = 2;
void SetUp() override {
- TransactionTest::SetUp();
- mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ Base::SetUp();
+ mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0,
+ Base::mFGSurfaceControl.get());
fillSurfaceRGBA8(mChild, LIGHT_GRAY);
- sFakeComposer->runVSyncAndWait();
- mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
- mBaseFrame[CHILD_LAYER].mSwapCount = 1;
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+ Base::sFakeComposer->runVSyncAndWait();
+ Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+ Base::mBaseFrame[CHILD_LAYER].mSwapCount = 1;
+ ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
+ ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
}
+
void TearDown() override {
mChild = 0;
- TransactionTest::TearDown();
+ Base::TearDown();
+ }
+
+ void Test_Positioning() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 10, 10);
+ // Move to the same position as in the original setup.
+ ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+ }
+
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ }
+
+ auto referenceFrame2 = Base::mBaseFrame;
+ referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_Cropping() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 0, 0);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 5, 5));
+ }
+ // NOTE: The foreground surface would be occluded by the child
+ // now, but is included in the stack because the child is
+ // transparent.
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_Constraints() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ ts.setPosition(mChild, 63, 63);
+ }
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_Scaling() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ }
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setMatrix(Base::mFGSurfaceControl, 2.0, 0, 0, 2.0);
+ }
+
+ auto referenceFrame2 = Base::mBaseFrame;
+ referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerAlpha() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 0, 0);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ ts.setAlpha(mChild, 0.5);
+ }
+
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setAlpha(Base::mFGSurfaceControl, 0.5);
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
+ referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_ReparentChildren() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 10, 10);
+ ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+ }
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.reparentChildren(Base::mFGSurfaceControl, Base::mBGSurfaceControl->getHandle());
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_DetachChildrenSameClient() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 10, 10);
+ ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+ }
+
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ ts.detachChildren(Base::mFGSurfaceControl);
+ }
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+ ts.hide(mChild);
+ }
+
+ std::vector<RenderState> refFrame(2);
+ refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
+ refFrame[Base::FG_LAYER] = Base::mBaseFrame[Base::FG_LAYER];
+
+ EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_DetachChildrenDifferentClient() {
+ sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
+ sp<SurfaceControl> childNewClient =
+ newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0,
+ Base::mFGSurfaceControl.get());
+ ASSERT_TRUE(childNewClient != nullptr);
+ ASSERT_TRUE(childNewClient->isValid());
+ fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.hide(mChild);
+ ts.show(childNewClient);
+ ts.setPosition(childNewClient, 10, 10);
+ ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+ }
+
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
+ referenceFrame[CHILD_LAYER].mDisplayFrame =
+ hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.detachChildren(Base::mFGSurfaceControl);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ }
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+ ts.setPosition(childNewClient, 0, 0);
+ ts.hide(childNewClient);
+ }
+
+ // Nothing should have changed. The child control becomes a no-op
+ // zombie on detach. See comments for detachChildren in the
+ // SurfaceControl.h file.
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_InheritNonTransformScalingFromParent() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 0, 0);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ }
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setOverrideScalingMode(Base::mFGSurfaceControl,
+ NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ // We cause scaling by 2.
+ ts.setSize(Base::mFGSurfaceControl, 128, 128);
+ }
+
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
+ referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
+ referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ // Regression test for b/37673612
+ void Test_ChildrenWithParentBufferTransform() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(mChild);
+ ts.setPosition(mChild, 0, 0);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ }
+
+ // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
+ // the WM specified state size.
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setSize(Base::mFGSurfaceControl, 128, 64);
+ }
+
+ sp<Surface> s = Base::mFGSurfaceControl->getSurface();
+ auto anw = static_cast<ANativeWindow*>(s.get());
+ native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
+ native_window_set_buffers_dimensions(anw, 64, 128);
+ fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+ Base::sFakeComposer->runVSyncAndWait();
+
+ // The child should still be in the same place and not have any strange scaling as in
+ // b/37673612.
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
+ referenceFrame[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
+ referenceFrame[Base::FG_LAYER].mSwapCount++;
+ referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_Bug36858924() {
+ // Destroy the child layer
+ mChild.clear();
+
+ // Now recreate it as hidden
+ mChild = Base::mComposerClient->createSurface(String8("Child surface"), 10, 10,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eHidden,
+ Base::mFGSurfaceControl.get());
+
+ // Show the child layer in a deferred transaction
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.deferTransactionUntil_legacy(mChild, Base::mFGSurfaceControl->getHandle(),
+ Base::mFGSurfaceControl->getSurface()
+ ->getNextFrameNumber());
+ ts.show(mChild);
+ }
+
+ // Render the foreground surface a few times
+ //
+ // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the
+ // third frame because SurfaceFlinger would never process the deferred transaction and would
+ // therefore never acquire/release the first buffer
+ ALOGI("Filling 1");
+ fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
+ Base::sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 2");
+ fillSurfaceRGBA8(Base::mFGSurfaceControl, BLUE);
+ Base::sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 3");
+ fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+ Base::sFakeComposer->runVSyncAndWait();
+ ALOGI("Filling 4");
+ fillSurfaceRGBA8(Base::mFGSurfaceControl, GREEN);
+ Base::sFakeComposer->runVSyncAndWait();
}
sp<SurfaceControl> mChild;
};
-TEST_F(ChildLayerTest, Positioning) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 10, 10);
- // Move to the same position as in the original setup.
- ts.setPosition(mFGSurfaceControl, 64, 64);
- }
+using ChildLayerTest_2_1 = ChildLayerTest<FakeComposerService_2_1>;
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- }
-
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 64, 0 + 64};
- referenceFrame2[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{0 + 10, 0 + 10, 0 + 10 + 10, 0 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Positioning) {
+ Test_Positioning();
}
-TEST_F(ChildLayerTest, Cropping) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 5, 5));
- }
- // NOTE: The foreground surface would be occluded by the child
- // now, but is included in the stack because the child is
- // transparent.
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
- referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 0 + 5, 0 + 5};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 5.f, 5.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Cropping) {
+ Test_Cropping();
}
-TEST_F(ChildLayerTest, Constraints) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.setPosition(mChild, 63, 63);
- }
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{63, 63, 64, 64};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 1.f, 1.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Constraints) {
+ Test_Constraints();
}
-TEST_F(ChildLayerTest, Scaling) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- }
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setMatrix(mFGSurfaceControl, 2.0, 0, 0, 2.0);
- }
-
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
- referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_Scaling) {
+ Test_Scaling();
}
-TEST_F(ChildLayerTest, LayerAlpha) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.setAlpha(mChild, 0.5);
- }
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 0.5);
- }
-
- auto referenceFrame2 = referenceFrame;
- referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
- referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_LayerAlpha) {
+ Test_LayerAlpha();
}
-TEST_F(ChildLayerTest, ReparentChildren) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 10, 10);
- ts.setPosition(mFGSurfaceControl, 64, 64);
- }
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.reparentChildren(mFGSurfaceControl, mBGSurfaceControl->getHandle());
- }
-
- auto referenceFrame2 = referenceFrame;
- referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame2[CHILD_LAYER].mDisplayFrame = hwc_rect_t{10, 10, 10 + 10, 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_ReparentChildren) {
+ Test_ReparentChildren();
}
-TEST_F(ChildLayerTest, DetachChildrenSameClient) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 10, 10);
- ts.setPosition(mFGSurfaceControl, 64, 64);
- }
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.detachChildren(mFGSurfaceControl);
- }
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 64, 64);
- ts.hide(mChild);
- }
-
- std::vector<RenderState> refFrame(2);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
- refFrame[FG_LAYER] = mBaseFrame[FG_LAYER];
-
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenSameClient) {
+ Test_DetachChildrenSameClient();
}
-TEST_F(ChildLayerTest, DetachChildrenDifferentClient) {
- sp<SurfaceComposerClient> newComposerClient = new SurfaceComposerClient;
- sp<SurfaceControl> childNewClient =
- newComposerClient->createSurface(String8("New Child Test Surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
- ASSERT_TRUE(childNewClient != nullptr);
- ASSERT_TRUE(childNewClient->isValid());
- fillSurfaceRGBA8(childNewClient, LIGHT_GRAY);
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.hide(mChild);
- ts.show(childNewClient);
- ts.setPosition(childNewClient, 10, 10);
- ts.setPosition(mFGSurfaceControl, 64, 64);
- }
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 64, 64 + 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame =
- hwc_rect_t{64 + 10, 64 + 10, 64 + 10 + 10, 64 + 10 + 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.detachChildren(mFGSurfaceControl);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- }
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setPosition(mFGSurfaceControl, 64, 64);
- ts.setPosition(childNewClient, 0, 0);
- ts.hide(childNewClient);
- }
-
- // Nothing should have changed. The child control becomes a no-op
- // zombie on detach. See comments for detachChildren in the
- // SurfaceControl.h file.
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_DetachChildrenDifferentClient) {
+ Test_DetachChildrenDifferentClient();
}
-TEST_F(ChildLayerTest, InheritNonTransformScalingFromParent) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- }
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- // We cause scaling by 2.
- ts.setSize(mFGSurfaceControl, 128, 128);
- }
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 128};
- referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 64.f};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 20, 20};
- referenceFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 10.f, 10.f};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_InheritNonTransformScalingFromParent) {
+ Test_InheritNonTransformScalingFromParent();
}
// Regression test for b/37673612
-TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- }
-
- // We set things up as in b/37673612 so that there is a mismatch between the buffer size and
- // the WM specified state size.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 64);
- }
-
- sp<Surface> s = mFGSurfaceControl->getSurface();
- auto anw = static_cast<ANativeWindow*>(s.get());
- native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90);
- native_window_set_buffers_dimensions(anw, 64, 128);
- fillSurfaceRGBA8(mFGSurfaceControl, RED);
- sFakeComposer->runVSyncAndWait();
-
- // The child should still be in the same place and not have any strange scaling as in
- // b/37673612.
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 128, 64};
- referenceFrame[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 64.f, 128.f};
- referenceFrame[FG_LAYER].mSwapCount++;
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildLayerTest_2_1, DISABLED_ChildrenWithParentBufferTransform) {
+ Test_ChildrenWithParentBufferTransform();
}
-TEST_F(ChildLayerTest, Bug36858924) {
- // Destroy the child layer
- mChild.clear();
-
- // Now recreate it as hidden
- mChild = mComposerClient->createSurface(String8("Child surface"), 10, 10,
- PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eHidden,
- mFGSurfaceControl.get());
-
- // Show the child layer in a deferred transaction
- {
- TransactionScope ts(*sFakeComposer);
- ts.deferTransactionUntil_legacy(mChild, mFGSurfaceControl->getHandle(),
- mFGSurfaceControl->getSurface()->getNextFrameNumber());
- ts.show(mChild);
- }
-
- // Render the foreground surface a few times
- //
- // Prior to the bugfix for b/36858924, this would usually hang while trying to fill the third
- // frame because SurfaceFlinger would never process the deferred transaction and would therefore
- // never acquire/release the first buffer
- ALOGI("Filling 1");
- fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
- sFakeComposer->runVSyncAndWait();
- ALOGI("Filling 2");
- fillSurfaceRGBA8(mFGSurfaceControl, BLUE);
- sFakeComposer->runVSyncAndWait();
- ALOGI("Filling 3");
- fillSurfaceRGBA8(mFGSurfaceControl, RED);
- sFakeComposer->runVSyncAndWait();
- ALOGI("Filling 4");
- fillSurfaceRGBA8(mFGSurfaceControl, GREEN);
- sFakeComposer->runVSyncAndWait();
+TEST_F(ChildLayerTest_2_1, DISABLED_Bug36858924) {
+ Test_Bug36858924();
}
-class ChildColorLayerTest : public ChildLayerTest {
+template <typename FakeComposerService>
+class ChildColorLayerTest : public ChildLayerTest<FakeComposerService> {
+ using Base = ChildLayerTest<FakeComposerService>;
+
protected:
void SetUp() override {
- TransactionTest::SetUp();
- mChild = mComposerClient->createSurface(String8("Child surface"), 0, 0,
- PIXEL_FORMAT_RGBA_8888,
- ISurfaceComposerClient::eFXSurfaceColor,
- mFGSurfaceControl.get());
+ Base::SetUp();
+ Base::mChild = Base::mComposerClient->createSurface(String8("Child surface"), 0, 0,
+ PIXEL_FORMAT_RGBA_8888,
+ ISurfaceComposerClient::eFXSurfaceColor,
+ Base::mFGSurfaceControl.get());
{
- TransactionScope ts(*sFakeComposer);
- ts.setColor(mChild,
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setColor(Base::mChild,
{LIGHT_GRAY.r / 255.0f, LIGHT_GRAY.g / 255.0f, LIGHT_GRAY.b / 255.0f});
- ts.setCrop_legacy(mChild, Rect(0, 0, 10, 10));
+ ts.setCrop_legacy(Base::mChild, Rect(0, 0, 10, 10));
}
- sFakeComposer->runVSyncAndWait();
- mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
- mBaseFrame[CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
- mBaseFrame[CHILD_LAYER].mSwapCount = 0;
- ASSERT_EQ(2, sFakeComposer->getFrameCount());
- ASSERT_TRUE(framesAreSame(mBaseFrame, sFakeComposer->getLatestFrame()));
+ Base::sFakeComposer->runVSyncAndWait();
+ Base::mBaseFrame.push_back(makeSimpleRect(64, 64, 64 + 10, 64 + 10));
+ Base::mBaseFrame[Base::CHILD_LAYER].mSourceCrop = hwc_frect_t{0.0f, 0.0f, 0.0f, 0.0f};
+ Base::mBaseFrame[Base::CHILD_LAYER].mSwapCount = 0;
+ ASSERT_EQ(2, Base::sFakeComposer->getFrameCount());
+ ASSERT_TRUE(framesAreSame(Base::mBaseFrame, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerAlpha() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(Base::mChild);
+ ts.setPosition(Base::mChild, 0, 0);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ ts.setAlpha(Base::mChild, 0.5);
+ }
+
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setAlpha(Base::mFGSurfaceControl, 0.5);
+ }
+
+ auto referenceFrame2 = referenceFrame;
+ referenceFrame2[Base::FG_LAYER].mPlaneAlpha = 0.5f;
+ referenceFrame2[Base::CHILD_LAYER].mPlaneAlpha = 0.25f;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_LayerZeroAlpha() {
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.show(Base::mChild);
+ ts.setPosition(Base::mChild, 0, 0);
+ ts.setPosition(Base::mFGSurfaceControl, 0, 0);
+ ts.setAlpha(Base::mChild, 0.5);
+ }
+
+ auto referenceFrame = Base::mBaseFrame;
+ referenceFrame[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
+ referenceFrame[Base::CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
+ referenceFrame[Base::CHILD_LAYER].mPlaneAlpha = 0.5f;
+ EXPECT_TRUE(framesAreSame(referenceFrame, Base::sFakeComposer->getLatestFrame()));
+
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setAlpha(Base::mFGSurfaceControl, 0.0f);
+ }
+
+ std::vector<RenderState> refFrame(1);
+ refFrame[Base::BG_LAYER] = Base::mBaseFrame[Base::BG_LAYER];
+
+ EXPECT_TRUE(framesAreSame(refFrame, Base::sFakeComposer->getLatestFrame()));
}
};
-TEST_F(ChildColorLayerTest, LayerAlpha) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.setAlpha(mChild, 0.5);
- }
+using ChildColorLayerTest_2_1 = ChildColorLayerTest<FakeComposerService_2_1>;
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 0.5);
- }
-
- auto referenceFrame2 = referenceFrame;
- referenceFrame2[FG_LAYER].mPlaneAlpha = 0.5f;
- referenceFrame2[CHILD_LAYER].mPlaneAlpha = 0.25f;
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerAlpha) {
+ Test_LayerAlpha();
}
-TEST_F(ChildColorLayerTest, LayerZeroAlpha) {
- {
- TransactionScope ts(*sFakeComposer);
- ts.show(mChild);
- ts.setPosition(mChild, 0, 0);
- ts.setPosition(mFGSurfaceControl, 0, 0);
- ts.setAlpha(mChild, 0.5);
- }
-
- auto referenceFrame = mBaseFrame;
- referenceFrame[FG_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 64, 64};
- referenceFrame[CHILD_LAYER].mDisplayFrame = hwc_rect_t{0, 0, 10, 10};
- referenceFrame[CHILD_LAYER].mPlaneAlpha = 0.5f;
- EXPECT_TRUE(framesAreSame(referenceFrame, sFakeComposer->getLatestFrame()));
-
- {
- TransactionScope ts(*sFakeComposer);
- ts.setAlpha(mFGSurfaceControl, 0.0f);
- }
-
- std::vector<RenderState> refFrame(1);
- refFrame[BG_LAYER] = mBaseFrame[BG_LAYER];
-
- EXPECT_TRUE(framesAreSame(refFrame, sFakeComposer->getLatestFrame()));
+TEST_F(ChildColorLayerTest_2_1, DISABLED_LayerZeroAlpha) {
+ Test_LayerZeroAlpha();
}
-class LatchingTest : public TransactionTest {
+template <typename FakeComposerService>
+class LatchingTest : public TransactionTest<FakeComposerService> {
+ using Base = TransactionTest<FakeComposerService>;
+
protected:
- void lockAndFillFGBuffer() { fillSurfaceRGBA8(mFGSurfaceControl, RED, false); }
+ void lockAndFillFGBuffer() { fillSurfaceRGBA8(Base::mFGSurfaceControl, RED, false); }
void unlockFGBuffer() {
- sp<Surface> s = mFGSurfaceControl->getSurface();
+ sp<Surface> s = Base::mFGSurfaceControl->getSurface();
ASSERT_EQ(NO_ERROR, s->unlockAndPost());
- sFakeComposer->runVSyncAndWait();
+ Base::sFakeComposer->runVSyncAndWait();
}
void completeFGResize() {
- fillSurfaceRGBA8(mFGSurfaceControl, RED);
- sFakeComposer->runVSyncAndWait();
+ fillSurfaceRGBA8(Base::mFGSurfaceControl, RED);
+ Base::sFakeComposer->runVSyncAndWait();
}
void restoreInitialState() {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 64, 64);
- ts.setPosition(mFGSurfaceControl, 64, 64);
- ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 64, 64));
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setSize(Base::mFGSurfaceControl, 64, 64);
+ ts.setPosition(Base::mFGSurfaceControl, 64, 64);
+ ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 64, 64));
+ }
+
+ void Test_SurfacePositionLatching() {
+ // By default position can be updated even while
+ // a resize is pending.
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setSize(Base::mFGSurfaceControl, 32, 32);
+ ts.setPosition(Base::mFGSurfaceControl, 100, 100);
+ }
+
+ // The size should not have updated as we have not provided a new buffer.
+ auto referenceFrame1 = Base::mBaseFrame;
+ referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ completeFGResize();
+
+ auto referenceFrame2 = Base::mBaseFrame;
+ referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
+ referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
+ referenceFrame2[Base::FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
+ }
+
+ void Test_CropLatching() {
+ // Normally the crop applies immediately even while a resize is pending.
+ {
+ TransactionScope ts(*Base::sFakeComposer);
+ ts.setSize(Base::mFGSurfaceControl, 128, 128);
+ ts.setCrop_legacy(Base::mFGSurfaceControl, Rect(0, 0, 63, 63));
+ }
+
+ auto referenceFrame1 = Base::mBaseFrame;
+ referenceFrame1[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+ referenceFrame1[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+ EXPECT_TRUE(framesAreSame(referenceFrame1, Base::sFakeComposer->getLatestFrame()));
+
+ restoreInitialState();
+
+ completeFGResize();
+
+ auto referenceFrame2 = Base::mBaseFrame;
+ referenceFrame2[Base::FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
+ referenceFrame2[Base::FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
+ referenceFrame2[Base::FG_LAYER].mSwapCount++;
+ EXPECT_TRUE(framesAreSame(referenceFrame2, Base::sFakeComposer->getLatestFrame()));
}
};
-TEST_F(LatchingTest, SurfacePositionLatching) {
- // By default position can be updated even while
- // a resize is pending.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 32, 32);
- ts.setPosition(mFGSurfaceControl, 100, 100);
- }
+using LatchingTest_2_1 = LatchingTest<FakeComposerService_2_1>;
- // The size should not have updated as we have not provided a new buffer.
- auto referenceFrame1 = mBaseFrame;
- referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 64, 100 + 64};
- EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
- restoreInitialState();
-
- completeFGResize();
-
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{100, 100, 100 + 32, 100 + 32};
- referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 32.f, 32.f};
- referenceFrame2[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(LatchingTest_2_1, DISABLED_SurfacePositionLatching) {
+ Test_SurfacePositionLatching();
}
-TEST_F(LatchingTest, CropLatching) {
- // Normally the crop applies immediately even while a resize is pending.
- {
- TransactionScope ts(*sFakeComposer);
- ts.setSize(mFGSurfaceControl, 128, 128);
- ts.setCrop_legacy(mFGSurfaceControl, Rect(0, 0, 63, 63));
- }
-
- auto referenceFrame1 = mBaseFrame;
- referenceFrame1[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
- referenceFrame1[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
- EXPECT_TRUE(framesAreSame(referenceFrame1, sFakeComposer->getLatestFrame()));
-
- restoreInitialState();
-
- completeFGResize();
-
- auto referenceFrame2 = mBaseFrame;
- referenceFrame2[FG_LAYER].mDisplayFrame = hwc_rect_t{64, 64, 64 + 63, 64 + 63};
- referenceFrame2[FG_LAYER].mSourceCrop = hwc_frect_t{0.f, 0.f, 63.f, 63.f};
- referenceFrame2[FG_LAYER].mSwapCount++;
- EXPECT_TRUE(framesAreSame(referenceFrame2, sFakeComposer->getLatestFrame()));
+TEST_F(LatchingTest_2_1, DISABLED_CropLatching) {
+ Test_CropLatching();
}
} // namespace
@@ -1357,7 +1965,7 @@
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
- sftest::FakeHwcEnvironment* fakeEnvironment = new sftest::FakeHwcEnvironment;
+ auto* fakeEnvironment = new sftest::FakeHwcEnvironment;
::testing::AddGlobalTestEnvironment(fakeEnvironment);
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp
index ccfa6c5..68adbfc 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -45,6 +45,7 @@
"OneShotTimerTest.cpp",
"LayerHistoryTest.cpp",
"LayerMetadataTest.cpp",
+ "PhaseOffsetsTest.cpp",
"SchedulerTest.cpp",
"SchedulerUtilsTest.cpp",
"RefreshRateConfigsTest.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 04991f9..cce21ce 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -361,7 +361,7 @@
static void setupRECompositionCallExpectations(CompositionTest* test) {
EXPECT_CALL(*test->mDisplaySurface,
- prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GLES))
+ prepareFrame(compositionengine::DisplaySurface::COMPOSITION_GPU))
.Times(1);
EXPECT_CALL(*test->mDisplaySurface, getClientTargetAcquireFence())
.WillRepeatedly(ReturnRef(test->mClientTargetAcquireFence));
@@ -1154,8 +1154,10 @@
static void cleanup(CompositionTest* test) {
Layer::cleanupInjectedLayers(test);
- for (auto& hwcDisplay : test->mFlinger.mFakeHwcDisplays) {
- hwcDisplay->mutableLayers().clear();
+ for (auto& displayData : test->mFlinger.mutableHwcDisplayData()) {
+ static_cast<TestableSurfaceFlinger::HWC2Display*>(displayData.second.hwcDisplay.get())
+ ->mutableLayers()
+ .clear();
}
}
};
diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
index 0aa8cf5..2e705da 100644
--- a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp
@@ -51,7 +51,6 @@
AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder;
static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms;
- static constexpr std::chrono::nanoseconds mOffsetThresholdForNextVsync = 16ms;
static constexpr int mIterations = 100;
};
@@ -79,8 +78,7 @@
void DispSyncSourceTest::createDispSyncSource() {
createDispSync();
- mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(),
- mOffsetThresholdForNextVsync.count(), true,
+ mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true,
"DispSyncSourceTest");
mDispSyncSource->setCallback(this);
}
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index da4eea0..b50ddf5 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -22,7 +22,7 @@
namespace android::scheduler {
-struct FakePhaseOffsets : PhaseOffsets {
+struct FakePhaseOffsets : PhaseConfiguration {
static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
@@ -30,8 +30,7 @@
Offsets getCurrentOffsets() const override {
return {{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
{FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- FAKE_PHASE_OFFSET_NS};
+ {FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS}};
}
void setRefreshRateFps(float) override {}
diff --git a/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
new file mode 100644
index 0000000..6360ec1
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/PhaseOffsetsTest.cpp
@@ -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.
+ */
+
+#undef LOG_TAG
+#define LOG_TAG "SchedulerUnittests"
+
+#include <gmock/gmock.h>
+#include <log/log.h>
+#include <thread>
+
+#include "Scheduler/PhaseOffsets.h"
+
+using namespace testing;
+
+namespace android {
+namespace scheduler {
+
+class TestablePhaseOffsetsAsDurations : public impl::PhaseDurations {
+public:
+ TestablePhaseOffsetsAsDurations(float currentFps, nsecs_t sfDuration, nsecs_t appDuration,
+ nsecs_t sfEarlyDuration, nsecs_t appEarlyDuration,
+ nsecs_t sfEarlyGlDuration, nsecs_t appEarlyGlDuration)
+ : impl::PhaseDurations({60.0f, 90.0f}, currentFps, sfDuration, appDuration,
+ sfEarlyDuration, appEarlyDuration, sfEarlyGlDuration,
+ appEarlyGlDuration) {}
+};
+
+class PhaseOffsetsTest : public testing::Test {
+protected:
+ PhaseOffsetsTest()
+ : mPhaseOffsets(60.0f, 10'500'000, 20'500'000, 16'000'000, 33'500'000, 13'500'000,
+ 38'000'000) {}
+
+ ~PhaseOffsetsTest() = default;
+
+ TestablePhaseOffsetsAsDurations mPhaseOffsets;
+};
+
+namespace {
+/* ------------------------------------------------------------------------
+ * Test cases
+ */
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_60Hz) {
+ mPhaseOffsets.setRefreshRateFps(60.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(60.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 6'166'667);
+
+ EXPECT_EQ(offsets.late.app, 2'333'334);
+
+ EXPECT_EQ(offsets.early.sf, 666'667);
+
+ EXPECT_EQ(offsets.early.app, 500'001);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 3'166'667);
+
+ EXPECT_EQ(offsets.earlyGl.app, 15'166'668);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_90Hz) {
+ mPhaseOffsets.setRefreshRateFps(90.0f);
+ auto currentOffsets = mPhaseOffsets.getCurrentOffsets();
+ auto offsets = mPhaseOffsets.getOffsetsForRefreshRate(90.0f);
+
+ EXPECT_EQ(currentOffsets, offsets);
+ EXPECT_EQ(offsets.late.sf, 611'111);
+
+ EXPECT_EQ(offsets.late.app, 2'333'333);
+
+ EXPECT_EQ(offsets.early.sf, -4'888'889);
+
+ EXPECT_EQ(offsets.early.app, 6'055'555);
+
+ EXPECT_EQ(offsets.earlyGl.sf, -2'388'889);
+
+ EXPECT_EQ(offsets.earlyGl.app, 4'055'555);
+}
+
+TEST_F(PhaseOffsetsTest, getOffsetsForRefreshRate_DefaultOffsets) {
+ TestablePhaseOffsetsAsDurations phaseOffsetsWithDefaultValues(60.0f, -1, -1, -1, -1, -1, -1);
+
+ auto validateOffsets = [](auto& offsets) {
+ EXPECT_EQ(offsets.late.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.late.app, 1'000'000);
+
+ EXPECT_EQ(offsets.early.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.early.app, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.sf, 1'000'000);
+
+ EXPECT_EQ(offsets.earlyGl.app, 1'000'000);
+ };
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(90.0f);
+ auto currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ auto offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+
+ phaseOffsetsWithDefaultValues.setRefreshRateFps(60.0f);
+ currentOffsets = phaseOffsetsWithDefaultValues.getCurrentOffsets();
+ offsets = phaseOffsetsWithDefaultValues.getOffsetsForRefreshRate(90.0f);
+ EXPECT_EQ(currentOffsets, offsets);
+ validateOffsets(offsets);
+}
+
+} // namespace
+} // namespace scheduler
+} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index 546e65c..ed620ef 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -79,6 +79,16 @@
ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
}
+TEST_F(RefreshRateConfigsTest, invalidPolicy) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_LT(refreshRateConfigs->setPolicy(HwcConfigIndexType(10), 60, 60, nullptr), 0);
+ ASSERT_LT(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 20, 40, nullptr), 0);
+}
+
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
std::vector<RefreshRateConfigs::InputConfig> configs{
{{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
@@ -123,7 +133,7 @@
ASSERT_EQ(expectedDefaultConfig, minRate60);
ASSERT_EQ(expectedDefaultConfig, performanceRate60);
- refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90);
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90, nullptr), 0);
refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
@@ -154,7 +164,7 @@
90};
ASSERT_EQ(expectedPerformanceConfig, performanceRate);
- refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
@@ -181,7 +191,7 @@
EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
}
- refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 90, 90);
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
{
auto current = refreshRateConfigs->getCurrentRefreshRate();
EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
@@ -207,20 +217,20 @@
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
- refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60, nullptr), 0);
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
- refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 90, 90);
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 90, 90, nullptr), 0);
ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
- refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120);
+ ASSERT_GE(refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 0, 120, nullptr), 0);
ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h
index 40c00c4..fa095aa 100644
--- a/services/surfaceflinger/tests/unittests/TestableScheduler.h
+++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h
@@ -26,17 +26,17 @@
namespace android {
-class TestableScheduler : public Scheduler {
+class TestableScheduler : public Scheduler, private ISchedulerCallback {
public:
explicit TestableScheduler(const scheduler::RefreshRateConfigs& configs)
- : Scheduler([](bool) {}, configs) {
+ : Scheduler([](bool) {}, configs, *this) {
mLayerHistory.emplace();
}
TestableScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
const scheduler::RefreshRateConfigs& configs)
- : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs) {
+ : Scheduler(std::move(primaryDispSync), std::move(eventControlThread), configs, *this) {
mLayerHistory.emplace();
}
@@ -68,6 +68,10 @@
mutablePrimaryDispSync().reset();
mConnections.clear();
}
+
+private:
+ void changeRefreshRate(const RefreshRate&, ConfigEvent) override {}
+ void repaintEverythingForHWC() override {}
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index b2210ec..b5245e2 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -79,12 +79,14 @@
return std::make_unique<android::impl::MessageQueue>();
}
- std::unique_ptr<scheduler::PhaseOffsets> createPhaseOffsets() override {
+ std::unique_ptr<scheduler::PhaseConfiguration> createPhaseConfiguration(
+ const scheduler::RefreshRateConfigs& /*refreshRateConfigs*/) override {
return std::make_unique<scheduler::FakePhaseOffsets>();
}
std::unique_ptr<Scheduler> createScheduler(std::function<void(bool)>,
- const scheduler::RefreshRateConfigs&) override {
+ const scheduler::RefreshRateConfigs&,
+ ISchedulerCallback&) override {
return nullptr;
}
@@ -206,6 +208,8 @@
scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
/*currentConfig=*/HwcConfigIndexType(0),
/*powerMode=*/HWC_POWER_MODE_OFF);
+ mFlinger->mPhaseConfiguration =
+ mFactory.createPhaseConfiguration(*mFlinger->mRefreshRateConfigs);
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
@@ -217,7 +221,7 @@
mFlinger->mVSyncModulator.emplace(*mScheduler, mFlinger->mAppConnectionHandle,
mFlinger->mSfConnectionHandle,
- mFlinger->mPhaseOffsets->getCurrentOffsets());
+ mFlinger->mPhaseConfiguration->getCurrentOffsets());
}
void resetScheduler(Scheduler* scheduler) { mFlinger->mScheduler.reset(scheduler); }
@@ -392,6 +396,11 @@
auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; }
auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; }
+ auto fromHandle(const sp<IBinder>& handle) {
+ Mutex::Autolock _l(mFlinger->mStateLock);
+ return mFlinger->fromHandle(handle);
+ }
+
~TestableSurfaceFlinger() {
// All these pointer and container clears help ensure that GMock does
// not report a leaked object, since the SurfaceFlinger instance may
@@ -509,15 +518,13 @@
display->mutableIsConnected() = true;
display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
- flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = display.get();
+ flinger->mutableHwcDisplayData()[mDisplayId].hwcDisplay = std::move(display);
if (mHwcDisplayType == HWC2::DisplayType::Physical) {
flinger->mutableHwcPhysicalDisplayIdMap().emplace(mHwcDisplayId, mDisplayId);
(mIsPrimary ? flinger->mutableInternalHwcDisplayId()
: flinger->mutableExternalHwcDisplayId()) = mHwcDisplayId;
}
-
- flinger->mFakeHwcDisplays.push_back(std::move(display));
}
private:
@@ -626,9 +633,6 @@
surfaceflinger::test::Factory mFactory;
sp<SurfaceFlinger> mFlinger = new SurfaceFlinger(mFactory, SurfaceFlinger::SkipInitialization);
TestableScheduler* mScheduler = nullptr;
-
- // We need to keep a reference to these so they are properly destroyed.
- std::vector<std::unique_ptr<HWC2Display>> mFakeHwcDisplays;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
index a465388..994a509 100644
--- a/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TransactionApplicationTest.cpp
@@ -315,4 +315,9 @@
BlockedByPriorTransaction(/*flags*/ 0, /*syncInputWindows*/ true);
}
+TEST_F(TransactionApplicationTest, FromHandle) {
+ sp<IBinder> badHandle;
+ auto ret = mFlinger.fromHandle(badHandle);
+ EXPECT_EQ(nullptr, ret.get());
+}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index 484947d..5846c77 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -92,6 +92,7 @@
struct VSyncDispatchRealtimeTest : testing::Test {
static nsecs_t constexpr mDispatchGroupThreshold = toNs(100us);
+ static nsecs_t constexpr mVsyncMoveThreshold = toNs(500us);
static size_t constexpr mIterations = 20;
};
@@ -148,7 +149,8 @@
TEST_F(VSyncDispatchRealtimeTest, triple_alarm) {
FixedRateIdealStubTracker tracker;
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold);
static size_t constexpr num_clients = 3;
std::array<RepeatingCallbackReceiver, num_clients>
@@ -176,7 +178,8 @@
TEST_F(VSyncDispatchRealtimeTest, vascillating_vrr) {
auto next_vsync_interval = toNs(3ms);
VRRStubTracker tracker(next_vsync_interval);
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold);
RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
@@ -193,7 +196,8 @@
// starts at 333hz, jumps to 200hz at frame 10
TEST_F(VSyncDispatchRealtimeTest, fixed_jump) {
VRRStubTracker tracker(toNs(3ms));
- VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold);
+ VSyncDispatchTimerQueue dispatch(std::make_unique<Timer>(), tracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold);
RepeatingCallbackReceiver cb_receiver(dispatch, toNs(1ms));
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index d668a41..5aff429 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -196,16 +196,18 @@
NiceMock<ControllableClock> mMockClock;
static nsecs_t constexpr mDispatchGroupThreshold = 5;
nsecs_t const mPeriod = 1000;
+ nsecs_t const mVsyncMoveThreshold = 300;
NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
- VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold};
+ VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold};
};
TEST_F(VSyncDispatchTimerQueueTest, unregistersSetAlarmOnDestruction) {
EXPECT_CALL(mMockClock, alarmIn(_, 900));
EXPECT_CALL(mMockClock, alarmCancel());
{
- VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker,
- mDispatchGroupThreshold};
+ VSyncDispatchTimerQueue mDispatch{createTimeKeeper(), mStubTracker, mDispatchGroupThreshold,
+ mVsyncMoveThreshold};
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::Scheduled);
}
@@ -468,14 +470,24 @@
TEST_F(VSyncDispatchTimerQueueTest, callbackReentrantWithPastWakeup) {
VSyncDispatch::CallbackToken tmp;
+ std::optional<nsecs_t> lastTarget;
tmp = mDispatch.registerCallback(
- [&](auto) {
- EXPECT_EQ(mDispatch.schedule(tmp, 400, 1000), ScheduleResult::CannotSchedule);
+ [&](auto timestamp) {
+ EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp - mVsyncMoveThreshold),
+ ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp), ScheduleResult::Scheduled);
+ EXPECT_EQ(mDispatch.schedule(tmp, 400, timestamp + mVsyncMoveThreshold),
+ ScheduleResult::Scheduled);
+ lastTarget = timestamp;
},
"oo");
mDispatch.schedule(tmp, 999, 1000);
advanceToNextCallback();
+ EXPECT_THAT(lastTarget, Eq(1000));
+
+ advanceToNextCallback();
+ EXPECT_THAT(lastTarget, Eq(2000));
}
TEST_F(VSyncDispatchTimerQueueTest, modificationsAroundVsyncTime) {
@@ -547,10 +559,33 @@
EXPECT_THAT(mDispatch.cancel(token), Eq(CancelResult::Error));
}
-TEST_F(VSyncDispatchTimerQueueTest, distinguishesScheduleAndReschedule) {
+TEST_F(VSyncDispatchTimerQueueTest, canMoveCallbackBackwardsInTime) {
CountingCallback cb0(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
- EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
+ EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::Scheduled);
+}
+
+// b/1450138150
+TEST_F(VSyncDispatchTimerQueueTest, doesNotMoveCallbackBackwardsAndSkipAScheduledTargetVSync) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 500));
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ mMockClock.advanceBy(400);
+
+ EXPECT_EQ(mDispatch.schedule(cb, 800, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ ASSERT_THAT(cb.mCalls.size(), Eq(1));
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, targetOffsetMovingBackALittleCanStillSchedule) {
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000))
+ .Times(2)
+ .WillOnce(Return(1000))
+ .WillOnce(Return(1002));
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 500, 1000), ScheduleResult::Scheduled);
+ mMockClock.advanceBy(400);
+ EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
}
TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
@@ -570,13 +605,15 @@
EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
}
-TEST_F(VSyncDispatchTimerQueueTest, cannotScheduleDoesNotAffectSchedulingState) {
+TEST_F(VSyncDispatchTimerQueueTest, scheduleUpdatesDoesNotAffectSchedulingState) {
EXPECT_CALL(mMockClock, alarmIn(_, 600));
CountingCallback cb(mDispatch);
EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+
+ EXPECT_EQ(mDispatch.schedule(cb, 1400, 1000), ScheduleResult::Scheduled);
+
advanceToNextCallback();
- EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::CannotSchedule);
}
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
@@ -612,19 +649,22 @@
class VSyncDispatchTimerQueueEntryTest : public testing::Test {
protected:
nsecs_t const mPeriod = 1000;
+ nsecs_t const mVsyncMoveThreshold = 200;
NiceMock<MockVSyncTracker> mStubTracker{mPeriod};
};
TEST_F(VSyncDispatchTimerQueueEntryTest, stateAfterInitialization) {
std::string name("basicname");
- VSyncDispatchTimerQueueEntry entry(name, [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ name, [](auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.name(), Eq(name));
EXPECT_FALSE(entry.lastExecutedVsyncTarget());
EXPECT_FALSE(entry.wakeupTime());
}
TEST_F(VSyncDispatchTimerQueueEntryTest, stateScheduling) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
@@ -643,7 +683,8 @@
EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(now + duration))
.Times(1)
.WillOnce(Return(10000));
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
@@ -655,10 +696,13 @@
TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
auto callCount = 0;
auto calledTime = 0;
- VSyncDispatchTimerQueueEntry entry("test", [&](auto time) {
- callCount++;
- calledTime = time;
- });
+ VSyncDispatchTimerQueueEntry entry(
+ "test",
+ [&](auto time) {
+ callCount++;
+ calledTime = time;
+ },
+ mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
auto const wakeup = entry.wakeupTime();
@@ -681,7 +725,8 @@
.WillOnce(Return(1000))
.WillOnce(Return(1020));
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_FALSE(entry.wakeupTime());
entry.update(mStubTracker, 0);
@@ -699,7 +744,8 @@
}
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
entry.update(mStubTracker, 0);
@@ -708,19 +754,52 @@
EXPECT_THAT(*wakeup, Eq(wakeup));
}
-TEST_F(VSyncDispatchTimerQueueEntryTest, reportsCannotScheduleIfMissedOpportunity) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest, willSnapToNextTargettableVSync) {
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- entry.executing();
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
- EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::CannotSchedule));
+ entry.executing(); // 1000 is executing
+ // had 1000 not been executing, this could have been scheduled for time 800.
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
+
+ EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(*entry.wakeupTime(), Eq(1950));
+
EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(*entry.wakeupTime(), Eq(1800));
}
-TEST_F(VSyncDispatchTimerQueueEntryTest, reportsReScheduleIfStillTime) {
- VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+TEST_F(VSyncDispatchTimerQueueEntryTest,
+ willRequestNextEstimateWhenSnappingToNextTargettableVSync) {
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
+
+ Sequence seq;
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(500))
+ .InSequence(seq)
+ .WillOnce(Return(1000));
+ EXPECT_CALL(mStubTracker, nextAnticipatedVSyncTimeFrom(1000 + mVsyncMoveThreshold))
+ .InSequence(seq)
+ .WillOnce(Return(2000));
+
EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
- EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::ReScheduled));
+
+ entry.executing(); // 1000 is executing
+
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsScheduledIfStillTime) {
+ VSyncDispatchTimerQueueEntry entry(
+ "test", [](auto) {}, mVsyncMoveThreshold);
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(50, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(1200, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
index d0c8090..4cb6a38 100644
--- a/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncPredictorTest.cpp
@@ -319,4 +319,36 @@
}
}
+// See b/145667109, and comment in prod code under test.
+TEST_F(VSyncPredictorTest, doesNotPredictBeforeTimePointWithHigherIntercept) {
+ std::vector<nsecs_t> const simulatedVsyncs{
+ 158929578733000,
+ 158929306806205, // oldest TS in ringbuffer
+ 158929650879052,
+ 158929661969209,
+ 158929684198847,
+ 158929695268171,
+ 158929706370359,
+ };
+ auto const idealPeriod = 11111111;
+ auto const expectedPeriod = 11113500;
+ auto const expectedIntercept = -395335;
+
+ tracker.setPeriod(idealPeriod);
+ for (auto const& timestamp : simulatedVsyncs) {
+ tracker.addVsyncTimestamp(timestamp);
+ }
+
+ auto [slope, intercept] = tracker.getVSyncPredictionModel();
+ EXPECT_THAT(slope, IsCloseTo(expectedPeriod, mMaxRoundingError));
+ EXPECT_THAT(intercept, IsCloseTo(expectedIntercept, mMaxRoundingError));
+
+ // (timePoint - oldestTS) % expectedPeriod works out to be: 395334
+ // (timePoint - oldestTS) / expectedPeriod works out to be: 38.96
+ // so failure to account for the offset will floor the ordinal to 38, which was in the past.
+ auto const timePoint = 158929728723871;
+ auto const prediction = tracker.nextAnticipatedVSyncTimeFrom(timePoint);
+ EXPECT_THAT(prediction, Ge(timePoint));
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
index 84df019..537cc80 100644
--- a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -122,21 +122,53 @@
return ft;
}
+class StubCallback : public DispSync::Callback {
+public:
+ void onDispSyncEvent(nsecs_t when) final {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mLastCallTime = when;
+ }
+ std::optional<nsecs_t> lastCallTime() const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ return mLastCallTime;
+ }
+
+private:
+ std::mutex mutable mMutex;
+ std::optional<nsecs_t> mLastCallTime GUARDED_BY(mMutex);
+};
+
class VSyncReactorTest : public testing::Test {
protected:
VSyncReactorTest()
- : mMockDispatch(std::make_shared<MockVSyncDispatch>()),
+ : mMockDispatch(std::make_shared<NiceMock<MockVSyncDispatch>>()),
mMockTracker(std::make_shared<NiceMock<MockVSyncTracker>>()),
mMockClock(std::make_shared<NiceMock<MockClock>>()),
mReactor(std::make_unique<ClockWrapper>(mMockClock),
std::make_unique<VSyncDispatchWrapper>(mMockDispatch),
- std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {}
+ std::make_unique<VSyncTrackerWrapper>(mMockTracker), kPendingLimit) {
+ ON_CALL(*mMockClock, now()).WillByDefault(Return(mFakeNow));
+ ON_CALL(*mMockTracker, currentPeriod()).WillByDefault(Return(period));
+ }
std::shared_ptr<MockVSyncDispatch> mMockDispatch;
std::shared_ptr<MockVSyncTracker> mMockTracker;
std::shared_ptr<MockClock> mMockClock;
static constexpr size_t kPendingLimit = 3;
- static constexpr nsecs_t dummyTime = 47;
+ static constexpr nsecs_t mDummyTime = 47;
+ static constexpr nsecs_t mPhase = 3000;
+ static constexpr nsecs_t mAnotherPhase = 5200;
+ static constexpr nsecs_t period = 10000;
+ static constexpr nsecs_t mAnotherPeriod = 23333;
+ static constexpr nsecs_t mFakeCbTime = 2093;
+ static constexpr nsecs_t mFakeNow = 2214;
+ static constexpr const char mName[] = "callbacky";
+ VSyncDispatch::CallbackToken const mFakeToken{2398};
+
+ nsecs_t lastCallbackTime = 0;
+ StubCallback outerCb;
+ std::function<void(nsecs_t)> innerCb;
+
VSyncReactor mReactor;
};
@@ -149,8 +181,8 @@
}
TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
- EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
@@ -161,9 +193,9 @@
EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
Mock::VerifyAndClearExpectations(mMockTracker.get());
- signalFenceWithTime(pendingFence, dummyTime);
+ signalFenceWithTime(pendingFence, mDummyTime);
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime));
EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
}
@@ -193,15 +225,15 @@
TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
static constexpr size_t aFewTimes = 8;
- EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(mDummyTime)).Times(1);
mReactor.setIgnorePresentFences(true);
for (auto i = 0; i < aFewTimes; i++) {
- mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+ mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime));
}
mReactor.setIgnorePresentFences(false);
- EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(mDummyTime)));
}
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
@@ -227,11 +259,11 @@
TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
nsecs_t const fakeTimestamp = 4839;
nsecs_t const fakePeriod = 1010;
- nsecs_t const fakeNow = 2214;
+ nsecs_t const mFakeNow = 2214;
int const numPeriodsOut = 3;
- EXPECT_CALL(*mMockClock, now()).WillOnce(Return(fakeNow));
+ EXPECT_CALL(*mMockClock, now()).WillOnce(Return(mFakeNow));
EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
- EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(fakeNow + numPeriodsOut * fakePeriod))
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(mFakeNow + numPeriodsOut * fakePeriod))
.WillOnce(Return(fakeTimestamp));
EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
}
@@ -267,4 +299,194 @@
EXPECT_TRUE(periodFlushed);
}
+static nsecs_t computeWorkload(nsecs_t period, nsecs_t phase) {
+ return period - phase;
+}
+
+TEST_F(VSyncReactorTest, addEventListener) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).Times(2).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerTwiceChangesPhase) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _)) // mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, eventListenerGetsACallbackAndReschedules) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+ .Times(2)
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ ASSERT_TRUE(innerCb);
+ innerCb(mFakeCbTime);
+ innerCb(mFakeCbTime);
+}
+
+TEST_F(VSyncReactorTest, callbackTimestampReadapted) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, _))
+ .InSequence(seq)
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeCbTime))
+ .InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ ASSERT_TRUE(innerCb);
+ innerCb(mFakeCbTime);
+ EXPECT_THAT(outerCb.lastCallTime(), Optional(mFakeCbTime - period));
+}
+
+TEST_F(VSyncReactorTest, eventListenersRemovedOnDestruction) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, addEventListenerChangePeriod) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, mAnotherPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, cancel(mFakeToken)).InSequence(seq);
+ EXPECT_CALL(*mMockDispatch, unregisterCallback(mFakeToken)).InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.addEventListener(mName, mAnotherPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, changingPeriodChangesOfsetsOnNextCb) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), mFakeNow))
+ .InSequence(seq);
+ EXPECT_CALL(*mMockTracker, setPeriod(mAnotherPeriod));
+ EXPECT_CALL(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(mAnotherPeriod, mPhase), mFakeNow))
+ .InSequence(seq);
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.setPeriod(mAnotherPeriod);
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+}
+
+TEST_F(VSyncReactorTest, offsetsAppliedOnNextOpportunity) {
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mPhase), _))
+ .InSequence(seq)
+ .WillOnce(Return(ScheduleResult::Scheduled));
+
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+ .InSequence(seq)
+ .WillOnce(Return(ScheduleResult::Scheduled));
+
+ EXPECT_CALL(*mMockDispatch, schedule(mFakeToken, computeWorkload(period, mAnotherPhase), _))
+ .InSequence(seq)
+ .WillOnce(Return(ScheduleResult::Scheduled));
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.changePhaseOffset(&outerCb, mAnotherPhase);
+ ASSERT_TRUE(innerCb);
+ innerCb(mFakeCbTime);
+}
+
+TEST_F(VSyncReactorTest, negativeOffsetsApplied) {
+ nsecs_t const negativePhase = -4000;
+ Sequence seq;
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .InSequence(seq)
+ .WillOnce(Return(mFakeToken));
+ EXPECT_CALL(*mMockDispatch,
+ schedule(mFakeToken, computeWorkload(period, negativePhase), mFakeNow))
+ .InSequence(seq);
+ mReactor.addEventListener(mName, negativePhase, &outerCb, lastCallbackTime);
+}
+
+using VSyncReactorDeathTest = VSyncReactorTest;
+TEST_F(VSyncReactorDeathTest, invalidRemoval) {
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+ EXPECT_DEATH(mReactor.removeEventListener(&outerCb, &lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, invalidChange) {
+ EXPECT_DEATH(mReactor.changePhaseOffset(&outerCb, mPhase), ".*");
+
+ // the current DispSync-interface usage pattern has evolved around an implementation quirk,
+ // which is a callback is assumed to always exist, and it is valid api usage to change the
+ // offset of an object that is in the removed state.
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ mReactor.removeEventListener(&outerCb, &lastCallbackTime);
+ mReactor.changePhaseOffset(&outerCb, mPhase);
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnRegistration) {
+ ON_CALL(*mMockDispatch, schedule(_, _, _))
+ .WillByDefault(Return(ScheduleResult::CannotSchedule));
+ EXPECT_DEATH(mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime), ".*");
+}
+
+TEST_F(VSyncReactorDeathTest, cannotScheduleOnCallback) {
+ EXPECT_CALL(*mMockDispatch, registerCallback(_, std::string(mName)))
+ .WillOnce(DoAll(SaveArg<0>(&innerCb), Return(mFakeToken)));
+ EXPECT_CALL(*mMockDispatch, schedule(_, _, _)).WillOnce(Return(ScheduleResult::Scheduled));
+
+ mReactor.addEventListener(mName, mPhase, &outerCb, lastCallbackTime);
+ ASSERT_TRUE(innerCb);
+ Mock::VerifyAndClearExpectations(mMockDispatch.get());
+
+ ON_CALL(*mMockDispatch, schedule(_, _, _))
+ .WillByDefault(Return(ScheduleResult::CannotSchedule));
+ EXPECT_DEATH(innerCb(mFakeCbTime), ".*");
+}
+
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 2453ccb..0f9dd5b 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -130,6 +130,10 @@
MOCK_METHOD4(setActiveConfigWithConstraints,
V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
VsyncPeriodChangeTimeline*));
+ MOCK_METHOD2(setAutoLowLatencyMode, V2_4::Error(Display, bool));
+ MOCK_METHOD2(getSupportedContentTypes,
+ V2_4::Error(Display, std::vector<IComposerClient::ContentType>*));
+ MOCK_METHOD2(setContentType, V2_4::Error(Display, IComposerClient::ContentType));
};
} // namespace mock
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index a020e74..a44b9e7 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -24,6 +24,7 @@
#include <system/window.h>
#include <ui/BufferQueueDefs.h>
#include <utils/StrongPointer.h>
+#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
@@ -228,8 +229,10 @@
mailbox_mode(present_mode == VK_PRESENT_MODE_MAILBOX_KHR),
pre_transform(pre_transform_),
frame_timestamps_enabled(false),
+ acquire_next_image_timeout(-1),
shared(present_mode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
- present_mode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
+ present_mode ==
+ VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
ANativeWindow* window = surface.window.get();
native_window_get_refresh_cycle_duration(
window,
@@ -251,6 +254,7 @@
int pre_transform;
bool frame_timestamps_enabled;
int64_t refresh_duration;
+ nsecs_t acquire_next_image_timeout;
bool shared;
struct Image {
@@ -363,7 +367,7 @@
int64_t composition_latch_time = 0;
int64_t actual_present_time = 0;
// Obtain timestamps:
- int ret = native_window_get_frame_timestamps(
+ int err = native_window_get_frame_timestamps(
swapchain.surface.window.get(), ti.native_frame_id_,
&desired_present_time, &render_complete_time,
&composition_latch_time,
@@ -374,7 +378,7 @@
nullptr, //&dequeue_ready_time,
nullptr /*&reads_done_time*/);
- if (ret != android::NO_ERROR) {
+ if (err != android::OK) {
continue;
}
@@ -530,7 +534,7 @@
surface->swapchain_handle = VK_NULL_HANDLE;
int err = native_window_get_consumer_usage(surface->window.get(),
&surface->consumer_usage);
- if (err != android::NO_ERROR) {
+ if (err != android::OK) {
ALOGE("native_window_get_consumer_usage() failed: %s (%d)",
strerror(-err), err);
surface->~Surface();
@@ -540,7 +544,7 @@
err =
native_window_api_connect(surface->window.get(), NATIVE_WINDOW_API_EGL);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("native_window_api_connect() failed: %s (%d)", strerror(-err),
err);
surface->~Surface();
@@ -588,7 +592,7 @@
int query_value;
int err = window->query(window, NATIVE_WINDOW_FORMAT, &query_value);
- if (err != 0 || query_value < 0) {
+ if (err != android::OK || query_value < 0) {
ALOGE("NATIVE_WINDOW_FORMAT query failed: %s (%d) value=%d",
strerror(-err), err, query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -629,13 +633,13 @@
int width, height;
err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -643,7 +647,7 @@
int transform_hint;
err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -651,7 +655,7 @@
int max_buffer_count;
err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &max_buffer_count);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -694,20 +698,6 @@
const InstanceData& instance_data = GetData(pdev);
- // TODO(b/143296550): Fill out the set of supported formats. Longer term,
- // add a new gralloc method to query whether a (format, usage) pair is
- // supported, and check that for each gralloc format that corresponds to a
- // Vulkan format. Shorter term, just add a few more formats to the ones
- // hardcoded below.
-
- const VkSurfaceFormatKHR kFormats[] = {
- {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
- };
- const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]);
- uint32_t total_num_formats = kNumFormats;
-
bool wide_color_support = false;
Surface& surface = *SurfaceFromHandle(surface_handle);
int err = native_window_get_wide_color_support(surface.window.get(),
@@ -720,43 +710,72 @@
wide_color_support &&
instance_data.hook_extensions.test(ProcHook::EXT_swapchain_colorspace);
- const VkSurfaceFormatKHR kWideColorFormats[] = {
- {VK_FORMAT_R8G8B8A8_UNORM,
- VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
- {VK_FORMAT_R8G8B8A8_SRGB,
- VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
- {VK_FORMAT_R16G16B16A16_SFLOAT,
- VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT},
- {VK_FORMAT_R16G16B16A16_SFLOAT,
- VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT},
- {VK_FORMAT_A2B10G10R10_UNORM_PACK32,
- VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT},
- };
- const uint32_t kNumWideColorFormats =
- sizeof(kWideColorFormats) / sizeof(kWideColorFormats[0]);
+ AHardwareBuffer_Desc desc = {};
+ desc.width = 1;
+ desc.height = 1;
+ desc.layers = 1;
+ desc.usage = surface.consumer_usage |
+ AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE |
+ AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER;
+
+ // We must support R8G8B8A8
+ std::vector<VkSurfaceFormatKHR> all_formats = {
+ {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
+ {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
+
if (wide_color_support) {
- total_num_formats += kNumWideColorFormats;
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ }
+
+ desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ }
+
+ desc.format = AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(VkSurfaceFormatKHR{
+ VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ if (wide_color_support) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_EXTENDED_SRGB_LINEAR_EXT});
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_R16G16B16A16_SFLOAT,
+ VK_COLOR_SPACE_EXTENDED_SRGB_NONLINEAR_EXT});
+ }
+ }
+
+ desc.format = AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+ if (AHardwareBuffer_isSupported(&desc)) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_SRGB_NONLINEAR_KHR});
+ if (wide_color_support) {
+ all_formats.emplace_back(
+ VkSurfaceFormatKHR{VK_FORMAT_A2B10G10R10_UNORM_PACK32,
+ VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT});
+ }
}
VkResult result = VK_SUCCESS;
if (formats) {
- uint32_t out_count = 0;
- uint32_t transfer_count = 0;
- if (*count < total_num_formats)
+ uint32_t transfer_count = all_formats.size();
+ if (transfer_count > *count) {
+ transfer_count = *count;
result = VK_INCOMPLETE;
- transfer_count = std::min(*count, kNumFormats);
- std::copy(kFormats, kFormats + transfer_count, formats);
- out_count += transfer_count;
- if (wide_color_support) {
- transfer_count = std::min(*count - out_count, kNumWideColorFormats);
- std::copy(kWideColorFormats, kWideColorFormats + transfer_count,
- formats + out_count);
- out_count += transfer_count;
}
- *count = out_count;
+ std::copy(all_formats.begin(), all_formats.begin() + transfer_count,
+ formats);
+ *count = transfer_count;
} else {
- *count = total_num_formats;
+ *count = all_formats.size();
}
+
return result;
}
@@ -841,7 +860,7 @@
ANativeWindow* window = SurfaceFromHandle(surface)->window.get();
err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS, &query_value);
- if (err != 0 || query_value < 0) {
+ if (err != android::OK || query_value < 0) {
ALOGE("NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS query failed: %s (%d) value=%d",
strerror(-err), err, query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -849,7 +868,7 @@
uint32_t min_undequeued_buffers = static_cast<uint32_t>(query_value);
err = window->query(window, NATIVE_WINDOW_MAX_BUFFER_COUNT, &query_value);
- if (err != 0 || query_value < 0) {
+ if (err != android::OK || query_value < 0) {
ALOGE("NATIVE_WINDOW_MAX_BUFFER_COUNT query failed: %s (%d) value=%d",
strerror(-err), err, query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -941,12 +960,12 @@
int width = 0, height = 0;
err = window->query(window, NATIVE_WINDOW_DEFAULT_WIDTH, &width);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
}
err = window->query(window, NATIVE_WINDOW_DEFAULT_HEIGHT, &height);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_DEFAULT_WIDTH query failed: %s (%d)",
strerror(-err), err);
}
@@ -1070,17 +1089,23 @@
// dequeue all buffers.
//
// TODO(http://b/134186185) recycle swapchain images more efficiently
- err = native_window_api_disconnect(surface.window.get(),
- NATIVE_WINDOW_API_EGL);
- ALOGW_IF(err != 0, "native_window_api_disconnect failed: %s (%d)",
+ ANativeWindow* window = surface.window.get();
+ err = native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != android::OK, "native_window_api_disconnect failed: %s (%d)",
strerror(-err), err);
- err =
- native_window_api_connect(surface.window.get(), NATIVE_WINDOW_API_EGL);
- ALOGW_IF(err != 0, "native_window_api_connect failed: %s (%d)",
+ err = native_window_api_connect(window, NATIVE_WINDOW_API_EGL);
+ ALOGW_IF(err != android::OK, "native_window_api_connect failed: %s (%d)",
strerror(-err), err);
- err = native_window_set_buffer_count(surface.window.get(), 0);
- if (err != 0) {
+ err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT, -1);
+ if (err != android::OK) {
+ ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+
+ err = native_window_set_buffer_count(window, 0);
+ if (err != android::OK) {
ALOGE("native_window_set_buffer_count(0) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1088,22 +1113,22 @@
int swap_interval =
create_info->presentMode == VK_PRESENT_MODE_MAILBOX_KHR ? 0 : 1;
- err = surface.window->setSwapInterval(surface.window.get(), swap_interval);
- if (err != 0) {
+ err = window->setSwapInterval(window, swap_interval);
+ if (err != android::OK) {
ALOGE("native_window->setSwapInterval(1) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
- err = native_window_set_shared_buffer_mode(surface.window.get(), false);
- if (err != 0) {
+ err = native_window_set_shared_buffer_mode(window, false);
+ if (err != android::OK) {
ALOGE("native_window_set_shared_buffer_mode(false) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
- err = native_window_set_auto_refresh(surface.window.get(), false);
- if (err != 0) {
+ err = native_window_set_auto_refresh(window, false);
+ if (err != android::OK) {
ALOGE("native_window_set_auto_refresh(false) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1113,25 +1138,23 @@
const auto& dispatch = GetData(device).driver;
- err = native_window_set_buffers_format(surface.window.get(),
- native_pixel_format);
- if (err != 0) {
+ err = native_window_set_buffers_format(window, native_pixel_format);
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_format(%d) failed: %s (%d)",
native_pixel_format, strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
- err = native_window_set_buffers_data_space(surface.window.get(),
- native_dataspace);
- if (err != 0) {
+ err = native_window_set_buffers_data_space(window, native_dataspace);
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_data_space(%d) failed: %s (%d)",
native_dataspace, strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
err = native_window_set_buffers_dimensions(
- surface.window.get(), static_cast<int>(create_info->imageExtent.width),
+ window, static_cast<int>(create_info->imageExtent.width),
static_cast<int>(create_info->imageExtent.height));
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_dimensions(%d,%d) failed: %s (%d)",
create_info->imageExtent.width, create_info->imageExtent.height,
strerror(-err), err);
@@ -1147,9 +1170,8 @@
// it's job the two transforms cancel each other out and the compositor ends
// up applying an identity transform to the app's buffer.
err = native_window_set_buffers_transform(
- surface.window.get(),
- InvertTransformToNative(create_info->preTransform));
- if (err != 0) {
+ window, InvertTransformToNative(create_info->preTransform));
+ if (err != android::OK) {
ALOGE("native_window_set_buffers_transform(%d) failed: %s (%d)",
InvertTransformToNative(create_info->preTransform),
strerror(-err), err);
@@ -1157,8 +1179,8 @@
}
err = native_window_set_scaling_mode(
- surface.window.get(), NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
- if (err != 0) {
+ window, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+ if (err != android::OK) {
ALOGE("native_window_set_scaling_mode(SCALE_TO_WINDOW) failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1168,26 +1190,25 @@
if (create_info->presentMode == VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR ||
create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
swapchain_image_usage |= VK_SWAPCHAIN_IMAGE_USAGE_SHARED_BIT_ANDROID;
- err = native_window_set_shared_buffer_mode(surface.window.get(), true);
- if (err != 0) {
+ err = native_window_set_shared_buffer_mode(window, true);
+ if (err != android::OK) {
ALOGE("native_window_set_shared_buffer_mode failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
}
if (create_info->presentMode == VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR) {
- err = native_window_set_auto_refresh(surface.window.get(), true);
- if (err != 0) {
+ err = native_window_set_auto_refresh(window, true);
+ if (err != android::OK) {
ALOGE("native_window_set_auto_refresh failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
}
int query_value;
- err = surface.window->query(surface.window.get(),
- NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
- &query_value);
- if (err != 0 || query_value < 0) {
+ err = window->query(window, NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS,
+ &query_value);
+ if (err != android::OK || query_value < 0) {
ALOGE("window->query failed: %s (%d) value=%d", strerror(-err), err,
query_value);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1203,8 +1224,8 @@
// in place for that to work yet. Note we only lie to the lower layer-- we
// don't want to give the app back a swapchain with extra images (which they
// can't actually use!).
- err = native_window_set_buffer_count(surface.window.get(), std::max(2u, num_images));
- if (err != 0) {
+ err = native_window_set_buffer_count(window, std::max(2u, num_images));
+ if (err != android::OK) {
ALOGE("native_window_set_buffer_count(%d) failed: %s (%d)", num_images,
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1242,16 +1263,15 @@
createProtectedSwapchain = true;
native_usage |= BufferUsage::PROTECTED;
}
- err = native_window_set_usage(surface.window.get(), native_usage);
- if (err != 0) {
+ err = native_window_set_usage(window, native_usage);
+ if (err != android::OK) {
ALOGE("native_window_set_usage failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
int transform_hint;
- err = surface.window->query(surface.window.get(),
- NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
- if (err != 0) {
+ err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT, &transform_hint);
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
@@ -1307,9 +1327,8 @@
Swapchain::Image& img = swapchain->images[i];
ANativeWindowBuffer* buffer;
- err = surface.window->dequeueBuffer(surface.window.get(), &buffer,
- &img.dequeue_fence);
- if (err != 0) {
+ err = window->dequeueBuffer(window, &buffer, &img.dequeue_fence);
+ if (err != android::OK) {
ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
switch (-err) {
case ENOMEM:
@@ -1353,8 +1372,8 @@
Swapchain::Image& img = swapchain->images[i];
if (img.dequeued) {
if (!swapchain->shared) {
- surface.window->cancelBuffer(surface.window.get(), img.buffer.get(),
- img.dequeue_fence);
+ window->cancelBuffer(window, img.buffer.get(),
+ img.dequeue_fence);
img.dequeue_fence = -1;
img.dequeued = false;
}
@@ -1432,10 +1451,6 @@
if (swapchain.surface.swapchain_handle != swapchain_handle)
return VK_ERROR_OUT_OF_DATE_KHR;
- ALOGW_IF(
- timeout != UINT64_MAX,
- "vkAcquireNextImageKHR: non-infinite timeouts not yet implemented");
-
if (swapchain.shared) {
// In shared mode, we keep the buffer dequeued all the time, so we don't
// want to dequeue a buffer here. Instead, just ask the driver to ensure
@@ -1446,10 +1461,27 @@
return result;
}
+ const nsecs_t acquire_next_image_timeout =
+ timeout > (uint64_t)std::numeric_limits<nsecs_t>::max() ? -1 : timeout;
+ if (acquire_next_image_timeout != swapchain.acquire_next_image_timeout) {
+ // Cache the timeout to avoid the duplicate binder cost.
+ err = window->perform(window, NATIVE_WINDOW_SET_DEQUEUE_TIMEOUT,
+ acquire_next_image_timeout);
+ if (err != android::OK) {
+ ALOGE("window->perform(SET_DEQUEUE_TIMEOUT) failed: %s (%d)",
+ strerror(-err), err);
+ return VK_ERROR_SURFACE_LOST_KHR;
+ }
+ swapchain.acquire_next_image_timeout = acquire_next_image_timeout;
+ }
+
ANativeWindowBuffer* buffer;
int fence_fd;
err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != 0) {
+ if (err == android::TIMED_OUT) {
+ ALOGW("dequeueBuffer timed out: %s (%d)", strerror(-err), err);
+ return timeout ? VK_TIMEOUT : VK_NOT_READY;
+ } else if (err != android::OK) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
return VK_ERROR_SURFACE_LOST_KHR;
}
@@ -1656,7 +1688,7 @@
uint64_t nativeFrameId = 0;
err = native_window_get_next_frame_id(
window, &nativeFrameId);
- if (err != android::NO_ERROR) {
+ if (err != android::OK) {
ALOGE("Failed to get next native frame ID.");
}
@@ -1680,7 +1712,7 @@
err = window->queueBuffer(window, img.buffer.get(), fence);
// queueBuffer always closes fence, even on error
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("queueBuffer failed: %s (%d)", strerror(-err), err);
swapchain_result = WorstPresentResult(
swapchain_result, VK_ERROR_OUT_OF_DATE_KHR);
@@ -1700,17 +1732,15 @@
ANativeWindowBuffer* buffer;
int fence_fd;
err = window->dequeueBuffer(window, &buffer, &fence_fd);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), err);
swapchain_result = WorstPresentResult(swapchain_result,
VK_ERROR_SURFACE_LOST_KHR);
- }
- else if (img.buffer != buffer) {
+ } else if (img.buffer != buffer) {
ALOGE("got wrong image back for shared swapchain");
swapchain_result = WorstPresentResult(swapchain_result,
VK_ERROR_SURFACE_LOST_KHR);
- }
- else {
+ } else {
img.dequeue_fence = fence_fd;
img.dequeued = true;
}
@@ -1722,7 +1752,7 @@
int window_transform_hint;
err = window->query(window, NATIVE_WINDOW_TRANSFORM_HINT,
&window_transform_hint);
- if (err != 0) {
+ if (err != android::OK) {
ALOGE("NATIVE_WINDOW_TRANSFORM_HINT query failed: %s (%d)",
strerror(-err), err);
swapchain_result = WorstPresentResult(