Merge "Adding transform to BLAST adapter"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 3fa5430..20bfe65 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
[Builtin Hooks Options]
# Only turn on clang-format check for the following subfolders.
clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp
+ cmds/idlcli/
include/input/
libs/binder/fuzzer/
libs/binder/ndk/
diff --git a/TEST_MAPPING b/TEST_MAPPING
new file mode 100644
index 0000000..8173c89
--- /dev/null
+++ b/TEST_MAPPING
@@ -0,0 +1,63 @@
+{
+ "presubmit": [
+ {
+ "name": "SurfaceFlinger_test",
+ "options": [
+ {
+ "include-filter": "*CredentialsTest.*"
+ },
+ {
+ "include-filter": "*SurfaceFlingerStress.*"
+ },
+ {
+ "include-filter": "*SurfaceInterceptorTest.*"
+ },
+ {
+ "include-filter": "*LayerTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerTypeTransactionTest.*"
+ },
+ {
+ "include-filter": "*LayerUpdateTest.*"
+ },
+ {
+ "include-filter": "*GeometryLatchingTest.*"
+ },
+ {
+ "include-filter": "*CropLatchingTest.*"
+ },
+ {
+ "include-filter": "*ChildLayerTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureTest.*"
+ },
+ {
+ "include-filter": "*ScreenCaptureChildOnlyTest.*"
+ },
+ {
+ "include-filter": "*DereferenceSurfaceControlTest.*"
+ },
+ {
+ "include-filter": "*BoundlessLayerTest.*"
+ },
+ {
+ "include-filter": "*MultiDisplayLayerBoundsTest.*"
+ },
+ {
+ "include-filter": "*InvalidHandleTest.*"
+ },
+ {
+ "include-filter": "*VirtualDisplayTest.*"
+ },
+ {
+ "include-filter": "*RelativeZTest.*"
+ }
+ ]
+ },
+ {
+ "name": "libsurfaceflinger_unittest"
+ }
+ ]
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 61e22a4..9d6a7ff 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -933,6 +933,31 @@
unlink(path.c_str());
}
+static void DumpVisibleWindowViews() {
+ if (!ds.IsZipping()) {
+ MYLOGD("Not dumping visible views because it's not a zipped bugreport\n");
+ return;
+ }
+ DurationReporter duration_reporter("VISIBLE WINDOW VIEWS");
+ const std::string path = ds.bugreport_internal_dir_ + "/tmp_visible_window_views";
+ auto fd = android::base::unique_fd(TEMP_FAILURE_RETRY(open(path.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)));
+ if (fd < 0) {
+ MYLOGE("Could not open %s to dump visible views.\n", path.c_str());
+ return;
+ }
+ RunCommandToFd(fd, "", {"cmd", "window", "dump-visible-window-views"},
+ CommandOptions::WithTimeout(120).Build());
+ bool empty = 0 == lseek(fd, 0, SEEK_END);
+ if (!empty) {
+ ds.AddZipEntry("visible_windows.zip", path);
+ } else {
+ MYLOGW("Failed to dump visible windows\n");
+ }
+ unlink(path.c_str());
+}
+
static void DumpIpTablesAsRoot() {
RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
@@ -1317,6 +1342,8 @@
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20);
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpVisibleWindowViews);
+
DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
DumpFile("SLAB INFO", "/proc/slabinfo");
@@ -1518,11 +1545,7 @@
* Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport
* with the caller.
*/
-static Dumpstate::RunStatus DumpstateDefault() {
- // Invoking the following dumpsys calls before DumpTraces() to try and
- // keep the system stats as close to its initial state as possible.
- RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical);
-
+Dumpstate::RunStatus Dumpstate::DumpstateDefaultAfterCritical() {
// Capture first logcat early on; useful to take a snapshot before dumpstate logs take over the
// buffer.
DoLogcat();
@@ -1607,6 +1630,7 @@
// This method collects dumpsys for telephony debugging only
static void DumpstateTelephonyOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+
const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
DumpstateRadioCommon();
@@ -2033,12 +2057,12 @@
? StringPrintf("[fd:%d]", ds.options_->bugreport_fd.get())
: ds.bugreport_internal_dir_.c_str();
MYLOGD(
- "Bugreport dir: %s\n"
- "Base name: %s\n"
- "Suffix: %s\n"
- "Log path: %s\n"
- "Temporary path: %s\n"
- "Screenshot path: %s\n",
+ "Bugreport dir: [%s] "
+ "Base name: [%s] "
+ "Suffix: [%s] "
+ "Log path: [%s] "
+ "Temporary path: [%s] "
+ "Screenshot path: [%s]\n",
destination.c_str(), ds.base_name_.c_str(), ds.name_.c_str(), ds.log_path_.c_str(),
ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
@@ -2140,21 +2164,14 @@
}
static void LogDumpOptions(const Dumpstate::DumpOptions& options) {
- MYLOGI("do_zip_file: %d\n", options.do_zip_file);
- MYLOGI("do_add_date: %d\n", options.do_add_date);
- MYLOGI("do_vibrate: %d\n", options.do_vibrate);
- MYLOGI("use_socket: %d\n", options.use_socket);
- MYLOGI("use_control_socket: %d\n", options.use_control_socket);
- MYLOGI("do_fb: %d\n", options.do_fb);
- MYLOGI("is_remote_mode: %d\n", options.is_remote_mode);
- MYLOGI("show_header_only: %d\n", options.show_header_only);
- MYLOGI("do_start_service: %d\n", options.do_start_service);
- MYLOGI("telephony_only: %d\n", options.telephony_only);
- MYLOGI("wifi_only: %d\n", options.wifi_only);
- MYLOGI("do_progress_updates: %d\n", options.do_progress_updates);
- MYLOGI("fd: %d\n", options.bugreport_fd.get());
- MYLOGI("bugreport_mode: %s\n", options.bugreport_mode.c_str());
- MYLOGI("args: %s\n", options.args.c_str());
+ MYLOGI(
+ "do_zip_file: %d do_vibrate: %d use_socket: %d use_control_socket: %d do_fb: %d "
+ "is_remote_mode: %d show_header_only: %d do_start_service: %d telephony_only: %d "
+ "wifi_only: %d do_progress_updates: %d fd: %d bugreport_mode: %s args: %s\n",
+ options.do_zip_file, options.do_vibrate, options.use_socket, options.use_control_socket,
+ options.do_fb, options.is_remote_mode, options.show_header_only, options.do_start_service,
+ options.telephony_only, options.wifi_only, options.do_progress_updates,
+ options.bugreport_fd.get(), options.bugreport_mode.c_str(), options.args.c_str());
}
void Dumpstate::DumpOptions::Initialize(BugreportMode bugreport_mode,
@@ -2326,11 +2343,6 @@
MYLOGD("dumpstate calling_uid = %d ; calling package = %s \n",
calling_uid, calling_package.c_str());
- if (CalledByApi()) {
- // If the output needs to be copied over to the caller's fd, get user consent.
- android::String16 package(calling_package.c_str());
- CheckUserConsent(calling_uid, package);
- }
// Redirect output if needed
bool is_redirecting = options_->OutputToFile();
@@ -2347,8 +2359,6 @@
id_ = ++last_id;
android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
- MYLOGI("begin\n");
-
if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
MYLOGE("Failed to acquire wake lock: %s\n", strerror(errno));
} else {
@@ -2371,10 +2381,8 @@
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
- MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s)\n", id_, options_->args.c_str(),
- options_->bugreport_mode.c_str());
-
- MYLOGI("bugreport format version: %s\n", version_.c_str());
+ MYLOGI("dumpstate info: id=%d, args='%s', bugreport_mode= %s bugreport format version: %s\n",
+ id_, options_->args.c_str(), options_->bugreport_mode.c_str(), version_.c_str());
do_early_screenshot_ = options_->do_progress_updates;
@@ -2473,13 +2481,23 @@
PrintHeader();
if (options_->telephony_only) {
+ MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateTelephonyOnly();
DumpstateBoard();
} else if (options_->wifi_only) {
+ MaybeCheckUserConsent(calling_uid, calling_package);
DumpstateWifiOnly();
} else {
+ // Invoking the critical dumpsys calls before DumpTraces() to try and
+ // keep the system stats as close to its initial state as possible.
+ RunDumpsysCritical();
+
+ // Run consent check only after critical dumpsys has finished -- so the consent
+ // isn't going to pollute the system state / logs.
+ MaybeCheckUserConsent(calling_uid, calling_package);
+
// Dump state for the default case. This also drops root.
- RunStatus s = DumpstateDefault();
+ RunStatus s = DumpstateDefaultAfterCritical();
if (s != RunStatus::OK) {
if (s == RunStatus::USER_CONSENT_DENIED) {
HandleUserConsentDenied();
@@ -2564,17 +2582,20 @@
: RunStatus::OK;
}
-void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& calling_package) {
- if (calling_uid == AID_SHELL) {
+void Dumpstate::MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package) {
+ if (calling_uid == AID_SHELL || !CalledByApi()) {
+ // No need to get consent for shell triggered dumpstates, or not through
+ // bugreporting API (i.e. no fd to copy back).
return;
}
consent_callback_ = new ConsentCallback();
const String16 incidentcompanion("incidentcompanion");
sp<android::IBinder> ics(defaultServiceManager()->getService(incidentcompanion));
+ android::String16 package(calling_package.c_str());
if (ics != nullptr) {
MYLOGD("Checking user consent via incidentcompanion service\n");
android::interface_cast<android::os::IIncidentCompanion>(ics)->authorizeReport(
- calling_uid, calling_package, String16(), String16(),
+ calling_uid, package, String16(), String16(),
0x1 /* FLAG_CONFIRMATION_DIALOG */, consent_callback_.get());
} else {
MYLOGD("Unable to check user consent; incidentcompanion service unavailable\n");
@@ -3471,8 +3492,8 @@
}
if (listener_ != nullptr) {
- if (percent % 5 == 0) {
- // We don't want to spam logcat, so only log multiples of 5.
+ if (percent % 10 == 0) {
+ // We don't want to spam logcat, so only log multiples of 10.
MYLOGD("Setting progress: %d/%d (%d%%)\n", progress, max, percent);
} else {
// stderr is ignored on normal invocations, but useful when calling
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 831574d..7d9b113 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -486,7 +486,9 @@
private:
RunStatus RunInternal(int32_t calling_uid, const std::string& calling_package);
- void CheckUserConsent(int32_t calling_uid, const android::String16& calling_package);
+ RunStatus DumpstateDefaultAfterCritical();
+
+ void MaybeCheckUserConsent(int32_t calling_uid, const std::string& calling_package);
// Removes the in progress files output files (tmp file, zip/txt file, screenshot),
// but leaves the log file alone.
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index bb92fd3..5476319 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -36,7 +36,10 @@
defaults: ["idlcli-defaults"],
srcs: [
"CommandVibrator.cpp",
+ "vibrator/CommandCompose.cpp",
"vibrator/CommandGetCapabilities.cpp",
+ "vibrator/CommandGetCompositionDelayMax.cpp",
+ "vibrator/CommandGetCompositionSizeMax.cpp",
"vibrator/CommandOff.cpp",
"vibrator/CommandOn.cpp",
"vibrator/CommandPerform.cpp",
diff --git a/cmds/idlcli/vibrator/CommandCompose.cpp b/cmds/idlcli/vibrator/CommandCompose.cpp
new file mode 100644
index 0000000..705e40b
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandCompose.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+using aidl::CompositeEffect;
+
+class CommandCompose : public Command {
+ std::string getDescription() const override { return "Compose vibration."; }
+
+ std::string getUsageSummary() const override { return "<delay> <primitive> <scale> ..."; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{
+ {"<delay>", {"In milliseconds"}},
+ {"<primitive>", {"Primitive ID."}},
+ {"<scale>", {"0.0 (exclusive) - 1.0 (inclusive)."}},
+ {"...", {"May repeat multiple times."}},
+ };
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ while (!args.empty()) {
+ CompositeEffect effect;
+ if (auto delay = args.pop<decltype(effect.delayMs)>()) {
+ effect.delayMs = *delay;
+ std::cout << "Delay: " << effect.delayMs << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Delay!" << std::endl;
+ return USAGE;
+ }
+ // TODO: Use range validation when supported by AIDL
+ if (auto primitive = args.pop<std::underlying_type_t<decltype(effect.primitive)>>()) {
+ effect.primitive = static_cast<decltype(effect.primitive)>(*primitive);
+ std::cout << "Primitive: " << toString(effect.primitive) << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Primitive!" << std::endl;
+ return USAGE;
+ }
+ if (auto scale = args.pop<decltype(effect.scale)>();
+ scale && *scale > 0.0 && scale <= 1.0) {
+ effect.scale = *scale;
+ std::cout << "Scale: " << effect.scale << std::endl;
+ } else {
+ std::cerr << "Missing or Invalid Scale!" << std::endl;
+ return USAGE;
+ }
+ mComposite.emplace_back(std::move(effect));
+ }
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ Status ret;
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::compose, mComposite, nullptr);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+
+ return ret;
+ }
+
+ std::vector<CompositeEffect> mComposite;
+};
+
+static const auto Command = CommandRegistry<CommandVibrator>::Register<CommandCompose>("compose");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
new file mode 100644
index 0000000..b414307
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCompositionDelayMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetCompositionDelayMax : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator composition delay max.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t maxDelayMs;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getCompositionDelayMax, &maxDelayMs);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Max Delay: " << maxDelayMs << " ms" << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetCompositionDelayMax>(
+ "getCompositionDelayMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
new file mode 100644
index 0000000..360fc9d
--- /dev/null
+++ b/cmds/idlcli/vibrator/CommandGetCompositionSizeMax.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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 "utils.h"
+#include "vibrator.h"
+
+namespace android {
+namespace idlcli {
+
+class CommandVibrator;
+
+namespace vibrator {
+
+class CommandGetCompositionSizeMax : public Command {
+ std::string getDescription() const override {
+ return "Retrieves vibrator composition size max.";
+ }
+
+ std::string getUsageSummary() const override { return ""; }
+
+ UsageDetails getUsageDetails() const override {
+ UsageDetails details{};
+ return details;
+ }
+
+ Status doArgs(Args &args) override {
+ if (!args.empty()) {
+ std::cerr << "Unexpected Arguments!" << std::endl;
+ return USAGE;
+ }
+ return OK;
+ }
+
+ Status doMain(Args && /*args*/) override {
+ std::string statusStr;
+ int32_t maxSize;
+ Status ret;
+
+ if (auto hal = getHal<aidl::IVibrator>()) {
+ auto status = hal->call(&aidl::IVibrator::getCompositionSizeMax, &maxSize);
+ statusStr = status.toString8();
+ ret = status.isOk() ? OK : ERROR;
+ } else {
+ return UNAVAILABLE;
+ }
+
+ std::cout << "Status: " << statusStr << std::endl;
+ std::cout << "Max Size: " << maxSize << std::endl;
+
+ return ret;
+ }
+};
+
+static const auto Command =
+ CommandRegistry<CommandVibrator>::Register<CommandGetCompositionSizeMax>(
+ "getCompositionSizeMax");
+
+} // namespace vibrator
+} // namespace idlcli
+} // namespace android
diff --git a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
index 6e2261f..33d7eed 100644
--- a/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
+++ b/cmds/idlcli/vibrator/CommandSetAmplitude.cpp
@@ -54,7 +54,8 @@
Status ret;
if (auto hal = getHal<aidl::IVibrator>()) {
- auto status = hal->call(&aidl::IVibrator::setAmplitude, mAmplitude);
+ auto status = hal->call(&aidl::IVibrator::setAmplitude,
+ static_cast<float>(mAmplitude) / UINT8_MAX);
statusStr = status.toString8();
ret = status.isOk() ? OK : ERROR;
} else if (auto hal = getHal<V1_0::IVibrator>()) {
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 4026f29..95957a0 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -92,15 +92,12 @@
static constexpr const char* CACHE_DIR_POSTFIX = "/cache";
static constexpr const char* CODE_CACHE_DIR_POSTFIX = "/code_cache";
-static constexpr const char *kIdMapPath = "/system/bin/idmap";
-static constexpr const char* IDMAP_PREFIX = "/data/resource-cache/";
-static constexpr const char* IDMAP_SUFFIX = "@idmap";
-
// fsverity assumes the page size is always 4096. If not, the feature can not be
// enabled.
static constexpr int kVerityPageSize = 4096;
static constexpr size_t kSha256Size = 32;
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
+static constexpr const char* kFuseProp = "persist.sys.fuse";
namespace {
@@ -592,12 +589,21 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
if (flags & FLAG_CLEAR_CACHE_ONLY) {
// Clear only cached data from shared storage
auto path = StringPrintf("%s/Android/data/%s/cache", extPath.c_str(), pkgname);
@@ -688,16 +694,26 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
for (const auto& n : mStorageMounts) {
auto extPath = n.second;
- if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
- extPath += StringPrintf("/%d", userId);
- } else if (userId != 0) {
- // TODO: support devices mounted under secondary users
- continue;
+
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ std::regex re("^\\/mnt\\/pass_through\\/[0-9]+\\/emulated");
+ if (std::regex_match(extPath, re)) {
+ extPath += "/" + std::to_string(userId);
+ }
+ } else {
+ if (n.first.compare(0, 14, "/mnt/media_rw/") != 0) {
+ extPath += StringPrintf("/%d", userId);
+ } else if (userId != 0) {
+ // TODO: support devices mounted under secondary users
+ continue;
+ }
}
+
auto path = StringPrintf("%s/Android/data/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
}
+
path = StringPrintf("%s/Android/media/%s", extPath.c_str(), pkgname);
if (delete_dir_contents_and_dir(path, true) != 0) {
res = error("Failed to delete contents of " + path);
@@ -2253,206 +2269,6 @@
return res;
}
-static void run_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
-{
- execl(kIdMapPath, kIdMapPath, "--fd", target_apk, overlay_apk,
- StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr);
- PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
-}
-
-static void run_verify_idmap(const char *target_apk, const char *overlay_apk, int idmap_fd)
-{
- execl(kIdMapPath, kIdMapPath, "--verify", target_apk, overlay_apk,
- StringPrintf("%d", idmap_fd).c_str(), (char*)nullptr);
- PLOG(ERROR) << "execl (" << kIdMapPath << ") failed";
-}
-
-static bool delete_stale_idmap(const char* target_apk, const char* overlay_apk,
- const char* idmap_path, int32_t uid) {
- int idmap_fd = open(idmap_path, O_RDWR);
- if (idmap_fd < 0) {
- PLOG(ERROR) << "idmap open failed: " << idmap_path;
- unlink(idmap_path);
- return true;
- }
-
- pid_t pid;
- pid = fork();
- if (pid == 0) {
- /* child -- drop privileges before continuing */
- if (setgid(uid) != 0) {
- LOG(ERROR) << "setgid(" << uid << ") failed during idmap";
- exit(1);
- }
- if (setuid(uid) != 0) {
- LOG(ERROR) << "setuid(" << uid << ") failed during idmap";
- exit(1);
- }
- if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
- PLOG(ERROR) << "flock(" << idmap_path << ") failed during idmap";
- exit(1);
- }
-
- run_verify_idmap(target_apk, overlay_apk, idmap_fd);
- exit(1); /* only if exec call to deleting stale idmap failed */
- } else {
- int status = wait_child(pid);
- close(idmap_fd);
-
- if (status != 0) {
- // Failed on verifying if idmap is made from target_apk and overlay_apk.
- LOG(DEBUG) << "delete stale idmap: " << idmap_path;
- unlink(idmap_path);
- return true;
- }
- }
- return false;
-}
-
-// Transform string /a/b/c.apk to (prefix)/a@b@c.apk@(suffix)
-// eg /a/b/c.apk to /data/resource-cache/a@b@c.apk@idmap
-static int flatten_path(const char *prefix, const char *suffix,
- const char *overlay_path, char *idmap_path, size_t N)
-{
- if (overlay_path == nullptr || idmap_path == nullptr) {
- return -1;
- }
- const size_t len_overlay_path = strlen(overlay_path);
- // will access overlay_path + 1 further below; requires absolute path
- if (len_overlay_path < 2 || *overlay_path != '/') {
- return -1;
- }
- const size_t len_idmap_root = strlen(prefix);
- const size_t len_suffix = strlen(suffix);
- if (SIZE_MAX - len_idmap_root < len_overlay_path ||
- SIZE_MAX - (len_idmap_root + len_overlay_path) < len_suffix) {
- // additions below would cause overflow
- return -1;
- }
- if (N < len_idmap_root + len_overlay_path + len_suffix) {
- return -1;
- }
- memset(idmap_path, 0, N);
- snprintf(idmap_path, N, "%s%s%s", prefix, overlay_path + 1, suffix);
- char *ch = idmap_path + len_idmap_root;
- while (*ch != '\0') {
- if (*ch == '/') {
- *ch = '@';
- }
- ++ch;
- }
- return 0;
-}
-
-binder::Status InstalldNativeService::idmap(const std::string& targetApkPath,
- const std::string& overlayApkPath, int32_t uid) {
- ENFORCE_UID(AID_SYSTEM);
- CHECK_ARGUMENT_PATH(targetApkPath);
- CHECK_ARGUMENT_PATH(overlayApkPath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- const char* target_apk = targetApkPath.c_str();
- const char* overlay_apk = overlayApkPath.c_str();
- ALOGV("idmap target_apk=%s overlay_apk=%s uid=%d\n", target_apk, overlay_apk, uid);
-
- int idmap_fd = -1;
- char idmap_path[PATH_MAX];
- struct stat idmap_stat;
- bool outdated = false;
-
- if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
- idmap_path, sizeof(idmap_path)) == -1) {
- ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
- goto fail;
- }
-
- if (stat(idmap_path, &idmap_stat) < 0) {
- outdated = true;
- } else {
- outdated = delete_stale_idmap(target_apk, overlay_apk, idmap_path, uid);
- }
-
- if (outdated) {
- idmap_fd = open(idmap_path, O_RDWR | O_CREAT | O_EXCL, 0644);
- } else {
- idmap_fd = open(idmap_path, O_RDWR);
- }
-
- if (idmap_fd < 0) {
- ALOGE("idmap cannot open '%s' for output: %s\n", idmap_path, strerror(errno));
- goto fail;
- }
- if (fchown(idmap_fd, AID_SYSTEM, uid) < 0) {
- ALOGE("idmap cannot chown '%s'\n", idmap_path);
- goto fail;
- }
- if (fchmod(idmap_fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
- ALOGE("idmap cannot chmod '%s'\n", idmap_path);
- goto fail;
- }
-
- if (!outdated) {
- close(idmap_fd);
- return ok();
- }
-
- pid_t pid;
- pid = fork();
- if (pid == 0) {
- /* child -- drop privileges before continuing */
- if (setgid(uid) != 0) {
- ALOGE("setgid(%d) failed during idmap\n", uid);
- exit(1);
- }
- if (setuid(uid) != 0) {
- ALOGE("setuid(%d) failed during idmap\n", uid);
- exit(1);
- }
- if (flock(idmap_fd, LOCK_EX | LOCK_NB) != 0) {
- ALOGE("flock(%s) failed during idmap: %s\n", idmap_path, strerror(errno));
- exit(1);
- }
-
- run_idmap(target_apk, overlay_apk, idmap_fd);
- exit(1); /* only if exec call to idmap failed */
- } else {
- int status = wait_child(pid);
- if (status != 0) {
- ALOGE("idmap failed, status=0x%04x\n", status);
- goto fail;
- }
- }
-
- close(idmap_fd);
- return ok();
-fail:
- if (idmap_fd >= 0) {
- close(idmap_fd);
- unlink(idmap_path);
- }
- return error();
-}
-
-binder::Status InstalldNativeService::removeIdmap(const std::string& overlayApkPath) {
- ENFORCE_UID(AID_SYSTEM);
- CHECK_ARGUMENT_PATH(overlayApkPath);
- std::lock_guard<std::recursive_mutex> lock(mLock);
-
- const char* overlay_apk = overlayApkPath.c_str();
- char idmap_path[PATH_MAX];
-
- if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
- idmap_path, sizeof(idmap_path)) == -1) {
- ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
- return error();
- }
- if (unlink(idmap_path) < 0) {
- ALOGE("couldn't unlink idmap file %s\n", idmap_path);
- return error();
- }
- return ok();
-}
-
binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid,
const std::string& packageName, int32_t userId, int32_t flags, int32_t appId,
const std::string& seInfo) {
@@ -2778,12 +2594,19 @@
std::getline(in, target, ' ');
std::getline(in, ignored);
+ if (android::base::GetBoolProperty(kFuseProp, false)) {
+ if (target.compare(0, 17, "/mnt/pass_through") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
+ } else {
#if !BYPASS_SDCARDFS
- if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
- LOG(DEBUG) << "Found storage mount " << source << " at " << target;
- mStorageMounts[source] = target;
- }
+ if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+ LOG(DEBUG) << "Found storage mount " << source << " at " << target;
+ mStorageMounts[source] = target;
+ }
#endif
+ }
}
return ok();
}
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 2b7bf33..ef91bf8 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -119,9 +119,6 @@
binder::Status destroyProfileSnapshot(const std::string& packageName,
const std::string& profileName);
- binder::Status idmap(const std::string& targetApkPath, const std::string& overlayApkPath,
- int32_t uid);
- binder::Status removeIdmap(const std::string& overlayApkPath);
binder::Status rmPackageDir(const std::string& packageDir);
binder::Status markBootComplete(const std::string& instructionSet);
binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes,
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index d99bcc8..6cc4bde 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -72,8 +72,6 @@
@utf8InCpp String profileName, @utf8InCpp String classpath);
void destroyProfileSnapshot(@utf8InCpp String packageName, @utf8InCpp String profileName);
- void idmap(@utf8InCpp String targetApkPath, @utf8InCpp String overlayApkPath, int uid);
- void removeIdmap(@utf8InCpp String overlayApkPath);
void rmPackageDir(@utf8InCpp String packageDir);
void markBootComplete(@utf8InCpp String instructionSet);
void freeCache(@nullable @utf8InCpp String uuid, long targetFreeBytes,
diff --git a/include/android/input.h b/include/android/input.h
index ce439c6..f51cd79 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -158,7 +158,10 @@
AINPUT_EVENT_TYPE_KEY = 1,
/** Indicates that the input event is a motion event. */
- AINPUT_EVENT_TYPE_MOTION = 2
+ AINPUT_EVENT_TYPE_MOTION = 2,
+
+ /** Focus event */
+ AINPUT_EVENT_TYPE_FOCUS = 3,
};
/**
diff --git a/include/binder b/include/binder
deleted file mode 120000
index 35a022a..0000000
--- a/include/binder
+++ /dev/null
@@ -1 +0,0 @@
-../libs/binder/include/binder/
\ No newline at end of file
diff --git a/include/binder/Binder.h b/include/binder/Binder.h
new file mode 120000
index 0000000..0fc6db7
--- /dev/null
+++ b/include/binder/Binder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Binder.h
\ No newline at end of file
diff --git a/include/binder/BinderService.h b/include/binder/BinderService.h
new file mode 120000
index 0000000..370b260
--- /dev/null
+++ b/include/binder/BinderService.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/BinderService.h
\ No newline at end of file
diff --git a/include/binder/IBinder.h b/include/binder/IBinder.h
new file mode 120000
index 0000000..93a6219
--- /dev/null
+++ b/include/binder/IBinder.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IBinder.h
\ No newline at end of file
diff --git a/include/binder/IInterface.h b/include/binder/IInterface.h
new file mode 120000
index 0000000..8579878
--- /dev/null
+++ b/include/binder/IInterface.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IInterface.h
\ No newline at end of file
diff --git a/include/binder/IMemory.h b/include/binder/IMemory.h
new file mode 120000
index 0000000..5171c08
--- /dev/null
+++ b/include/binder/IMemory.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IMemory.h
\ No newline at end of file
diff --git a/include/binder/IPCThreadState.h b/include/binder/IPCThreadState.h
new file mode 120000
index 0000000..ecd4f81
--- /dev/null
+++ b/include/binder/IPCThreadState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IPCThreadState.h
\ No newline at end of file
diff --git a/include/binder/IServiceManager.h b/include/binder/IServiceManager.h
new file mode 120000
index 0000000..33d18cc
--- /dev/null
+++ b/include/binder/IServiceManager.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/IServiceManager.h
\ No newline at end of file
diff --git a/include/binder/MemoryDealer.h b/include/binder/MemoryDealer.h
new file mode 120000
index 0000000..71881fb
--- /dev/null
+++ b/include/binder/MemoryDealer.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryDealer.h
\ No newline at end of file
diff --git a/include/binder/MemoryHeapBase.h b/include/binder/MemoryHeapBase.h
new file mode 120000
index 0000000..8fb51cc
--- /dev/null
+++ b/include/binder/MemoryHeapBase.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/MemoryHeapBase.h
\ No newline at end of file
diff --git a/include/binder/Parcel.h b/include/binder/Parcel.h
new file mode 120000
index 0000000..23492be
--- /dev/null
+++ b/include/binder/Parcel.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcel.h
\ No newline at end of file
diff --git a/include/binder/ParcelFileDescriptor.h b/include/binder/ParcelFileDescriptor.h
new file mode 120000
index 0000000..777bd49
--- /dev/null
+++ b/include/binder/ParcelFileDescriptor.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ParcelFileDescriptor.h
\ No newline at end of file
diff --git a/include/binder/Parcelable.h b/include/binder/Parcelable.h
new file mode 120000
index 0000000..438e223
--- /dev/null
+++ b/include/binder/Parcelable.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Parcelable.h
\ No newline at end of file
diff --git a/include/binder/PermissionCache.h b/include/binder/PermissionCache.h
new file mode 120000
index 0000000..e910c12
--- /dev/null
+++ b/include/binder/PermissionCache.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PermissionCache.h
\ No newline at end of file
diff --git a/include/binder/PersistableBundle.h b/include/binder/PersistableBundle.h
new file mode 120000
index 0000000..785f2b4
--- /dev/null
+++ b/include/binder/PersistableBundle.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/PersistableBundle.h
\ No newline at end of file
diff --git a/include/binder/ProcessState.h b/include/binder/ProcessState.h
new file mode 120000
index 0000000..4cbe7a5
--- /dev/null
+++ b/include/binder/ProcessState.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/ProcessState.h
\ No newline at end of file
diff --git a/include/binder/Stability.h b/include/binder/Stability.h
new file mode 120000
index 0000000..9b431d2
--- /dev/null
+++ b/include/binder/Stability.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Stability.h
\ No newline at end of file
diff --git a/include/binder/Status.h b/include/binder/Status.h
new file mode 120000
index 0000000..ccb994e
--- /dev/null
+++ b/include/binder/Status.h
@@ -0,0 +1 @@
+../../libs/binder/include/binder/Status.h
\ No newline at end of file
diff --git a/include/input/Input.h b/include/input/Input.h
index cbd1a41..f871847 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -31,8 +31,8 @@
#include <utils/RefBase.h>
#include <utils/Timers.h>
#include <utils/Vector.h>
-
#include <limits>
+#include <queue>
/*
* Additional private constants not defined in ndk/ui/input.h.
@@ -167,6 +167,8 @@
class Parcel;
#endif
+const char* inputEventTypeToString(int32_t type);
+
/*
* Flags that flow alongside events in the input dispatch system to help with certain
* policy decisions such as waking from device sleep.
@@ -687,6 +689,28 @@
};
/*
+ * Focus events.
+ */
+class FocusEvent : public InputEvent {
+public:
+ virtual ~FocusEvent() {}
+
+ virtual int32_t getType() const override { return AINPUT_EVENT_TYPE_FOCUS; }
+
+ inline bool getHasFocus() const { return mHasFocus; }
+
+ inline bool getInTouchMode() const { return mInTouchMode; }
+
+ void initialize(bool hasFocus, bool inTouchMode);
+
+ void initialize(const FocusEvent& from);
+
+protected:
+ bool mHasFocus;
+ bool mInTouchMode;
+};
+
+/*
* Input event factory.
*/
class InputEventFactoryInterface {
@@ -698,6 +722,7 @@
virtual KeyEvent* createKeyEvent() = 0;
virtual MotionEvent* createMotionEvent() = 0;
+ virtual FocusEvent* createFocusEvent() = 0;
};
/*
@@ -709,12 +734,14 @@
PreallocatedInputEventFactory() { }
virtual ~PreallocatedInputEventFactory() { }
- virtual KeyEvent* createKeyEvent() { return & mKeyEvent; }
- virtual MotionEvent* createMotionEvent() { return & mMotionEvent; }
+ virtual KeyEvent* createKeyEvent() override { return &mKeyEvent; }
+ virtual MotionEvent* createMotionEvent() override { return &mMotionEvent; }
+ virtual FocusEvent* createFocusEvent() override { return &mFocusEvent; }
private:
KeyEvent mKeyEvent;
MotionEvent mMotionEvent;
+ FocusEvent mFocusEvent;
};
/*
@@ -725,16 +752,18 @@
explicit PooledInputEventFactory(size_t maxPoolSize = 20);
virtual ~PooledInputEventFactory();
- virtual KeyEvent* createKeyEvent();
- virtual MotionEvent* createMotionEvent();
+ virtual KeyEvent* createKeyEvent() override;
+ virtual MotionEvent* createMotionEvent() override;
+ virtual FocusEvent* createFocusEvent() override;
void recycle(InputEvent* event);
private:
const size_t mMaxPoolSize;
- Vector<KeyEvent*> mKeyEventPool;
- Vector<MotionEvent*> mMotionEventPool;
+ std::queue<std::unique_ptr<KeyEvent>> mKeyEventPool;
+ std::queue<std::unique_ptr<MotionEvent>> mMotionEventPool;
+ std::queue<std::unique_ptr<FocusEvent>> mFocusEventPool;
};
} // namespace android
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 94d90ad..ae47438 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -64,6 +64,7 @@
KEY,
MOTION,
FINISHED,
+ FOCUS,
};
struct Header {
@@ -92,9 +93,7 @@
uint32_t empty2;
nsecs_t downTime __attribute__((aligned(8)));
- inline size_t size() const {
- return sizeof(Key);
- }
+ inline size_t size() const { return sizeof(Key); }
} key;
struct Motion {
@@ -110,7 +109,7 @@
int32_t metaState;
int32_t buttonState;
MotionClassification classification; // base type: uint8_t
- uint8_t empty2[3];
+ uint8_t empty2[3]; // 3 bytes to fill gap created by classification
int32_t edgeFlags;
nsecs_t downTime __attribute__((aligned(8)));
float xOffset;
@@ -121,11 +120,16 @@
float yCursorPosition;
uint32_t pointerCount;
uint32_t empty3;
- // Note that PointerCoords requires 8 byte alignment.
+ /**
+ * The "pointers" field must be the last field of the struct InputMessage.
+ * When we send the struct InputMessage across the socket, we are not
+ * writing the entire "pointers" array, but only the pointerCount portion
+ * of it as an optimization. Adding a field after "pointers" would break this.
+ */
struct Pointer {
PointerProperties properties;
PointerCoords coords;
- } pointers[MAX_POINTERS];
+ } pointers[MAX_POINTERS] __attribute__((aligned(8)));
int32_t getActionId() const {
uint32_t index = (action & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK)
@@ -141,12 +145,19 @@
struct Finished {
uint32_t seq;
- bool handled;
+ uint32_t handled; // actually a bool, but we must maintain 8-byte alignment
- inline size_t size() const {
- return sizeof(Finished);
- }
+ inline size_t size() const { return sizeof(Finished); }
} finished;
+
+ struct Focus {
+ uint32_t seq;
+ // The following two fields take up 4 bytes total
+ uint16_t hasFocus; // actually a bool
+ uint16_t inTouchMode; // actually a bool, but we must maintain 8-byte alignment
+
+ inline size_t size() const { return sizeof(Focus); }
+ } focus;
} __attribute__((aligned(8))) body;
bool isValid(size_t actualSize) const;
@@ -289,6 +300,15 @@
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords);
+ /* Publishes a focus event to the input channel.
+ *
+ * Returns OK on success.
+ * Returns WOULD_BLOCK if the channel is full.
+ * Returns DEAD_OBJECT if the channel's peer has been closed.
+ * Other errors probably indicate that the channel is broken.
+ */
+ status_t publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode);
+
/* Receives the finished signal from the consumer in reply to the original dispatch signal.
* If a signal was received, returns the message sequence number,
* and whether the consumer handled the message.
@@ -344,8 +364,8 @@
* Returns NO_MEMORY if the event could not be created.
* Other errors probably indicate that the channel is broken.
*/
- status_t consume(InputEventFactoryInterface* factory, bool consumeBatches,
- nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent);
+ status_t consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime,
+ uint32_t* outSeq, InputEvent** outEvent);
/* Sends a finished signal to the publisher to inform it that the message
* with the specified sequence number has finished being process and whether
@@ -516,6 +536,7 @@
static void rewriteMessage(TouchState& state, InputMessage& msg);
static void initializeKeyEvent(KeyEvent* event, const InputMessage* msg);
static void initializeMotionEvent(MotionEvent* event, const InputMessage* msg);
+ static void initializeFocusEvent(FocusEvent* event, const InputMessage* msg);
static void addSample(MotionEvent* event, const InputMessage* msg);
static bool canAddSample(const Batch& batch, const InputMessage* msg);
static ssize_t findSampleNoLaterThan(const Batch& batch, nsecs_t time);
diff --git a/include/ui b/include/ui
deleted file mode 120000
index 2fb3147..0000000
--- a/include/ui
+++ /dev/null
@@ -1 +0,0 @@
-../libs/ui/include/ui
\ No newline at end of file
diff --git a/include/ui/DisplayInfo.h b/include/ui/DisplayInfo.h
new file mode 120000
index 0000000..9a195ea
--- /dev/null
+++ b/include/ui/DisplayInfo.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/DisplayInfo.h
\ No newline at end of file
diff --git a/include/ui/FloatRect.h b/include/ui/FloatRect.h
new file mode 120000
index 0000000..d7bd737
--- /dev/null
+++ b/include/ui/FloatRect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/FloatRect.h
\ No newline at end of file
diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h
new file mode 120000
index 0000000..4085433
--- /dev/null
+++ b/include/ui/PixelFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PixelFormat.h
\ No newline at end of file
diff --git a/include/ui/Point.h b/include/ui/Point.h
new file mode 120000
index 0000000..443938b
--- /dev/null
+++ b/include/ui/Point.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Point.h
\ No newline at end of file
diff --git a/include/ui/PublicFormat.h b/include/ui/PublicFormat.h
new file mode 120000
index 0000000..7984c0e
--- /dev/null
+++ b/include/ui/PublicFormat.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/PublicFormat.h
\ No newline at end of file
diff --git a/include/ui/Rect.h b/include/ui/Rect.h
new file mode 120000
index 0000000..a99c5f2
--- /dev/null
+++ b/include/ui/Rect.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Rect.h
\ No newline at end of file
diff --git a/include/ui/Region.h b/include/ui/Region.h
new file mode 120000
index 0000000..2e46e0f
--- /dev/null
+++ b/include/ui/Region.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Region.h
\ No newline at end of file
diff --git a/include/ui/Size.h b/include/ui/Size.h
new file mode 120000
index 0000000..c0da99b
--- /dev/null
+++ b/include/ui/Size.h
@@ -0,0 +1 @@
+../../libs/ui/include/ui/Size.h
\ No newline at end of file
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 4f47db1..bac8b66 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -280,19 +280,31 @@
std::condition_variable mCv;
};
+ // Simple RAII object to ensure a function call immediately before going out of scope
+ class Defer {
+ public:
+ Defer(std::function<void()>&& f) : mF(std::move(f)) {}
+ ~Defer() { mF(); }
+ private:
+ std::function<void()> mF;
+ };
+
const std::string name = String8(name16).c_str();
sp<IBinder> out;
if (!mTheRealServiceManager->getService(name, &out).isOk()) {
return nullptr;
}
- if(out != nullptr) return out;
+ if (out != nullptr) return out;
sp<Waiter> waiter = new Waiter;
if (!mTheRealServiceManager->registerForNotifications(
name, waiter).isOk()) {
return nullptr;
}
+ Defer unregister ([&] {
+ mTheRealServiceManager->unregisterForNotifications(name, waiter);
+ });
while(true) {
{
@@ -316,7 +328,7 @@
if (!mTheRealServiceManager->getService(name, &out).isOk()) {
return nullptr;
}
- if(out != nullptr) return out;
+ if (out != nullptr) return out;
ALOGW("Waited one second for %s", name.c_str());
}
diff --git a/libs/binder/include/binder/IInterface.h b/libs/binder/include/binder/IInterface.h
index 28ffa48..79d9b79 100644
--- a/libs/binder/include/binder/IInterface.h
+++ b/libs/binder/include/binder/IInterface.h
@@ -38,12 +38,32 @@
// ----------------------------------------------------------------------
+/**
+ * If this is a local object and the descriptor matches, this will return the
+ * actual local object which is implementing the interface. Otherwise, this will
+ * return a proxy to the interface without checking the interface descriptor.
+ * This means that subsequent calls may fail with BAD_TYPE.
+ */
template<typename INTERFACE>
inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj)
{
return INTERFACE::asInterface(obj);
}
+/**
+ * This is the same as interface_cast, except that it always checks to make sure
+ * the descriptor matches, and if it doesn't match, it will return nullptr.
+ */
+template<typename INTERFACE>
+inline sp<INTERFACE> checked_interface_cast(const sp<IBinder>& obj)
+{
+ if (obj->getInterfaceDescriptor() != INTERFACE::descriptor) {
+ return nullptr;
+ }
+
+ return interface_cast<INTERFACE>(obj);
+}
+
// ----------------------------------------------------------------------
template<typename INTERFACE>
@@ -89,7 +109,27 @@
#define __IINTF_CONCAT(x, y) (x ## y)
+
+#ifndef DO_NOT_CHECK_MANUAL_BINDER_INTERFACES
+
#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ static_assert(internal::allowedManualInterface(NAME), \
+ "b/64223827: Manually written binder interfaces are " \
+ "considered error prone and frequently have bugs. " \
+ "The preferred way to add interfaces is to define " \
+ "an .aidl file to auto-generate the interface. If " \
+ "an interface must be manually written, add its " \
+ "name to the whitelist."); \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+
+#else
+
+#define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+ DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \
+
+#endif
+
+#define DO_NOT_DIRECTLY_USE_ME_IMPLEMENT_META_INTERFACE(INTERFACE, NAME)\
const ::android::StaticString16 \
I##INTERFACE##_descriptor_static_str16(__IINTF_CONCAT(u, NAME));\
const ::android::String16 I##INTERFACE::descriptor( \
@@ -172,6 +212,122 @@
// ----------------------------------------------------------------------
+namespace internal {
+constexpr const char* const kManualInterfaces[] = {
+ "android.app.IActivityManager",
+ "android.app.IUidObserver",
+ "android.drm.IDrm",
+ "android.dvr.IVsyncCallback",
+ "android.dvr.IVsyncService",
+ "android.gfx.tests.ICallback",
+ "android.gfx.tests.IIPCTest",
+ "android.gfx.tests.ISafeInterfaceTest",
+ "android.graphicsenv.IGpuService",
+ "android.gui.DisplayEventConnection",
+ "android.gui.IConsumerListener",
+ "android.gui.IGraphicBufferConsumer",
+ "android.gui.IRegionSamplingListener",
+ "android.gui.ITransactionComposerListener",
+ "android.gui.SensorEventConnection",
+ "android.gui.SensorServer",
+ "android.hardware.ICamera",
+ "android.hardware.ICameraClient",
+ "android.hardware.ICameraRecordingProxy",
+ "android.hardware.ICameraRecordingProxyListener",
+ "android.hardware.ICrypto",
+ "android.hardware.IOMXObserver",
+ "android.hardware.ISoundTrigger",
+ "android.hardware.ISoundTriggerClient",
+ "android.hardware.ISoundTriggerHwService",
+ "android.hardware.IStreamListener",
+ "android.hardware.IStreamSource",
+ "android.input.IInputFlinger",
+ "android.input.ISetInputWindowsListener",
+ "android.media.IAudioFlinger",
+ "android.media.IAudioFlingerClient",
+ "android.media.IAudioPolicyService",
+ "android.media.IAudioPolicyServiceClient",
+ "android.media.IAudioService",
+ "android.media.IAudioTrack",
+ "android.media.IDataSource",
+ "android.media.IDrmClient",
+ "android.media.IEffect",
+ "android.media.IEffectClient",
+ "android.media.IMediaAnalyticsService",
+ "android.media.IMediaCodecList",
+ "android.media.IMediaDrmService",
+ "android.media.IMediaExtractor",
+ "android.media.IMediaExtractorService",
+ "android.media.IMediaHTTPConnection",
+ "android.media.IMediaHTTPService",
+ "android.media.IMediaLogService",
+ "android.media.IMediaMetadataRetriever",
+ "android.media.IMediaPlayer",
+ "android.media.IMediaPlayerClient",
+ "android.media.IMediaPlayerService",
+ "android.media.IMediaRecorder",
+ "android.media.IMediaRecorderClient",
+ "android.media.IMediaResourceMonitor",
+ "android.media.IMediaSource",
+ "android.media.IRemoteDisplay",
+ "android.media.IRemoteDisplayClient",
+ "android.media.IResourceManagerClient",
+ "android.media.IResourceManagerService",
+ "android.os.IComplexTypeInterface",
+ "android.os.IPermissionController",
+ "android.os.IPingResponder",
+ "android.os.IPowerManager",
+ "android.os.IProcessInfoService",
+ "android.os.ISchedulingPolicyService",
+ "android.os.IStringConstants",
+ "android.os.storage.IObbActionListener",
+ "android.os.storage.IStorageEventListener",
+ "android.os.storage.IStorageManager",
+ "android.os.storage.IStorageShutdownObserver",
+ "android.service.vr.IPersistentVrStateCallbacks",
+ "android.service.vr.IVrManager",
+ "android.service.vr.IVrStateCallbacks",
+ "android.ui.ISurfaceComposer",
+ "android.ui.ISurfaceComposerClient",
+ "android.utils.IMemory",
+ "android.utils.IMemoryHeap",
+ "com.android.car.procfsinspector.IProcfsInspector",
+ "com.android.internal.app.IAppOpsCallback",
+ "com.android.internal.app.IAppOpsService",
+ "com.android.internal.app.IBatteryStats",
+ "com.android.internal.os.IResultReceiver",
+ "com.android.internal.os.IShellCallback",
+ "drm.IDrmManagerService",
+ "drm.IDrmServiceListener",
+ "IAAudioClient",
+ "IAAudioService",
+ "VtsFuzzer",
+ nullptr,
+};
+
+constexpr const char* const kDownstreamManualInterfaces[] = {
+ // Add downstream interfaces here.
+ nullptr,
+};
+
+constexpr bool equals(const char* a, const char* b) {
+ if (*a != *b) return false;
+ if (*a == '\0') return true;
+ return equals(a + 1, b + 1);
+}
+
+constexpr bool inList(const char* a, const char* const* whitelist) {
+ if (*whitelist == nullptr) return false;
+ if (equals(a, *whitelist)) return true;
+ return inList(a, whitelist + 1);
+}
+
+constexpr bool allowedManualInterface(const char* name) {
+ return inList(name, kManualInterfaces) ||
+ inList(name, kDownstreamManualInterfaces);
+}
+
+} // namespace internal
} // namespace android
#endif // ANDROID_IINTERFACE_H
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 4d5c044..4560f22 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -34,6 +34,12 @@
#include <android/binder_status.h>
__BEGIN_DECLS
+
+#ifndef __ANDROID_API__
+#error Android builds must be compiled against a specific API. If this is an \
+ android platform host build, you must use libbinder_ndk_host_user.
+#endif
+
#if __ANDROID_API__ >= 29
// Also see TF_* in kernel's binder.h
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 56b94c1..0477801 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,7 +16,9 @@
#include <set>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <dumputils/dump_utils.h>
#include <log/log.h>
@@ -68,13 +70,34 @@
NULL,
};
-bool should_dump_hal_interface(const char* interface) {
+/* list of extra hal interfaces to dump containing process during native dumps */
+// This is filled when dumpstate is called.
+static std::set<const std::string> extra_hal_interfaces_to_dump;
+
+static void read_extra_hals_to_dump_from_property() {
+ // extra hals to dump are already filled
+ if (extra_hal_interfaces_to_dump.size() > 0) {
+ return;
+ }
+ std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
+ std::vector<std::string> tokens = android::base::Split(value, ",");
+ for (const auto &token : tokens) {
+ std::string trimmed_token = android::base::Trim(token);
+ if (trimmed_token.length() == 0) {
+ continue;
+ }
+ extra_hal_interfaces_to_dump.insert(trimmed_token);
+ }
+}
+
+// check if interface is included in either default hal list or extra hal list
+bool should_dump_hal_interface(const std::string& interface) {
for (const char** i = hal_interfaces_to_dump; *i; i++) {
- if (!strcmp(*i, interface)) {
+ if (interface == *i) {
return true;
}
}
- return false;
+ return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end();
}
bool should_dump_native_traces(const char* path) {
@@ -94,13 +117,15 @@
sp<IServiceManager> manager = IServiceManager::getService();
std::set<int> pids;
+ read_extra_hals_to_dump_from_property();
+
Return<void> ret = manager->debugDump([&](auto& hals) {
for (const auto &info : hals) {
if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
continue;
}
- if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+ if (!should_dump_hal_interface(info.interfaceName)) {
continue;
}
diff --git a/libs/gralloc/types/Android.bp b/libs/gralloc/types/Android.bp
new file mode 100644
index 0000000..cb5305f
--- /dev/null
+++ b/libs/gralloc/types/Android.bp
@@ -0,0 +1,54 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library {
+ name: "libgralloctypes",
+ defaults: ["libbinder_ndk_host_user"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-enum-compare",
+ ],
+ host_supported: true,
+
+ vendor_available: true,
+ vndk: {
+ enabled: true,
+ support_system_process: true,
+ },
+
+ srcs: [
+ "Gralloc4.cpp"
+ ],
+
+ shared_libs: [
+ "android.hardware.graphics.mapper@4.0",
+ "libhidlbase",
+ "liblog",
+ "vintf-graphics-common-ndk_platform",
+ ],
+
+ export_shared_lib_headers: [
+ "android.hardware.graphics.mapper@4.0",
+ "vintf-graphics-common-ndk_platform",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ sanitize: {
+ misc_undefined: ["integer"],
+ },
+}
diff --git a/libs/gralloc/types/Gralloc4.cpp b/libs/gralloc/types/Gralloc4.cpp
new file mode 100644
index 0000000..0330dac
--- /dev/null
+++ b/libs/gralloc/types/Gralloc4.cpp
@@ -0,0 +1,696 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstring>
+#include <cinttypes>
+#include <limits>
+
+#include <hidl/HidlSupport.h>
+#include <log/log.h>
+
+#include "gralloctypes/Gralloc4.h"
+
+using android::hardware::hidl_vec;
+
+using ::aidl::android::hardware::graphics::common::BlendMode;
+using ::aidl::android::hardware::graphics::common::Dataspace;
+using ::aidl::android::hardware::graphics::common::PlaneLayout;
+using ::aidl::android::hardware::graphics::common::PlaneLayoutComponent;
+using ::aidl::android::hardware::graphics::common::ExtendableType;
+using ::aidl::android::hardware::graphics::common::Rect;
+using ::aidl::android::hardware::graphics::common::StandardMetadataType;
+
+using MetadataType = ::android::hardware::graphics::mapper::V4_0::IMapper::MetadataType;
+
+namespace android {
+
+namespace gralloc4 {
+
+static inline bool hasAdditionOverflow(size_t a, size_t b) {
+ return a > SIZE_MAX - b;
+}
+
+/**
+ * OutputHidlVec represents the hidl_vec that is outputed when a type is encoded into a byte stream.
+ * This class is used to track the current state of a hidl_vec as it is filled with the encoded
+ * byte stream.
+ *
+ * This type is needed because hidl_vec's resize() allocates a new backing array every time.
+ * This type does not need an copies and only needs one resize operation.
+ */
+class OutputHidlVec {
+public:
+ OutputHidlVec(hidl_vec<uint8_t>* vec)
+ : mVec(vec) {}
+
+ status_t resize() {
+ if (!mVec) {
+ return BAD_VALUE;
+ }
+ mVec->resize(mNeededResize);
+ return NO_ERROR;
+ }
+
+ status_t encode(const uint8_t* data, size_t size) {
+ if (!mVec) {
+ return BAD_VALUE;
+ }
+ if (mVec->size() == 0) {
+ if (hasAdditionOverflow(mNeededResize, size)) {
+ clear();
+ return BAD_VALUE;
+ }
+ mNeededResize += size;
+ return NO_ERROR;
+ }
+
+ if (hasAdditionOverflow(mOffset, size) || (mVec->size() < size + mOffset)) {
+ clear();
+ return BAD_VALUE;
+ }
+
+ std::copy(data, data + size, mVec->data() + mOffset);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ void clear() {
+ if (mVec) {
+ mVec->resize(0);
+ }
+ mNeededResize = 0;
+ mOffset = 0;
+ }
+
+private:
+ hidl_vec<uint8_t>* mVec;
+ size_t mNeededResize = 0;
+ size_t mOffset = 0;
+};
+
+/**
+ * InputHidlVec represents the hidl_vec byte stream that is inputed when a type is decoded.
+ * This class is used to track the current index of the byte stream of the hidl_vec as it is
+ * decoded.
+ */
+class InputHidlVec {
+public:
+ InputHidlVec(const hidl_vec<uint8_t>* vec)
+ : mVec(vec) {}
+
+ status_t decode(uint8_t* data, size_t size) {
+ if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+ return BAD_VALUE;
+ }
+
+ std::copy(mVec->data() + mOffset, mVec->data() + mOffset + size, data);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ status_t decode(std::string* string, size_t size) {
+ if (!mVec || hasAdditionOverflow(mOffset, size) || mOffset + size > mVec->size()) {
+ return BAD_VALUE;
+ }
+
+ string->assign(mVec->data() + mOffset, mVec->data() + mOffset + size);
+
+ mOffset += size;
+ return NO_ERROR;
+ }
+
+ bool hasRemainingData() {
+ if (!mVec) {
+ return false;
+ }
+ return mVec->size() - mOffset;
+ }
+
+private:
+ const hidl_vec<uint8_t>* mVec;
+ size_t mOffset = 0;
+};
+
+/**
+ * EncodeHelper is a function type that encodes T into the OutputHidlVec.
+ */
+template<class T>
+using EncodeHelper = status_t(*)(const T&, OutputHidlVec*);
+
+/**
+ * DecodeHelper is a function type that decodes InputHidlVec into T.
+ */
+template<class T>
+using DecodeHelper = status_t(*)(InputHidlVec*, T*);
+
+/**
+ * ErrorHandler is a function type that is called when the corresponding DecodeHelper function
+ * fails. ErrorHandler cleans up the object T so the caller doesn't receive a partially created
+ * T.
+ */
+template<class T>
+using ErrorHandler = void(*)(T*);
+
+/**
+ * encode is the main encoding function. It takes in T and uses the encodeHelper function to turn T
+ * into the hidl_vec byte stream.
+ *
+ * This function first calls the encodeHelper function to determine how large the hidl_vec
+ * needs to be. It resizes the hidl_vec. Finally, it reruns the encodeHelper function which
+ * 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) {
+ OutputHidlVec outputHidlVec{output};
+ status_t err = encodeHelper(input, &outputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ err = outputHidlVec.resize();
+ if (err) {
+ return err;
+ }
+
+ return encodeHelper(input, &outputHidlVec);
+}
+
+/**
+ * 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) {
+ InputHidlVec inputHidlVec{&input};
+ status_t err = decodeHelper(&inputHidlVec, output);
+ if (err) {
+ return err;
+ }
+
+ err = inputHidlVec.hasRemainingData();
+ if (err) {
+ if (errorHandler) {
+ errorHandler(output);
+ }
+ return BAD_VALUE;
+ }
+
+ return NO_ERROR;
+}
+
+/**
+ * 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);
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ const uint8_t* tmp = reinterpret_cast<const uint8_t*>(&input);
+ return output->encode(tmp, sizeof(input));
+}
+
+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);
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ uint8_t* tmp = reinterpret_cast<uint8_t*>(output);
+ return input->decode(tmp, sizeof(*output));
+}
+
+status_t encodeString(const std::string& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(input.size(), output);
+ if (err) {
+ return err;
+ }
+
+ return output->encode(reinterpret_cast<const uint8_t*>(input.c_str()), input.size());
+}
+
+status_t decodeString(InputHidlVec* input, std::string* 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);
+}
+
+status_t encodeExtendableType(const ExtendableType& 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 decodeExtendableType(InputHidlVec* input, ExtendableType* output) {
+ status_t err = decodeString(input, &output->name);
+ if (err) {
+ return err;
+ }
+
+ err = decodeInteger<int64_t>(input, &output->value);
+ if (err) {
+ return err;
+ }
+
+ return NO_ERROR;
+}
+
+void clearExtendableType(ExtendableType* output) {
+ if (!output) {
+ return;
+ }
+ output->name.clear();
+ output->value = 0;
+}
+
+status_t encodeRect(const Rect& input, OutputHidlVec* output) {
+ status_t err = encodeInteger<int32_t>(static_cast<int32_t>(input.left), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.top), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int32_t>(static_cast<int32_t>(input.right), output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<int32_t>(static_cast<int32_t>(input.bottom), output);
+}
+
+status_t decodeRect(InputHidlVec* input, Rect* output) {
+ status_t err = decodeInteger<int32_t>(input, &output->left);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, &output->top);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int32_t>(input, &output->right);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<int32_t>(input, &output->bottom);
+}
+
+status_t encodePlaneLayoutComponent(const PlaneLayoutComponent& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeExtendableType(input.type, output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int64_t>(input.offsetInBits), output);
+ if (err) {
+ return err;
+ }
+ return encodeInteger<int64_t>(static_cast<int64_t>(input.sizeInBits), output);
+}
+
+status_t decodePlaneLayoutComponent(InputHidlVec* input, PlaneLayoutComponent* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = decodeExtendableType(input, &output->type);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->offsetInBits);
+ if (err) {
+ return err;
+ }
+ return decodeInteger<int64_t>(input, &output->sizeInBits);
+}
+
+status_t encodePlaneLayoutComponents(const std::vector<PlaneLayoutComponent>& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(input.size()), output);
+ if (err) {
+ return err;
+ }
+
+ for (const auto& planeLayoutComponent: input) {
+ err = encodePlaneLayoutComponent(planeLayoutComponent, output);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t decodePlaneLayoutComponents(InputHidlVec* input, std::vector<PlaneLayoutComponent>* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ int64_t size = 0;
+ status_t err = decodeInteger<int64_t>(input, &size);
+ if (err || size < 0) {
+ return err;
+ }
+
+ for (int i = 0; i < size; i++) {
+ output->emplace_back();
+ err = decodePlaneLayoutComponent(input, &output->back());
+ if (err) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+status_t encodePlaneLayout(const PlaneLayout& input, OutputHidlVec* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = encodePlaneLayoutComponents(input.components, output);
+ if (err) {
+ return err;
+ }
+
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.offsetInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.sampleIncrementInBits), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.strideInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.widthInSamples), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.heightInSamples), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.totalSizeInBytes), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.horizontalSubsampling), output);
+ if (err) {
+ return err;
+ }
+ err = encodeInteger<int64_t>(static_cast<int32_t>(input.verticalSubsampling), output);
+ if (err) {
+ return err;
+ }
+
+ return encodeRect(input.crop, output);
+}
+
+status_t decodePlaneLayout(InputHidlVec* input, PlaneLayout* output) {
+ if (!output) {
+ return BAD_VALUE;
+ }
+
+ status_t err = decodePlaneLayoutComponents(input, &output->components);
+ if (err) {
+ return err;
+ }
+
+ err = decodeInteger<int64_t>(input, &output->offsetInBytes);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->sampleIncrementInBits);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->strideInBytes);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->widthInSamples);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->heightInSamples);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->totalSizeInBytes);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->horizontalSubsampling);
+ if (err) {
+ return err;
+ }
+ err = decodeInteger<int64_t>(input, &output->verticalSubsampling);
+ if (err) {
+ return err;
+ }
+
+ return decodeRect(input, &output->crop);
+}
+
+status_t encodePlaneLayoutsHelper(const std::vector<PlaneLayout>& planeLayouts, OutputHidlVec* outOutputHidlVec) {
+ status_t err = encodeInteger<int64_t>(static_cast<int64_t>(planeLayouts.size()), outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+
+ for (const auto& planeLayout : planeLayouts) {
+ err = encodePlaneLayout(planeLayout, outOutputHidlVec);
+ if (err) {
+ return err;
+ }
+ }
+
+ return NO_ERROR;
+}
+
+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) {
+ return err;
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ outPlaneLayouts->emplace_back();
+ err = decodePlaneLayout(inputHidlVec, &outPlaneLayouts->back());
+ if (err) {
+ return err;
+ }
+ }
+ return NO_ERROR;
+}
+
+void clearPlaneLayouts(std::vector<PlaneLayout>* output) {
+ if (!output) {
+ return;
+ }
+ output->clear();
+}
+
+/**
+ * Public API functions
+ */
+bool isStandardMetadataType(const MetadataType& metadataType) {
+ return !std::strncmp(metadataType.name.c_str(), GRALLOC4_STANDARD_METADATA_TYPE, metadataType.name.size());
+}
+
+StandardMetadataType getStandardMetadataTypeValue(const MetadataType& metadataType) {
+ return static_cast<StandardMetadataType>(metadataType.value);
+}
+
+status_t encodeBufferId(uint64_t bufferId, hidl_vec<uint8_t>* outBufferId) {
+ return encode(bufferId, outBufferId, encodeInteger);
+}
+
+status_t decodeBufferId(const hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId) {
+ return decode(bufferId, outBufferId, decodeInteger);
+}
+
+status_t encodeName(const std::string& name, hidl_vec<uint8_t>* outName) {
+ return encode(name, outName, encodeString);
+}
+
+status_t decodeName(const hidl_vec<uint8_t>& name, std::string* outName) {
+ return decode(name, outName, decodeString);
+}
+
+status_t encodeWidth(uint64_t width, hidl_vec<uint8_t>* outWidth) {
+ return encode(width, outWidth, encodeInteger);
+}
+
+status_t decodeWidth(const hidl_vec<uint8_t>& width, uint64_t* outWidth) {
+ return decode(width, outWidth, decodeInteger);
+}
+
+status_t encodeHeight(uint64_t height, hidl_vec<uint8_t>* outHeight) {
+ return encode(height, outHeight, encodeInteger);
+}
+
+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);
+}
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/libs/gralloc/types/fuzzer/Android.bp b/libs/gralloc/types/fuzzer/Android.bp
new file mode 100644
index 0000000..8444883
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/Android.bp
@@ -0,0 +1,32 @@
+cc_fuzz {
+ name: "libgralloctypes_fuzzer",
+ defaults: ["libbinder_ndk_host_user"],
+ host_supported: true,
+
+ fuzz_config: {
+ cc: ["marissaw@google.com"],
+ },
+
+ srcs: [
+ "gralloctypes.cpp",
+ "main.cpp",
+ "util.cpp",
+ ],
+ static_libs: [
+ "libbase",
+ "libcgrouprc",
+ "libcgrouprc_format",
+ "libcutils",
+ "libgralloctypes",
+ "libhidlbase",
+ "liblog",
+ "libprocessgroup",
+ "libjsoncpp",
+ "libutils",
+ ],
+
+ // This flag enables verbose output in the fuzz target, and is very useful
+ // for debugging a failure. If you are trying to diagnose how a crash was
+ // produced, you may find uncommenting the below line very useful.
+ // cflags: ["-DENABLE_LOG_FUZZ"],
+}
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.cpp b/libs/gralloc/types/fuzzer/gralloctypes.cpp
new file mode 100644
index 0000000..23c90b8
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 FUZZ_LOG_TAG "gralloctypes"
+
+#include <cstdint>
+
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+#include <utils/Errors.h>
+
+#include "gralloctypes.h"
+#include "util.h"
+
+using ::android::status_t;
+
+#define GRALLOCTYPES_DECODE(T, FUNC) \
+ [] (const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t /*data*/) {\
+ FUZZ_LOG() << "about to read " #T " using " #FUNC;\
+ T t;\
+ status_t err = FUNC(vec, &t);\
+ (void) err;\
+ FUZZ_LOG() << #T " done " /* << "err: " << err*/;\
+ }
+
+
+// clang-format off
+std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS {
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeBufferId),
+ GRALLOCTYPES_DECODE(std::string, ::android::gralloc4::decodeName),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeWidth),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeHeight),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeLayerCount),
+ GRALLOCTYPES_DECODE(::android::hardware::graphics::common::V1_2::PixelFormat, ::android::gralloc4::decodePixelFormatRequested),
+ GRALLOCTYPES_DECODE(uint32_t, ::android::gralloc4::decodePixelFormatFourCC),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodePixelFormatModifier),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeUsage),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeAllocationSize),
+ GRALLOCTYPES_DECODE(uint64_t, ::android::gralloc4::decodeProtectedContent),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeCompression),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeInterlaced),
+ GRALLOCTYPES_DECODE(aidl::android::hardware::graphics::common::ExtendableType, ::android::gralloc4::decodeChromaSiting),
+ 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),
+};
+// clang-format on
diff --git a/libs/gralloc/types/fuzzer/gralloctypes.h b/libs/gralloc/types/fuzzer/gralloctypes.h
new file mode 100644
index 0000000..b995fce
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/gralloctypes.h
@@ -0,0 +1,25 @@
+/*
+ * 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 <gralloctypes/Gralloc4.h>
+#include <hidl/HidlSupport.h>
+
+#include <vector>
+
+using GrallocTypesDecode = std::function<void(const ::android::hardware::hidl_vec<uint8_t>& vec, uint8_t data)>;
+
+extern std::vector<GrallocTypesDecode> GRALLOCTYPES_DECODE_FUNCTIONS;
diff --git a/libs/gralloc/types/fuzzer/main.cpp b/libs/gralloc/types/fuzzer/main.cpp
new file mode 100644
index 0000000..2807878
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/main.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 FUZZ_LOG_TAG "main"
+
+#include "gralloctypes.h"
+#include "util.h"
+
+#include <android-base/logging.h>
+#include <log/log.h>
+
+#include <cstdlib>
+#include <ctime>
+
+void doFuzz(
+ const std::vector<GrallocTypesDecode>& decodes,
+ const std::vector<uint8_t>& input,
+ const std::vector<uint8_t>& instructions) {
+
+ ::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();
+
+ for (size_t i = 0; i < instructions.size() - 1; i += 2) {
+ uint8_t a = instructions[i];
+ uint8_t decodeIdx = a % decodes.size();
+
+ uint8_t b = instructions[i + 1];
+
+ 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();
+
+ decodes[decodeIdx](vec, b);
+ }
+}
+
+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;
+
+ // data to use to determine what to do
+ size_t instructionLen = size;
+ std::vector<uint8_t> instructions(data, data + instructionLen);
+ data += instructionLen;
+ size -= instructionLen;
+
+ CHECK(size == 0) << "size: " << size;
+
+ FUZZ_LOG() << "inputLen: " << inputLen << " instructionLen: " << instructionLen;
+ FUZZ_LOG() << "input: " << hexString(input);
+ FUZZ_LOG() << "instructions: " << hexString(instructions);
+
+ doFuzz(GRALLOCTYPES_DECODE_FUNCTIONS, input, instructions);
+ return 0;
+}
diff --git a/libs/gralloc/types/fuzzer/util.cpp b/libs/gralloc/types/fuzzer/util.cpp
new file mode 100644
index 0000000..479f406
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.cpp
@@ -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.
+ */
+#define FUZZ_LOG_TAG "util"
+#include "util.h"
+
+#include <android-base/logging.h>
+
+#include <iomanip>
+#include <sstream>
+
+std::string hexString(const void* bytes, size_t len) {
+ if (bytes == nullptr) return "<null>";
+
+ const uint8_t* bytes8 = static_cast<const uint8_t*>(bytes);
+ char chars[] = "0123456789abcdef";
+ std::string result;
+ result.resize(len * 2);
+
+ for (size_t i = 0; i < len; i++) {
+ result[2 * i] = chars[bytes8[i] >> 4];
+ result[2 * i + 1] = chars[bytes8[i] & 0xf];
+ }
+
+ return result;
+}
+std::string hexString(const std::vector<uint8_t>& bytes) {
+ return hexString(bytes.data(), bytes.size());
+}
diff --git a/libs/gralloc/types/fuzzer/util.h b/libs/gralloc/types/fuzzer/util.h
new file mode 100644
index 0000000..aa504d2
--- /dev/null
+++ b/libs/gralloc/types/fuzzer/util.h
@@ -0,0 +1,53 @@
+/*
+ * 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 <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#ifndef FUZZ_LOG_TAG
+#error "Must define FUZZ_LOG_TAG"
+#endif
+
+#define FUZZ_LOG() FuzzLog(FUZZ_LOG_TAG).log()
+
+#ifdef ENABLE_LOG_FUZZ
+class FuzzLog {
+public:
+ FuzzLog(const char* tag) : mTag(tag) {}
+ ~FuzzLog() { std::cout << mTag << ": " << mOs.str() << std::endl; }
+
+ std::stringstream& log() { return mOs; }
+
+private:
+ const char* mTag = nullptr;
+ std::stringstream mOs;
+};
+#else
+class FuzzLog {
+public:
+ FuzzLog(const char* /*tag*/) {}
+ template <typename T>
+ FuzzLog& operator<<(const T& /*t*/) {
+ return *this;
+ }
+ FuzzLog& log() { return *this; }
+};
+#endif
+
+std::string hexString(const void* bytes, size_t len);
+std::string hexString(const std::vector<uint8_t>& bytes);
diff --git a/libs/gralloc/types/include/gralloctypes/Gralloc4.h b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
new file mode 100644
index 0000000..80588cd
--- /dev/null
+++ b/libs/gralloc/types/include/gralloctypes/Gralloc4.h
@@ -0,0 +1,270 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+#include <aidl/android/hardware/graphics/common/ExtendableType.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayout.h>
+#include <aidl/android/hardware/graphics/common/ChromaSiting.h>
+#include <aidl/android/hardware/graphics/common/Compression.h>
+#include <aidl/android/hardware/graphics/common/Interlaced.h>
+#include <aidl/android/hardware/graphics/common/StandardMetadataType.h>
+#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
+
+#include <android/hardware/graphics/mapper/4.0/IMapper.h>
+
+namespace android {
+
+namespace gralloc4 {
+
+#define GRALLOC4_STANDARD_METADATA_TYPE "android.hardware.graphics.common.StandardMetadataType"
+#define GRALLOC4_CHROMA_SITING "android.hardware.graphics.common.ChromaSiting"
+#define GRALLOC4_COMPRESSION "android.hardware.graphics.common.Compression"
+#define GRALLOC4_INTERLACED "android.hardware.graphics.common.Interlaced"
+#define GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE "android.hardware.graphics.common.PlaneLayoutComponentType"
+
+/*---------------------------------------------------------------------------------------------*/
+/**
+ * Definitions of the standard buffer metadata types. It is recommended that everyone uses
+ * these definitions directly for standard buffer metadata types.
+ */
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BufferId = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BUFFER_ID)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Name = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::NAME)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Width = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::WIDTH)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Height = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::HEIGHT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_LayerCount = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::LAYER_COUNT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatRequested = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_REQUESTED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatFourCC = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_FOURCC)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PixelFormatModifier = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PIXEL_FORMAT_MODIFIER)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Usage = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::USAGE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_AllocationSize = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::ALLOCATION_SIZE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ProtectedContent = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PROTECTED_CONTENT)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Compression = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::COMPRESSION)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Interlaced = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::INTERLACED)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_ChromaSiting = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::CHROMA_SITING)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_PlaneLayouts = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::PLANE_LAYOUTS)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_Dataspace = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::DATASPACE)
+};
+
+static const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType MetadataType_BlendMode = {
+ GRALLOC4_STANDARD_METADATA_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::StandardMetadataType::BLEND_MODE)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard compression strategies. It is recommended that everyone uses
+ * these definitions directly for standard compression strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_None = {
+ GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::NONE)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Compression_DisplayStreamCompression = {
+ GRALLOC4_COMPRESSION, static_cast<int64_t>(aidl::android::hardware::graphics::common::Compression::DISPLAY_STREAM_COMPRESSION)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard interlaced strategies. It is recommended that everyone uses
+ * these definitions directly for standard interlaced strategies.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_None = {
+ GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::NONE)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_TopBottom = {
+ GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::TOP_BOTTOM)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType Interlaced_RightLeft = {
+ GRALLOC4_INTERLACED, static_cast<int64_t>(aidl::android::hardware::graphics::common::Interlaced::RIGHT_LEFT)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard chroma siting. It is recommended that everyone uses
+ * these definitions directly for standard chroma siting.
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_None = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::NONE)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_Unknown = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::UNKNOWN)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_SitedInterstitial = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::SITED_INTERSTITIAL)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType ChromaSiting_CositedHorizontal = {
+ GRALLOC4_CHROMA_SITING, static_cast<int64_t>(aidl::android::hardware::graphics::common::ChromaSiting::COSITED_HORIZONTAL)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * Definitions of the standard plane layout component types. It is recommended that everyone uses
+ * these definitions directly for standard plane layout component types
+ */
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_Y = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::Y)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CB = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CB)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_CR = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::CR)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_R = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::R)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_G = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::G)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_B = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::B)
+};
+
+static const aidl::android::hardware::graphics::common::ExtendableType PlaneLayoutComponentType_A = {
+ GRALLOC4_PLANE_LAYOUT_COMPONENT_TYPE, static_cast<int64_t>(aidl::android::hardware::graphics::common::PlaneLayoutComponentType::A)
+};
+
+/*---------------------------------------------------------------------------------------------*/
+
+/**
+ * The functions below can be used to parse a StandardMetadataType.
+ */
+bool isStandardMetadataType(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+
+aidl::android::hardware::graphics::common::StandardMetadataType getStandardMetadataTypeValue(const android::hardware::graphics::mapper::V4_0::IMapper::MetadataType& metadataType);
+
+/**
+ * The functions below encode and decode standard metadata into a byte stream. It is STRONGLY
+ * recommended that both the vendor and system partitions use these functions when getting
+ * and setting metadata through gralloc 4 (IMapper 4.0).
+ */
+status_t encodeBufferId(uint64_t bufferId, android::hardware::hidl_vec<uint8_t>* outBufferId);
+status_t decodeBufferId(const android::hardware::hidl_vec<uint8_t>& bufferId, uint64_t* outBufferId);
+
+status_t encodeName(const std::string& name, android::hardware::hidl_vec<uint8_t>* outName);
+status_t decodeName(const android::hardware::hidl_vec<uint8_t>& name, std::string* outName);
+
+status_t encodeWidth(uint64_t width, android::hardware::hidl_vec<uint8_t>* outWidth);
+status_t decodeWidth(const android::hardware::hidl_vec<uint8_t>& width, uint64_t* outWidth);
+
+status_t encodeHeight(uint64_t height, android::hardware::hidl_vec<uint8_t>* outHeight);
+status_t decodeHeight(const android::hardware::hidl_vec<uint8_t>& height, uint64_t* outHeight);
+
+status_t encodeLayerCount(uint64_t layerCount, android::hardware::hidl_vec<uint8_t>* outLayerCount);
+status_t decodeLayerCount(const android::hardware::hidl_vec<uint8_t>& layerCount, uint64_t* outLayerCount);
+
+status_t encodePixelFormatRequested(const hardware::graphics::common::V1_2::PixelFormat& pixelFormatRequested, android::hardware::hidl_vec<uint8_t>* outPixelFormatRequested);
+status_t decodePixelFormatRequested(const android::hardware::hidl_vec<uint8_t>& pixelFormatRequested, hardware::graphics::common::V1_2::PixelFormat* outPixelFormatRequested);
+
+status_t encodePixelFormatFourCC(uint32_t pixelFormatFourCC, android::hardware::hidl_vec<uint8_t>* outPixelFormatFourCC);
+status_t decodePixelFormatFourCC(const android::hardware::hidl_vec<uint8_t>& pixelFormatFourCC, uint32_t* outPixelFormatFourCC);
+
+status_t encodePixelFormatModifier(uint64_t pixelFormatModifier, android::hardware::hidl_vec<uint8_t>* outPixelFormatModifier);
+status_t decodePixelFormatModifier(const android::hardware::hidl_vec<uint8_t>& pixelFormatModifier, uint64_t* outPixelFormatModifier);
+
+status_t encodeUsage(uint64_t usage, android::hardware::hidl_vec<uint8_t>* outUsage);
+status_t decodeUsage(const android::hardware::hidl_vec<uint8_t>& usage, uint64_t* outUsage);
+
+status_t encodeAllocationSize(uint64_t allocationSize, android::hardware::hidl_vec<uint8_t>* outAllocationSize);
+status_t decodeAllocationSize(const android::hardware::hidl_vec<uint8_t>& allocationSize, uint64_t* outAllocationSize);
+
+status_t encodeProtectedContent(uint64_t protectedContent, android::hardware::hidl_vec<uint8_t>* outProtectedContent);
+status_t decodeProtectedContent(const android::hardware::hidl_vec<uint8_t>& protectedContent, uint64_t* outProtectedContent);
+
+status_t encodeCompression(const aidl::android::hardware::graphics::common::ExtendableType& compression, android::hardware::hidl_vec<uint8_t>* outCompression);
+status_t decodeCompression(const android::hardware::hidl_vec<uint8_t>& compression, aidl::android::hardware::graphics::common::ExtendableType* outCompression);
+
+status_t encodeInterlaced(const aidl::android::hardware::graphics::common::ExtendableType& interlaced, android::hardware::hidl_vec<uint8_t>* outInterlaced);
+status_t decodeInterlaced(const android::hardware::hidl_vec<uint8_t>& interlaced, aidl::android::hardware::graphics::common::ExtendableType* outInterlaced);
+
+status_t encodeChromaSiting(const aidl::android::hardware::graphics::common::ExtendableType& chromaSiting, android::hardware::hidl_vec<uint8_t>* outChromaSiting);
+status_t decodeChromaSiting(const android::hardware::hidl_vec<uint8_t>& chromaSiting, aidl::android::hardware::graphics::common::ExtendableType* outChromaSiting);
+
+status_t encodePlaneLayouts(const std::vector<aidl::android::hardware::graphics::common::PlaneLayout>& planeLayouts, android::hardware::hidl_vec<uint8_t>* outPlaneLayouts);
+status_t decodePlaneLayouts(const android::hardware::hidl_vec<uint8_t>& planeLayouts, std::vector<aidl::android::hardware::graphics::common::PlaneLayout>* outPlaneLayouts);
+
+status_t encodeDataspace(const aidl::android::hardware::graphics::common::Dataspace& dataspace, android::hardware::hidl_vec<uint8_t>* outDataspace);
+status_t decodeDataspace(const android::hardware::hidl_vec<uint8_t>& dataspace, aidl::android::hardware::graphics::common::Dataspace* outDataspace);
+
+status_t encodeBlendMode(const aidl::android::hardware::graphics::common::BlendMode& blendMode, android::hardware::hidl_vec<uint8_t>* outBlendMode);
+status_t decodeBlendMode(const android::hardware::hidl_vec<uint8_t>& blendMode, aidl::android::hardware::graphics::common::BlendMode* outBlendMode);
+
+} // namespace gralloc4
+
+} // namespace android
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 4f605e0..1ae148c 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -50,6 +50,7 @@
"ConsumerBase.cpp",
"CpuConsumer.cpp",
"DebugEGLImageTracker.cpp",
+ "DisplayEventDispatcher.cpp",
"DisplayEventReceiver.cpp",
"GLConsumer.cpp",
"GuiConfig.cpp",
diff --git a/libs/gui/DisplayEventDispatcher.cpp b/libs/gui/DisplayEventDispatcher.cpp
new file mode 100644
index 0000000..54f383e
--- /dev/null
+++ b/libs/gui/DisplayEventDispatcher.cpp
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 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 "DisplayEventDispatcher"
+
+#include <cinttypes>
+#include <cstdint>
+
+#include <gui/DisplayEventDispatcher.h>
+#include <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+#include <utils/Timers.h>
+
+namespace android {
+
+// Number of events to read at a time from the DisplayEventDispatcher pipe.
+// The value should be large enough that we can quickly drain the pipe
+// using just a few large reads.
+static const size_t EVENT_BUFFER_SIZE = 100;
+
+DisplayEventDispatcher::DisplayEventDispatcher(const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource,
+ ISurfaceComposer::ConfigChanged configChanged)
+ : mLooper(looper), mReceiver(vsyncSource, configChanged), mWaitingForVsync(false) {
+ ALOGV("dispatcher %p ~ Initializing display event dispatcher.", this);
+}
+
+status_t DisplayEventDispatcher::initialize() {
+ status_t result = mReceiver.initCheck();
+ if (result) {
+ ALOGW("Failed to initialize display event receiver, status=%d", result);
+ return result;
+ }
+
+ int rc = mLooper->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT, this, NULL);
+ if (rc < 0) {
+ return UNKNOWN_ERROR;
+ }
+ return OK;
+}
+
+void DisplayEventDispatcher::dispose() {
+ ALOGV("dispatcher %p ~ Disposing display event dispatcher.", this);
+
+ if (!mReceiver.initCheck()) {
+ mLooper->removeFd(mReceiver.getFd());
+ }
+}
+
+status_t DisplayEventDispatcher::scheduleVsync() {
+ if (!mWaitingForVsync) {
+ ALOGV("dispatcher %p ~ Scheduling vsync.", this);
+
+ // Drain all pending events.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGE("dispatcher %p ~ last event processed while scheduling was for %" PRId64 "", this,
+ ns2ms(static_cast<nsecs_t>(vsyncTimestamp)));
+ }
+
+ status_t status = mReceiver.requestNextVsync();
+ if (status) {
+ ALOGW("Failed to request next vsync, status=%d", status);
+ return status;
+ }
+
+ mWaitingForVsync = true;
+ }
+ return OK;
+}
+
+int DisplayEventDispatcher::handleEvent(int, int events, void*) {
+ if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
+ ALOGE("Display event receiver pipe was closed or an error occurred. "
+ "events=0x%x",
+ events);
+ return 0; // remove the callback
+ }
+
+ if (!(events & Looper::EVENT_INPUT)) {
+ ALOGW("Received spurious callback for unhandled poll event. "
+ "events=0x%x",
+ events);
+ return 1; // keep the callback
+ }
+
+ // Drain all pending events, keep the last vsync.
+ nsecs_t vsyncTimestamp;
+ PhysicalDisplayId vsyncDisplayId;
+ uint32_t vsyncCount;
+ if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
+ ALOGV("dispatcher %p ~ Vsync pulse: timestamp=%" PRId64
+ ", displayId=%" ANDROID_PHYSICAL_DISPLAY_ID_FORMAT ", count=%d",
+ this, ns2ms(vsyncTimestamp), vsyncDisplayId, vsyncCount);
+ mWaitingForVsync = false;
+ dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
+ }
+
+ return 1; // keep the callback
+}
+
+bool DisplayEventDispatcher::processPendingEvents(nsecs_t* outTimestamp,
+ PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount) {
+ bool gotVsync = false;
+ DisplayEventReceiver::Event buf[EVENT_BUFFER_SIZE];
+ ssize_t n;
+ while ((n = mReceiver.getEvents(buf, EVENT_BUFFER_SIZE)) > 0) {
+ ALOGV("dispatcher %p ~ Read %d events.", this, int(n));
+ for (ssize_t i = 0; i < n; i++) {
+ const DisplayEventReceiver::Event& ev = buf[i];
+ switch (ev.header.type) {
+ case DisplayEventReceiver::DISPLAY_EVENT_VSYNC:
+ // Later vsync events will just overwrite the info from earlier
+ // ones. That's fine, we only care about the most recent.
+ gotVsync = true;
+ *outTimestamp = ev.header.timestamp;
+ *outDisplayId = ev.header.displayId;
+ *outCount = ev.vsync.count;
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG:
+ dispatchHotplug(ev.header.timestamp, ev.header.displayId, ev.hotplug.connected);
+ break;
+ case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED:
+ dispatchConfigChanged(ev.header.timestamp, ev.header.displayId,
+ ev.config.configId);
+ break;
+ default:
+ ALOGW("dispatcher %p ~ ignoring unknown event type %#x", this, ev.header.type);
+ break;
+ }
+ }
+ }
+ if (n < 0) {
+ ALOGW("Failed to get events from display event dispatcher, status=%d", status_t(n));
+ }
+ return gotVsync;
+}
+} // namespace android
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 5805797..546757b 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -905,6 +905,85 @@
return reply.readInt32();
}
+ virtual status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId, float minRefreshRate,
+ float maxRefreshRate) {
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(displayToken);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to write display token: %d", result);
+ return result;
+ }
+ result = data.writeInt32(defaultModeId);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write defaultModeId: %d", result);
+ return result;
+ }
+ result = data.writeFloat(minRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write minRefreshRate: %d", result);
+ return result;
+ }
+ result = data.writeFloat(maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to write maxRefreshRate: %d", result);
+ return result;
+ }
+
+ result = remote()->transact(BnSurfaceComposer::SET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs failed to transact: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
+
+ virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) {
+ if (!outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) return BAD_VALUE;
+ Parcel data, reply;
+ status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to writeInterfaceToken: %d", result);
+ return result;
+ }
+ result = data.writeStrongBinder(displayToken);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to writeStrongBinder: %d", result);
+ return result;
+ }
+ result = remote()->transact(BnSurfaceComposer::GET_DESIRED_DISPLAY_CONFIG_SPECS, data,
+ &reply);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to transact: %d", result);
+ return result;
+ }
+ result = reply.readInt32(outDefaultModeId);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read defaultModeId: %d", result);
+ return result;
+ }
+ result = reply.readFloat(outMinRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read minRefreshRate: %d", result);
+ return result;
+ }
+ result = reply.readFloat(outMaxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs failed to read maxRefreshRate: %d", result);
+ return result;
+ }
+ return reply.readInt32();
+ }
+
virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const {
Parcel data, reply;
@@ -978,6 +1057,35 @@
}
return NO_ERROR;
}
+
+ virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius) {
+ Parcel data, reply;
+ status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to write interface token: %d", error);
+ return error;
+ }
+
+ std::vector<float> shadowConfig = {ambientColor.r, ambientColor.g, ambientColor.b,
+ ambientColor.a, spotColor.r, spotColor.g,
+ spotColor.b, spotColor.a, lightPosY,
+ lightPosZ, lightRadius};
+
+ error = data.writeFloatVector(shadowConfig);
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to write shadowConfig: %d", error);
+ return error;
+ }
+
+ error = remote()->transact(BnSurfaceComposer::SET_GLOBAL_SHADOW_SETTINGS, data, &reply,
+ IBinder::FLAG_ONEWAY);
+ if (error != NO_ERROR) {
+ ALOGE("setGlobalShadowSettings: failed to transact: %d", error);
+ return error;
+ }
+ return NO_ERROR;
+ }
};
// Out-of-line virtual method definition to trigger vtable emission in this
@@ -1554,6 +1662,72 @@
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);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read defaultModeId: %d", result);
+ return result;
+ }
+ float minRefreshRate;
+ result = data.readFloat(&minRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read minRefreshRate: %d", result);
+ return result;
+ }
+ float maxRefreshRate;
+ result = data.readFloat(&maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to read maxRefreshRate: %d", result);
+ return result;
+ }
+ result = setDesiredDisplayConfigSpecs(displayToken, defaultModeId, minRefreshRate,
+ maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("setDesiredDisplayConfigSpecs: failed to call setDesiredDisplayConfigSpecs: "
+ "%d",
+ result);
+ return result;
+ }
+ reply->writeInt32(result);
+ return result;
+ }
+ case GET_DESIRED_DISPLAY_CONFIG_SPECS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+ sp<IBinder> displayToken = data.readStrongBinder();
+ int32_t defaultModeId;
+ float minRefreshRate;
+ float maxRefreshRate;
+
+ status_t result = getDesiredDisplayConfigSpecs(displayToken, &defaultModeId,
+ &minRefreshRate, &maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to get getDesiredDisplayConfigSpecs: "
+ "%d",
+ result);
+ return result;
+ }
+
+ result = reply->writeInt32(defaultModeId);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write defaultModeId: %d", result);
+ return result;
+ }
+ result = reply->writeFloat(minRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write minRefreshRate: %d", result);
+ return result;
+ }
+ result = reply->writeFloat(maxRefreshRate);
+ if (result != NO_ERROR) {
+ ALOGE("getDesiredDisplayConfigSpecs: failed to write maxRefreshRate: %d", result);
+ return result;
+ }
+ reply->writeInt32(result);
+ return result;
+ }
case GET_DISPLAY_BRIGHTNESS_SUPPORT: {
CHECK_INTERFACE(ISurfaceComposer, data, reply);
sp<IBinder> displayToken;
@@ -1593,6 +1767,25 @@
}
return notifyPowerHint(hintId);
}
+ case SET_GLOBAL_SHADOW_SETTINGS: {
+ CHECK_INTERFACE(ISurfaceComposer, data, reply);
+
+ std::vector<float> shadowConfig;
+ status_t error = data.readFloatVector(&shadowConfig);
+ if (error != NO_ERROR || shadowConfig.size() != 11) {
+ ALOGE("setGlobalShadowSettings: failed to read shadowConfig: %d", error);
+ return error;
+ }
+
+ half4 ambientColor = {shadowConfig[0], shadowConfig[1], shadowConfig[2],
+ shadowConfig[3]};
+ half4 spotColor = {shadowConfig[4], shadowConfig[5], shadowConfig[6], shadowConfig[7]};
+ float lightPosY = shadowConfig[8];
+ float lightPosZ = shadowConfig[9];
+ float lightRadius = shadowConfig[10];
+ return setGlobalShadowSettings(ambientColor, spotColor, lightPosY, lightPosZ,
+ lightRadius);
+ }
default: {
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 2c8d4ca..9d7d7d0 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1617,6 +1617,26 @@
outAllowedConfigs);
}
+status_t SurfaceComposerClient::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId,
+ float minRefreshRate,
+ float maxRefreshRate) {
+ return ComposerService::getComposerService()->setDesiredDisplayConfigSpecs(displayToken,
+ defaultModeId,
+ minRefreshRate,
+ maxRefreshRate);
+}
+
+status_t SurfaceComposerClient::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) {
+ return ComposerService::getComposerService()->getDesiredDisplayConfigSpecs(displayToken,
+ outDefaultModeId,
+ outMinRefreshRate,
+ outMaxRefreshRate);
+}
+
status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display,
Vector<ColorMode>* outColorModes) {
return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes);
@@ -1727,6 +1747,14 @@
return ComposerService::getComposerService()->notifyPowerHint(hintId);
}
+status_t SurfaceComposerClient::setGlobalShadowSettings(const half4& ambientColor,
+ const half4& spotColor, float lightPosY,
+ float lightPosZ, float lightRadius) {
+ return ComposerService::getComposerService()->setGlobalShadowSettings(ambientColor, spotColor,
+ lightPosY, lightPosZ,
+ lightRadius);
+}
+
// ----------------------------------------------------------------------------
status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace,
diff --git a/libs/gui/include/gui/DisplayEventDispatcher.h b/libs/gui/include/gui/DisplayEventDispatcher.h
new file mode 100644
index 0000000..f0b7ff5
--- /dev/null
+++ b/libs/gui/include/gui/DisplayEventDispatcher.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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 <gui/DisplayEventReceiver.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+
+namespace android {
+
+class DisplayEventDispatcher : public LooperCallback {
+public:
+ explicit DisplayEventDispatcher(
+ const sp<Looper>& looper,
+ ISurfaceComposer::VsyncSource vsyncSource = ISurfaceComposer::eVsyncSourceApp,
+ ISurfaceComposer::ConfigChanged configChanged =
+ ISurfaceComposer::eConfigChangedSuppress);
+
+ status_t initialize();
+ void dispose();
+ status_t scheduleVsync();
+
+protected:
+ virtual ~DisplayEventDispatcher() = default;
+
+private:
+ sp<Looper> mLooper;
+ DisplayEventReceiver mReceiver;
+ bool mWaitingForVsync;
+
+ virtual void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count) = 0;
+ virtual void dispatchHotplug(nsecs_t timestamp, PhysicalDisplayId displayId,
+ bool connected) = 0;
+ virtual void dispatchConfigChanged(nsecs_t timestamp, PhysicalDisplayId displayId,
+ int32_t configId) = 0;
+
+ virtual int handleEvent(int receiveFd, int events, void* data);
+ bool processPendingEvents(nsecs_t* outTimestamp, PhysicalDisplayId* outDisplayId,
+ uint32_t* outCount);
+};
+} // namespace android
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index f2bae98..345425d 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -25,6 +25,8 @@
#include <gui/ITransactionCompletedListener.h>
+#include <math/vec4.h>
+
#include <ui/ConfigStoreTypes.h>
#include <ui/DisplayedFrameStats.h>
#include <ui/FrameStats.h>
@@ -399,6 +401,19 @@
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,
+ float maxRefreshRate) = 0;
+
+ virtual status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) = 0;
+ /*
* Gets whether brightness operations are supported on a display.
*
* displayToken
@@ -439,6 +454,28 @@
* Returns NO_ERROR upon success.
*/
virtual status_t notifyPowerHint(int32_t hintId) = 0;
+
+ /*
+ * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+ * material design guidelines.
+ *
+ * ambientColor
+ * Color to the ambient shadow. The alpha is premultiplied.
+ *
+ * spotColor
+ * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ * depends on the light position.
+ *
+ * lightPosY/lightPosZ
+ * Position of the light used to cast the spot shadow. The X value is always the display
+ * width / 2.
+ *
+ * lightRadius
+ * Radius of the light casting the shadow.
+ */
+ virtual status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ,
+ float lightRadius) = 0;
};
// ----------------------------------------------------------------------------
@@ -488,10 +525,13 @@
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,
SET_DISPLAY_BRIGHTNESS,
CAPTURE_SCREEN_BY_ID,
NOTIFY_POWER_HINT,
+ SET_GLOBAL_SHADOW_SETTINGS,
// Always append new enum to the end.
};
diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h
index 37387ac..2c0b143 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -129,6 +129,22 @@
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,
+ float maxRefreshRate);
+ // Gets 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().
+ // 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,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate);
+
// Gets the list of supported color modes for the given display
static status_t getDisplayColorModes(const sp<IBinder>& display,
Vector<ui::ColorMode>* outColorModes);
@@ -214,6 +230,27 @@
*/
static status_t notifyPowerHint(int32_t hintId);
+ /*
+ * Sets the global configuration for all the shadows drawn by SurfaceFlinger. Shadow follows
+ * material design guidelines.
+ *
+ * ambientColor
+ * Color to the ambient shadow. The alpha is premultiplied.
+ *
+ * spotColor
+ * Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ * depends on the light position.
+ *
+ * lightPosY/lightPosZ
+ * Position of the light used to cast the spot shadow. The X value is always the display
+ * width / 2.
+ *
+ * lightRadius
+ * Radius of the light casting the shadow.
+ */
+ static status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius);
+
// ------------------------------------------------------------------------
// surface creation / destruction
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a4fdb35..3d90369 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -831,8 +831,25 @@
std::vector<int32_t>* /*outAllowedConfigs*/) override {
return NO_ERROR;
}
+ status_t setDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+ int32_t /*defaultModeId*/, float /*minRefreshRate*/,
+ float /*maxRefreshRate*/) override {
+ return NO_ERROR;
+ }
+ status_t getDesiredDisplayConfigSpecs(const sp<IBinder>& /*displayToken*/,
+ int32_t* /*outDefaultModeId*/,
+ float* /*outMinRefreshRate*/,
+ float* /*outMaxRefreshRate*/) override {
+ return NO_ERROR;
+ };
status_t notifyPowerHint(int32_t /*hintId*/) override { return NO_ERROR; }
+ status_t setGlobalShadowSettings(const half4& /*ambientColor*/, const half4& /*spotColor*/,
+ float /*lightPosY*/, float /*lightPosZ*/,
+ float /*lightRadius*/) override {
+ return NO_ERROR;
+ }
+
protected:
IBinder* onAsBinder() override { return nullptr; }
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 34b305e..8ccbc7f 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -20,6 +20,7 @@
#include <limits.h>
#include <input/Input.h>
+#include <input/InputDevice.h>
#include <input/InputEventLabels.h>
#ifdef __ANDROID__
@@ -41,6 +42,21 @@
// --- InputEvent ---
+const char* inputEventTypeToString(int32_t type) {
+ switch (type) {
+ case AINPUT_EVENT_TYPE_KEY: {
+ return "KEY";
+ }
+ case AINPUT_EVENT_TYPE_MOTION: {
+ return "MOTION";
+ }
+ case AINPUT_EVENT_TYPE_FOCUS: {
+ return "FOCUS";
+ }
+ }
+ return "UNKNOWN";
+}
+
void InputEvent::initialize(int32_t deviceId, int32_t source, int32_t displayId) {
mDeviceId = deviceId;
mSource = source;
@@ -587,6 +603,20 @@
return getAxisByLabel(label);
}
+// --- FocusEvent ---
+
+void FocusEvent::initialize(bool hasFocus, bool inTouchMode) {
+ InputEvent::initialize(ReservedInputDeviceId::VIRTUAL_KEYBOARD_ID, AINPUT_SOURCE_UNKNOWN,
+ ADISPLAY_ID_NONE);
+ mHasFocus = hasFocus;
+ mInTouchMode = inTouchMode;
+}
+
+void FocusEvent::initialize(const FocusEvent& from) {
+ InputEvent::initialize(from);
+ mHasFocus = from.mHasFocus;
+ mInTouchMode = from.mInTouchMode;
+}
// --- PooledInputEventFactory ---
@@ -595,43 +625,52 @@
}
PooledInputEventFactory::~PooledInputEventFactory() {
- for (size_t i = 0; i < mKeyEventPool.size(); i++) {
- delete mKeyEventPool.itemAt(i);
- }
- for (size_t i = 0; i < mMotionEventPool.size(); i++) {
- delete mMotionEventPool.itemAt(i);
- }
}
KeyEvent* PooledInputEventFactory::createKeyEvent() {
- if (!mKeyEventPool.isEmpty()) {
- KeyEvent* event = mKeyEventPool.top();
- mKeyEventPool.pop();
- return event;
+ if (mKeyEventPool.empty()) {
+ return new KeyEvent();
}
- return new KeyEvent();
+ KeyEvent* event = mKeyEventPool.front().release();
+ mKeyEventPool.pop();
+ return event;
}
MotionEvent* PooledInputEventFactory::createMotionEvent() {
- if (!mMotionEventPool.isEmpty()) {
- MotionEvent* event = mMotionEventPool.top();
- mMotionEventPool.pop();
- return event;
+ if (mMotionEventPool.empty()) {
+ return new MotionEvent();
}
- return new MotionEvent();
+ MotionEvent* event = mMotionEventPool.front().release();
+ mMotionEventPool.pop();
+ return event;
+}
+
+FocusEvent* PooledInputEventFactory::createFocusEvent() {
+ if (mFocusEventPool.empty()) {
+ return new FocusEvent();
+ }
+ FocusEvent* event = mFocusEventPool.front().release();
+ mFocusEventPool.pop();
+ return event;
}
void PooledInputEventFactory::recycle(InputEvent* event) {
switch (event->getType()) {
case AINPUT_EVENT_TYPE_KEY:
if (mKeyEventPool.size() < mMaxPoolSize) {
- mKeyEventPool.push(static_cast<KeyEvent*>(event));
+ mKeyEventPool.push(std::unique_ptr<KeyEvent>(static_cast<KeyEvent*>(event)));
return;
}
break;
case AINPUT_EVENT_TYPE_MOTION:
if (mMotionEventPool.size() < mMaxPoolSize) {
- mMotionEventPool.push(static_cast<MotionEvent*>(event));
+ mMotionEventPool.push(std::unique_ptr<MotionEvent>(static_cast<MotionEvent*>(event)));
+ return;
+ }
+ break;
+ case AINPUT_EVENT_TYPE_FOCUS:
+ if (mFocusEventPool.size() < mMaxPoolSize) {
+ mFocusEventPool.push(std::unique_ptr<FocusEvent>(static_cast<FocusEvent*>(event)));
return;
}
break;
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index a5dd3c0..200e1f3 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -14,7 +14,7 @@
static constexpr bool DEBUG_CHANNEL_LIFECYCLE = false;
// Log debug messages about transport actions
-#define DEBUG_TRANSPORT_ACTIONS 0
+static constexpr bool DEBUG_TRANSPORT_ACTIONS = false;
// Log debug messages about touch event resampling
#define DEBUG_RESAMPLING 0
@@ -88,6 +88,10 @@
return (source & AINPUT_SOURCE_CLASS_POINTER) == AINPUT_SOURCE_CLASS_POINTER;
}
+inline static const char* toString(bool value) {
+ return value ? "true" : "false";
+}
+
// --- InputMessage ---
bool InputMessage::isValid(size_t actualSize) const {
@@ -99,6 +103,8 @@
return body.motion.pointerCount > 0 && body.motion.pointerCount <= MAX_POINTERS;
case Type::FINISHED:
return true;
+ case Type::FOCUS:
+ return true;
}
}
return false;
@@ -112,6 +118,8 @@
return sizeof(Header) + body.motion.size();
case Type::FINISHED:
return sizeof(Header) + body.finished.size();
+ case Type::FOCUS:
+ return sizeof(Header) + body.focus.size();
}
return sizeof(Header);
}
@@ -216,8 +224,10 @@
msg->body.finished.handled = body.finished.handled;
break;
}
- default: {
- LOG_FATAL("Unexpected message type %i", header.type);
+ case InputMessage::Type::FOCUS: {
+ msg->body.focus.seq = body.focus.seq;
+ msg->body.focus.hasFocus = body.focus.hasFocus;
+ msg->body.focus.inTouchMode = body.focus.inTouchMode;
break;
}
}
@@ -432,14 +442,13 @@
mChannel->getName().c_str(), keyCode);
ATRACE_NAME(message.c_str());
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
- "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
- "downTime=%" PRId64 ", eventTime=%" PRId64,
- mChannel->getName().c_str(), seq,
- deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
- downTime, eventTime);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
+ "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
+ "downTime=%" PRId64 ", eventTime=%" PRId64,
+ mChannel->getName().c_str(), seq, deviceId, source, action, flags, keyCode, scanCode,
+ metaState, repeatCount, downTime, eventTime);
+ }
if (!seq) {
ALOGE("Attempted to publish a key event with sequence number 0.");
@@ -476,18 +485,18 @@
mChannel->getName().c_str(), action);
ATRACE_NAME(message.c_str());
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
- "displayId=%" PRId32 ", "
- "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
- "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
- "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
- "pointerCount=%" PRIu32,
- mChannel->getName().c_str(), seq,
- deviceId, source, displayId, action, actionButton, flags, edgeFlags, metaState,
- buttonState, motionClassificationToString(classification),
- xOffset, yOffset, xPrecision, yPrecision, downTime, eventTime, pointerCount);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ publishMotionEvent: seq=%u, deviceId=%d, source=0x%x, "
+ "displayId=%" PRId32 ", "
+ "action=0x%x, actionButton=0x%08x, flags=0x%x, edgeFlags=0x%x, "
+ "metaState=0x%x, buttonState=0x%x, classification=%s, xOffset=%f, yOffset=%f, "
+ "xPrecision=%f, yPrecision=%f, downTime=%" PRId64 ", eventTime=%" PRId64 ", "
+ "pointerCount=%" PRIu32,
+ mChannel->getName().c_str(), seq, deviceId, source, displayId, action, actionButton,
+ flags, edgeFlags, metaState, buttonState,
+ motionClassificationToString(classification), xOffset, yOffset, xPrecision,
+ yPrecision, downTime, eventTime, pointerCount);
+ }
if (!seq) {
ALOGE("Attempted to publish a motion event with sequence number 0.");
@@ -530,11 +539,27 @@
return mChannel->sendMessage(&msg);
}
+status_t InputPublisher::publishFocusEvent(uint32_t seq, bool hasFocus, bool inTouchMode) {
+ if (ATRACE_ENABLED()) {
+ std::string message =
+ StringPrintf("publishFocusEvent(inputChannel=%s, hasFocus=%s, inTouchMode=%s)",
+ mChannel->getName().c_str(), toString(hasFocus),
+ toString(inTouchMode));
+ ATRACE_NAME(message.c_str());
+ }
+
+ InputMessage msg;
+ msg.header.type = InputMessage::Type::FOCUS;
+ msg.body.focus.seq = seq;
+ msg.body.focus.hasFocus = hasFocus ? 1 : 0;
+ msg.body.focus.inTouchMode = inTouchMode ? 1 : 0;
+ return mChannel->sendMessage(&msg);
+}
+
status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
- mChannel->getName().c_str());
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' publisher ~ receiveFinishedSignal", mChannel->getName().c_str());
+ }
InputMessage msg;
status_t result = mChannel->receiveMessage(&msg);
@@ -549,7 +574,7 @@
return UNKNOWN_ERROR;
}
*outSeq = msg.body.finished.seq;
- *outHandled = msg.body.finished.handled;
+ *outHandled = msg.body.finished.handled == 1;
return OK;
}
@@ -567,12 +592,12 @@
return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true);
}
-status_t InputConsumer::consume(InputEventFactoryInterface* factory,
- bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
- mChannel->getName().c_str(), consumeBatches ? "true" : "false", frameTime);
-#endif
+status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches,
+ nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent) {
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consume: consumeBatches=%s, frameTime=%" PRId64,
+ mChannel->getName().c_str(), toString(consumeBatches), frameTime);
+ }
*outSeq = 0;
*outEvent = nullptr;
@@ -592,10 +617,10 @@
if (consumeBatches || result != WOULD_BLOCK) {
result = consumeBatch(factory, frameTime, outSeq, outEvent);
if (*outEvent) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed batch event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
}
@@ -611,10 +636,10 @@
initializeKeyEvent(keyEvent, &mMsg);
*outSeq = mMsg.body.key.seq;
*outEvent = keyEvent;
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed key event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
@@ -624,10 +649,10 @@
Batch& batch = mBatches.editItemAt(batchIndex);
if (canAddSample(batch, &mMsg)) {
batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ appended to batch event",
- mChannel->getName().c_str());
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ appended to batch event",
+ mChannel->getName().c_str());
+ }
break;
} else if (isPointerEvent(mMsg.body.motion.source) &&
mMsg.body.motion.action == AMOTION_EVENT_ACTION_CANCEL) {
@@ -649,47 +674,58 @@
if (result) {
return result;
}
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed batch event and "
- "deferred current event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed batch event and "
+ "deferred current event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
}
- // Start a new batch if needed.
- if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE
- || mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
- mBatches.push();
- Batch& batch = mBatches.editTop();
- batch.samples.push(mMsg);
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ started batch event",
- mChannel->getName().c_str());
-#endif
+ // Start a new batch if needed.
+ if (mMsg.body.motion.action == AMOTION_EVENT_ACTION_MOVE ||
+ mMsg.body.motion.action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+ mBatches.push();
+ Batch& batch = mBatches.editTop();
+ batch.samples.push(mMsg);
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ started batch event",
+ mChannel->getName().c_str());
+ }
+ break;
+ }
+
+ MotionEvent* motionEvent = factory->createMotionEvent();
+ if (!motionEvent) return NO_MEMORY;
+
+ updateTouchState(mMsg);
+ initializeMotionEvent(motionEvent, &mMsg);
+ *outSeq = mMsg.body.motion.seq;
+ *outEvent = motionEvent;
+
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
+ mChannel->getName().c_str(), *outSeq);
+ }
break;
}
- MotionEvent* motionEvent = factory->createMotionEvent();
- if (! motionEvent) return NO_MEMORY;
-
- updateTouchState(mMsg);
- initializeMotionEvent(motionEvent, &mMsg);
- *outSeq = mMsg.body.motion.seq;
- *outEvent = motionEvent;
-
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ consumed motion event, seq=%u",
- mChannel->getName().c_str(), *outSeq);
-#endif
- break;
+ case InputMessage::Type::FINISHED: {
+ LOG_ALWAYS_FATAL("Consumed a FINISHED message, which should never be seen by "
+ "InputConsumer!");
+ break;
}
- default:
- ALOGE("channel '%s' consumer ~ Received unexpected message of type %d",
- mChannel->getName().c_str(), mMsg.header.type);
- return UNKNOWN_ERROR;
+ case InputMessage::Type::FOCUS: {
+ FocusEvent* focusEvent = factory->createFocusEvent();
+ if (!focusEvent) return NO_MEMORY;
+
+ initializeFocusEvent(focusEvent, &mMsg);
+ *outSeq = mMsg.body.focus.seq;
+ *outEvent = focusEvent;
+ break;
+ }
}
}
return OK;
@@ -1014,10 +1050,10 @@
}
status_t InputConsumer::sendFinishedSignal(uint32_t seq, bool handled) {
-#if DEBUG_TRANSPORT_ACTIONS
- ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
- mChannel->getName().c_str(), seq, handled ? "true" : "false");
-#endif
+ if (DEBUG_TRANSPORT_ACTIONS) {
+ ALOGD("channel '%s' consumer ~ sendFinishedSignal: seq=%u, handled=%s",
+ mChannel->getName().c_str(), seq, toString(handled));
+ }
if (!seq) {
ALOGE("Attempted to send a finished signal with sequence number 0.");
@@ -1066,7 +1102,7 @@
InputMessage msg;
msg.header.type = InputMessage::Type::FINISHED;
msg.body.finished.seq = seq;
- msg.body.finished.handled = handled;
+ msg.body.finished.handled = handled ? 1 : 0;
return mChannel->sendMessage(&msg);
}
@@ -1114,6 +1150,10 @@
msg->body.key.eventTime);
}
+void InputConsumer::initializeFocusEvent(FocusEvent* event, const InputMessage* msg) {
+ event->initialize(msg->body.focus.hasFocus == 1, msg->body.focus.inTouchMode == 1);
+}
+
void InputConsumer::initializeMotionEvent(MotionEvent* event, const InputMessage* msg) {
uint32_t pointerCount = msg->body.motion.pointerCount;
PointerProperties pointerProperties[pointerCount];
diff --git a/libs/input/tests/InputPublisherAndConsumer_test.cpp b/libs/input/tests/InputPublisherAndConsumer_test.cpp
index a362f32..2fc77e9 100644
--- a/libs/input/tests/InputPublisherAndConsumer_test.cpp
+++ b/libs/input/tests/InputPublisherAndConsumer_test.cpp
@@ -60,6 +60,7 @@
void PublishAndConsumeKeyEvent();
void PublishAndConsumeMotionEvent();
+ void PublishAndConsumeFocusEvent();
};
TEST_F(InputPublisherAndConsumerTest, GetChannel_ReturnsTheChannel) {
@@ -256,6 +257,43 @@
<< "publisher receiveFinishedSignal should have set handled to consumer's reply";
}
+void InputPublisherAndConsumerTest::PublishAndConsumeFocusEvent() {
+ status_t status;
+
+ constexpr uint32_t seq = 15;
+ constexpr bool hasFocus = true;
+ constexpr bool inTouchMode = true;
+
+ status = mPublisher->publishFocusEvent(seq, hasFocus, inTouchMode);
+ ASSERT_EQ(OK, status) << "publisher publishKeyEvent should return OK";
+
+ uint32_t consumeSeq;
+ InputEvent* event;
+ status = mConsumer->consume(&mEventFactory, true /*consumeBatches*/, -1, &consumeSeq, &event);
+ ASSERT_EQ(OK, status) << "consumer consume should return OK";
+
+ ASSERT_TRUE(event != nullptr) << "consumer should have returned non-NULL event";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_FOCUS, event->getType())
+ << "consumer should have returned a focus event";
+
+ FocusEvent* focusEvent = static_cast<FocusEvent*>(event);
+ EXPECT_EQ(seq, consumeSeq);
+ EXPECT_EQ(hasFocus, focusEvent->getHasFocus());
+ EXPECT_EQ(inTouchMode, focusEvent->getInTouchMode());
+
+ status = mConsumer->sendFinishedSignal(seq, true);
+ ASSERT_EQ(OK, status) << "consumer sendFinishedSignal should return OK";
+
+ uint32_t finishedSeq = 0;
+ bool handled = false;
+ status = mPublisher->receiveFinishedSignal(&finishedSeq, &handled);
+ ASSERT_EQ(OK, status) << "publisher receiveFinishedSignal should return OK";
+ ASSERT_EQ(seq, finishedSeq)
+ << "publisher receiveFinishedSignal should have returned the original sequence number";
+ ASSERT_TRUE(handled)
+ << "publisher receiveFinishedSignal should have set handled to consumer's reply";
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishKeyEvent_EndToEnd) {
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
@@ -264,6 +302,10 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
}
+TEST_F(InputPublisherAndConsumerTest, PublishFocusEvent_EndToEnd) {
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
+}
+
TEST_F(InputPublisherAndConsumerTest, PublishMotionEvent_WhenSequenceNumberIsZero_ReturnsError) {
status_t status;
const size_t pointerCount = 1;
@@ -322,6 +364,7 @@
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
+ ASSERT_NO_FATAL_FAILURE(PublishAndConsumeFocusEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeMotionEvent());
ASSERT_NO_FATAL_FAILURE(PublishAndConsumeKeyEvent());
}
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index 8d8cf06..9ab0dba 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -69,8 +69,29 @@
CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 88);
CHECK_OFFSET(InputMessage::Body::Motion, pointers, 96);
+ CHECK_OFFSET(InputMessage::Body::Focus, seq, 0);
+ CHECK_OFFSET(InputMessage::Body::Focus, hasFocus, 4);
+ CHECK_OFFSET(InputMessage::Body::Focus, inTouchMode, 6);
+
CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
+void TestHeaderSize() {
+ static_assert(sizeof(InputMessage::Header) == 8);
+}
+
+/**
+ * We cannot use the Body::size() method here because it is not static for
+ * the Motion type, where "pointerCount" variable affects the size and can change at runtime.
+ */
+void TestBodySize() {
+ static_assert(sizeof(InputMessage::Body::Key) == 64);
+ static_assert(sizeof(InputMessage::Body::Motion) ==
+ offsetof(InputMessage::Body::Motion, pointers) +
+ sizeof(InputMessage::Body::Motion::Pointer) * MAX_POINTERS);
+ static_assert(sizeof(InputMessage::Body::Finished) == 8);
+ static_assert(sizeof(InputMessage::Body::Focus) == 8);
+}
+
} // namespace android
diff --git a/libs/nativedisplay/AChoreographer.cpp b/libs/nativedisplay/AChoreographer.cpp
index 63e0734..05ff93e 100644
--- a/libs/nativedisplay/AChoreographer.cpp
+++ b/libs/nativedisplay/AChoreographer.cpp
@@ -22,7 +22,7 @@
#include <thread>
#include <android/choreographer.h>
-#include <androidfw/DisplayEventDispatcher.h>
+#include <gui/DisplayEventDispatcher.h>
#include <gui/ISurfaceComposer.h>
#include <gui/SurfaceComposerClient.h>
#include <utils/Looper.h>
diff --git a/libs/nativedisplay/ADisplay.cpp b/libs/nativedisplay/ADisplay.cpp
index 6665635..6566538 100644
--- a/libs/nativedisplay/ADisplay.cpp
+++ b/libs/nativedisplay/ADisplay.cpp
@@ -18,6 +18,7 @@
#include <gui/SurfaceComposerClient.h>
#include <ui/DisplayInfo.h>
#include <ui/GraphicTypes.h>
+#include <ui/PixelFormat.h>
#include <algorithm>
#include <optional>
@@ -82,6 +83,16 @@
ADisplayType type;
/**
+ * The preferred WCG dataspace
+ */
+ ADataSpace wcgDataspace;
+
+ /**
+ * The preferred WCG pixel format
+ */
+ AHardwareBuffer_Format wcgPixelFormat;
+
+ /**
* Number of supported configs
*/
size_t numConfigs;
@@ -151,6 +162,17 @@
const std::optional<PhysicalDisplayId> internalId =
SurfaceComposerClient::getInternalDisplayId();
+ ui::Dataspace defaultDataspace;
+ ui::PixelFormat defaultPixelFormat;
+ ui::Dataspace wcgDataspace;
+ ui::PixelFormat wcgPixelFormat;
+
+ const status_t status =
+ SurfaceComposerClient::getCompositionPreference(&defaultDataspace, &defaultPixelFormat,
+ &wcgDataspace, &wcgPixelFormat);
+ if (status != NO_ERROR) {
+ return status;
+ }
// Here we allocate all our required memory in one block. The layout is as
// follows:
@@ -176,7 +198,12 @@
const std::vector<DisplayConfigImpl>& configs = configsPerDisplay[i];
memcpy(configData, configs.data(), sizeof(DisplayConfigImpl) * configs.size());
- displayData[i] = DisplayImpl{id, type, configs.size(), configData};
+ displayData[i] = DisplayImpl{id,
+ type,
+ static_cast<ADataSpace>(wcgDataspace),
+ static_cast<AHardwareBuffer_Format>(wcgPixelFormat),
+ configs.size(),
+ configData};
impls[i] = displayData + i;
// Advance the configData pointer so that future configs are written to
// the correct display.
@@ -210,6 +237,17 @@
return reinterpret_cast<DisplayImpl*>(display)->type;
}
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+ AHardwareBuffer_Format* outPixelFormat) {
+ CHECK_NOT_NULL(display);
+ CHECK_NOT_NULL(outDataspace);
+ CHECK_NOT_NULL(outPixelFormat);
+
+ DisplayImpl* impl = reinterpret_cast<DisplayImpl*>(display);
+ *outDataspace = impl->wcgDataspace;
+ *outPixelFormat = impl->wcgPixelFormat;
+}
+
int ADisplay_getCurrentConfig(ADisplay* display, ADisplayConfig** outConfig) {
CHECK_NOT_NULL(display);
diff --git a/libs/nativedisplay/Android.bp b/libs/nativedisplay/Android.bp
index 45b935a..7a497ea 100644
--- a/libs/nativedisplay/Android.bp
+++ b/libs/nativedisplay/Android.bp
@@ -46,9 +46,9 @@
],
shared_libs: [
- "libandroidfw",
"libgui",
"liblog",
+ "libnativewindow",
"libui",
"libutils",
],
diff --git a/libs/nativedisplay/include/apex/display.h b/libs/nativedisplay/include/apex/display.h
index 7af452a..9be401e 100644
--- a/libs/nativedisplay/include/apex/display.h
+++ b/libs/nativedisplay/include/apex/display.h
@@ -16,6 +16,8 @@
#pragma once
+#include <android/data_space.h>
+#include <android/hardware_buffer.h>
#include <inttypes.h>
__BEGIN_DECLS
@@ -72,6 +74,12 @@
ADisplayType ADisplay_getDisplayType(ADisplay* display);
/**
+ * Queries the display's preferred WCG format
+ */
+void ADisplay_getPreferredWideColorFormat(ADisplay* display, ADataSpace* outDataspace,
+ AHardwareBuffer_Format* outPixelFormat);
+
+/**
* Gets the current display configuration for the given display.
*
* Memory is *not* allocated for the caller. As such, the returned output
diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp
index 15d025b..394d05a 100644
--- a/libs/renderengine/gl/GLESRenderEngine.cpp
+++ b/libs/renderengine/gl/GLESRenderEngine.cpp
@@ -983,6 +983,8 @@
Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2);
for (auto layer : layers) {
+ mState.maxMasteringLuminance = layer.source.buffer.maxMasteringLuminance;
+ mState.maxContentLuminance = layer.source.buffer.maxContentLuminance;
mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform;
const FloatRect bounds = layer.geometry.boundaries;
@@ -998,7 +1000,6 @@
bool usePremultipliedAlpha = true;
bool disableTexture = true;
bool isOpaque = false;
-
if (layer.source.buffer.buffer != nullptr) {
disableTexture = false;
isOpaque = layer.source.buffer.isOpaque;
diff --git a/libs/renderengine/gl/Program.cpp b/libs/renderengine/gl/Program.cpp
index fe9d909..4eb5eb6 100644
--- a/libs/renderengine/gl/Program.cpp
+++ b/libs/renderengine/gl/Program.cpp
@@ -65,6 +65,8 @@
mSamplerLoc = glGetUniformLocation(programId, "sampler");
mColorLoc = glGetUniformLocation(programId, "color");
mDisplayMaxLuminanceLoc = glGetUniformLocation(programId, "displayMaxLuminance");
+ mMaxMasteringLuminanceLoc = glGetUniformLocation(programId, "maxMasteringLuminance");
+ mMaxContentLuminanceLoc = glGetUniformLocation(programId, "maxContentLuminance");
mInputTransformMatrixLoc = glGetUniformLocation(programId, "inputTransformMatrix");
mOutputTransformMatrixLoc = glGetUniformLocation(programId, "outputTransformMatrix");
mCornerRadiusLoc = glGetUniformLocation(programId, "cornerRadius");
@@ -138,6 +140,12 @@
if (mDisplayMaxLuminanceLoc >= 0) {
glUniform1f(mDisplayMaxLuminanceLoc, desc.displayMaxLuminance);
}
+ if (mMaxMasteringLuminanceLoc >= 0) {
+ glUniform1f(mMaxMasteringLuminanceLoc, desc.maxMasteringLuminance);
+ }
+ if (mMaxContentLuminanceLoc >= 0) {
+ glUniform1f(mMaxContentLuminanceLoc, desc.maxContentLuminance);
+ }
if (mCornerRadiusLoc >= 0) {
glUniform1f(mCornerRadiusLoc, desc.cornerRadius);
}
diff --git a/libs/renderengine/gl/Program.h b/libs/renderengine/gl/Program.h
index bc9cf08..c9beb68 100644
--- a/libs/renderengine/gl/Program.h
+++ b/libs/renderengine/gl/Program.h
@@ -90,6 +90,10 @@
/* location of display luminance uniform */
GLint mDisplayMaxLuminanceLoc;
+ /* location of max mastering luminance uniform */
+ GLint mMaxMasteringLuminanceLoc;
+ /* location of max content luminance uniform */
+ GLint mMaxContentLuminanceLoc;
/* location of transform matrix */
GLint mInputTransformMatrixLoc;
diff --git a/libs/renderengine/gl/ProgramCache.cpp b/libs/renderengine/gl/ProgramCache.cpp
index 494623e..e2757e1 100644
--- a/libs/renderengine/gl/ProgramCache.cpp
+++ b/libs/renderengine/gl/ProgramCache.cpp
@@ -339,9 +339,9 @@
default:
fs << R"__SHADER__(
highp vec3 ToneMap(highp vec3 color) {
- const float maxMasteringLumi = 1000.0;
- const float maxContentLumi = 1000.0;
- const float maxInLumi = min(maxMasteringLumi, maxContentLumi);
+ float maxMasteringLumi = maxMasteringLuminance;
+ float maxContentLumi = maxContentLuminance;
+ float maxInLumi = min(maxMasteringLumi, maxContentLumi);
float maxOutLumi = displayMaxLuminance;
float nits = color.y;
@@ -633,9 +633,10 @@
}
if (needs.hasTransformMatrix() || (needs.getInputTF() != needs.getOutputTF())) {
- // Currently, display maximum luminance is needed when doing tone mapping.
if (needs.needsToneMapping()) {
fs << "uniform float displayMaxLuminance;";
+ fs << "uniform float maxMasteringLuminance;";
+ fs << "uniform float maxContentLuminance;";
}
if (needs.hasInputTransformMatrix()) {
diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h
index b8bf801..d890ccd 100644
--- a/libs/renderengine/include/renderengine/LayerSettings.h
+++ b/libs/renderengine/include/renderengine/LayerSettings.h
@@ -60,6 +60,8 @@
// HDR color-space setting for Y410.
bool isY410BT2020 = false;
+ float maxMasteringLuminance = 0.0;
+ float maxContentLuminance = 0.0;
};
// Metadata describing the layer geometry.
@@ -96,6 +98,33 @@
half3 solidColor = half3(0.0f, 0.0f, 0.0f);
};
+/*
+ * Contains the configuration for the shadows drawn by single layer. Shadow follows
+ * material design guidelines.
+ */
+struct ShadowSettings {
+ // Color to the ambient shadow. The alpha is premultiplied.
+ vec4 ambientColor = vec4();
+
+ // Color to the spot shadow. The alpha is premultiplied. The position of the spot shadow
+ // depends on the light position.
+ vec4 spotColor = vec4();
+
+ // Position of the light source used to cast the spot shadow.
+ vec3 lightPos = vec3();
+
+ // Radius of the spot light source. Smaller radius will have sharper edges,
+ // larger radius will have softer shadows
+ float lightRadius = 0.f;
+
+ // Length of the cast shadow. If length is <= 0.f no shadows will be drawn.
+ float length = 0.f;
+
+ // If true fill in the casting layer is translucent and the shadow needs to fill the bounds.
+ // Otherwise the shadow will only be drawn around the edges of the casting layer.
+ bool casterIsTranslucent = false;
+};
+
// The settings that RenderEngine requires for correctly rendering a Layer.
struct LayerSettings {
// Geometry information
@@ -116,6 +145,8 @@
// True if blending will be forced to be disabled.
bool disableBlending = false;
+
+ ShadowSettings shadow;
};
} // namespace renderengine
diff --git a/libs/renderengine/include/renderengine/private/Description.h b/libs/renderengine/include/renderengine/private/Description.h
index bd2055f..bad64c2 100644
--- a/libs/renderengine/include/renderengine/private/Description.h
+++ b/libs/renderengine/include/renderengine/private/Description.h
@@ -71,6 +71,8 @@
TransferFunction outputTransferFunction = TransferFunction::LINEAR;
float displayMaxLuminance;
+ float maxMasteringLuminance;
+ float maxContentLuminance;
// projection matrix
mat4 projectionMatrix;
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index afa6a2b..3775e2b 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -69,6 +69,10 @@
include_dirs: [
"frameworks/native/include",
],
+ export_include_dirs: [
+ "include",
+ "include_private",
+ ],
// Uncomment the following line to enable VALIDATE_REGIONS traces
//defaults: ["libui-validate-regions-defaults"],
diff --git a/libs/ui/Gralloc2.cpp b/libs/ui/Gralloc2.cpp
index 5dc4530..0e23ddf 100644
--- a/libs/ui/Gralloc2.cpp
+++ b/libs/ui/Gralloc2.cpp
@@ -381,7 +381,8 @@
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) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo = {};
descriptorInfo.width = width;
descriptorInfo.height = height;
@@ -404,19 +405,33 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
-
*outStride = tmpStride;
});
diff --git a/libs/ui/Gralloc3.cpp b/libs/ui/Gralloc3.cpp
index eb43765..e189281 100644
--- a/libs/ui/Gralloc3.cpp
+++ b/libs/ui/Gralloc3.cpp
@@ -362,7 +362,8 @@
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) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -381,16 +382,31 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
*outStride = tmpStride;
diff --git a/libs/ui/Gralloc4.cpp b/libs/ui/Gralloc4.cpp
index dc105c0..afe26b7 100644
--- a/libs/ui/Gralloc4.cpp
+++ b/libs/ui/Gralloc4.cpp
@@ -32,7 +32,6 @@
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 android::hardware::graphics::mapper::V4_0::YCbCrLayout;
namespace android {
@@ -190,6 +189,16 @@
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;
+ }
+
auto buffer = const_cast<native_handle_t*>(bufferHandle);
IMapper::Rect accessRegion = sGralloc4Rect(bounds);
@@ -205,19 +214,12 @@
Error error;
auto ret = mMapper->lock(buffer, usage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpData,
- const auto& tmpBytesPerPixel, const auto& tmpBytesPerStride) {
+ [&](const auto& tmpError, const auto& tmpData) {
error = tmpError;
if (error != Error::NONE) {
return;
}
*outData = tmpData;
- if (outBytesPerPixel) {
- *outBytesPerPixel = tmpBytesPerPixel;
- }
- if (outBytesPerStride) {
- *outBytesPerStride = tmpBytesPerStride;
- }
});
// we own acquireFence even on errors
@@ -232,48 +234,11 @@
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 {
- auto buffer = const_cast<native_handle_t*>(bufferHandle);
-
- IMapper::Rect accessRegion = sGralloc4Rect(bounds);
-
- // put acquireFence in a hidl_handle
- hardware::hidl_handle acquireFenceHandle;
- NATIVE_HANDLE_DECLARE_STORAGE(acquireFenceStorage, 1, 0);
- if (acquireFence >= 0) {
- auto h = native_handle_init(acquireFenceStorage, 1, 0);
- h->data[0] = acquireFence;
- acquireFenceHandle = h;
- }
-
- YCbCrLayout layout;
- Error error;
- auto ret = mMapper->lockYCbCr(buffer, usage, accessRegion, acquireFenceHandle,
- [&](const auto& tmpError, const auto& tmpLayout) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
-
- layout = tmpLayout;
- });
-
- if (error == Error::NONE) {
- ycbcr->y = layout.y;
- ycbcr->cb = layout.cb;
- ycbcr->cr = layout.cr;
- ycbcr->ystride = static_cast<size_t>(layout.yStride);
- ycbcr->cstride = static_cast<size_t>(layout.cStride);
- ycbcr->chroma_step = static_cast<size_t>(layout.chromaStep);
- }
-
- // we own acquireFence even on errors
- if (acquireFence >= 0) {
- close(acquireFence);
- }
-
- return static_cast<status_t>((ret.isOk()) ? error : kTransactionError);
+status_t Gralloc4Mapper::lock(buffer_handle_t /*bufferHandle*/, uint64_t /*usage*/,
+ const Rect& /*bounds*/, int /*acquireFence*/,
+ android_ycbcr* /*ycbcr*/) const {
+ // TODO add lockYCbCr support
+ return static_cast<status_t>(Error::UNSUPPORTED);
}
int Gralloc4Mapper::unlock(buffer_handle_t bufferHandle) const {
@@ -362,7 +327,8 @@
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) const {
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers) const {
IMapper::BufferDescriptorInfo descriptorInfo;
sBufferDescriptorInfo(width, height, format, layerCount, usage, &descriptorInfo);
@@ -381,16 +347,31 @@
return;
}
- // import buffers
- for (uint32_t i = 0; i < bufferCount; i++) {
- error = mMapper.importBuffer(tmpBuffers[i],
- &outBufferHandles[i]);
- if (error != NO_ERROR) {
- for (uint32_t j = 0; j < i; j++) {
- mMapper.freeBuffer(outBufferHandles[j]);
- outBufferHandles[j] = nullptr;
+ if (importBuffers) {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ error = mMapper.importBuffer(tmpBuffers[i],
+ &outBufferHandles[i]);
+ if (error != NO_ERROR) {
+ for (uint32_t j = 0; j < i; j++) {
+ mMapper.freeBuffer(outBufferHandles[j]);
+ outBufferHandles[j] = nullptr;
+ }
+ return;
}
- return;
+ }
+ } else {
+ for (uint32_t i = 0; i < bufferCount; i++) {
+ outBufferHandles[i] = native_handle_clone(
+ tmpBuffers[i].getNativeHandle());
+ if (!outBufferHandles[i]) {
+ for (uint32_t j = 0; j < i; j++) {
+ auto buffer = const_cast<native_handle_t*>(
+ outBufferHandles[j]);
+ native_handle_close(buffer);
+ native_handle_delete(buffer);
+ outBufferHandles[j] = nullptr;
+ }
+ }
}
}
*outStride = tmpStride;
diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp
index 579e68e..05fc590 100644
--- a/libs/ui/GraphicBuffer.cpp
+++ b/libs/ui/GraphicBuffer.cpp
@@ -27,7 +27,6 @@
#include <ui/BufferHubBuffer.h>
#endif // LIBUI_IN_VNDK
-#include <ui/Gralloc2.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <utils/Trace.h>
diff --git a/libs/ui/GraphicBufferAllocator.cpp b/libs/ui/GraphicBufferAllocator.cpp
index fcc2547..b2b9680 100644
--- a/libs/ui/GraphicBufferAllocator.cpp
+++ b/libs/ui/GraphicBufferAllocator.cpp
@@ -111,11 +111,10 @@
ALOGD("%s", s.c_str());
}
-status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height,
- PixelFormat format, uint32_t layerCount, uint64_t usage,
- buffer_handle_t* handle, uint32_t* stride,
- uint64_t /*graphicBufferId*/, std::string requestorName)
-{
+status_t GraphicBufferAllocator::allocateHelper(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName, bool importBuffer) {
ATRACE_CALL();
// make sure to not allocate a N x 0 or 0 x N buffer, since this is
@@ -138,8 +137,18 @@
// 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);
+ status_t error = mAllocator->allocate(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",
+ width, height, layerCount, format, usage, error);
+ return NO_MEMORY;
+ }
+
+ if (!importBuffer) {
+ return NO_ERROR;
+ }
size_t bufSize;
// if stride has no meaning or is too large,
@@ -151,28 +160,44 @@
bufSize = static_cast<size_t>((*stride)) * height * bpp;
}
- if (error == NO_ERROR) {
- Mutex::Autolock _l(sLock);
- KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
- alloc_rec_t rec;
- rec.width = width;
- rec.height = height;
- rec.stride = *stride;
- rec.format = format;
- rec.layerCount = layerCount;
- rec.usage = usage;
- rec.size = bufSize;
- rec.requestorName = std::move(requestorName);
- list.add(*handle, rec);
+ Mutex::Autolock _l(sLock);
+ KeyedVector<buffer_handle_t, alloc_rec_t>& list(sAllocList);
+ alloc_rec_t rec;
+ rec.width = width;
+ rec.height = height;
+ rec.stride = *stride;
+ rec.format = format;
+ rec.layerCount = layerCount;
+ rec.usage = usage;
+ rec.size = bufSize;
+ rec.requestorName = std::move(requestorName);
+ list.add(*handle, rec);
- return NO_ERROR;
- } else {
- ALOGE("Failed to allocate (%u x %u) layerCount %u format %d "
- "usage %" PRIx64 ": %d",
- width, height, layerCount, format, usage,
- error);
- return NO_MEMORY;
- }
+ return NO_ERROR;
+}
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ true);
+}
+
+status_t GraphicBufferAllocator::allocateRawHandle(uint32_t width, uint32_t height,
+ PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle,
+ uint32_t* stride, std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ false);
+}
+
+// DEPRECATED
+status_t GraphicBufferAllocator::allocate(uint32_t width, uint32_t height, PixelFormat format,
+ uint32_t layerCount, uint64_t usage,
+ buffer_handle_t* handle, uint32_t* stride,
+ uint64_t /*graphicBufferId*/, std::string requestorName) {
+ return allocateHelper(width, height, format, layerCount, usage, handle, stride, requestorName,
+ true);
}
status_t GraphicBufferAllocator::free(buffer_handle_t handle)
diff --git a/libs/ui/Region.cpp b/libs/ui/Region.cpp
index 1222cd6..83ebeca 100644
--- a/libs/ui/Region.cpp
+++ b/libs/ui/Region.cpp
@@ -23,11 +23,10 @@
#include <utils/Log.h>
+#include <ui/Point.h>
#include <ui/Rect.h>
#include <ui/Region.h>
-#include <ui/Point.h>
-
-#include <private/ui/RegionHelper.h>
+#include <ui/RegionHelper.h>
// ----------------------------------------------------------------------------
diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp
index 28c3f7b..85abb38 100644
--- a/libs/ui/Transform.cpp
+++ b/libs/ui/Transform.cpp
@@ -33,8 +33,8 @@
: mMatrix(other.mMatrix), mType(other.mType) {
}
-Transform::Transform(uint32_t orientation) {
- set(orientation, 0, 0);
+Transform::Transform(uint32_t orientation, int w, int h) {
+ set(orientation, w, h);
}
Transform::~Transform() = default;
diff --git a/libs/ui/include/ui/Gralloc.h b/libs/ui/include/ui/Gralloc.h
index 6cc23f0..c28f7a5 100644
--- a/libs/ui/include/ui/Gralloc.h
+++ b/libs/ui/include/ui/Gralloc.h
@@ -94,7 +94,8 @@
*/
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) const = 0;
+ uint32_t* outStride, buffer_handle_t* outBufferHandles,
+ bool importBuffers = true) const = 0;
};
} // namespace android
diff --git a/libs/ui/include/ui/Gralloc2.h b/libs/ui/include/ui/Gralloc2.h
index 948f597..12c772a 100644
--- a/libs/ui/include/ui/Gralloc2.h
+++ b/libs/ui/include/ui/Gralloc2.h
@@ -85,7 +85,7 @@
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) const override;
+ 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 0965f52..bfbc2aa 100644
--- a/libs/ui/include/ui/Gralloc3.h
+++ b/libs/ui/include/ui/Gralloc3.h
@@ -83,7 +83,7 @@
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) const override;
+ 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 14b65bc..60115f9 100644
--- a/libs/ui/include/ui/Gralloc4.h
+++ b/libs/ui/include/ui/Gralloc4.h
@@ -83,7 +83,7 @@
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) const override;
+ 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 324d9e1..34a5b17 100644
--- a/libs/ui/include/ui/GraphicBufferAllocator.h
+++ b/libs/ui/include/ui/GraphicBufferAllocator.h
@@ -42,6 +42,29 @@
public:
static inline GraphicBufferAllocator& get() { return getInstance(); }
+ /**
+ * Allocates and imports a gralloc buffer.
+ *
+ * The handle must be freed with GraphicBufferAllocator::free() when no longer needed.
+ */
+ status_t allocate(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName);
+
+ /**
+ * Allocates and does NOT import a gralloc buffer. Buffers cannot be used until they have
+ * been imported. This function is for advanced use cases only.
+ *
+ * The raw native handle must be freed by calling native_handle_close() followed by
+ * native_handle_delete().
+ */
+ status_t allocateRawHandle(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName);
+
+ /**
+ * DEPRECATED: GraphicBufferAllocator does not use the graphicBufferId.
+ */
status_t allocate(uint32_t w, uint32_t h, PixelFormat format,
uint32_t layerCount, uint64_t usage,
buffer_handle_t* handle, uint32_t* stride, uint64_t graphicBufferId,
@@ -66,6 +89,10 @@
std::string requestorName;
};
+ status_t allocateHelper(uint32_t w, uint32_t h, PixelFormat format, uint32_t layerCount,
+ uint64_t usage, buffer_handle_t* handle, uint32_t* stride,
+ std::string requestorName, bool importBuffer);
+
static Mutex sLock;
static KeyedVector<buffer_handle_t, alloc_rec_t> sAllocList;
diff --git a/libs/ui/include/ui/GraphicBufferMapper.h b/libs/ui/include/ui/GraphicBufferMapper.h
index c401a48..83fb144 100644
--- a/libs/ui/include/ui/GraphicBufferMapper.h
+++ b/libs/ui/include/ui/GraphicBufferMapper.h
@@ -23,6 +23,7 @@
#include <memory>
#include <ui/PixelFormat.h>
+#include <ui/Rect.h>
#include <utils/Singleton.h>
@@ -36,7 +37,6 @@
// ---------------------------------------------------------------------------
class GrallocMapper;
-class Rect;
class GraphicBufferMapper : public Singleton<GraphicBufferMapper>
{
diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h
index f29a370..fab2d9e 100644
--- a/libs/ui/include/ui/Transform.h
+++ b/libs/ui/include/ui/Transform.h
@@ -38,7 +38,7 @@
public:
Transform();
Transform(const Transform& other);
- explicit Transform(uint32_t orientation);
+ explicit Transform(uint32_t orientation, int w = 0, int h = 0);
~Transform();
enum orientation_flags {
diff --git a/include/private/ui/RegionHelper.h b/libs/ui/include_private/ui/RegionHelper.h
similarity index 70%
rename from include/private/ui/RegionHelper.h
rename to libs/ui/include_private/ui/RegionHelper.h
index 0ec3e94..92cfba8 100644
--- a/include/private/ui/RegionHelper.h
+++ b/libs/ui/include_private/ui/RegionHelper.h
@@ -17,18 +17,17 @@
#ifndef ANDROID_UI_PRIVATE_REGION_HELPER_H
#define ANDROID_UI_PRIVATE_REGION_HELPER_H
-#include <limits>
#include <stdint.h>
#include <sys/types.h>
+#include <limits>
#include <limits>
namespace android {
// ----------------------------------------------------------------------------
-template<typename RECT>
-class region_operator
-{
+template <typename RECT>
+class region_operator {
public:
typedef typename RECT::value_type TYPE;
static const TYPE max_value = std::numeric_limits<TYPE>::max();
@@ -40,39 +39,32 @@
* their corresponding value with the above formulae and use
* it when instantiating a region_operator.
*/
- static const uint32_t LHS = 0x5; // 0b101
- static const uint32_t RHS = 0x6; // 0b110
- enum {
- op_nand = LHS & ~RHS,
- op_and = LHS & RHS,
- op_or = LHS | RHS,
- op_xor = LHS ^ RHS
- };
+ static const uint32_t LHS = 0x5; // 0b101
+ static const uint32_t RHS = 0x6; // 0b110
+ enum { op_nand = LHS & ~RHS, op_and = LHS & RHS, op_or = LHS | RHS, op_xor = LHS ^ RHS };
struct region {
RECT const* rects;
size_t count;
TYPE dx;
TYPE dy;
- inline region(const region& rhs)
- : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) { }
- inline region(RECT const* _r, size_t _c)
- : rects(_r), count(_c), dx(), dy() { }
+ inline region(const region& rhs)
+ : rects(rhs.rects), count(rhs.count), dx(rhs.dx), dy(rhs.dy) {}
+ inline region(RECT const* _r, size_t _c) : rects(_r), count(_c), dx(), dy() {}
inline region(RECT const* _r, size_t _c, TYPE _dx, TYPE _dy)
- : rects(_r), count(_c), dx(_dx), dy(_dy) { }
+ : rects(_r), count(_c), dx(_dx), dy(_dy) {}
};
class region_rasterizer {
friend class region_operator;
virtual void operator()(const RECT& rect) = 0;
+
public:
- virtual ~region_rasterizer() { }
+ virtual ~region_rasterizer() {}
};
-
+
inline region_operator(uint32_t op, const region& lhs, const region& rhs)
- : op_mask(op), spanner(lhs, rhs)
- {
- }
+ : op_mask(op), spanner(lhs, rhs) {}
void operator()(region_rasterizer& rasterizer) {
RECT current(Rect::EMPTY_RECT);
@@ -83,31 +75,26 @@
do {
int inner_inside = spannerInner.next(current.left, current.right);
if ((op_mask >> inner_inside) & 1) {
- if (current.left < current.right &&
- current.top < current.bottom) {
+ if (current.left < current.right && current.top < current.bottom) {
rasterizer(current);
}
}
- } while(!spannerInner.isDone());
- } while(!spanner.isDone());
+ } while (!spannerInner.isDone());
+ } while (!spanner.isDone());
}
-private:
+private:
uint32_t op_mask;
- class SpannerBase
- {
+ class SpannerBase {
public:
SpannerBase()
- : lhs_head(max_value), lhs_tail(max_value),
- rhs_head(max_value), rhs_tail(max_value) {
- }
+ : lhs_head(max_value),
+ lhs_tail(max_value),
+ rhs_head(max_value),
+ rhs_tail(max_value) {}
- enum {
- lhs_before_rhs = 0,
- lhs_after_rhs = 1,
- lhs_coincide_rhs = 2
- };
+ enum { lhs_before_rhs = 0, lhs_after_rhs = 1, lhs_coincide_rhs = 2 };
protected:
TYPE lhs_head;
@@ -115,9 +102,7 @@
TYPE rhs_head;
TYPE rhs_tail;
- inline int next(TYPE& head, TYPE& tail,
- bool& more_lhs, bool& more_rhs)
- {
+ inline int next(TYPE& head, TYPE& tail, bool& more_lhs, bool& more_rhs) {
int inside;
more_lhs = false;
more_rhs = false;
@@ -157,32 +142,26 @@
}
};
- class Spanner : protected SpannerBase
- {
+ class Spanner : protected SpannerBase {
friend class region_operator;
region lhs;
region rhs;
public:
- inline Spanner(const region& _lhs, const region& _rhs)
- : lhs(_lhs), rhs(_rhs)
- {
+ inline Spanner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {
if (lhs.count) {
- SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
- SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
+ SpannerBase::lhs_head = lhs.rects->top + lhs.dy;
+ SpannerBase::lhs_tail = lhs.rects->bottom + lhs.dy;
}
if (rhs.count) {
- SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
- SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
+ SpannerBase::rhs_head = rhs.rects->top + rhs.dy;
+ SpannerBase::rhs_tail = rhs.rects->bottom + rhs.dy;
}
}
- inline bool isDone() const {
- return !rhs.count && !lhs.count;
- }
+ inline bool isDone() const { return !rhs.count && !lhs.count; }
- inline int next(TYPE& top, TYPE& bottom)
- {
+ inline int next(TYPE& top, TYPE& bottom) {
bool more_lhs = false;
bool more_rhs = false;
int inside = SpannerBase::next(top, bottom, more_lhs, more_rhs);
@@ -196,22 +175,21 @@
}
private:
- static inline
- void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
+ static inline void advance(region& reg, TYPE& aTop, TYPE& aBottom) {
// got to next span
size_t count = reg.count;
- RECT const * rects = reg.rects;
- RECT const * const end = rects + count;
+ RECT const* rects = reg.rects;
+ RECT const* const end = rects + count;
const int top = rects->top;
while (rects != end && rects->top == top) {
rects++;
count--;
}
if (rects != end) {
- aTop = rects->top + reg.dy;
+ aTop = rects->top + reg.dy;
aBottom = rects->bottom + reg.dy;
} else {
- aTop = max_value;
+ aTop = max_value;
aBottom = max_value;
}
reg.rects = rects;
@@ -219,21 +197,17 @@
}
};
- class SpannerInner : protected SpannerBase
- {
+ class SpannerInner : protected SpannerBase {
region lhs;
region rhs;
-
+
public:
- inline SpannerInner(const region& _lhs, const region& _rhs)
- : lhs(_lhs), rhs(_rhs)
- {
- }
+ inline SpannerInner(const region& _lhs, const region& _rhs) : lhs(_lhs), rhs(_rhs) {}
inline void prepare(int inside) {
if (inside == SpannerBase::lhs_before_rhs) {
if (lhs.count) {
- SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
+ SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
}
SpannerBase::rhs_head = max_value;
@@ -242,28 +216,26 @@
SpannerBase::lhs_head = max_value;
SpannerBase::lhs_tail = max_value;
if (rhs.count) {
- SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
+ SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
}
} else {
if (lhs.count) {
- SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
+ SpannerBase::lhs_head = lhs.rects->left + lhs.dx;
SpannerBase::lhs_tail = lhs.rects->right + lhs.dx;
}
if (rhs.count) {
- SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
+ SpannerBase::rhs_head = rhs.rects->left + rhs.dx;
SpannerBase::rhs_tail = rhs.rects->right + rhs.dx;
}
}
}
inline bool isDone() const {
- return SpannerBase::lhs_head == max_value &&
- SpannerBase::rhs_head == max_value;
+ return SpannerBase::lhs_head == max_value && SpannerBase::rhs_head == max_value;
}
- inline int next(TYPE& left, TYPE& right)
- {
+ inline int next(TYPE& left, TYPE& right) {
bool more_lhs = false;
bool more_rhs = false;
int inside = SpannerBase::next(left, right, more_lhs, more_rhs);
@@ -277,17 +249,16 @@
}
private:
- static inline
- void advance(region& reg, TYPE& left, TYPE& right) {
+ static inline void advance(region& reg, TYPE& left, TYPE& right) {
if (reg.rects && reg.count) {
const int cur_span_top = reg.rects->top;
reg.rects++;
reg.count--;
if (!reg.count || reg.rects->top != cur_span_top) {
- left = max_value;
+ left = max_value;
right = max_value;
} else {
- left = reg.rects->left + reg.dx;
+ left = reg.rects->left + reg.dx;
right = reg.rects->right + reg.dx;
}
}
@@ -298,6 +269,6 @@
};
// ----------------------------------------------------------------------------
-};
+}; // namespace android
#endif /* ANDROID_UI_PRIVATE_REGION_HELPER_H */
diff --git a/libs/ui/tests/mock/MockGrallocAllocator.h b/libs/ui/tests/mock/MockGrallocAllocator.h
index 22c80a4..7660e9f 100644
--- a/libs/ui/tests/mock/MockGrallocAllocator.h
+++ b/libs/ui/tests/mock/MockGrallocAllocator.h
@@ -36,7 +36,7 @@
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,
- buffer_handle_t* outBufferHandles),
+ buffer_handle_t* outBufferHandles, bool less),
(const, override));
};
diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp
index 67607af..188ac6b 100644
--- a/libs/vr/libvrflinger/hardware_composer.cpp
+++ b/libs/vr/libvrflinger/hardware_composer.cpp
@@ -1201,6 +1201,20 @@
return Void();
}
+Return<void> HardwareComposer::ComposerCallback::onVsync_2_4(
+ Hwc2::Display /*display*/, int64_t /*timestamp*/,
+ Hwc2::VsyncPeriodNanos /*vsyncPeriodNanos*/) {
+ LOG_ALWAYS_FATAL("Unexpected onVsync_2_4 callback");
+ return Void();
+}
+
+Return<void> HardwareComposer::ComposerCallback::onVsyncPeriodTimingChanged(
+ Hwc2::Display /*display*/,
+ const Hwc2::VsyncPeriodChangeTimeline& /*updatedTimeline*/) {
+ LOG_ALWAYS_FATAL("Unexpected onVsyncPeriodTimingChanged callback");
+ return Void();
+}
+
void HardwareComposer::ComposerCallback::SetVsyncService(
const sp<VsyncService>& vsync_service) {
std::lock_guard<std::mutex> lock(mutex_);
diff --git a/libs/vr/libvrflinger/hardware_composer.h b/libs/vr/libvrflinger/hardware_composer.h
index 989ce35..8698814 100644
--- a/libs/vr/libvrflinger/hardware_composer.h
+++ b/libs/vr/libvrflinger/hardware_composer.h
@@ -375,6 +375,12 @@
hardware::Return<void> onRefresh(Hwc2::Display display) override;
hardware::Return<void> onVsync(Hwc2::Display display,
int64_t timestamp) override;
+ hardware::Return<void> onVsync_2_4(
+ Hwc2::Display display, int64_t timestamp,
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override;
+ hardware::Return<void> onVsyncPeriodTimingChanged(
+ Hwc2::Display display,
+ const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override;
bool GotFirstHotplug() { return got_first_hotplug_; }
void SetVsyncService(const sp<VsyncService>& vsync_service);
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index 48a68af..e255b9d 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -161,6 +161,10 @@
],
ldflags: ["-Wl,--exclude-libs=ALL,--Bsymbolic-functions"],
export_include_dirs: ["EGL/include"],
+ stubs: {
+ symbol_file: "libEGL.map.txt",
+ versions: ["29"],
+ },
}
cc_test {
diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp
index 7b8e0f8..baba64f 100644
--- a/services/gpuservice/Android.bp
+++ b/services/gpuservice/Android.bp
@@ -1,20 +1,6 @@
-filegroup {
- name: "gpuservice_sources",
- srcs: [
- "GpuService.cpp",
- "gpustats/GpuStats.cpp"
- ],
-}
-
-filegroup {
- name: "gpuservice_binary_sources",
- srcs: ["main_gpuservice.cpp"],
-}
-
cc_defaults {
name: "gpuservice_defaults",
cflags: [
- "-DLOG_TAG=\"GpuService\"",
"-Wall",
"-Werror",
"-Wformat",
@@ -22,12 +8,13 @@
"-Wunused",
"-Wunreachable-code",
],
- srcs: [
- ":gpuservice_sources",
- ],
- include_dirs: [
- "frameworks/native/vulkan/vkjson",
- "frameworks/native/vulkan/include",
+}
+
+cc_defaults {
+ name: "libgpuservice_defaults",
+ defaults: ["gpuservice_defaults"],
+ cflags: [
+ "-DLOG_TAG=\"GpuService\"",
],
shared_libs: [
"libbase",
@@ -36,17 +23,22 @@
"libgraphicsenv",
"liblog",
"libutils",
- "libvulkan",
+ "libvkjson",
],
static_libs: [
"libserviceutils",
- "libvkjson",
+ ],
+ export_static_lib_headers: [
+ "libserviceutils",
+ ],
+ export_shared_lib_headers: [
+ "libgraphicsenv",
],
}
cc_defaults {
- name: "gpuservice_production_defaults",
- defaults: ["gpuservice_defaults"],
+ name: "libgpuservice_production_defaults",
+ defaults: ["libgpuservice_defaults"],
cflags: [
"-fvisibility=hidden",
"-fwhole-program-vtables", // requires ThinLTO
@@ -56,8 +48,24 @@
},
}
+filegroup {
+ name: "libgpuservice_sources",
+ srcs: [
+ "GpuService.cpp",
+ "gpustats/GpuStats.cpp"
+ ],
+}
+
+cc_library_shared {
+ name: "libgpuservice",
+ defaults: ["libgpuservice_production_defaults"],
+ srcs: [
+ ":libgpuservice_sources",
+ ],
+}
+
cc_defaults {
- name: "gpuservice_binary",
+ name: "libgpuservice_binary",
defaults: ["gpuservice_defaults"],
shared_libs: [
"libbinder",
@@ -68,9 +76,17 @@
ldflags: ["-Wl,--export-dynamic"],
}
+filegroup {
+ name: "gpuservice_binary_sources",
+ srcs: ["main_gpuservice.cpp"],
+}
+
cc_binary {
name: "gpuservice",
- defaults: ["gpuservice_binary"],
+ defaults: ["libgpuservice_binary"],
init_rc: ["gpuservice.rc"],
srcs: [":gpuservice_binary_sources"],
+ shared_libs: [
+ "libgpuservice",
+ ],
}
diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp
index e7640dd..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,30 +37,24 @@
mDispatcher = createInputDispatcher(dispatcherPolicy);
mClassifier = new InputClassifier(mDispatcher);
mReader = createInputReader(readerPolicy, mClassifier);
- initialize();
}
InputManager::~InputManager() {
stop();
}
-void InputManager::initialize() {
- mReaderThread = new InputReaderThread(mReader);
- 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;
}
- result = mReaderThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+ result = mReader->start();
if (result) {
- ALOGE("Could not start InputReader thread due to error %d.", result);
+ ALOGE("Could not start InputReader due to error %d.", result);
- mDispatcherThread->requestExit();
+ mDispatcher->stop();
return result;
}
@@ -69,17 +62,21 @@
}
status_t InputManager::stop() {
- status_t result = mReaderThread->requestExitAndWait();
+ status_t status = OK;
+
+ status_t result = mReader->stop();
if (result) {
- ALOGW("Could not stop InputReader thread due to error %d.", 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 40f66d8..586097f 100644
--- a/services/inputflinger/InputManager.h
+++ b/services/inputflinger/InputManager.h
@@ -43,15 +43,15 @@
/*
* The input manager is the core of the system event processing.
*
- * The input manager uses two threads.
+ * The input manager has two components.
*
- * 1. The InputReaderThread (called "InputReader") reads and preprocesses raw input events,
- * applies policy, and posts messages to a queue managed by the DispatcherThread.
- * 2. The InputDispatcherThread (called "InputDispatcher") thread waits for new events on the
+ * 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 InputDispatcher class starts a thread that waits for new events on the
* queue and asynchronously dispatches them to applications.
*
- * By design, the InputReaderThread class and InputDispatcherThread class do not share any
- * internal state. Moreover, all communication is done one way from the InputReaderThread
+ * 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. */
@@ -102,14 +102,10 @@
private:
sp<InputReaderInterface> mReader;
- sp<InputReaderThread> mReaderThread;
sp<InputClassifierInterface> mClassifier;
sp<InputDispatcherInterface> mDispatcher;
- sp<InputDispatcherThread> mDispatcherThread;
-
- void initialize();
};
} // namespace android
diff --git a/services/inputflinger/InputReaderBase.cpp b/services/inputflinger/InputReaderBase.cpp
index 0422d83..2d6f2c1 100644
--- a/services/inputflinger/InputReaderBase.cpp
+++ b/services/inputflinger/InputReaderBase.cpp
@@ -33,20 +33,6 @@
namespace android {
-// --- InputReaderThread ---
-
-InputReaderThread::InputReaderThread(const sp<InputReaderInterface>& reader) :
- Thread(/*canCallJava*/ true), mReader(reader) {
-}
-
-InputReaderThread::~InputReaderThread() {
-}
-
-bool InputReaderThread::threadLoop() {
- mReader->loopOnce();
- return true;
-}
-
// --- InputReaderConfiguration ---
std::string InputReaderConfiguration::changesToString(uint32_t changes) {
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/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index 930c7c7..e925f5b 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -60,7 +60,7 @@
// --- EventEntry ---
-EventEntry::EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags)
+EventEntry::EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags)
: sequenceNum(sequenceNum),
refCount(1),
type(type),
@@ -92,7 +92,7 @@
// --- ConfigurationChangedEntry ---
ConfigurationChangedEntry::ConfigurationChangedEntry(uint32_t sequenceNum, nsecs_t eventTime)
- : EventEntry(sequenceNum, TYPE_CONFIGURATION_CHANGED, eventTime, 0) {}
+ : EventEntry(sequenceNum, Type::CONFIGURATION_CHANGED, eventTime, 0) {}
ConfigurationChangedEntry::~ConfigurationChangedEntry() {}
@@ -103,7 +103,7 @@
// --- DeviceResetEntry ---
DeviceResetEntry::DeviceResetEntry(uint32_t sequenceNum, nsecs_t eventTime, int32_t deviceId)
- : EventEntry(sequenceNum, TYPE_DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
+ : EventEntry(sequenceNum, Type::DEVICE_RESET, eventTime, 0), deviceId(deviceId) {}
DeviceResetEntry::~DeviceResetEntry() {}
@@ -117,7 +117,7 @@
int32_t displayId, uint32_t policyFlags, int32_t action, int32_t flags,
int32_t keyCode, int32_t scanCode, int32_t metaState, int32_t repeatCount,
nsecs_t downTime)
- : EventEntry(sequenceNum, TYPE_KEY, eventTime, policyFlags),
+ : EventEntry(sequenceNum, Type::KEY, eventTime, policyFlags),
deviceId(deviceId),
source(source),
displayId(displayId),
@@ -165,7 +165,7 @@
float xCursorPosition, float yCursorPosition, nsecs_t downTime,
uint32_t pointerCount, const PointerProperties* pointerProperties,
const PointerCoords* pointerCoords, float xOffset, float yOffset)
- : EventEntry(sequenceNum, TYPE_MOTION, eventTime, policyFlags),
+ : EventEntry(sequenceNum, Type::MOTION, eventTime, policyFlags),
eventTime(eventTime),
deviceId(deviceId),
source(source),
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index 28c2799..9dcaadc 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -33,11 +33,24 @@
constexpr uint32_t SYNTHESIZED_EVENT_SEQUENCE_NUM = 0;
struct EventEntry {
- enum { TYPE_CONFIGURATION_CHANGED, TYPE_DEVICE_RESET, TYPE_KEY, TYPE_MOTION };
+ enum class Type { CONFIGURATION_CHANGED, DEVICE_RESET, KEY, MOTION };
+
+ static const char* typeToString(Type type) {
+ switch (type) {
+ case Type::CONFIGURATION_CHANGED:
+ return "CONFIGURATION_CHANGED";
+ case Type::DEVICE_RESET:
+ return "DEVICE_RESET";
+ case Type::KEY:
+ return "KEY";
+ case Type::MOTION:
+ return "MOTION";
+ }
+ }
uint32_t sequenceNum;
mutable int32_t refCount;
- int32_t type;
+ Type type;
nsecs_t eventTime;
uint32_t policyFlags;
InjectionState* injectionState;
@@ -66,7 +79,7 @@
virtual void appendDescription(std::string& msg) const = 0;
protected:
- EventEntry(uint32_t sequenceNum, int32_t type, nsecs_t eventTime, uint32_t policyFlags);
+ EventEntry(uint32_t sequenceNum, Type type, nsecs_t eventTime, uint32_t policyFlags);
virtual ~EventEntry();
void releaseInjectionState();
};
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 58a5b3c..dcb3ebc 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -240,6 +240,24 @@
return removed;
}
+// --- 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)
@@ -252,6 +270,10 @@
mDispatchEnabled(false),
mDispatchFrozen(false),
mInputFilterEnabled(false),
+ // mInTouchMode will be initialized by the WindowManager to the default device config.
+ // To avoid leaking stack in case that call never comes, and for tests,
+ // initialize it here anyways.
+ mInTouchMode(true),
mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) {
mLooper = new Looper(false);
@@ -260,6 +282,8 @@
mKeyRepeatState.lastKeyEntry = nullptr;
policy->getDispatcherConfiguration(&mConfig);
+
+ mThread = new InputDispatcherThread(this);
}
InputDispatcher::~InputDispatcher() {
@@ -277,6 +301,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
@@ -294,6 +340,12 @@
if (runCommandsLockedInterruptible()) {
nextWakeupTime = LONG_LONG_MIN;
}
+
+ // We are about to enter an infinitely long sleep, because we have no commands or
+ // pending or queued events
+ if (nextWakeupTime == LONG_LONG_MAX) {
+ mDispatcherEnteredIdle.notify_all();
+ }
} // release lock
// Wait for callback or timeout or wake. (make sure we round up, not down)
@@ -386,7 +438,7 @@
}
switch (mPendingEvent->type) {
- case EventEntry::TYPE_CONFIGURATION_CHANGED: {
+ case EventEntry::Type::CONFIGURATION_CHANGED: {
ConfigurationChangedEntry* typedEntry =
static_cast<ConfigurationChangedEntry*>(mPendingEvent);
done = dispatchConfigurationChangedLocked(currentTime, typedEntry);
@@ -394,14 +446,14 @@
break;
}
- case EventEntry::TYPE_DEVICE_RESET: {
+ case EventEntry::Type::DEVICE_RESET: {
DeviceResetEntry* typedEntry = static_cast<DeviceResetEntry*>(mPendingEvent);
done = dispatchDeviceResetLocked(currentTime, typedEntry);
dropReason = DropReason::NOT_DROPPED; // device resets are never dropped
break;
}
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent);
if (isAppSwitchDue) {
if (isAppSwitchKeyEvent(*typedEntry)) {
@@ -421,7 +473,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent);
if (dropReason == DropReason::NOT_DROPPED && isAppSwitchDue) {
dropReason = DropReason::APP_SWITCH;
@@ -435,10 +487,6 @@
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}
-
- default:
- ALOG_ASSERT(false);
- break;
}
if (done) {
@@ -458,7 +506,7 @@
traceInboundQueueLengthLocked();
switch (entry->type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
@@ -480,7 +528,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
// Optimize case where the current application is unresponsive and the user
// decides to touch a window in a different application.
// If the application takes too long to catch up then we drop all events preceding
@@ -508,6 +556,11 @@
}
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ // nothing to do
+ break;
+ }
}
return needWake;
@@ -627,12 +680,12 @@
}
switch (entry.type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS, reason);
synthesizeCancelationEventsForAllConnectionsLocked(options);
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
if (motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) {
CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS, reason);
@@ -643,6 +696,11 @@
}
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("Should not drop %s events", EventEntry::typeToString(entry.type));
+ break;
+ }
}
}
@@ -1174,18 +1232,19 @@
int32_t InputDispatcher::getTargetDisplayId(const EventEntry& entry) {
int32_t displayId;
switch (entry.type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
displayId = keyEntry.displayId;
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(entry);
displayId = motionEntry.displayId;
break;
}
- default: {
- ALOGE("Unsupported event type '%" PRId32 "' for target display.", entry.type);
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ ALOGE("%s events do not have a target display", EventEntry::typeToString(entry.type));
return ADISPLAY_ID_NONE;
}
}
@@ -1849,7 +1908,7 @@
}
// Ensure that the dispatch queues aren't too far backed up for this event.
- if (eventEntry.type == EventEntry::TYPE_KEY) {
+ if (eventEntry.type == EventEntry::Type::KEY) {
// If the event is a key event, then we must wait for all previous events to
// complete before delivering it because previous events may have the
// side-effect of transferring focus to a different window and we want to
@@ -1937,7 +1996,7 @@
int32_t eventType = USER_ACTIVITY_EVENT_OTHER;
switch (eventEntry.type) {
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
if (motionEntry.action == AMOTION_EVENT_ACTION_CANCEL) {
return;
@@ -1948,7 +2007,7 @@
}
break;
}
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(eventEntry);
if (keyEntry.flags & AKEY_EVENT_FLAG_CANCELED) {
return;
@@ -1956,6 +2015,12 @@
eventType = USER_ACTIVITY_EVENT_BUTTON;
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s events are not user activity",
+ EventEntry::typeToString(eventEntry.type));
+ break;
+ }
}
std::unique_ptr<CommandEntry> commandEntry =
@@ -1996,7 +2061,7 @@
// Split a motion event if needed.
if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
- ALOG_ASSERT(eventEntry->type == EventEntry::TYPE_MOTION);
+ ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) {
@@ -2080,7 +2145,7 @@
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(*eventEntry);
dispatchEntry->resolvedAction = keyEntry.action;
dispatchEntry->resolvedFlags = keyEntry.flags;
@@ -2097,7 +2162,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
@@ -2147,6 +2212,12 @@
break;
}
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s events should not go to apps",
+ EventEntry::typeToString(eventEntry->type));
+ break;
+ }
}
// Remember that we are waiting for this dispatch to complete.
@@ -2206,7 +2277,7 @@
status_t status;
EventEntry* eventEntry = dispatchEntry->eventEntry;
switch (eventEntry->type) {
- case EventEntry::TYPE_KEY: {
+ case EventEntry::Type::KEY: {
KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
// Publish the key event.
@@ -2221,7 +2292,7 @@
break;
}
- case EventEntry::TYPE_MOTION: {
+ case EventEntry::Type::MOTION: {
MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
PointerCoords scaledCoords[MAX_POINTERS];
@@ -2276,10 +2347,12 @@
reportTouchEventForStatistics(*motionEntry);
break;
}
-
- default:
- ALOG_ASSERT(false);
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("Should never start dispatch cycles for %s events",
+ EventEntry::typeToString(eventEntry->type));
return;
+ }
}
// Check the result.
@@ -2502,15 +2575,23 @@
for (size_t i = 0; i < cancelationEvents.size(); i++) {
EventEntry* cancelationEventEntry = cancelationEvents[i];
switch (cancelationEventEntry->type) {
- case EventEntry::TYPE_KEY:
+ case EventEntry::Type::KEY: {
logOutboundKeyDetails("cancel - ",
static_cast<const KeyEntry&>(*cancelationEventEntry));
break;
- case EventEntry::TYPE_MOTION:
+ }
+ case EventEntry::Type::MOTION: {
logOutboundMotionDetails("cancel - ",
static_cast<const MotionEntry&>(
*cancelationEventEntry));
break;
+ }
+ case EventEntry::Type::CONFIGURATION_CHANGED:
+ case EventEntry::Type::DEVICE_RESET: {
+ LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
+ EventEntry::typeToString(cancelationEventEntry->type));
+ break;
+ }
}
InputTarget target;
@@ -3003,7 +3084,7 @@
}
default:
- ALOGW("Cannot inject event of type %d", event->getType());
+ ALOGW("Cannot inject %s events", inputEventTypeToString(event->getType()));
return INPUT_EVENT_INJECTION_FAILED;
}
@@ -3509,6 +3590,11 @@
mLooper->wake();
}
+void InputDispatcher::setInTouchMode(bool inTouchMode) {
+ std::scoped_lock lock(mLock);
+ mInTouchMode = inTouchMode;
+}
+
bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
@@ -4237,11 +4323,11 @@
}
bool restartEvent;
- if (dispatchEntry->eventEntry->type == EventEntry::TYPE_KEY) {
+ if (dispatchEntry->eventEntry->type == EventEntry::Type::KEY) {
KeyEntry* keyEntry = static_cast<KeyEntry*>(dispatchEntry->eventEntry);
restartEvent =
afterKeyEventLockedInterruptible(connection, dispatchEntry, keyEntry, handled);
- } else if (dispatchEntry->eventEntry->type == EventEntry::TYPE_MOTION) {
+ } else if (dispatchEntry->eventEntry->type == EventEntry::Type::MOTION) {
MotionEntry* motionEntry = static_cast<MotionEntry*>(dispatchEntry->eventEntry);
restartEvent = afterMotionEventLockedInterruptible(connection, dispatchEntry, motionEntry,
handled);
@@ -4544,4 +4630,22 @@
mDispatcherIsAlive.wait(_l);
}
+/**
+ * Wake up the dispatcher and wait until it processes all events and commands.
+ * The notification of mDispatcherEnteredIdle is guaranteed to happen after wake(), so
+ * this method can be safely called from any thread, as long as you've ensured that
+ * the work you are interested in completing has already been queued.
+ */
+bool InputDispatcher::waitForIdle() {
+ /**
+ * Timeout should represent the longest possible time that a device might spend processing
+ * events and commands.
+ */
+ constexpr std::chrono::duration TIMEOUT = 100ms;
+ std::unique_lock lock(mLock);
+ mLooper->wake();
+ std::cv_status result = mDispatcherEnteredIdle.wait_for(lock, TIMEOUT);
+ return result == std::cv_status::no_timeout;
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index d21b0a1..96a09e3 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -81,8 +81,9 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
-
- virtual void dispatchOnce() override;
+ virtual bool waitForIdle() 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;
@@ -102,6 +103,7 @@
virtual void setFocusedDisplay(int32_t displayId) override;
virtual void setInputDispatchMode(bool enabled, bool frozen) override;
virtual void setInputFilterEnabled(bool enabled) override;
+ virtual void setInTouchMode(bool inTouchMode) override;
virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
const sp<IBinder>& toToken) override;
@@ -122,12 +124,16 @@
STALE,
};
+ class InputDispatcherThread;
+ sp<InputDispatcherThread> mThread;
+
sp<InputDispatcherPolicyInterface> mPolicy;
android::InputDispatcherConfiguration mConfig;
std::mutex mLock;
std::condition_variable mDispatcherIsAlive;
+ std::condition_variable mDispatcherEnteredIdle;
sp<Looper> mLooper;
@@ -138,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.
@@ -247,6 +258,7 @@
bool mDispatchEnabled GUARDED_BY(mLock);
bool mDispatchFrozen GUARDED_BY(mLock);
bool mInputFilterEnabled GUARDED_BY(mLock);
+ bool mInTouchMode GUARDED_BY(mLock);
std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay
GUARDED_BY(mLock);
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/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 5acf92b..2e9bca2 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -93,21 +93,22 @@
sp<InputChannel> inputChannel;
// Flags for the input target.
- int32_t flags;
+ int32_t flags = 0;
// The x and y offset to add to a MotionEvent as it is delivered.
// (ignored for KeyEvents)
- float xOffset, yOffset;
+ float xOffset = 0.0f;
+ float yOffset = 0.0f;
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
- float globalScaleFactor;
+ float globalScaleFactor = 1.0f;
float windowXScale = 1.0f;
float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
- BitSet32 pointerIds;
+ BitSet32 pointerIds{};
};
std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index ce7366f..3424f4c 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -63,12 +63,23 @@
/* Called by the heatbeat to ensures that the dispatcher has not deadlocked. */
virtual void monitor() = 0;
- /* Runs a single iteration of the dispatch loop.
- * Nominally processes one queued event, a timeout, or a response from an input consumer.
- *
- * This method should only be called on the input dispatcher thread.
+ /**
+ * Wait until dispatcher is idle. That means, there are no further events to be processed,
+ * and all of the policy callbacks have been completed.
+ * Return true if the dispatcher is idle.
+ * Return false if the timeout waiting for the dispatcher to become idle has expired.
*/
- virtual void dispatchOnce() = 0;
+ virtual bool waitForIdle() = 0;
+
+ /* Make the dispatcher start processing events.
+ *
+ * The dispatcher will start consuming events from the InputListenerInterface
+ * in the order that they were received.
+ */
+ 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
@@ -116,6 +127,14 @@
*/
virtual void setInputFilterEnabled(bool enabled) = 0;
+ /**
+ * Set the touch mode state.
+ * Touch mode is a global state that apps may enter / exit based on specific
+ * user interactions with input devices.
+ * If true, the device is in touch mode.
+ */
+ virtual void setInTouchMode(bool inTouchMode) = 0;
+
/* Transfers touch focus from one window to another window.
*
* Returns true on success. False if the window did not actually have touch focus.
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/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h
index 5d576b9..56c0a73 100644
--- a/services/inputflinger/include/InputReaderBase.h
+++ b/services/inputflinger/include/InputReaderBase.h
@@ -19,12 +19,12 @@
#include "PointerControllerInterface.h"
+#include <input/DisplayViewport.h>
#include <input/Input.h>
#include <input/InputDevice.h>
-#include <input/DisplayViewport.h>
#include <input/VelocityControl.h>
#include <input/VelocityTracker.h>
-#include <utils/Thread.h>
+#include <utils/Errors.h>
#include <utils/RefBase.h>
#include <stddef.h>
@@ -44,7 +44,16 @@
namespace android {
-/* Processes raw input events and sends cooked event data to an input listener. */
+// --- InputReaderInterface ---
+
+/* The interface for the InputReader shared library.
+ *
+ * Manages one or more threads that process raw input events and sends cooked event data to an
+ * input listener.
+ *
+ * The implementation must guarantee thread safety for this interface. However, since the input
+ * listener is NOT thread safe, all calls to the listener must happen from the same thread.
+ */
class InputReaderInterface : public virtual RefBase {
protected:
InputReaderInterface() { }
@@ -56,18 +65,17 @@
* This method may be called on any thread (usually by the input manager). */
virtual void dump(std::string& dump) = 0;
- /* Called by the heatbeat to ensures that the reader has not deadlocked. */
+ /* Called by the heartbeat to ensures that the reader has not deadlocked. */
virtual void monitor() = 0;
/* Returns true if the input device is enabled. */
virtual bool isInputDeviceEnabled(int32_t deviceId) = 0;
- /* Runs a single iteration of the processing loop.
- * Nominally reads and processes one incoming message from the EventHub.
- *
- * This method should be called on the input reader thread.
- */
- virtual void loopOnce() = 0;
+ /* Makes the reader start processing events from the kernel. */
+ virtual status_t start() = 0;
+
+ /* Makes the reader stop processing any more events. */
+ virtual status_t stop() = 0;
/* Gets information about all input devices.
*
@@ -104,17 +112,7 @@
virtual bool canDispatchToDisplay(int32_t deviceId, int32_t displayId) = 0;
};
-/* Reads raw events from the event hub and processes them, endlessly. */
-class InputReaderThread : public Thread {
-public:
- explicit InputReaderThread(const sp<InputReaderInterface>& reader);
- virtual ~InputReaderThread();
-
-private:
- sp<InputReaderInterface> mReader;
-
- virtual bool threadLoop();
-};
+// --- InputReaderConfiguration ---
/*
* Input reader configuration.
@@ -285,6 +283,8 @@
std::vector<DisplayViewport> mDisplays;
};
+// --- TouchAffineTransformation ---
+
struct TouchAffineTransformation {
float x_scale;
float x_ymix;
@@ -307,6 +307,8 @@
void applyTo(float& x, float& y) const;
};
+// --- InputReaderPolicyInterface ---
+
/*
* Input reader policy interface.
*
@@ -316,8 +318,8 @@
* The actual implementation is partially supported by callbacks into the DVM
* via JNI. This interface is also mocked in the unit tests.
*
- * These methods must NOT re-enter the input reader since they may be called while
- * holding the input reader lock.
+ * These methods will NOT re-enter the input reader interface, so they may be called from
+ * any method in the input reader interface.
*/
class InputReaderPolicyInterface : public virtual RefBase {
protected:
diff --git a/services/inputflinger/reader/Android.bp b/services/inputflinger/reader/Android.bp
index 3c16070..23c08b2 100644
--- a/services/inputflinger/reader/Android.bp
+++ b/services/inputflinger/reader/Android.bp
@@ -58,7 +58,6 @@
"liblog",
"libui",
"libutils",
- "libhardware_legacy",
],
header_libs: [
diff --git a/services/inputflinger/reader/EventHub.cpp b/services/inputflinger/reader/EventHub.cpp
index c8da0ab..264d287 100644
--- a/services/inputflinger/reader/EventHub.cpp
+++ b/services/inputflinger/reader/EventHub.cpp
@@ -37,8 +37,6 @@
#include "EventHub.h"
-#include <hardware_legacy/power.h>
-
#include <android-base/stringprintf.h>
#include <cutils/properties.h>
#include <openssl/sha.h>
@@ -71,7 +69,6 @@
static constexpr bool DEBUG = false;
-static const char* WAKE_LOCK_ID = "KeyEvents";
static const char* DEVICE_PATH = "/dev/input";
// v4l2 devices go directly into /dev
static const char* VIDEO_DEVICE_PATH = "/dev";
@@ -296,7 +293,6 @@
mPendingEventIndex(0),
mPendingINotify(false) {
ensureProcessCanBlockSuspend();
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
mEpollFd = epoll_create1(EPOLL_CLOEXEC);
LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
@@ -354,8 +350,6 @@
::close(mINotifyFd);
::close(mWakeReadPipeFd);
::close(mWakeWritePipeFd);
-
- release_wake_lock(WAKE_LOCK_ID);
}
InputDeviceIdentifier EventHub::getDeviceIdentifier(int32_t deviceId) const {
@@ -1046,26 +1040,24 @@
break;
}
- // Poll for events. Mind the wake lock dance!
- // We hold a wake lock at all times except during epoll_wait(). This works due to some
- // subtle choreography. When a device driver has pending (unread) events, it acquires
- // a kernel wake lock. However, once the last pending event has been read, the device
- // driver will release the kernel wake lock. To prevent the system from going to sleep
- // when this happens, the EventHub holds onto its own user wake lock while the client
- // is processing events. Thus the system can only sleep if there are no events
- // pending or currently being processed.
+ // Poll for events.
+ // When a device driver has pending (unread) events, it acquires
+ // a kernel wake lock. Once the last pending event has been read, the device
+ // driver will release the kernel wake lock, but the epoll will hold the wakelock,
+ // since we are using EPOLLWAKEUP. The wakelock is released by the epoll when epoll_wait
+ // is called again for the same fd that produced the event.
+ // Thus the system can only sleep if there are no events pending or
+ // currently being processed.
//
// The timeout is advisory only. If the device is asleep, it will not wake just to
// service the timeout.
mPendingEventIndex = 0;
- mLock.unlock(); // release lock before poll, must be before release_wake_lock
- release_wake_lock(WAKE_LOCK_ID);
+ mLock.unlock(); // release lock before poll
int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis);
- acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID);
- mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock
+ mLock.lock(); // reacquire lock after poll
if (pollResult == 0) {
// Timed out.
diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp
index 1c5adc3..05f0db1 100644
--- a/services/inputflinger/reader/InputReader.cpp
+++ b/services/inputflinger/reader/InputReader.cpp
@@ -38,16 +38,38 @@
#include <unistd.h>
#include <log/log.h>
+#include <utils/Errors.h>
#include <android-base/stringprintf.h>
#include <input/Keyboard.h>
#include <input/VirtualKeyMap.h>
-
+#include <utils/Thread.h>
using android::base::StringPrintf;
namespace android {
+// --- InputReader::InputReaderThread ---
+
+/* Thread that reads raw events from the event hub and processes them, endlessly. */
+class InputReader::InputReaderThread : public Thread {
+public:
+ explicit InputReaderThread(InputReader* reader)
+ : Thread(/* canCallJava */ true), mReader(reader) {}
+
+ ~InputReaderThread() {}
+
+private:
+ InputReader* mReader;
+
+ bool threadLoop() override {
+ mReader->loopOnce();
+ return true;
+ }
+};
+
+// --- InputReader ---
+
InputReader::InputReader(std::shared_ptr<EventHubInterface> eventHub,
const sp<InputReaderPolicyInterface>& policy,
const sp<InputListenerInterface>& listener)
@@ -61,6 +83,7 @@
mNextTimeout(LLONG_MAX),
mConfigurationChangesToRefresh(0) {
mQueuedListener = new QueuedInputListener(listener);
+ mThread = new InputReaderThread(this);
{ // acquire lock
AutoMutex _l(mLock);
@@ -76,6 +99,28 @@
}
}
+status_t InputReader::start() {
+ if (mThread->isRunning()) {
+ return ALREADY_EXISTS;
+ }
+ return mThread->run("InputReader", PRIORITY_URGENT_DISPLAY);
+}
+
+status_t InputReader::stop() {
+ if (!mThread->isRunning()) {
+ return OK;
+ }
+ if (gettid() == mThread->getTid()) {
+ ALOGE("InputReader can only be stopped from outside of the InputReaderThread!");
+ return INVALID_OPERATION;
+ }
+ // Directly calling requestExitAndWait() causes the thread to not exit
+ // if mEventHub is waiting for a long timeout.
+ mThread->requestExit();
+ mEventHub->wake();
+ return mThread->requestExitAndWait();
+}
+
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h
index 557eb3b..5024906 100644
--- a/services/inputflinger/reader/include/InputReader.h
+++ b/services/inputflinger/reader/include/InputReader.h
@@ -38,12 +38,12 @@
* that it sends to the input listener. Some functions of the input reader, such as early
* event filtering in low power states, are controlled by a separate policy object.
*
- * The InputReader owns a collection of InputMappers. Most of the work it does happens
- * on the input reader thread but the InputReader can receive queries from other system
+ * The InputReader owns a collection of InputMappers. InputReader starts its own thread, where
+ * most of the work happens, but the InputReader can receive queries from other system
* components running on arbitrary threads. To keep things manageable, the InputReader
* uses a single Mutex to guard its state. The Mutex may be held while calling into the
* EventHub or the InputReaderPolicy but it is never held while calling into the
- * InputListener.
+ * InputListener. All calls to InputListener must happen from InputReader's thread.
*/
class InputReader : public InputReaderInterface {
public:
@@ -55,7 +55,8 @@
virtual void dump(std::string& dump) override;
virtual void monitor() override;
- virtual void loopOnce() override;
+ virtual status_t start() override;
+ virtual status_t stop() override;
virtual void getInputDevices(std::vector<InputDeviceInfo>& outInputDevices) override;
@@ -86,6 +87,10 @@
const InputDeviceIdentifier& identifier,
uint32_t classes);
+ // With each iteration of the loop, InputReader reads and processes one incoming message from
+ // the EventHub.
+ void loopOnce();
+
class ContextImpl : public InputReaderContext {
InputReader* mReader;
@@ -111,6 +116,9 @@
friend class ContextImpl;
private:
+ class InputReaderThread;
+ sp<InputReaderThread> mThread;
+
Mutex mLock;
Condition mReaderIsAliveCondition;
diff --git a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
index 4ff941f..16095b9 100644
--- a/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/SwitchInputMapper.cpp
@@ -56,8 +56,8 @@
void SwitchInputMapper::sync(nsecs_t when) {
if (mUpdatedSwitchMask) {
uint32_t updatedSwitchValues = mSwitchValues & mUpdatedSwitchMask;
- NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0, updatedSwitchValues,
- mUpdatedSwitchMask);
+ NotifySwitchArgs args(mContext->getNextSequenceNum(), when, 0 /*policyFlags*/,
+ updatedSwitchValues, mUpdatedSwitchMask);
getListener()->notifySwitch(&args);
mUpdatedSwitchMask = 0;
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 2a29e0d..d861a5f 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -16,9 +16,8 @@
#include "../dispatcher/InputDispatcher.h"
-#include <InputDispatcherThread.h>
-
#include <binder/Binder.h>
+#include <input/Input.h>
#include <gtest/gtest.h>
#include <linux/input.h>
@@ -50,49 +49,55 @@
public:
FakeInputDispatcherPolicy() {
- mOnPointerDownToken.clear();
}
void assertFilterInputEventWasCalled(const NotifyKeyArgs& args) {
- ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_KEY);
-
- const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
- ASSERT_EQ(keyEvent.getEventTime(), args.eventTime);
- ASSERT_EQ(keyEvent.getAction(), args.action);
- ASSERT_EQ(keyEvent.getDisplayId(), args.displayId);
-
- reset();
+ assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_KEY, args.eventTime, args.action,
+ args.displayId);
}
void assertFilterInputEventWasCalled(const NotifyMotionArgs& args) {
- ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
- ASSERT_EQ(mFilteredEvent->getType(), AINPUT_EVENT_TYPE_MOTION);
-
- const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
- ASSERT_EQ(motionEvent.getEventTime(), args.eventTime);
- ASSERT_EQ(motionEvent.getAction(), args.action);
- ASSERT_EQ(motionEvent.getDisplayId(), args.displayId);
-
- reset();
+ assertFilterInputEventWasCalled(AINPUT_EVENT_TYPE_MOTION, args.eventTime, args.action,
+ args.displayId);
}
- void assertFilterInputEventWasNotCalled() {
- ASSERT_EQ(nullptr, mFilteredEvent)
- << "Expected filterInputEvent() to not have been called.";
+ void assertFilterInputEventWasNotCalled() { ASSERT_EQ(nullptr, mFilteredEvent); }
+
+ void assertNotifyConfigurationChangedWasCalled(nsecs_t when) {
+ ASSERT_TRUE(mConfigurationChangedTime)
+ << "Timed out waiting for configuration changed call";
+ ASSERT_EQ(*mConfigurationChangedTime, when);
+ mConfigurationChangedTime = std::nullopt;
+ }
+
+ void assertNotifySwitchWasCalled(const NotifySwitchArgs& args) {
+ ASSERT_TRUE(mLastNotifySwitch);
+ // We do not check sequenceNum because it is not exposed to the policy
+ EXPECT_EQ(args.eventTime, mLastNotifySwitch->eventTime);
+ EXPECT_EQ(args.policyFlags, mLastNotifySwitch->policyFlags);
+ EXPECT_EQ(args.switchValues, mLastNotifySwitch->switchValues);
+ EXPECT_EQ(args.switchMask, mLastNotifySwitch->switchMask);
+ mLastNotifySwitch = std::nullopt;
}
void assertOnPointerDownEquals(const sp<IBinder>& touchedToken) {
- ASSERT_EQ(mOnPointerDownToken, touchedToken)
- << "Expected token from onPointerDownOutsideFocus was not matched";
- reset();
+ ASSERT_EQ(touchedToken, mOnPointerDownToken);
+ mOnPointerDownToken.clear();
+ }
+
+ void assertOnPointerDownWasNotCalled() {
+ ASSERT_TRUE(mOnPointerDownToken == nullptr)
+ << "Expected onPointerDownOutsideFocus to not have been called";
}
private:
std::unique_ptr<InputEvent> mFilteredEvent;
+ std::optional<nsecs_t> mConfigurationChangedTime;
sp<IBinder> mOnPointerDownToken;
+ std::optional<NotifySwitchArgs> mLastNotifySwitch;
- virtual void notifyConfigurationChanged(nsecs_t) {
+ virtual void notifyConfigurationChanged(nsecs_t when) override {
+ mConfigurationChangedTime = when;
}
virtual nsecs_t notifyANR(const sp<InputApplicationHandle>&,
@@ -144,7 +149,13 @@
return false;
}
- virtual void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) {
+ virtual void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
+ uint32_t policyFlags) override {
+ /** We simply reconstruct NotifySwitchArgs in policy because InputDispatcher is
+ * essentially a passthrough for notifySwitch.
+ */
+ mLastNotifySwitch =
+ NotifySwitchArgs(1 /*sequenceNum*/, when, policyFlags, switchValues, switchMask);
}
virtual void pokeUserActivity(nsecs_t, int32_t) {
@@ -158,9 +169,26 @@
mOnPointerDownToken = newToken;
}
- void reset() {
+ void assertFilterInputEventWasCalled(int type, nsecs_t eventTime, int32_t action,
+ int32_t displayId) {
+ ASSERT_NE(nullptr, mFilteredEvent) << "Expected filterInputEvent() to have been called.";
+ ASSERT_EQ(mFilteredEvent->getType(), type);
+
+ if (type == AINPUT_EVENT_TYPE_KEY) {
+ const KeyEvent& keyEvent = static_cast<const KeyEvent&>(*mFilteredEvent);
+ EXPECT_EQ(keyEvent.getEventTime(), eventTime);
+ EXPECT_EQ(keyEvent.getAction(), action);
+ EXPECT_EQ(keyEvent.getDisplayId(), displayId);
+ } else if (type == AINPUT_EVENT_TYPE_MOTION) {
+ const MotionEvent& motionEvent = static_cast<const MotionEvent&>(*mFilteredEvent);
+ EXPECT_EQ(motionEvent.getEventTime(), eventTime);
+ EXPECT_EQ(motionEvent.getAction(), action);
+ EXPECT_EQ(motionEvent.getDisplayId(), displayId);
+ } else {
+ FAIL() << "Unknown type: " << type;
+ }
+
mFilteredEvent = nullptr;
- mOnPointerDownToken.clear();
}
};
@@ -171,20 +199,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();
}
@@ -348,9 +373,30 @@
<< "Should reject motion events with duplicate pointer ids.";
}
+/* Test InputDispatcher for notifyConfigurationChanged and notifySwitch events */
+
+TEST_F(InputDispatcherTest, NotifyConfigurationChanged_CallsPolicy) {
+ constexpr nsecs_t eventTime = 20;
+ NotifyConfigurationChangedArgs args(10 /*sequenceNum*/, eventTime);
+ mDispatcher->notifyConfigurationChanged(&args);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+
+ mFakePolicy->assertNotifyConfigurationChangedWasCalled(eventTime);
+}
+
+TEST_F(InputDispatcherTest, NotifySwitch_CallsPolicy) {
+ NotifySwitchArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, 0 /*policyFlags*/,
+ 1 /*switchValues*/, 2 /*switchMask*/);
+ mDispatcher->notifySwitch(&args);
+
+ // InputDispatcher adds POLICY_FLAG_TRUSTED because the event went through InputListener
+ args.policyFlags |= POLICY_FLAG_TRUSTED;
+ mFakePolicy->assertNotifySwitchWasCalled(args);
+}
+
// --- InputDispatcherTest SetInputWindowTest ---
-static const int32_t INJECT_EVENT_TIMEOUT = 500;
-static const int32_t DISPATCHING_TIMEOUT = 100;
+static constexpr int32_t INJECT_EVENT_TIMEOUT = 500;
+static constexpr int32_t DISPATCHING_TIMEOUT = 100;
class FakeApplicationHandle : public InputApplicationHandle {
public:
@@ -365,19 +411,51 @@
class FakeInputReceiver {
public:
- void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
- int32_t expectedFlags) {
+ InputEvent* consume() {
uint32_t consumeSeq;
InputEvent* event;
- status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
- &consumeSeq, &event);
- ASSERT_EQ(OK, status)
- << mName.c_str() << ": consumer consume should return OK.";
- ASSERT_TRUE(event != nullptr)
- << mName.c_str() << ": consumer should have returned non-NULL event.";
+ std::chrono::time_point start = std::chrono::steady_clock::now();
+ status_t status = WOULD_BLOCK;
+ while (status == WOULD_BLOCK) {
+ status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1, &consumeSeq,
+ &event);
+ std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
+ if (elapsed > 100ms) {
+ break;
+ }
+ }
+
+ if (status == WOULD_BLOCK) {
+ // Just means there's no event available.
+ return nullptr;
+ }
+
+ if (status != OK) {
+ ADD_FAILURE() << mName.c_str() << ": consumer consume should return OK.";
+ return nullptr;
+ }
+ if (event == nullptr) {
+ ADD_FAILURE() << "Consumed correctly, but received NULL event from consumer";
+ return nullptr;
+ }
+
+ status = mConsumer->sendFinishedSignal(consumeSeq, handled());
+ if (status != OK) {
+ ADD_FAILURE() << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
+ }
+ return event;
+ }
+
+ void consumeEvent(int32_t expectedEventType, int32_t expectedAction, int32_t expectedDisplayId,
+ int32_t expectedFlags) {
+ InputEvent* event = consume();
+
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
ASSERT_EQ(expectedEventType, event->getType())
- << mName.c_str() << ": event type should match.";
+ << mName.c_str() << "expected " << inputEventTypeToString(expectedEventType)
+ << " event, got " << inputEventTypeToString(event->getType()) << " event";
EXPECT_EQ(expectedDisplayId, event->getDisplayId());
@@ -398,10 +476,6 @@
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
}
-
- status = mConsumer->sendFinishedSignal(consumeSeq, handled());
- ASSERT_EQ(OK, status)
- << mName.c_str() << ": consumer sendFinishedSignal should return OK.";
}
void consumeKeyDown(int32_t expectedDisplayId, int32_t expectedFlags = 0) {
@@ -415,13 +489,10 @@
}
void assertNoEvents() {
- uint32_t consumeSeq;
- InputEvent* event;
- status_t status = mConsumer->consume(&mEventFactory, false /*consumeBatches*/, -1,
- &consumeSeq, &event);
- ASSERT_NE(OK, status)
+ InputEvent* event = consume();
+ ASSERT_EQ(nullptr, event)
<< mName.c_str()
- << ": should not have received any events, so consume(..) should not return OK.";
+ << ": should not have received any events, so consume() should return NULL";
}
protected:
@@ -508,9 +579,7 @@
InputWindowHandle::releaseChannel();
}
protected:
- virtual bool handled() {
- return true;
- }
+ virtual bool handled() override { return true; }
bool mFocused;
Rect mFrame;
@@ -610,10 +679,7 @@
sp<FakeWindowHandle> window = new FakeWindowHandle(application, mDispatcher, "Fake Window",
ADISPLAY_ID_DEFAULT);
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(window);
-
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -630,11 +696,7 @@
sp<FakeWindowHandle> windowSecond = new FakeWindowHandle(application, mDispatcher, "Second",
ADISPLAY_ID_DEFAULT);
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
-
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -656,11 +718,8 @@
// Expect one focus window exist in display.
windowSecond->setFocus();
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -682,11 +741,8 @@
// Display has two focused windows. Add them to inputWindowsHandles in z-order (top most first)
windowTop->setFocus();
windowSecond->setFocus();
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
@@ -708,12 +764,9 @@
windowTop->setFocus();
windowSecond->setFocus();
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowTop);
- inputWindowHandles.push_back(windowSecond);
// Release channel for window is no longer valid.
windowTop->releaseChannel();
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowTop, windowSecond}, ADISPLAY_ID_DEFAULT);
// Test inject a key down, should dispatch to a valid window.
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher))
@@ -738,8 +791,7 @@
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- std::vector<sp<InputWindowHandle>> inputWindowHandles{windowLeft, windowRight};
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowLeft, windowRight}, ADISPLAY_ID_DEFAULT);
// Inject an event with coordinate in the area of right window, with mouse cursor in the area of
// left window. This event should be dispatched to the left window.
@@ -750,38 +802,80 @@
windowRight->assertNoEvents();
}
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsKeyStream) {
+ sp<FakeApplicationHandle> application = new FakeApplicationHandle();
+ sp<FakeWindowHandle> window =
+ new FakeWindowHandle(application, mDispatcher, "Fake Window", ADISPLAY_ID_DEFAULT);
+ window->setFocus();
+
+ mDispatcher->setInputWindows({window}, ADISPLAY_ID_DEFAULT);
+
+ NotifyKeyArgs keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_DOWN, ADISPLAY_ID_DEFAULT);
+ mDispatcher->notifyKey(&keyArgs);
+
+ // Window should receive key down event.
+ window->consumeKeyDown(ADISPLAY_ID_DEFAULT);
+
+ // When device reset happens, that key stream should be terminated with FLAG_CANCELED
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_DEFAULT,
+ AKEY_EVENT_FLAG_CANCELED);
+}
+
+TEST_F(InputDispatcherTest, NotifyDeviceReset_CancelsMotionStream) {
+ 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);
+
+ // When device reset happens, that motion stream should be terminated with ACTION_CANCEL
+ // on the app side.
+ NotifyDeviceResetArgs args(10 /*sequenceNum*/, 20 /*eventTime*/, DEVICE_ID);
+ mDispatcher->notifyDeviceReset(&args);
+ window->consumeEvent(AINPUT_EVENT_TYPE_MOTION, AMOTION_EVENT_ACTION_CANCEL, 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();
windowInPrimary = new FakeWindowHandle(application1, mDispatcher, "D_1",
ADISPLAY_ID_DEFAULT);
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(windowInPrimary);
+
// Set focus window for primary display, but focused display would be second one.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application1);
windowInPrimary->setFocus();
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({windowInPrimary}, ADISPLAY_ID_DEFAULT);
application2 = new FakeApplicationHandle();
windowInSecondary = new FakeWindowHandle(application2, mDispatcher, "D_2",
SECOND_DISPLAY_ID);
// Set focus to second display window.
- std::vector<sp<InputWindowHandle>> inputWindowHandles_Second;
- inputWindowHandles_Second.push_back(windowInSecondary);
// Set focus display to second one.
mDispatcher->setFocusedDisplay(SECOND_DISPLAY_ID);
// Set focus window for second display.
mDispatcher->setFocusedApplication(SECOND_DISPLAY_ID, application2);
windowInSecondary->setFocus();
- mDispatcher->setInputWindows(inputWindowHandles_Second, SECOND_DISPLAY_ID);
+ mDispatcher->setInputWindows({windowInSecondary}, SECOND_DISPLAY_ID);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
InputDispatcherTest::TearDown();
application1.clear();
@@ -826,9 +920,8 @@
windowInPrimary->assertNoEvents();
windowInSecondary->consumeKeyDown(ADISPLAY_ID_NONE);
- // Remove secondary display.
- std::vector<sp<InputWindowHandle>> noWindows;
- mDispatcher->setInputWindows(noWindows, SECOND_DISPLAY_ID);
+ // Remove all windows in secondary display.
+ mDispatcher->setInputWindows({}, SECOND_DISPLAY_ID);
// Expect old focus should receive a cancel event.
windowInSecondary->consumeEvent(AINPUT_EVENT_TYPE_KEY, AKEY_EVENT_ACTION_UP, ADISPLAY_ID_NONE,
@@ -917,7 +1010,7 @@
motionArgs = generateMotionArgs(
AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN, displayId);
mDispatcher->notifyMotion(&motionArgs);
-
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(motionArgs);
} else {
@@ -932,6 +1025,7 @@
mDispatcher->notifyKey(&keyArgs);
keyArgs = generateKeyArgs(AKEY_EVENT_ACTION_UP);
mDispatcher->notifyKey(&keyArgs);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
if (expectToBeFiltered) {
mFakePolicy->assertFilterInputEventWasCalled(keyArgs);
@@ -977,7 +1071,7 @@
}
class InputDispatcherOnPointerDownOutsideFocus : public InputDispatcherTest {
- virtual void SetUp() {
+ virtual void SetUp() override {
InputDispatcherTest::SetUp();
sp<FakeApplicationHandle> application = new FakeApplicationHandle();
@@ -988,34 +1082,31 @@
// window.
mUnfocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
- mWindowFocused = new FakeWindowHandle(application, mDispatcher, "Second",
- ADISPLAY_ID_DEFAULT);
- mWindowFocused->setFrame(Rect(50, 50, 100, 100));
- mWindowFocused->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
- mWindowFocusedTouchPoint = 60;
+ mFocusedWindow =
+ new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);
+ mFocusedWindow->setFrame(Rect(50, 50, 100, 100));
+ mFocusedWindow->setLayoutParamFlags(InputWindowInfo::FLAG_NOT_TOUCH_MODAL);
+ mFocusedWindowTouchPoint = 60;
// Set focused application.
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, application);
- mWindowFocused->setFocus();
+ mFocusedWindow->setFocus();
// Expect one focus window exist in display.
- std::vector<sp<InputWindowHandle>> inputWindowHandles;
- inputWindowHandles.push_back(mUnfocusedWindow);
- inputWindowHandles.push_back(mWindowFocused);
- mDispatcher->setInputWindows(inputWindowHandles, ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows({mUnfocusedWindow, mFocusedWindow}, ADISPLAY_ID_DEFAULT);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
InputDispatcherTest::TearDown();
mUnfocusedWindow.clear();
- mWindowFocused.clear();
+ mFocusedWindow.clear();
}
protected:
sp<FakeWindowHandle> mUnfocusedWindow;
- sp<FakeWindowHandle> mWindowFocused;
- int32_t mWindowFocusedTouchPoint;
+ sp<FakeWindowHandle> mFocusedWindow;
+ int32_t mFocusedWindowTouchPoint;
};
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1025,9 +1116,8 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
+ ASSERT_TRUE(mDispatcher->waitForIdle());
mFakePolicy->assertOnPointerDownEquals(mUnfocusedWindow->getToken());
}
@@ -1038,10 +1128,9 @@
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
AINPUT_SOURCE_TRACKBALL, ADISPLAY_ID_DEFAULT, 20, 20))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject KeyEvent with action DOWN on the window that doesn't
@@ -1049,10 +1138,9 @@
TEST_F(InputDispatcherOnPointerDownOutsideFocus, OnPointerDownOutsideFocus_NonMotionFailure) {
ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectKeyDown(mDispatcher, ADISPLAY_ID_DEFAULT))
<< "Inject key event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
// Have two windows, one with focus. Inject MotionEvent with source TOUCHSCREEN and action
@@ -1060,14 +1148,13 @@
// onPointerDownOutsideFocus callback.
TEST_F(InputDispatcherOnPointerDownOutsideFocus,
OnPointerDownOutsideFocus_OnAlreadyFocusedWindow) {
- ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED, injectMotionDown(mDispatcher,
- AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, mWindowFocusedTouchPoint,
- mWindowFocusedTouchPoint))
+ ASSERT_EQ(INPUT_EVENT_INJECTION_SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ mFocusedWindowTouchPoint, mFocusedWindowTouchPoint))
<< "Inject motion event should return INPUT_EVENT_INJECTION_SUCCEEDED";
- // Call monitor to wait for the command queue to get flushed.
- mDispatcher->monitor();
- mFakePolicy->assertOnPointerDownEquals(nullptr);
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ mFakePolicy->assertOnPointerDownWasNotCalled();
}
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp
index c1c9122..d6624c9 100644
--- a/services/inputflinger/tests/InputReader_test.cpp
+++ b/services/inputflinger/tests/InputReader_test.cpp
@@ -1113,6 +1113,9 @@
classes);
}
+ // Make the protected loopOnce method accessible to tests.
+ using InputReader::loopOnce;
+
protected:
virtual InputDevice* createDeviceLocked(int32_t deviceId, int32_t controllerNumber,
const InputDeviceIdentifier& identifier,
@@ -1133,12 +1136,8 @@
protected:
sp<FakeInputReaderPolicy> mFakePolicy;
- virtual void SetUp() {
- mFakePolicy = new FakeInputReaderPolicy();
- }
- virtual void TearDown() {
- mFakePolicy.clear();
- }
+ virtual void SetUp() override { mFakePolicy = new FakeInputReaderPolicy(); }
+ virtual void TearDown() override { mFakePolicy.clear(); }
};
/**
@@ -1321,19 +1320,18 @@
sp<TestInputListener> mFakeListener;
sp<FakeInputReaderPolicy> mFakePolicy;
std::shared_ptr<FakeEventHub> mFakeEventHub;
- sp<InstrumentedInputReader> mReader;
+ std::unique_ptr<InstrumentedInputReader> mReader;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
- mReader = new InstrumentedInputReader(mFakeEventHub, mFakePolicy, mFakeListener);
+ mReader = std::make_unique<InstrumentedInputReader>(mFakeEventHub, mFakePolicy,
+ mFakeListener);
}
- virtual void TearDown() {
- mReader.clear();
-
+ virtual void TearDown() override {
mFakeListener.clear();
mFakePolicy.clear();
}
@@ -1354,16 +1352,12 @@
void disableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->addDisabledDevice(deviceId);
- configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
}
void enableDevice(int32_t deviceId, InputDevice* device) {
mFakePolicy->removeDisabledDevice(deviceId);
- configureDevice(InputReaderConfiguration::CHANGE_ENABLED_STATE, device);
- }
-
- void configureDevice(uint32_t changes, InputDevice* device) {
- device->configure(ARBITRARY_TIME, mFakePolicy->getReaderConfiguration(), changes);
+ mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_ENABLED_STATE);
}
FakeInputMapper* addDeviceWithFakeInputMapper(int32_t deviceId, int32_t controllerNumber,
@@ -1417,7 +1411,6 @@
NotifyDeviceResetArgs resetArgs;
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
@@ -1425,7 +1418,6 @@
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), false);
@@ -1438,7 +1430,6 @@
enableDevice(deviceId, device);
mReader->loopOnce();
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled(&resetArgs));
- ASSERT_EQ(ARBITRARY_TIME, resetArgs.eventTime);
ASSERT_EQ(deviceId, resetArgs.deviceId);
ASSERT_EQ(device->isEnabled(), true);
}
@@ -1629,7 +1620,6 @@
FakeInputMapper* mapper = new FakeInputMapper(device, AINPUT_SOURCE_TOUCHSCREEN);
device->addMapper(mapper);
mReader->setNextDevice(device);
- addDevice(deviceId, "fake", deviceClass, nullptr);
const uint8_t hdmi1 = 1;
@@ -1637,13 +1627,21 @@
mFakePolicy->addInputPortAssociation(DEVICE_LOCATION, hdmi1);
// Add default and second display.
+ mFakePolicy->clearViewports();
mFakePolicy->addDisplayViewport(DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:0", NO_PORT, ViewportType::VIEWPORT_INTERNAL);
mFakePolicy->addDisplayViewport(SECONDARY_DISPLAY_ID, DISPLAY_WIDTH, DISPLAY_HEIGHT,
DISPLAY_ORIENTATION_0, "local:1", hdmi1, ViewportType::VIEWPORT_EXTERNAL);
mReader->requestRefreshConfiguration(InputReaderConfiguration::CHANGE_DISPLAY_INFO);
mReader->loopOnce();
+
+ // Add the device, and make sure all of the callbacks are triggered.
+ // The device is added after the input port associations are processed since
+ // we do not yet support dynamic device-to-display associations.
+ ASSERT_NO_FATAL_FAILURE(addDevice(deviceId, "fake", deviceClass, nullptr));
ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyConfigurationChangedWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyDeviceResetWasCalled());
+ ASSERT_NO_FATAL_FAILURE(mapper->assertConfigureWasCalled());
// Device should only dispatch to the specified display.
ASSERT_EQ(deviceId, device->getId());
@@ -1652,6 +1650,7 @@
// Can't dispatch event from a disabled device.
disableDevice(deviceId, device);
+ mReader->loopOnce();
ASSERT_FALSE(mReader->canDispatchToDisplay(deviceId, SECONDARY_DISPLAY_ID));
}
@@ -1674,7 +1673,7 @@
InputDevice* mDevice;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1688,7 +1687,7 @@
DEVICE_CONTROLLER_NUMBER, identifier, DEVICE_CLASSES);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
delete mDevice;
delete mFakeContext;
@@ -1912,7 +1911,7 @@
FakeInputReaderContext* mFakeContext;
InputDevice* mDevice;
- virtual void SetUp() {
+ virtual void SetUp() override {
mFakeEventHub = std::make_unique<FakeEventHub>();
mFakePolicy = new FakeInputReaderPolicy();
mFakeListener = new TestInputListener();
@@ -1926,7 +1925,7 @@
mFakeEventHub->addDevice(mDevice->getId(), DEVICE_NAME, 0);
}
- virtual void TearDown() {
+ virtual void TearDown() override {
delete mDevice;
delete mFakeContext;
mFakeListener.clear();
@@ -2589,7 +2588,7 @@
sp<FakePointerController> mFakePointerController;
- virtual void SetUp() {
+ virtual void SetUp() override {
InputMapperTest::SetUp();
mFakePointerController = new FakePointerController();
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 14ed73d..c2e1204 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1173,6 +1173,11 @@
return nullptr;
}
int fd = resource->data[0];
+ if (!ashmem_valid(fd)) {
+ ALOGE("Supplied Ashmem memory region is invalid");
+ return nullptr;
+ }
+
int size2 = ashmem_get_size_region(fd);
// check size consistency
if (size2 < static_cast<int64_t>(size)) {
diff --git a/services/sensorservice/tests/sensorservicetest.cpp b/services/sensorservice/tests/sensorservicetest.cpp
index 1cb0489..caf7f03 100644
--- a/services/sensorservice/tests/sensorservicetest.cpp
+++ b/services/sensorservice/tests/sensorservicetest.cpp
@@ -15,19 +15,20 @@
*/
#include <inttypes.h>
+#include <android/hardware_buffer.h>
#include <android/sensor.h>
#include <sensor/Sensor.h>
#include <sensor/SensorManager.h>
#include <sensor/SensorEventQueue.h>
#include <utils/Looper.h>
+#include <vndk/hardware_buffer.h>
using namespace android;
static nsecs_t sStartTime = 0;
-int receiver(__unused int fd, __unused int events, void* data)
-{
+int receiver(__unused int fd, __unused int events, void* data) {
sp<SensorEventQueue> q((SensorEventQueue*)data);
ssize_t n;
ASensorEvent buffer[8];
@@ -59,11 +60,42 @@
return 1;
}
+void testInvalidSharedMem_NoCrash(SensorManager &mgr) {
+ AHardwareBuffer *hardwareBuffer;
+ char* buffer;
-int main()
-{
+ constexpr size_t kEventSize = sizeof(ASensorEvent);
+ constexpr size_t kNEvent = 4096; // enough to contain 1.5 * 800 * 2.2 events
+ constexpr size_t kMemSize = kEventSize * kNEvent;
+ AHardwareBuffer_Desc desc = {
+ .width = static_cast<uint32_t>(kMemSize),
+ .height = 1,
+ .layers = 1,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB,
+ .usage = AHARDWAREBUFFER_USAGE_SENSOR_DIRECT_DATA
+ | AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN,
+ };
+
+ AHardwareBuffer_allocate(&desc, &hardwareBuffer);
+ AHardwareBuffer_lock(hardwareBuffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY,
+ -1, nullptr, reinterpret_cast<void **>(&buffer));
+
+ const native_handle_t *resourceHandle = AHardwareBuffer_getNativeHandle(hardwareBuffer);
+
+ // Pass in AHardwareBuffer, but with the wrong DIRECT_CHANNEL_TYPE to see
+ // if anything in the Sensor framework crashes
+ int ret = mgr.createDirectChannel(
+ kMemSize, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, resourceHandle);
+
+ // Should print -22 (BAD_VALUE) and the device runtime shouldn't restart
+ printf("createInvalidDirectChannel=%d\n", ret);
+}
+
+int main() {
SensorManager& mgr = SensorManager::getInstanceForPackage(String16("Sensor Service Test"));
+ testInvalidSharedMem_NoCrash(mgr);
+
Sensor const* const* list;
ssize_t count = mgr.getSensorList(&list);
printf("numSensors=%d\n", int(count));
diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp
index 4b71bd8..d476f7b4 100644
--- a/services/surfaceflinger/Android.bp
+++ b/services/surfaceflinger/Android.bp
@@ -97,6 +97,10 @@
"android.hardware.power@1.3",
"libhidlbase",
],
+ // TODO (marissaw): this library is not used by surfaceflinger. This is here so
+ // the library compiled in a way that is accessible to system partition when running
+ // IMapper's VTS.
+ required: ["libgralloctypes"]
}
cc_defaults {
@@ -163,12 +167,14 @@
"Scheduler/LayerInfo.cpp",
"Scheduler/MessageQueue.cpp",
"Scheduler/PhaseOffsets.cpp",
+ "Scheduler/RefreshRateConfigs.cpp",
"Scheduler/Scheduler.cpp",
"Scheduler/SchedulerUtils.cpp",
"Scheduler/Timer.cpp",
"Scheduler/VSyncDispatchTimerQueue.cpp",
"Scheduler/VSyncPredictor.cpp",
"Scheduler/VSyncModulator.cpp",
+ "Scheduler/VSyncReactor.cpp",
"StartPropertySetThread.cpp",
"SurfaceFlinger.cpp",
"SurfaceFlingerDefaultFactory.cpp",
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 94c4a81..054acc5 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -56,6 +56,9 @@
namespace android {
+static constexpr float defaultMaxMasteringLuminance = 1000.0;
+static constexpr float defaultMaxContentLuminance = 1000.0;
+
BufferLayer::BufferLayer(const LayerCreationArgs& args)
: Layer(args),
mTextureName(args.textureName),
@@ -184,6 +187,14 @@
layer.source.buffer.textureName = mTextureName;
layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha();
layer.source.buffer.isY410BT2020 = isHdrY410();
+ bool hasSmpte2086 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::SMPTE2086;
+ bool hasCta861_3 = mBufferInfo.mHdrMetadata.validTypes & HdrMetadata::CTA861_3;
+ layer.source.buffer.maxMasteringLuminance = hasSmpte2086
+ ? mBufferInfo.mHdrMetadata.smpte2086.maxLuminance
+ : defaultMaxMasteringLuminance;
+ layer.source.buffer.maxContentLuminance = hasCta861_3
+ ? mBufferInfo.mHdrMetadata.cta8613.maxContentLightLevel
+ : defaultMaxContentLuminance;
// TODO: we could be more subtle with isFixedSize()
const bool useFiltering = targetSettings.needsFiltering || mNeedsFiltering || isFixedSize();
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index d51d34b..945abd7 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -35,6 +35,7 @@
BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {}
BufferQueueLayer::~BufferQueueLayer() {
+ mContentsChangedListener->abandon();
mConsumer->abandon();
}
@@ -399,8 +400,7 @@
// Add this buffer from our internal queue tracker
{ // Autolock scope
const nsecs_t presentTime = item.mIsAutoTimestamp ? 0 : item.mTimestamp;
- const bool isHDR = item.mHdrMetadata.validTypes != 0;
- mFlinger->mScheduler->recordLayerHistory(this, presentTime, isHDR);
+ mFlinger->mScheduler->recordLayerHistory(this, presentTime);
Mutex::Autolock lock(mQueueItemLock);
// Reset the frame number tracker when we receive the first buffer after
@@ -480,7 +480,9 @@
mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(),
mTextureName, this);
mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
- mConsumer->setContentsChangedListener(this);
+
+ mContentsChangedListener = new ContentsChangedListener(this);
+ mConsumer->setContentsChangedListener(mContentsChangedListener);
mConsumer->setName(String8(mName.data(), mName.size()));
// BufferQueueCore::mMaxDequeuedBufferCount is default to 1
@@ -552,4 +554,57 @@
return layer;
}
+// -----------------------------------------------------------------------
+// Interface implementation for BufferLayerConsumer::ContentsChangedListener
+// -----------------------------------------------------------------------
+
+void BufferQueueLayer::ContentsChangedListener::onFrameAvailable(const BufferItem& item) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameAvailable(item);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameReplaced(const BufferItem& item) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameReplaced(item);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onSidebandStreamChanged() {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onSidebandStreamChanged();
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDequeued(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameDequeued(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameDetached(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameDetached(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::onFrameCancelled(const uint64_t bufferId) {
+ Mutex::Autolock lock(mMutex);
+ if (mBufferQueueLayer != nullptr) {
+ mBufferQueueLayer->onFrameCancelled(bufferId);
+ }
+}
+
+void BufferQueueLayer::ContentsChangedListener::abandon() {
+ Mutex::Autolock lock(mMutex);
+ mBufferQueueLayer = nullptr;
+}
+
+// -----------------------------------------------------------------------
+
} // namespace android
diff --git a/services/surfaceflinger/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h
index 1b1fccd..b040556 100644
--- a/services/surfaceflinger/BufferQueueLayer.h
+++ b/services/surfaceflinger/BufferQueueLayer.h
@@ -29,7 +29,7 @@
* This also implements onFrameAvailable(), which notifies SurfaceFlinger
* that new data has arrived.
*/
-class BufferQueueLayer : public BufferLayer, public BufferLayerConsumer::ContentsChangedListener {
+class BufferQueueLayer : public BufferLayer {
public:
// Only call while mStateLock is held
explicit BufferQueueLayer(const LayerCreationArgs&);
@@ -84,18 +84,37 @@
void latchPerFrameState(compositionengine::LayerFECompositionState&) const override;
sp<Layer> createClone() override;
- // -----------------------------------------------------------------------
- // Interface implementation for BufferLayerConsumer::ContentsChangedListener
- // -----------------------------------------------------------------------
+ void onFrameAvailable(const BufferItem& item);
+ void onFrameReplaced(const BufferItem& item);
+ void onSidebandStreamChanged();
+ void onFrameDequeued(const uint64_t bufferId);
+ void onFrameDetached(const uint64_t bufferId);
+ void onFrameCancelled(const uint64_t bufferId);
+
protected:
void gatherBufferInfo() override;
- void onFrameAvailable(const BufferItem& item) override;
- void onFrameReplaced(const BufferItem& item) override;
- void onSidebandStreamChanged() override;
- void onFrameDequeued(const uint64_t bufferId) override;
- void onFrameDetached(const uint64_t bufferId) override;
- void onFrameCancelled(const uint64_t bufferId) override;
+ // -----------------------------------------------------------------------
+ // Interface implementation for BufferLayerConsumer::ContentsChangedListener
+ // -----------------------------------------------------------------------
+ class ContentsChangedListener : public BufferLayerConsumer::ContentsChangedListener {
+ public:
+ ContentsChangedListener(BufferQueueLayer* bufferQueueLayer)
+ : mBufferQueueLayer(bufferQueueLayer) {}
+ void abandon();
+
+ protected:
+ void onFrameAvailable(const BufferItem& item) override;
+ void onFrameReplaced(const BufferItem& item) override;
+ void onSidebandStreamChanged() override;
+ void onFrameDequeued(const uint64_t bufferId) override;
+ void onFrameDetached(const uint64_t bufferId) override;
+ void onFrameCancelled(const uint64_t bufferId) override;
+
+ private:
+ BufferQueueLayer* mBufferQueueLayer = nullptr;
+ Mutex mMutex;
+ };
// -----------------------------------------------------------------------
public:
@@ -130,6 +149,8 @@
// thread-safe
std::atomic<int32_t> mQueuedFrames{0};
std::atomic<bool> mSidebandStreamChanged{false};
+
+ sp<ContentsChangedListener> mContentsChangedListener;
};
} // namespace android
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index d68fe8e..1e471e5 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -247,8 +247,7 @@
FrameTracer::FrameEvent::POST);
mCurrentState.desiredPresentTime = desiredPresentTime;
- const bool isHDR = mCurrentState.hdrMetadata.validTypes != 0;
- mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime, isHDR);
+ mFlinger->mScheduler->recordLayerHistory(this, desiredPresentTime);
return true;
}
diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp
index 738a2a4..78f8104 100644
--- a/services/surfaceflinger/CompositionEngine/Android.bp
+++ b/services/surfaceflinger/CompositionEngine/Android.bp
@@ -20,6 +20,7 @@
"liblayers_proto",
"liblog",
"libnativewindow",
+ "libprotobuf-cpp-lite",
"libsync",
"libtimestats_proto",
"libui",
@@ -28,6 +29,7 @@
static_libs: [
"libmath",
"librenderengine",
+ "libtimestats",
"libtrace_proto",
],
header_libs: [
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
index 8687d0c..e3650f3 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/CompositionEngine.h
@@ -16,10 +16,11 @@
#pragma once
-#include <memory>
-
+#include <TimeStats/TimeStats.h>
#include <utils/Timers.h>
+#include <memory>
+
namespace android {
class HWComposer;
@@ -55,6 +56,9 @@
virtual renderengine::RenderEngine& getRenderEngine() const = 0;
virtual void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) = 0;
+ virtual TimeStats& getTimeStats() const = 0;
+ virtual void setTimeStats(const std::shared_ptr<TimeStats>&) = 0;
+
virtual bool needsAnotherUpdate() const = 0;
virtual nsecs_t getLastFrameRefreshTimestamp() const = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
index e585769..0a70165 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -93,6 +93,13 @@
virtual std::optional<renderengine::LayerSettings> prepareClientComposition(
ClientCompositionTargetSettings&) = 0;
+ // Returns the LayerSettings used to draw shadows around a layer. It is passed
+ // to RenderEngine::drawLayers. Returns nullopt_t if the layer does not render
+ // shadows.
+ virtual std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+ const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) = 0;
+
// Called after the layer is displayed to update the presentation fence
virtual void onLayerDisplayed(const sp<Fence>&) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
index 5f42ea1..076fdad 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h
@@ -272,7 +272,7 @@
virtual bool getSkipColorTransform() const = 0;
virtual FrameFences presentAndGetFrameFences() = 0;
virtual std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) = 0;
+ bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) = 0;
virtual void appendRegionFlashRequests(
const Region& flashRegion,
std::vector<renderengine::LayerSettings>& clientCompositionLayers) = 0;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
index f416c9c..450b9ca 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/CompositionEngine.h
@@ -36,6 +36,9 @@
renderengine::RenderEngine& getRenderEngine() const override;
void setRenderEngine(std::unique_ptr<renderengine::RenderEngine>) override;
+ TimeStats& getTimeStats() const override;
+ void setTimeStats(const std::shared_ptr<TimeStats>&) override;
+
bool needsAnotherUpdate() const override;
nsecs_t getLastFrameRefreshTimestamp() const override;
@@ -56,6 +59,7 @@
private:
std::unique_ptr<HWComposer> mHwComposer;
std::unique_ptr<renderengine::RenderEngine> mRenderEngine;
+ std::shared_ptr<TimeStats> mTimeStats;
bool mNeedsAnotherUpdate = false;
nsecs_t mRefreshStartTime = 0;
};
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
index a2342ae..159e928 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h
@@ -97,7 +97,8 @@
bool getSkipColorTransform() const override;
compositionengine::Output::FrameFences presentAndGetFrameFences() override;
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) override;
+ bool supportsProtectedContent, Region& clearRegion,
+ ui::Dataspace outputDataspace) override;
void appendRegionFlashRequests(const Region&,
std::vector<renderengine::LayerSettings>&) override;
void setExpensiveRenderingExpected(bool enabled) override;
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
index 8e6f2e2..104e20d 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/CompositionEngine.h
@@ -41,6 +41,9 @@
MOCK_CONST_METHOD0(getRenderEngine, renderengine::RenderEngine&());
MOCK_METHOD1(setRenderEngine, void(std::unique_ptr<renderengine::RenderEngine>));
+ MOCK_CONST_METHOD0(getTimeStats, TimeStats&());
+ MOCK_METHOD1(setTimeStats, void(const std::shared_ptr<TimeStats>&));
+
MOCK_CONST_METHOD0(needsAnotherUpdate, bool());
MOCK_CONST_METHOD0(getLastFrameRefreshTimestamp, nsecs_t());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
index 3eada3c..739490f 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h
@@ -38,6 +38,10 @@
MOCK_METHOD1(prepareClientComposition,
std::optional<renderengine::LayerSettings>(
compositionengine::LayerFE::ClientCompositionTargetSettings&));
+ MOCK_METHOD3(prepareShadowClientComposition,
+ std::optional<renderengine::LayerSettings>(const renderengine::LayerSettings&,
+ const Rect&, ui::Dataspace));
+
MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&));
MOCK_CONST_METHOD0(getDebugName, const char*());
diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
index 02e68fc..7f5bd06 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h
@@ -106,8 +106,8 @@
MOCK_METHOD0(postFramebuffer, void());
MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
- MOCK_METHOD2(generateClientCompositionRequests,
- std::vector<renderengine::LayerSettings>(bool, Region&));
+ MOCK_METHOD3(generateClientCompositionRequests,
+ std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
diff --git a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
index be8646c..5eabecd 100644
--- a/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/CompositionEngine.cpp
@@ -64,6 +64,14 @@
mRenderEngine = std::move(renderEngine);
}
+TimeStats& CompositionEngine::getTimeStats() const {
+ return *mTimeStats.get();
+}
+
+void CompositionEngine::setTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+ mTimeStats = timeStats;
+}
+
bool CompositionEngine::needsAnotherUpdate() const {
return mNeedsAnotherUpdate;
}
diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp
index 6877f8b..7e5a720 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -799,7 +799,8 @@
// Generate the client composition requests for the layers on this output.
std::vector<renderengine::LayerSettings> clientCompositionLayers =
generateClientCompositionRequests(supportsProtectedContent,
- clientCompositionDisplay.clearRegion);
+ clientCompositionDisplay.clearRegion,
+ clientCompositionDisplay.outputDataspace);
appendRegionFlashRequests(debugRegion, clientCompositionLayers);
// If we the display is secure, protected content support is enabled, and at
@@ -838,9 +839,18 @@
setExpensiveRenderingExpected(true);
}
+ const nsecs_t renderEngineStart = systemTime();
renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers,
buf->getNativeBuffer(), /*useFramebufferCache=*/true, std::move(fd),
&readyFence);
+ auto& timeStats = getCompositionEngine().getTimeStats();
+ if (readyFence.get() < 0) {
+ timeStats.recordRenderEngineDuration(renderEngineStart, systemTime());
+ } else {
+ timeStats.recordRenderEngineDuration(renderEngineStart,
+ std::make_shared<FenceTime>(
+ new Fence(dup(readyFence.get()))));
+ }
if (expensiveRenderingExpected) {
setExpensiveRenderingExpected(false);
@@ -850,7 +860,7 @@
}
std::vector<renderengine::LayerSettings> Output::generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) {
+ bool supportsProtectedContent, Region& clearRegion, ui::Dataspace outputDataspace) {
std::vector<renderengine::LayerSettings> clientCompositionLayers;
ALOGV("Rendering client layers");
@@ -902,6 +912,13 @@
layerSettings.source.solidColor = half3(0.0, 0.0, 0.0);
layerSettings.alpha = half(0.0);
layerSettings.disableBlending = true;
+ } else {
+ std::optional<renderengine::LayerSettings> shadowLayer =
+ layerFE.prepareShadowClientComposition(*result, outputState.viewport,
+ outputDataspace);
+ if (shadowLayer) {
+ clientCompositionLayers.push_back(*shadowLayer);
+ }
}
layer->editState().clientCompositionTimestamp = systemTime();
diff --git a/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
new file mode 100644
index 0000000..2675dcf
--- /dev/null
+++ b/services/surfaceflinger/CompositionEngine/tests/CallOrderStateMachineHelper.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+/**
+ * CallOrderStateMachineHelper is a helper class for setting up a compile-time
+ * checked state machine that a sequence of calls is correct for completely
+ * setting up the state for some other type.
+ *
+ * Two examples where this could be used are with setting up a "Builder" flow
+ * for initializing an instance of some type, and writing tests where the state
+ * machine sets up expectations and preconditions, calls the function under
+ * test, and then evaluations postconditions.
+ *
+ * The purpose of this helper is to offload some of the boilerplate code to
+ * simplify the actual state classes, and is also a place to document how to
+ * go about setting up the state classes.
+ *
+ * To work at compile time, the idea is that each state is a unique C++ type,
+ * and the valid transitions between states are given by member functions on
+ * those types, with those functions returning a simple value type expressing
+ * the new state to use. Illegal state transitions become a compile error because
+ * a named member function does not exist.
+ *
+ * Example usage in a test:
+ *
+ * A two step (+ terminator step) setup process can defined using:
+ *
+ * class Step1 : public CallOrderStateMachineHelper<TestFixtureType, Step1> {
+ * [[nodiscard]] auto firstMockCalledWith(int value1) {
+ * // Set up an expectation or initial state using the fixture
+ * EXPECT_CALL(getInstance->firstMock, FirstCall(value1));
+ * return nextState<Step2>();
+ * }
+ * };
+ *
+ * class Step2 : public CallOrderStateMachineHelper<TestFixtureType, Step2> {
+ * [[nodiscard]] auto secondMockCalledWith(int value2) {
+ * // Set up an expectation or initial state using the fixture
+ * EXPECT_CALL(getInstance()->secondMock, SecondCall(value2));
+ * return nextState<StepExecute>();
+ * }
+ * };
+ *
+ * class StepExecute : public CallOrderStateMachineHelper<TestFixtureType, Step3> {
+ * void execute() {
+ * invokeFunctionUnderTest();
+ * }
+ * };
+ *
+ * Note how the non-terminator steps return by value and use [[nodiscard]] to
+ * enforce the setup flow. Only the terminator step returns void.
+ *
+ * This can then be used in the tests with:
+ *
+ * Step1::make(this).firstMockCalledWith(value1)
+ * .secondMockCalledWith(value2)
+ * .execute);
+ *
+ * If the test fixture defines a `verify()` helper function which returns
+ * `Step1::make(this)`, this can be simplified to:
+ *
+ * verify().firstMockCalledWith(value1)
+ * .secondMockCalledWith(value2)
+ * .execute();
+ *
+ * This is equivalent to the following calls made by the text function:
+ *
+ * EXPECT_CALL(firstMock, FirstCall(value1));
+ * EXPECT_CALL(secondMock, SecondCall(value2));
+ * invokeFunctionUnderTest();
+ */
+template <typename InstanceType, typename CurrentStateType>
+class CallOrderStateMachineHelper {
+public:
+ CallOrderStateMachineHelper() = default;
+
+ // Disallow copying
+ CallOrderStateMachineHelper(const CallOrderStateMachineHelper&) = delete;
+ CallOrderStateMachineHelper& operator=(const CallOrderStateMachineHelper&) = delete;
+
+ // Moving is intended use case.
+ CallOrderStateMachineHelper(CallOrderStateMachineHelper&&) = default;
+ CallOrderStateMachineHelper& operator=(CallOrderStateMachineHelper&&) = default;
+
+ // Using a static "Make" function means the CurrentStateType classes do not
+ // need anything other than a default no-argument constructor.
+ static CurrentStateType make(InstanceType* instance) {
+ auto helper = CurrentStateType();
+ helper.mInstance = instance;
+ return helper;
+ }
+
+ // Each non-terminal state function
+ template <typename NextStateType>
+ auto nextState() {
+ // Note: Further operations on the current state become undefined
+ // operations as the instance pointer is moved to the next state type.
+ // But that doesn't stop someone from storing an intermediate state
+ // instance as a local and possibly calling one than one member function
+ // on it. By swapping with nullptr, we at least can try to catch this
+ // this at runtime.
+ InstanceType* instance = nullptr;
+ std::swap(instance, mInstance);
+ return NextStateType::make(instance);
+ }
+
+ InstanceType* getInstance() const { return mInstance; }
+
+private:
+ InstanceType* mInstance;
+};
diff --git a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
index 49e7c70..989494d 100644
--- a/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/CompositionEngineTest.cpp
@@ -25,6 +25,7 @@
#include <renderengine/mock/RenderEngine.h>
#include "MockHWComposer.h"
+#include "TimeStats/TimeStats.h"
namespace android::compositionengine {
namespace {
@@ -41,6 +42,8 @@
android::mock::HWComposer* mHwc = new StrictMock<android::mock::HWComposer>();
renderengine::mock::RenderEngine* mRenderEngine =
new StrictMock<renderengine::mock::RenderEngine>();
+ std::shared_ptr<TimeStats> mTimeStats;
+
impl::CompositionEngine mEngine;
CompositionRefreshArgs mRefreshArgs;
@@ -71,6 +74,12 @@
EXPECT_EQ(mRenderEngine, &mEngine.getRenderEngine());
}
+TEST_F(CompositionEngineTest, canSetTimeStats) {
+ mEngine.setTimeStats(mTimeStats);
+
+ EXPECT_EQ(mTimeStats.get(), &mEngine.getTimeStats());
+}
+
/*
* CompositionEngine::present
*/
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
index c07dfbb..21b9aa9 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayColorProfileTest.cpp
@@ -19,35 +19,6 @@
#include <compositionengine/mock/CompositionEngine.h>
#include <gtest/gtest.h>
-namespace android::hardware::graphics::common::V1_1 {
-
-// Note: These operator overloads need to be defined in the same namespace as
-// the values they print.
-
-std::ostream& operator<<(std::ostream& os, const RenderIntent& value) {
- return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
- << ")";
-}
-
-} // namespace android::hardware::graphics::common::V1_1
-
-namespace android::hardware::graphics::common::V1_2 {
-
-// Note: These operator overloads need to be defined in the same namespace as
-// the values they print.
-
-std::ostream& operator<<(std::ostream& os, const Dataspace& value) {
- return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
- << ")";
-}
-
-std::ostream& operator<<(std::ostream& os, const ColorMode& value) {
- return os << toString(value) << " (" << static_cast<std::underlying_type_t<Dataspace>>(value)
- << ")";
-}
-
-} // namespace android::hardware::graphics::common::V1_2
-
namespace android::compositionengine {
namespace {
diff --git a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
index 4d71d43..3a4df74 100644
--- a/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/DisplayTest.cpp
@@ -21,8 +21,10 @@
#include <compositionengine/DisplaySurface.h>
#include <compositionengine/RenderSurfaceCreationArgs.h>
#include <compositionengine/impl/Display.h>
+#include <compositionengine/impl/RenderSurface.h>
#include <compositionengine/mock/CompositionEngine.h>
#include <compositionengine/mock/DisplayColorProfile.h>
+#include <compositionengine/mock/DisplaySurface.h>
#include <compositionengine/mock/Layer.h>
#include <compositionengine/mock/LayerFE.h>
#include <compositionengine/mock/NativeWindow.h>
@@ -39,6 +41,8 @@
using testing::_;
using testing::DoAll;
+using testing::InSequence;
+using testing::NiceMock;
using testing::Return;
using testing::ReturnRef;
using testing::Sequence;
@@ -46,6 +50,8 @@
using testing::StrictMock;
constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42};
+constexpr int32_t DEFAULT_DISPLAY_WIDTH = 1920;
+constexpr int32_t DEFAULT_DISPLAY_HEIGHT = 1080;
struct DisplayTest : public testing::Test {
class Display : public impl::Display {
@@ -768,5 +774,59 @@
nonHwcDisplay->finishFrame(refreshArgs);
}
+/*
+ * Display functional tests
+ */
+
+struct DisplayFunctionalTest : public testing::Test {
+ class Display : public impl::Display {
+ public:
+ explicit Display(const compositionengine::DisplayCreationArgs& args)
+ : impl::Display(args) {}
+
+ using impl::Display::injectOutputLayerForTest;
+ virtual void injectOutputLayerForTest(std::unique_ptr<compositionengine::OutputLayer>) = 0;
+ };
+
+ static std::shared_ptr<Display> createDisplay(
+ const compositionengine::CompositionEngine& compositionEngine,
+ compositionengine::DisplayCreationArgs&& args) {
+ return impl::createDisplayTemplated<Display>(compositionEngine, args);
+ }
+
+ DisplayFunctionalTest() {
+ EXPECT_CALL(mCompositionEngine, getHwComposer()).WillRepeatedly(ReturnRef(mHwComposer));
+
+ mDisplay->setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ NiceMock<android::mock::HWComposer> mHwComposer;
+ NiceMock<Hwc2::mock::PowerAdvisor> mPowerAdvisor;
+ NiceMock<mock::CompositionEngine> mCompositionEngine;
+ sp<mock::NativeWindow> mNativeWindow = new NiceMock<mock::NativeWindow>();
+ sp<mock::DisplaySurface> mDisplaySurface = new NiceMock<mock::DisplaySurface>();
+ std::shared_ptr<Display> mDisplay = createDisplay(mCompositionEngine,
+ DisplayCreationArgsBuilder()
+ .setDisplayId(DEFAULT_DISPLAY_ID)
+ .setPowerAdvisor(&mPowerAdvisor)
+ .build());
+ impl::RenderSurface* mRenderSurface =
+ new impl::RenderSurface{mCompositionEngine, *mDisplay,
+ RenderSurfaceCreationArgs{DEFAULT_DISPLAY_WIDTH,
+ DEFAULT_DISPLAY_HEIGHT, mNativeWindow,
+ mDisplaySurface}};
+};
+
+TEST_F(DisplayFunctionalTest, postFramebufferCriticalCallsAreOrdered) {
+ InSequence seq;
+
+ mDisplay->editState().isEnabled = true;
+
+ EXPECT_CALL(mHwComposer, presentAndGetReleaseFences(_));
+ EXPECT_CALL(*mDisplaySurface, onFrameCommitted());
+
+ mDisplay->postFramebuffer();
+}
+
} // namespace
} // namespace android::compositionengine
diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
index 5cfec77..364661b 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -81,6 +81,11 @@
MOCK_CONST_METHOD1(getColorModes, std::vector<ui::ColorMode>(DisplayId));
MOCK_METHOD3(setActiveColorMode, status_t(DisplayId, ui::ColorMode, ui::RenderIntent));
MOCK_CONST_METHOD0(isUsingVrComposer, bool());
+ MOCK_CONST_METHOD1(isVsyncPeriodSwitchSupported, bool(DisplayId));
+ MOCK_CONST_METHOD1(getDisplayVsyncPeriod, nsecs_t(DisplayId));
+ MOCK_METHOD4(setActiveConfigWithConstraints,
+ status_t(DisplayId, size_t, const HWC2::VsyncPeriodChangeConstraints&,
+ HWC2::VsyncPeriodChangeTimeline*));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_CONST_METHOD0(getComposer, android::Hwc2::Composer*());
diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
index 09ddc60..37b62d8 100644
--- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
+++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp
@@ -16,6 +16,7 @@
#include <cmath>
+#include <android-base/stringprintf.h>
#include <compositionengine/LayerFECompositionState.h>
#include <compositionengine/impl/Output.h>
#include <compositionengine/impl/OutputCompositionState.h>
@@ -31,6 +32,8 @@
#include <ui/Rect.h>
#include <ui/Region.h>
+#include "CallOrderStateMachineHelper.h"
+#include "MockHWC2.h"
#include "RegionMatcher.h"
#include "TransformMatcher.h"
@@ -38,10 +41,16 @@
namespace {
using testing::_;
+using testing::ByMove;
+using testing::DoAll;
+using testing::Eq;
using testing::InSequence;
+using testing::Mock;
+using testing::Property;
using testing::Ref;
using testing::Return;
using testing::ReturnRef;
+using testing::SetArgPointee;
using testing::StrictMock;
constexpr auto TR_IDENT = 0u;
@@ -51,6 +60,9 @@
const mat4 kNonIdentityHalf = mat4() * 0.5;
const mat4 kNonIdentityQuarter = mat4() * 0.25;
+constexpr OutputColorSetting kVendorSpecifiedOutputColorSetting =
+ static_cast<OutputColorSetting>(0x100);
+
struct OutputPartialMockBase : public impl::Output {
// compositionengine::Output overrides
const OutputCompositionState& getState() const override { return mState; }
@@ -104,8 +116,70 @@
std::shared_ptr<Output> mOutput = createOutput(mCompositionEngine);
};
+// Extension of the base test useful for checking interactions with the LayerFE
+// functions to latch composition state.
+struct OutputLatchFEStateTest : public OutputTest {
+ OutputLatchFEStateTest() {
+ EXPECT_CALL(*mOutputLayer1, getLayer()).WillRepeatedly(ReturnRef(mLayer1));
+ EXPECT_CALL(*mOutputLayer2, getLayer()).WillRepeatedly(ReturnRef(mLayer2));
+ EXPECT_CALL(*mOutputLayer3, getLayer()).WillRepeatedly(ReturnRef(mLayer3));
+
+ EXPECT_CALL(*mOutputLayer1, getLayerFE()).WillRepeatedly(ReturnRef(mLayer1FE));
+ EXPECT_CALL(*mOutputLayer2, getLayerFE()).WillRepeatedly(ReturnRef(mLayer2FE));
+ EXPECT_CALL(*mOutputLayer3, getLayerFE()).WillRepeatedly(ReturnRef(mLayer3FE));
+
+ EXPECT_CALL(mLayer1, editFEState()).WillRepeatedly(ReturnRef(mLayer1FEState));
+ EXPECT_CALL(mLayer2, editFEState()).WillRepeatedly(ReturnRef(mLayer2FEState));
+ EXPECT_CALL(mLayer3, editFEState()).WillRepeatedly(ReturnRef(mLayer3FEState));
+ }
+
+ void injectLayer(std::unique_ptr<mock::OutputLayer> layer) {
+ mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(layer.release()));
+ }
+
+ std::unique_ptr<mock::OutputLayer> mOutputLayer1{new StrictMock<mock::OutputLayer>};
+ std::unique_ptr<mock::OutputLayer> mOutputLayer2{new StrictMock<mock::OutputLayer>};
+ std::unique_ptr<mock::OutputLayer> mOutputLayer3{new StrictMock<mock::OutputLayer>};
+
+ StrictMock<mock::Layer> mLayer1;
+ StrictMock<mock::Layer> mLayer2;
+ StrictMock<mock::Layer> mLayer3;
+
+ StrictMock<mock::LayerFE> mLayer1FE;
+ StrictMock<mock::LayerFE> mLayer2FE;
+ StrictMock<mock::LayerFE> mLayer3FE;
+
+ LayerFECompositionState mLayer1FEState;
+ LayerFECompositionState mLayer2FEState;
+ LayerFECompositionState mLayer3FEState;
+};
+
const Rect OutputTest::kDefaultDisplaySize{100, 200};
+using ColorProfile = compositionengine::Output::ColorProfile;
+
+void dumpColorProfile(ColorProfile profile, std::string& result, const char* name) {
+ android::base::StringAppendF(&result, "%s (%s[%d] %s[%d] %s[%d] %s[%d]) ", name,
+ toString(profile.mode).c_str(), profile.mode,
+ toString(profile.dataspace).c_str(), profile.dataspace,
+ toString(profile.renderIntent).c_str(), profile.renderIntent,
+ toString(profile.colorSpaceAgnosticDataspace).c_str(),
+ profile.colorSpaceAgnosticDataspace);
+}
+
+// Checks for a ColorProfile match
+MATCHER_P(ColorProfileEq, expected, "") {
+ std::string buf;
+ buf.append("ColorProfiles are not equal\n");
+ dumpColorProfile(expected, buf, "expected value");
+ dumpColorProfile(arg, buf, "actual value");
+ *result_listener << buf;
+
+ return (expected.mode == arg.mode) && (expected.dataspace == arg.dataspace) &&
+ (expected.renderIntent == arg.renderIntent) &&
+ (expected.colorSpaceAgnosticDataspace == arg.colorSpaceAgnosticDataspace);
+}
+
/*
* Basic construction
*/
@@ -295,10 +369,12 @@
}
/*
- * Output::setColorMode
+ * Output::setColorProfile
*/
-TEST_F(OutputTest, setColorModeSetsStateAndDirtiesOutputIfChanged) {
+using OutputSetColorProfileTest = OutputTest;
+
+TEST_F(OutputSetColorProfileTest, setsStateAndDirtiesOutputIfChanged) {
using ColorProfile = Output::ColorProfile;
EXPECT_CALL(*mDisplayColorProfile,
@@ -319,7 +395,7 @@
EXPECT_THAT(mOutput->getState().dirtyRegion, RegionEq(Region(kDefaultDisplaySize)));
}
-TEST_F(OutputTest, setColorModeDoesNothingIfNoChange) {
+TEST_F(OutputSetColorProfileTest, doesNothingIfNoChange) {
using ColorProfile = Output::ColorProfile;
EXPECT_CALL(*mDisplayColorProfile,
@@ -535,26 +611,138 @@
}
/*
+ * Output::updateLayerStateFromFE()
+ */
+
+using OutputUpdateLayerStateFromFETest = OutputLatchFEStateTest;
+
+TEST_F(OutputUpdateLayerStateFromFETest, handlesNoOutputLayerCase) {
+ CompositionRefreshArgs refreshArgs;
+
+ mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, latchesContentStateForAllContainedLayers) {
+ EXPECT_CALL(mLayer1FE,
+ latchCompositionState(Ref(mLayer1FEState), LayerFE::StateSubset::Content));
+ EXPECT_CALL(mLayer2FE,
+ latchCompositionState(Ref(mLayer2FEState), LayerFE::StateSubset::Content));
+ EXPECT_CALL(mLayer3FE,
+ latchCompositionState(Ref(mLayer3FEState), LayerFE::StateSubset::Content));
+
+ // Note: Must be performed after any expectations on these mocks
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ CompositionRefreshArgs refreshArgs;
+ refreshArgs.updatingGeometryThisFrame = false;
+
+ mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+TEST_F(OutputUpdateLayerStateFromFETest, latchesGeometryAndContentStateForAllContainedLayers) {
+ EXPECT_CALL(mLayer1FE,
+ latchCompositionState(Ref(mLayer1FEState),
+ LayerFE::StateSubset::GeometryAndContent));
+ EXPECT_CALL(mLayer2FE,
+ latchCompositionState(Ref(mLayer2FEState),
+ LayerFE::StateSubset::GeometryAndContent));
+ EXPECT_CALL(mLayer3FE,
+ latchCompositionState(Ref(mLayer3FEState),
+ LayerFE::StateSubset::GeometryAndContent));
+
+ // Note: Must be performed after any expectations on these mocks
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ CompositionRefreshArgs refreshArgs;
+ refreshArgs.updatingGeometryThisFrame = true;
+
+ mOutput->updateLayerStateFromFE(refreshArgs);
+}
+
+/*
* Output::updateAndWriteCompositionState()
*/
-TEST_F(OutputTest, updateAndWriteCompositionState_takesEarlyOutIfNotEnabled) {
- mOutput->editState().isEnabled = false;
+using OutputUpdateAndWriteCompositionStateTest = OutputLatchFEStateTest;
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfLayers) {
+ mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
mOutput->updateAndWriteCompositionState(args);
}
-TEST_F(OutputTest, updateAndWriteCompositionState_updatesLayers) {
- mOutput->editState().isEnabled = true;
- mock::OutputLayer* outputLayer = new StrictMock<mock::OutputLayer>();
- mOutput->injectOutputLayerForTest(std::unique_ptr<OutputLayer>(outputLayer));
+TEST_F(OutputUpdateAndWriteCompositionStateTest, doesNothingIfOutputNotEnabled) {
+ mOutput->editState().isEnabled = false;
- EXPECT_CALL(*outputLayer, updateCompositionState(true, true)).Times(1);
- EXPECT_CALL(*outputLayer, writeStateToHWC(true)).Times(1);
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ CompositionRefreshArgs args;
+ mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerContentForAllLayers) {
+ EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, false));
+ EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, false));
+ EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, false));
+ EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
+ args.devOptForceClientComposition = false;
+ mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, updatesLayerGeometryAndContentForAllLayers) {
+ EXPECT_CALL(*mOutputLayer1, updateCompositionState(true, false));
+ EXPECT_CALL(*mOutputLayer1, writeStateToHWC(true));
+ EXPECT_CALL(*mOutputLayer2, updateCompositionState(true, false));
+ EXPECT_CALL(*mOutputLayer2, writeStateToHWC(true));
+ EXPECT_CALL(*mOutputLayer3, updateCompositionState(true, false));
+ EXPECT_CALL(*mOutputLayer3, writeStateToHWC(true));
+
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ mOutput->editState().isEnabled = true;
CompositionRefreshArgs args;
args.updatingGeometryThisFrame = true;
+ args.devOptForceClientComposition = false;
+ mOutput->updateAndWriteCompositionState(args);
+}
+
+TEST_F(OutputUpdateAndWriteCompositionStateTest, forcesClientCompositionForAllLayers) {
+ EXPECT_CALL(*mOutputLayer1, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer1, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer2, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer2, writeStateToHWC(false));
+ EXPECT_CALL(*mOutputLayer3, updateCompositionState(false, true));
+ EXPECT_CALL(*mOutputLayer3, writeStateToHWC(false));
+
+ injectLayer(std::move(mOutputLayer1));
+ injectLayer(std::move(mOutputLayer2));
+ injectLayer(std::move(mOutputLayer3));
+
+ mOutput->editState().isEnabled = true;
+
+ CompositionRefreshArgs args;
+ args.updatingGeometryThisFrame = false;
args.devOptForceClientComposition = true;
mOutput->updateAndWriteCompositionState(args);
}
@@ -565,8 +753,8 @@
struct OutputPrepareFrameTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by prepareFrame to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_METHOD0(chooseCompositionStrategy, void());
};
@@ -615,14 +803,647 @@
}
/*
+ * Output::prepare()
+ */
+
+struct OutputPrepareTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD2(rebuildLayerStacks,
+ void(const compositionengine::CompositionRefreshArgs&,
+ compositionengine::LayerFESet&));
+ };
+
+ StrictMock<OutputPartialMock> mOutput;
+ CompositionRefreshArgs mRefreshArgs;
+ LayerFESet mGeomSnapshots;
+};
+
+TEST_F(OutputPrepareTest, justInvokesRebuildLayerStacks) {
+ InSequence seq;
+ EXPECT_CALL(mOutput, rebuildLayerStacks(Ref(mRefreshArgs), Ref(mGeomSnapshots)));
+
+ mOutput.prepare(mRefreshArgs, mGeomSnapshots);
+}
+
+/*
+ * Output::rebuildLayerStacks()
+ */
+
+struct OutputRebuildLayerStacksTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD2(collectVisibleLayers,
+ void(const compositionengine::CompositionRefreshArgs&,
+ compositionengine::Output::CoverageState&));
+ };
+
+ OutputRebuildLayerStacksTest() {
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.transform = kIdentityTransform;
+ mOutput.mState.bounds = kOutputBounds;
+
+ mRefreshArgs.updatingOutputGeometryThisFrame = true;
+
+ mCoverageAboveCoveredLayersToSet = Region(Rect(0, 0, 10, 10));
+
+ EXPECT_CALL(mOutput, collectVisibleLayers(Ref(mRefreshArgs), _))
+ .WillRepeatedly(Invoke(this, &OutputRebuildLayerStacksTest::setTestCoverageValues));
+ }
+
+ void setTestCoverageValues(const CompositionRefreshArgs&,
+ compositionengine::Output::CoverageState& state) {
+ state.aboveCoveredLayers = mCoverageAboveCoveredLayersToSet;
+ state.aboveOpaqueLayers = mCoverageAboveOpaqueLayersToSet;
+ state.dirtyRegion = mCoverageDirtyRegionToSet;
+ }
+
+ static const ui::Transform kIdentityTransform;
+ static const ui::Transform kRotate90Transform;
+ static const Rect kOutputBounds;
+
+ StrictMock<OutputPartialMock> mOutput;
+ CompositionRefreshArgs mRefreshArgs;
+ LayerFESet mGeomSnapshots;
+ Region mCoverageAboveCoveredLayersToSet;
+ Region mCoverageAboveOpaqueLayersToSet;
+ Region mCoverageDirtyRegionToSet;
+};
+
+const ui::Transform OutputRebuildLayerStacksTest::kIdentityTransform{TR_IDENT, 1920, 1080};
+const ui::Transform OutputRebuildLayerStacksTest::kRotate90Transform{TR_ROT_90, 1920, 1080};
+const Rect OutputRebuildLayerStacksTest::kOutputBounds{0, 0, 1920, 1080};
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotEnabled) {
+ mOutput.mState.isEnabled = false;
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, doesNothingIfNotUpdatingGeometryThisFrame) {
+ mRefreshArgs.updatingOutputGeometryThisFrame = false;
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndFullCoverage) {
+ mOutput.mState.transform = kIdentityTransform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1920, 1080));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWithNoRotationAndPartialCoverage) {
+ mOutput.mState.transform = kIdentityTransform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 960, 1080));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(960, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndFullCoverage) {
+ mOutput.mState.transform = kRotate90Transform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 1920));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 0, 0))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, computesUndefinedRegionWith90RotationAndPartialCoverage) {
+ mOutput.mState.transform = kRotate90Transform;
+
+ mCoverageAboveOpaqueLayersToSet = Region(Rect(0, 0, 1080, 960));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.undefinedRegion, RegionEq(Region(Rect(0, 0, 960, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWithNoRotation) {
+ mOutput.mState.transform = kIdentityTransform;
+ mOutput.mState.dirtyRegion = Region(Rect(960, 0, 1920, 1080));
+
+ mCoverageDirtyRegionToSet = Region(Rect(0, 0, 960, 1080));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1920, 1080))));
+}
+
+TEST_F(OutputRebuildLayerStacksTest, addsToDirtyRegionWith90Rotation) {
+ mOutput.mState.transform = kRotate90Transform;
+ mOutput.mState.dirtyRegion = Region(Rect(0, 960, 1080, 1920));
+
+ mCoverageDirtyRegionToSet = Region(Rect(0, 0, 1080, 960));
+
+ mOutput.rebuildLayerStacks(mRefreshArgs, mGeomSnapshots);
+
+ EXPECT_THAT(mOutput.mState.dirtyRegion, RegionEq(Region(Rect(0, 0, 1080, 1920))));
+}
+
+/*
+ * Output::collectVisibleLayers()
+ */
+
+struct OutputCollectVisibleLayersTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD2(ensureOutputLayerIfVisible,
+ void(std::shared_ptr<compositionengine::Layer>,
+ compositionengine::Output::CoverageState&));
+ MOCK_METHOD1(setReleasedLayers, void(const compositionengine::CompositionRefreshArgs&));
+ MOCK_METHOD0(finalizePendingOutputLayers, void());
+ };
+
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(outputLayer, getState()).WillRepeatedly(ReturnRef(outputLayerState));
+ EXPECT_CALL(outputLayer, editState()).WillRepeatedly(ReturnRef(outputLayerState));
+ }
+
+ StrictMock<mock::OutputLayer> outputLayer;
+ std::shared_ptr<StrictMock<mock::Layer>> layer{new StrictMock<mock::Layer>()};
+ impl::OutputLayerCompositionState outputLayerState;
+ };
+
+ OutputCollectVisibleLayersTest() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+ .WillRepeatedly(Return(&mLayer3.outputLayer));
+
+ mRefreshArgs.layers.push_back(mLayer1.layer);
+ mRefreshArgs.layers.push_back(mLayer2.layer);
+ mRefreshArgs.layers.push_back(mLayer3.layer);
+ }
+
+ StrictMock<OutputPartialMock> mOutput;
+ CompositionRefreshArgs mRefreshArgs;
+ LayerFESet mGeomSnapshots;
+ Output::CoverageState mCoverageState{mGeomSnapshots};
+ Layer mLayer1;
+ Layer mLayer2;
+ Layer mLayer3;
+};
+
+TEST_F(OutputCollectVisibleLayersTest, doesMinimalWorkIfNoLayers) {
+ mRefreshArgs.layers.clear();
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+
+ EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+ EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+ mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+}
+
+TEST_F(OutputCollectVisibleLayersTest, processesCandidateLayersReversedAndSetsOutputLayerZ) {
+ // Enforce a call order sequence for this test.
+ InSequence seq;
+
+ // Layer coverage is evaluated from front to back!
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer3.layer), Ref(mCoverageState)));
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer2.layer), Ref(mCoverageState)));
+ EXPECT_CALL(mOutput, ensureOutputLayerIfVisible(Eq(mLayer1.layer), Ref(mCoverageState)));
+
+ EXPECT_CALL(mOutput, setReleasedLayers(Ref(mRefreshArgs)));
+ EXPECT_CALL(mOutput, finalizePendingOutputLayers());
+
+ mOutput.collectVisibleLayers(mRefreshArgs, mCoverageState);
+
+ // Ensure all output layers have been assigned a simple/flattened z-order.
+ EXPECT_EQ(0u, mLayer1.outputLayerState.z);
+ EXPECT_EQ(1u, mLayer2.outputLayerState.z);
+ EXPECT_EQ(2u, mLayer3.outputLayerState.z);
+}
+
+/*
+ * Output::ensureOutputLayerIfVisible()
+ */
+
+struct OutputEnsureOutputLayerIfVisibleTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_CONST_METHOD1(belongsInOutput, bool(const compositionengine::Layer*));
+ MOCK_CONST_METHOD1(getOutputLayerOrderedByZByIndex, OutputLayer*(size_t));
+ MOCK_METHOD3(ensureOutputLayer,
+ compositionengine::OutputLayer*(
+ std::optional<size_t>,
+ const std::shared_ptr<compositionengine::Layer>&, const sp<LayerFE>&));
+ };
+
+ OutputEnsureOutputLayerIfVisibleTest() {
+ EXPECT_CALL(*mLayer, getLayerFE()).WillRepeatedly(Return(mLayerFE));
+ EXPECT_CALL(*mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ EXPECT_CALL(*mLayer, editFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+
+ EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillRepeatedly(Return(true));
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&mOutputLayer));
+
+ EXPECT_CALL(mOutputLayer, getState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ EXPECT_CALL(mOutputLayer, editState()).WillRepeatedly(ReturnRef(mOutputLayerState));
+ EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(*mLayer.get()));
+
+ mOutput.mState.bounds = Rect(0, 0, 200, 300);
+ mOutput.mState.viewport = Rect(0, 0, 200, 300);
+ mOutput.mState.transform = ui::Transform(TR_IDENT, 200, 300);
+
+ mLayerFEState.isVisible = true;
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+ mLayerFEState.transparentRegionHint = Region(Rect(0, 0, 100, 100));
+
+ mOutputLayerState.visibleRegion = Region(Rect(0, 0, 50, 200));
+ mOutputLayerState.coveredRegion = Region(Rect(50, 0, 100, 200));
+
+ mGeomSnapshots.insert(mLayerFE);
+ }
+
+ static const Region kEmptyRegion;
+ static const Region kFullBoundsNoRotation;
+ static const Region kRightHalfBoundsNoRotation;
+ static const Region kLowerHalfBoundsNoRotation;
+ static const Region kFullBounds90Rotation;
+
+ StrictMock<OutputPartialMock> mOutput;
+ LayerFESet mGeomSnapshots;
+ Output::CoverageState mCoverageState{mGeomSnapshots};
+
+ std::shared_ptr<mock::Layer> mLayer{new StrictMock<mock::Layer>()};
+ sp<StrictMock<mock::LayerFE>> mLayerFE{new StrictMock<mock::LayerFE>()};
+ LayerFECompositionState mLayerFEState;
+ mock::OutputLayer mOutputLayer;
+ impl::OutputLayerCompositionState mOutputLayerState;
+};
+
+const Region OutputEnsureOutputLayerIfVisibleTest::kEmptyRegion = Region(Rect(0, 0, 0, 0));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBoundsNoRotation =
+ Region(Rect(0, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kRightHalfBoundsNoRotation =
+ Region(Rect(0, 100, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kLowerHalfBoundsNoRotation =
+ Region(Rect(50, 0, 100, 200));
+const Region OutputEnsureOutputLayerIfVisibleTest::kFullBounds90Rotation =
+ Region(Rect(0, 0, 200, 100));
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, doesNothingIfNoLayerFE) {
+ EXPECT_CALL(*mLayer, getLayerFE).WillOnce(Return(sp<LayerFE>()));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, performsGeomLatchBeforeCheckingIfLayerBelongs) {
+ EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
+ EXPECT_CALL(*mLayerFE.get(),
+ latchCompositionState(Ref(mLayerFEState),
+ compositionengine::LayerFE::StateSubset::BasicGeometry));
+
+ mGeomSnapshots.clear();
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ skipsLatchIfAlreadyLatchedBeforeCheckingIfLayerBelongs) {
+ EXPECT_CALL(mOutput, belongsInOutput(mLayer.get())).WillOnce(Return(false));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerNotVisible) {
+ mLayerFEState.isVisible = false;
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesEarlyOutIfLayerHasEmptyVisibleRegion) {
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 0, 0};
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, takesNotSoEarlyOutifDrawRegionEmpty) {
+ mOutput.mState.bounds = Rect(0, 0, 0, 0);
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = false;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ RegionEq(kRightHalfBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForTransparentDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = false;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion,
+ RegionEq(kRightHalfBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = false;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueNonDirtyNotRotatedLayer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = false;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kLowerHalfBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyRotated90Layer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+ mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+ mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueDirtyRotated90Layer) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 200, 100};
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_ROT_90, 100, 200);
+ mOutputLayerState.visibleRegion = Region(Rect(0, 0, 100, 100));
+ mOutputLayerState.coveredRegion = Region(Rect(100, 0, 200, 100));
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBoundsNoRotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesUpdatingOutputLayerForOpaqueDirtyNotRotatedLayerRotatedOutput) {
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ mOutput.mState.viewport = Rect(0, 0, 300, 200);
+ mOutput.mState.transform = ui::Transform(TR_ROT_90, 200, 300);
+
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(0u), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kFullBoundsNoRotation));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kFullBoundsNoRotation));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kFullBounds90Rotation));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest,
+ handlesCreatingOutputLayerForOpaqueDirtyArbitraryTransformLayer) {
+ ui::Transform arbitraryTransform;
+ arbitraryTransform.set(1, 1, -1, 1);
+ arbitraryTransform.set(0, 100);
+
+ mLayerFEState.isOpaque = true;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerBounds = FloatRect{0, 0, 100, 200};
+ mLayerFEState.geomLayerTransform = arbitraryTransform;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+ EXPECT_CALL(mOutput, ensureOutputLayer(Eq(std::nullopt), Eq(mLayer), Eq(mLayerFE)))
+ .WillOnce(Return(&mOutputLayer));
+
+ mOutput.ensureOutputLayerIfVisible(mLayer, mCoverageState);
+
+ const Region kRegion = Region(Rect(0, 0, 300, 300));
+ const Region kRegionClipped = Region(Rect(0, 0, 200, 300));
+
+ EXPECT_THAT(mCoverageState.dirtyRegion, RegionEq(kRegion));
+ EXPECT_THAT(mCoverageState.aboveCoveredLayers, RegionEq(kRegion));
+ EXPECT_THAT(mCoverageState.aboveOpaqueLayers, RegionEq(kEmptyRegion));
+
+ EXPECT_THAT(mOutputLayerState.visibleRegion, RegionEq(kRegion));
+ EXPECT_THAT(mOutputLayerState.visibleNonTransparentRegion, RegionEq(kRegion));
+ EXPECT_THAT(mOutputLayerState.coveredRegion, RegionEq(kEmptyRegion));
+ EXPECT_THAT(mOutputLayerState.outputSpaceVisibleRegion, RegionEq(kRegionClipped));
+}
+
+TEST_F(OutputEnsureOutputLayerIfVisibleTest, coverageAccumulatesTest) {
+ mLayerFEState.isOpaque = false;
+ mLayerFEState.contentDirty = true;
+ mLayerFEState.geomLayerTransform = ui::Transform(TR_IDENT, 100, 200);
+
+ mCoverageState.dirtyRegion = Region(Rect(0, 0, 500, 500));
+ mCoverageState.aboveCoveredLayers = Region(Rect(50, 0, 150, 200));
+ mCoverageState.aboveOpaqueLayers = Region(Rect(50, 0, 150, 200));
+
+ 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(0, 0, 150, 200));
+ const Region kExpectedAboveOpaqueRegion = Region(Rect(50, 0, 150, 200));
+ const Region kExpectedLayerVisibleRegion = Region(Rect(0, 0, 50, 200));
+ const Region kExpectedLayerCoveredRegion = Region(Rect(50, 0, 100, 200));
+ const Region kExpectedLayerVisibleNonTransparentRegion = Region(Rect(0, 100, 50, 200));
+
+ 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(kExpectedLayerVisibleRegion));
+}
+
+/*
* Output::present()
*/
struct OutputPresentTest : public testing::Test {
struct OutputPartialMock : public OutputPartialMockBase {
- // All child helper functions Output::present() are defined as mocks,
- // and those are tested separately, allowing the present() test to
- // just cover the high level flow.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_METHOD1(updateColorProfile, void(const compositionengine::CompositionRefreshArgs&));
MOCK_METHOD1(updateAndWriteCompositionState,
void(const compositionengine::CompositionRefreshArgs&));
@@ -657,29 +1478,1190 @@
* Output::updateColorProfile()
*/
-// TODO(b/144060211) - Add coverage
+struct OutputUpdateColorProfileTest : public testing::Test {
+ using TestType = OutputUpdateColorProfileTest;
+
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD1(setColorProfile, void(const ColorProfile&));
+ };
+
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(mOutputLayer, getLayer()).WillRepeatedly(ReturnRef(mLayer));
+ EXPECT_CALL(mOutputLayer, getLayerFE()).WillRepeatedly(ReturnRef(mLayerFE));
+ EXPECT_CALL(mLayer, getFEState()).WillRepeatedly(ReturnRef(mLayerFEState));
+ }
+
+ StrictMock<mock::OutputLayer> mOutputLayer;
+ StrictMock<mock::Layer> mLayer;
+ StrictMock<mock::LayerFE> mLayerFE;
+ LayerFECompositionState mLayerFEState;
+ };
+
+ OutputUpdateColorProfileTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0))
+ .WillRepeatedly(Return(&mLayer1.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1))
+ .WillRepeatedly(Return(&mLayer2.mOutputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2))
+ .WillRepeatedly(Return(&mLayer3.mOutputLayer));
+ }
+
+ struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+ void execute() { getInstance()->mOutput.updateColorProfile(getInstance()->mRefreshArgs); }
+ };
+
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ StrictMock<OutputPartialMock> mOutput;
+
+ Layer mLayer1;
+ Layer mLayer2;
+ Layer mLayer3;
+
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+// TODO(b/144522012): Refactor Output::updateColorProfile and the related code
+// to make it easier to write unit tests.
+
+TEST_F(OutputUpdateColorProfileTest, setsAColorProfileWhenUnmanaged) {
+ // When the outputColorSetting is set to kUnmanaged, the implementation sets
+ // a simple default color profile without looking at anything else.
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput,
+ setColorProfile(ColorProfileEq(
+ ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC, ui::Dataspace::UNKNOWN})));
+
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kUnmanaged;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+ mOutput.updateColorProfile(mRefreshArgs);
+}
+
+struct OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile
+ : public OutputUpdateColorProfileTest {
+ OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+ }
+
+ struct ExpectBestColorModeCallResultUsedToSetColorProfileState
+ : public CallOrderStateMachineHelper<
+ TestType, ExpectBestColorModeCallResultUsedToSetColorProfileState> {
+ [[nodiscard]] auto expectBestColorModeCallResultUsedToSetColorProfile(
+ ui::ColorMode colorMode, ui::Dataspace dataspace, ui::RenderIntent renderIntent) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _,
+ _))
+ .WillOnce(DoAll(SetArgPointee<2>(dataspace), SetArgPointee<3>(colorMode),
+ SetArgPointee<4>(renderIntent)));
+ EXPECT_CALL(getInstance()->mOutput,
+ setColorProfile(
+ ColorProfileEq(ColorProfile{colorMode, dataspace, renderIntent,
+ ui::Dataspace::UNKNOWN})));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() {
+ return ExpectBestColorModeCallResultUsedToSetColorProfileState::make(this);
+ }
+};
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+ Native_Unknown_Colorimetric_Set) {
+ verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::NATIVE,
+ ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_GetBestColorModeResultBecomesSetProfile,
+ DisplayP3_DisplayP3_Enhance_Set) {
+ verify().expectBestColorModeCallResultUsedToSetColorProfile(ui::ColorMode::DISPLAY_P3,
+ ui::Dataspace::DISPLAY_P3,
+ ui::RenderIntent::ENHANCE)
+ .execute();
+}
+
+struct OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile
+ : public OutputUpdateColorProfileTest {
+ OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile() {
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(0));
+ EXPECT_CALL(*mDisplayColorProfile,
+ getBestColorMode(ui::Dataspace::V0_SRGB, ui::RenderIntent::ENHANCE, _, _, _))
+ .WillRepeatedly(DoAll(SetArgPointee<2>(ui::Dataspace::UNKNOWN),
+ SetArgPointee<3>(ui::ColorMode::NATIVE),
+ SetArgPointee<4>(ui::RenderIntent::COLORIMETRIC)));
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ }
+
+ struct IfColorSpaceAgnosticDataspaceSetToState
+ : public CallOrderStateMachineHelper<TestType, IfColorSpaceAgnosticDataspaceSetToState> {
+ [[nodiscard]] auto ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace dataspace) {
+ getInstance()->mRefreshArgs.colorSpaceAgnosticDataspace = dataspace;
+ return nextState<ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState>();
+ }
+ };
+
+ struct ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState
+ : public CallOrderStateMachineHelper<
+ TestType, ThenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspaceState> {
+ [[nodiscard]] auto thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(
+ ui::Dataspace dataspace) {
+ EXPECT_CALL(getInstance()->mOutput,
+ setColorProfile(ColorProfileEq(
+ ColorProfile{ui::ColorMode::NATIVE, ui::Dataspace::UNKNOWN,
+ ui::RenderIntent::COLORIMETRIC, dataspace})));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfColorSpaceAgnosticDataspaceSetToState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, DisplayP3) {
+ verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::DISPLAY_P3)
+ .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ColorSpaceAgnosticeDataspaceAffectsSetColorProfile, V0_SRGB) {
+ verify().ifColorSpaceAgnosticDataspaceSetTo(ui::Dataspace::V0_SRGB)
+ .thenExpectSetColorProfileCallUsesColorSpaceAgnosticDataspace(ui::Dataspace::V0_SRGB)
+ .execute();
+}
+
+struct OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference
+ : public OutputUpdateColorProfileTest {
+ // Internally the implementation looks through the dataspaces of all the
+ // visible layers. The topmost one that also has an actual dataspace
+ // preference set is used to drive subsequent choices.
+
+ OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ }
+
+ struct IfTopLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+ [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer3.mLayerFEState.dataspace = dataspace;
+ return nextState<AndIfMiddleLayerDataspaceState>();
+ }
+ [[nodiscard]] auto ifTopLayerHasNoPreference() {
+ return ifTopLayerIs(ui::Dataspace::UNKNOWN);
+ }
+ };
+
+ struct AndIfMiddleLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, AndIfMiddleLayerDataspaceState> {
+ [[nodiscard]] auto andIfMiddleLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+ return nextState<AndIfBottomLayerDataspaceState>();
+ }
+ [[nodiscard]] auto andIfMiddleLayerHasNoPreference() {
+ return andIfMiddleLayerIs(ui::Dataspace::UNKNOWN);
+ }
+ };
+
+ struct AndIfBottomLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+ [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ [[nodiscard]] auto andIfBottomLayerHasNoPreference() {
+ return andIfBottomLayerIs(ui::Dataspace::UNKNOWN);
+ }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(dataspace, _, _, _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ noStrongLayerPrefenceUses_V0_SRGB) {
+ // If none of the layers indicate a preference, then V0_SRGB is the
+ // preferred choice (subject to additional checks).
+ verify().ifTopLayerHasNoPreference()
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerHasNoPreference()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifTopmostUses_DisplayP3_Then_DisplayP3_Chosen) {
+ // If only the topmost layer has a preference, then that is what is chosen.
+ verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerHasNoPreference()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifMiddleUses_DisplayP3_Then_DisplayP3_Chosen) {
+ // If only the middle layer has a preference, that that is what is chosen.
+ verify().ifTopLayerHasNoPreference()
+ .andIfMiddleLayerIs(ui::Dataspace::DISPLAY_P3)
+ .andIfBottomLayerHasNoPreference()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifBottomUses_DisplayP3_Then_DisplayP3_Chosen) {
+ // If only the middle layer has a preference, that that is what is chosen.
+ verify().ifTopLayerHasNoPreference()
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifTopUses_DisplayBT2020_AndBottomUses_DisplayP3_Then_DisplayBT2020_Chosen) {
+ // If multiple layers have a preference, the topmost value is what is used.
+ verify().ifTopLayerIs(ui::Dataspace::DISPLAY_BT2020)
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerIs(ui::Dataspace::DISPLAY_P3)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_TopmostLayerPreferenceSetsOutputPreference,
+ ifTopUses_DisplayP3_AndBottomUses_V0_SRGB_Then_DisplayP3_Chosen) {
+ // If multiple layers have a preference, the topmost value is what is used.
+ verify().ifTopLayerIs(ui::Dataspace::DISPLAY_P3)
+ .andIfMiddleLayerHasNoPreference()
+ .andIfBottomLayerIs(ui::Dataspace::DISPLAY_BT2020)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+struct OutputUpdateColorProfileTest_ForceOutputColorOverrides
+ : public OutputUpdateColorProfileTest {
+ // If CompositionRefreshArgs::forceOutputColorMode is set to some specific
+ // values, it overrides the layer dataspace choice.
+
+ OutputUpdateColorProfileTest_ForceOutputColorOverrides() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+
+ mLayer1.mLayerFEState.dataspace = ui::Dataspace::DISPLAY_BT2020;
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ }
+
+ struct IfForceOutputColorModeState
+ : public CallOrderStateMachineHelper<TestType, IfForceOutputColorModeState> {
+ [[nodiscard]] auto ifForceOutputColorMode(ui::ColorMode colorMode) {
+ getInstance()->mRefreshArgs.forceOutputColorMode = colorMode;
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ [[nodiscard]] auto ifNoOverride() { return ifForceOutputColorMode(ui::ColorMode::NATIVE); }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(dataspace, _, _, _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfForceOutputColorModeState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, NoOverride_DoesNotOverride) {
+ // By default the layer state is used to set the preferred dataspace
+ verify().ifNoOverride()
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_BT2020)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, SRGB_Override_USES_V0_SRGB) {
+ // Setting ui::ColorMode::SRGB overrides it with ui::Dataspace::V0_SRGB
+ verify().ifForceOutputColorMode(ui::ColorMode::SRGB)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::V0_SRGB)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_ForceOutputColorOverrides, DisplayP3_Override_Uses_DisplayP3) {
+ // Setting ui::ColorMode::DISPLAY_P3 overrides it with ui::Dataspace::DISPLAY_P3
+ verify().ifForceOutputColorMode(ui::ColorMode::DISPLAY_P3)
+ .thenExpectBestColorModeCallUses(ui::Dataspace::DISPLAY_P3)
+ .execute();
+}
+
+// HDR output requires all layers to be compatible with the chosen HDR
+// dataspace, along with there being proper support.
+struct OutputUpdateColorProfileTest_Hdr : public OutputUpdateColorProfileTest {
+ OutputUpdateColorProfileTest_Hdr() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(2));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ }
+
+ static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+ static constexpr ui::Dataspace BT2020_PQ = ui::Dataspace::BT2020_PQ;
+ static constexpr ui::Dataspace BT2020_HLG = ui::Dataspace::BT2020_HLG;
+ static constexpr ui::Dataspace DISPLAY_P3 = ui::Dataspace::DISPLAY_P3;
+
+ struct IfTopLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, IfTopLayerDataspaceState> {
+ [[nodiscard]] auto ifTopLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer2.mLayerFEState.dataspace = dataspace;
+ return nextState<AndTopLayerCompositionTypeState>();
+ }
+ [[nodiscard]] auto ifTopLayerIsNotHdr() { return ifTopLayerIs(kNonHdrDataspace); }
+ };
+
+ struct AndTopLayerCompositionTypeState
+ : public CallOrderStateMachineHelper<TestType, AndTopLayerCompositionTypeState> {
+ [[nodiscard]] auto andTopLayerIsREComposed(bool renderEngineComposed) {
+ getInstance()->mLayer2.mLayerFEState.forceClientComposition = renderEngineComposed;
+ return nextState<AndIfBottomLayerDataspaceState>();
+ }
+ };
+
+ struct AndIfBottomLayerDataspaceState
+ : public CallOrderStateMachineHelper<TestType, AndIfBottomLayerDataspaceState> {
+ [[nodiscard]] auto andIfBottomLayerIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+ return nextState<AndBottomLayerCompositionTypeState>();
+ }
+ [[nodiscard]] auto andIfBottomLayerIsNotHdr() {
+ return andIfBottomLayerIs(kNonHdrDataspace);
+ }
+ };
+
+ struct AndBottomLayerCompositionTypeState
+ : public CallOrderStateMachineHelper<TestType, AndBottomLayerCompositionTypeState> {
+ [[nodiscard]] auto andBottomLayerIsREComposed(bool renderEngineComposed) {
+ getInstance()->mLayer1.mLayerFEState.forceClientComposition = renderEngineComposed;
+ return nextState<AndIfHasLegacySupportState>();
+ }
+ };
+
+ struct AndIfHasLegacySupportState
+ : public CallOrderStateMachineHelper<TestType, AndIfHasLegacySupportState> {
+ [[nodiscard]] auto andIfLegacySupportFor(ui::Dataspace dataspace, bool legacySupport) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile, hasLegacyHdrSupport(dataspace))
+ .WillOnce(Return(legacySupport));
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::Dataspace dataspace) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(dataspace, _, _, _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Call this member function to start using the mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfTopLayerDataspaceState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_Uses_PQ) {
+ // If all layers use BT2020_PQ, and there are no other special conditions,
+ // BT2020_PQ is used.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_PQ is not used if there is only legacy support for it.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_PQ_RE_Uses_PQ) {
+ // BT2020_PQ is still used if the bottom layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_PQ_HW_Uses_DisplayP3) {
+ // BT2020_PQ is not used if the top layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_Uses_PQ) {
+ // If there is mixed HLG/PQ use, and the topmost layer is PQ, then PQ is used if there
+ // are no other special conditions.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_PQ is not used if there is only legacy support for it.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_HLG_RE_Uses_PQ) {
+ // BT2020_PQ is used if the bottom HLG layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_RE_On_HLG_HW_Uses_DisplayP3) {
+ // BT2020_PQ is not used if the top PQ layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_Uses_PQ) {
+ // If there is mixed HLG/PQ use, and the topmost layer is HLG, then PQ is
+ // used if there are no other special conditions.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_PQ is not used if there is only legacy support for it.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_PQ_RE_Uses_DisplayP3) {
+ // BT2020_PQ is not used if the bottom PQ layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_PQ_HW_Uses_PQ) {
+ // BT2020_PQ is still used if the top HLG layer is RenderEngine composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_PQ)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_Uses_HLG) {
+ // If all layers use HLG then HLG is used if there are no other special
+ // conditions.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_HW_IfPQHasLegacySupport_Uses_DisplayP3) {
+ // BT2020_HLG is not used if there is legacy support for it.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_HLG, true)
+ .thenExpectBestColorModeCallUses(DISPLAY_P3)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_HLG_RE_Uses_HLG) {
+ // BT2020_HLG is used even if the bottom layer is client composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_RE_On_HLG_HW_Uses_HLG) {
+ // BT2020_HLG is used even if the top layer is client composed.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(true)
+ .andIfBottomLayerIs(BT2020_HLG)
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, PQ_HW_On_NonHdr_HW_Uses_PQ) {
+ // Even if there are non-HDR layers present, BT2020_PQ can still be used.
+ verify().ifTopLayerIs(BT2020_PQ)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIsNotHdr()
+ .andBottomLayerIsREComposed(false)
+ .andIfLegacySupportFor(BT2020_PQ, false)
+ .thenExpectBestColorModeCallUses(BT2020_PQ)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfileTest_Hdr, HLG_HW_On_NonHdr_RE_Uses_HLG) {
+ // If all layers use HLG then HLG is used if there are no other special
+ // conditions.
+ verify().ifTopLayerIs(BT2020_HLG)
+ .andTopLayerIsREComposed(false)
+ .andIfBottomLayerIsNotHdr()
+ .andBottomLayerIsREComposed(true)
+ .andIfLegacySupportFor(BT2020_HLG, false)
+ .thenExpectBestColorModeCallUses(BT2020_HLG)
+ .execute();
+}
+
+struct OutputUpdateColorProfile_AffectsChosenRenderIntentTest
+ : public OutputUpdateColorProfileTest {
+ // The various values for CompositionRefreshArgs::outputColorSetting affect
+ // the chosen renderIntent, along with whether the preferred dataspace is an
+ // HDR dataspace or not.
+
+ OutputUpdateColorProfile_AffectsChosenRenderIntentTest() {
+ mRefreshArgs.outputColorSetting = OutputColorSetting::kEnhanced;
+ mRefreshArgs.colorSpaceAgnosticDataspace = ui::Dataspace::UNKNOWN;
+ mLayer1.mLayerFEState.dataspace = ui::Dataspace::BT2020_PQ;
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(1));
+ EXPECT_CALL(mOutput, setColorProfile(_)).WillRepeatedly(Return());
+ EXPECT_CALL(*mDisplayColorProfile, hasLegacyHdrSupport(ui::Dataspace::BT2020_PQ))
+ .WillRepeatedly(Return(false));
+ }
+
+ // The tests here involve enough state and GMock setup that using a mini-DSL
+ // makes the tests much more readable, and allows the test to focus more on
+ // the intent than on some of the details.
+
+ static constexpr ui::Dataspace kNonHdrDataspace = ui::Dataspace::DISPLAY_P3;
+ static constexpr ui::Dataspace kHdrDataspace = ui::Dataspace::BT2020_PQ;
+
+ struct IfDataspaceChosenState
+ : public CallOrderStateMachineHelper<TestType, IfDataspaceChosenState> {
+ [[nodiscard]] auto ifDataspaceChosenIs(ui::Dataspace dataspace) {
+ getInstance()->mLayer1.mLayerFEState.dataspace = dataspace;
+ return nextState<AndOutputColorSettingState>();
+ }
+ [[nodiscard]] auto ifDataspaceChosenIsNonHdr() {
+ return ifDataspaceChosenIs(kNonHdrDataspace);
+ }
+ [[nodiscard]] auto ifDataspaceChosenIsHdr() { return ifDataspaceChosenIs(kHdrDataspace); }
+ };
+
+ struct AndOutputColorSettingState
+ : public CallOrderStateMachineHelper<TestType, AndOutputColorSettingState> {
+ [[nodiscard]] auto andOutputColorSettingIs(OutputColorSetting setting) {
+ getInstance()->mRefreshArgs.outputColorSetting = setting;
+ return nextState<ThenExpectBestColorModeCallUsesState>();
+ }
+ };
+
+ struct ThenExpectBestColorModeCallUsesState
+ : public CallOrderStateMachineHelper<TestType, ThenExpectBestColorModeCallUsesState> {
+ [[nodiscard]] auto thenExpectBestColorModeCallUses(ui::RenderIntent intent) {
+ EXPECT_CALL(*getInstance()->mDisplayColorProfile,
+ getBestColorMode(getInstance()->mLayer1.mLayerFEState.dataspace, intent, _,
+ _, _));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ // Tests call one of these two helper member functions to start using the
+ // mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfDataspaceChosenState::make(this); }
+};
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+ Managed_NonHdr_Prefers_Colorimetric) {
+ verify().ifDataspaceChosenIsNonHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kManaged)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::COLORIMETRIC)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+ Managed_Hdr_Prefers_ToneMapColorimetric) {
+ verify().ifDataspaceChosenIsHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kManaged)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_COLORIMETRIC)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Enhanced_NonHdr_Prefers_Enhance) {
+ verify().ifDataspaceChosenIsNonHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::ENHANCE)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest,
+ Enhanced_Hdr_Prefers_ToneMapEnhance) {
+ verify().ifDataspaceChosenIsHdr()
+ .andOutputColorSettingIs(OutputColorSetting::kEnhanced)
+ .thenExpectBestColorModeCallUses(ui::RenderIntent::TONE_MAP_ENHANCE)
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_NonHdr_Prefers_Vendor) {
+ verify().ifDataspaceChosenIsNonHdr()
+ .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+ .thenExpectBestColorModeCallUses(
+ static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+ .execute();
+}
+
+TEST_F(OutputUpdateColorProfile_AffectsChosenRenderIntentTest, Vendor_Hdr_Prefers_Vendor) {
+ verify().ifDataspaceChosenIsHdr()
+ .andOutputColorSettingIs(kVendorSpecifiedOutputColorSetting)
+ .thenExpectBestColorModeCallUses(
+ static_cast<ui::RenderIntent>(kVendorSpecifiedOutputColorSetting))
+ .execute();
+}
/*
* Output::beginFrame()
*/
+struct OutputBeginFrameTest : public ::testing::Test {
+ using TestType = OutputBeginFrameTest;
+
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ };
+
+ OutputBeginFrameTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ struct IfGetDirtyRegionExpectationState
+ : public CallOrderStateMachineHelper<TestType, IfGetDirtyRegionExpectationState> {
+ [[nodiscard]] auto ifGetDirtyRegionReturns(Region dirtyRegion) {
+ EXPECT_CALL(getInstance()->mOutput, getDirtyRegion(false))
+ .WillOnce(Return(dirtyRegion));
+ return nextState<AndIfGetOutputLayerCountExpectationState>();
+ }
+ };
+
+ struct AndIfGetOutputLayerCountExpectationState
+ : public CallOrderStateMachineHelper<TestType, AndIfGetOutputLayerCountExpectationState> {
+ [[nodiscard]] auto andIfGetOutputLayerCountReturns(size_t layerCount) {
+ EXPECT_CALL(getInstance()->mOutput, getOutputLayerCount()).WillOnce(Return(layerCount));
+ return nextState<AndIfLastCompositionHadVisibleLayersState>();
+ }
+ };
+
+ struct AndIfLastCompositionHadVisibleLayersState
+ : public CallOrderStateMachineHelper<TestType,
+ AndIfLastCompositionHadVisibleLayersState> {
+ [[nodiscard]] auto andIfLastCompositionHadVisibleLayersIs(bool hadOutputLayers) {
+ getInstance()->mOutput.mState.lastCompositionHadVisibleLayers = hadOutputLayers;
+ return nextState<ThenExpectRenderSurfaceBeginFrameCallState>();
+ }
+ };
+
+ struct ThenExpectRenderSurfaceBeginFrameCallState
+ : public CallOrderStateMachineHelper<TestType,
+ ThenExpectRenderSurfaceBeginFrameCallState> {
+ [[nodiscard]] auto thenExpectRenderSurfaceBeginFrameCall(bool mustRecompose) {
+ EXPECT_CALL(*getInstance()->mRenderSurface, beginFrame(mustRecompose));
+ return nextState<ExecuteState>();
+ }
+ };
+
+ struct ExecuteState : public CallOrderStateMachineHelper<TestType, ExecuteState> {
+ [[nodiscard]] auto execute() {
+ getInstance()->mOutput.beginFrame();
+ return nextState<CheckPostconditionHadVisibleLayersState>();
+ }
+ };
+
+ struct CheckPostconditionHadVisibleLayersState
+ : public CallOrderStateMachineHelper<TestType, CheckPostconditionHadVisibleLayersState> {
+ void checkPostconditionHadVisibleLayers(bool expected) {
+ EXPECT_EQ(expected, getInstance()->mOutput.mState.lastCompositionHadVisibleLayers);
+ }
+ };
+
+ // Tests call one of these two helper member functions to start using the
+ // mini-DSL defined above.
+ [[nodiscard]] auto verify() { return IfGetDirtyRegionExpectationState::make(this); }
+
+ static const Region kEmptyRegion;
+ static const Region kNotEmptyRegion;
+
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ StrictMock<OutputPartialMock> mOutput;
+};
+
+const Region OutputBeginFrameTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputBeginFrameTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(true)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(true)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(true)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, hasDirtyNotHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kNotEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(true)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(true);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(1u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
+TEST_F(OutputBeginFrameTest, notHasDirtyNotHasLayersNotHadLayersLastFrame) {
+ verify().ifGetDirtyRegionReturns(kEmptyRegion)
+ .andIfGetOutputLayerCountReturns(0u)
+ .andIfLastCompositionHadVisibleLayersIs(false)
+ .thenExpectRenderSurfaceBeginFrameCall(false)
+ .execute()
+ .checkPostconditionHadVisibleLayers(false);
+}
+
/*
* Output::devOptRepaintFlash()
*/
+struct OutputDevOptRepaintFlashTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_CONST_METHOD1(getDirtyRegion, Region(bool));
+ MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+ MOCK_METHOD0(postFramebuffer, void());
+ MOCK_METHOD0(prepareFrame, void());
+ };
+
+ OutputDevOptRepaintFlashTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ static const Region kEmptyRegion;
+ static const Region kNotEmptyRegion;
+
+ StrictMock<OutputPartialMock> mOutput;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+const Region OutputDevOptRepaintFlashTest::kEmptyRegion{Rect{0, 0, 0, 0}};
+const Region OutputDevOptRepaintFlashTest::kNotEmptyRegion{Rect{0, 0, 1, 1}};
+
+TEST_F(OutputDevOptRepaintFlashTest, doesNothingIfFlashDelayNotSet) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = {};
+ mRefreshArgs.repaintEverything = true;
+ mOutput.mState.isEnabled = true;
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotEnabled) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+ mRefreshArgs.repaintEverything = true;
+ mOutput.mState.isEnabled = false;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, prepareFrame());
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, postsAndPreparesANewFrameIfNotDirty) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+ mRefreshArgs.repaintEverything = true;
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, getDirtyRegion(true)).WillOnce(Return(kEmptyRegion));
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, prepareFrame());
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
+TEST_F(OutputDevOptRepaintFlashTest, alsoComposesSurfacesAndQueuesABufferIfDirty) {
+ mRefreshArgs.devOptFlashDirtyRegionsDelay = std::chrono::microseconds(1);
+ mRefreshArgs.repaintEverything = false;
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, getDirtyRegion(false)).WillOnce(Return(kNotEmptyRegion));
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(kNotEmptyRegion)));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+ EXPECT_CALL(mOutput, postFramebuffer());
+ EXPECT_CALL(mOutput, prepareFrame());
+
+ mOutput.devOptRepaintFlash(mRefreshArgs);
+}
+
// TODO(b/144060211) - Add coverage
/*
* Output::finishFrame()
*/
-// TODO(b/144060211) - Add coverage
+struct OutputFinishFrameTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD1(composeSurfaces, std::optional<base::unique_fd>(const Region&));
+ MOCK_METHOD0(postFramebuffer, void());
+ };
+
+ OutputFinishFrameTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+ }
+
+ StrictMock<OutputPartialMock> mOutput;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+ CompositionRefreshArgs mRefreshArgs;
+};
+
+TEST_F(OutputFinishFrameTest, ifNotEnabledDoesNothing) {
+ mOutput.mState.isEnabled = false;
+
+ mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, takesEarlyOutifComposeSurfacesReturnsNoFence) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)));
+
+ mOutput.finishFrame(mRefreshArgs);
+}
+
+TEST_F(OutputFinishFrameTest, queuesBufferIfComposeSurfacesReturnsAFence) {
+ mOutput.mState.isEnabled = true;
+
+ InSequence seq;
+ EXPECT_CALL(mOutput, composeSurfaces(RegionEq(Region::INVALID_REGION)))
+ .WillOnce(Return(ByMove(base::unique_fd())));
+ EXPECT_CALL(*mRenderSurface, queueBuffer(_));
+
+ mOutput.finishFrame(mRefreshArgs);
+}
/*
* Output::postFramebuffer()
*/
-// TODO(b/144060211) - Add coverage
+struct OutputPostFramebufferTest : public testing::Test {
+ struct OutputPartialMock : public OutputPartialMockBase {
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
+ MOCK_METHOD0(presentAndGetFrameFences, compositionengine::Output::FrameFences());
+ };
+
+ struct Layer {
+ Layer() {
+ EXPECT_CALL(outputLayer, getLayerFE()).WillRepeatedly(ReturnRef(layerFE));
+ EXPECT_CALL(outputLayer, getHwcLayer()).WillRepeatedly(Return(&hwc2Layer));
+ }
+
+ StrictMock<mock::OutputLayer> outputLayer;
+ StrictMock<mock::LayerFE> layerFE;
+ StrictMock<HWC2::mock::Layer> hwc2Layer;
+ };
+
+ OutputPostFramebufferTest() {
+ mOutput.setDisplayColorProfileForTest(
+ std::unique_ptr<DisplayColorProfile>(mDisplayColorProfile));
+ mOutput.setRenderSurfaceForTest(std::unique_ptr<RenderSurface>(mRenderSurface));
+
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillRepeatedly(Return(3u));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(0u))
+ .WillRepeatedly(Return(&mLayer1.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(1u))
+ .WillRepeatedly(Return(&mLayer2.outputLayer));
+ EXPECT_CALL(mOutput, getOutputLayerOrderedByZByIndex(2u))
+ .WillRepeatedly(Return(&mLayer3.outputLayer));
+ }
+
+ StrictMock<OutputPartialMock> mOutput;
+ mock::DisplayColorProfile* mDisplayColorProfile = new StrictMock<mock::DisplayColorProfile>();
+ mock::RenderSurface* mRenderSurface = new StrictMock<mock::RenderSurface>();
+
+ Layer mLayer1;
+ Layer mLayer2;
+ Layer mLayer3;
+};
+
+TEST_F(OutputPostFramebufferTest, ifNotEnabledDoesNothing) {
+ mOutput.mState.isEnabled = false;
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, ifEnabledMustFlipThenPresentThenSendPresentCompleted) {
+ mOutput.mState.isEnabled = true;
+
+ compositionengine::Output::FrameFences frameFences;
+
+ // This should happen even if there are no output layers.
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // For this test in particular we want to make sure the call expectations
+ // setup below are satisfied in the specific order.
+ InSequence seq;
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesAreSentToLayerFE) {
+ // Simulate getting release fences from each layer, and ensure they are passed to the
+ // front-end layer interface for each layer correctly.
+
+ mOutput.mState.isEnabled = true;
+
+ // Create three unique fence instances
+ sp<Fence> layer1Fence = new Fence();
+ sp<Fence> layer2Fence = new Fence();
+ sp<Fence> layer3Fence = new Fence();
+
+ Output::FrameFences frameFences;
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Compare the pointers values of each fence to make sure the correct ones
+ // are passed. This happens to work with the current implementation, but
+ // would not survive certain calls like Fence::merge() which would return a
+ // new instance.
+ EXPECT_CALL(mLayer1.layerFE,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer1Fence.get()))));
+ EXPECT_CALL(mLayer2.layerFE,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer2Fence.get()))));
+ EXPECT_CALL(mLayer3.layerFE,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(layer3Fence.get()))));
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releaseFencesIncludeClientTargetAcquireFence) {
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ sp<Fence> clientTargetAcquireFence = new Fence();
+ sp<Fence> layer1Fence = new Fence();
+ sp<Fence> layer2Fence = new Fence();
+ sp<Fence> layer3Fence = new Fence();
+ Output::FrameFences frameFences;
+ frameFences.clientTargetAcquireFence = clientTargetAcquireFence;
+ frameFences.layerFences.emplace(&mLayer1.hwc2Layer, layer1Fence);
+ frameFences.layerFences.emplace(&mLayer2.hwc2Layer, layer2Fence);
+ frameFences.layerFences.emplace(&mLayer3.hwc2Layer, layer3Fence);
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Fence::merge is called, and since none of the fences are actually valid,
+ // Fence::NO_FENCE is returned and passed to each onLayerDisplayed() call.
+ // This is the best we can do without creating a real kernel fence object.
+ EXPECT_CALL(mLayer1.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(mLayer2.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+ EXPECT_CALL(mLayer3.layerFE, onLayerDisplayed(Fence::NO_FENCE));
+
+ mOutput.postFramebuffer();
+}
+
+TEST_F(OutputPostFramebufferTest, releasedLayersSentPresentFence) {
+ mOutput.mState.isEnabled = true;
+ mOutput.mState.usesClientComposition = true;
+
+ // This should happen even if there are no (current) output layers.
+ EXPECT_CALL(mOutput, getOutputLayerCount()).WillOnce(Return(0u));
+
+ // Load up the released layers with some mock instances
+ sp<StrictMock<mock::LayerFE>> releasedLayer1{new StrictMock<mock::LayerFE>()};
+ sp<StrictMock<mock::LayerFE>> releasedLayer2{new StrictMock<mock::LayerFE>()};
+ sp<StrictMock<mock::LayerFE>> releasedLayer3{new StrictMock<mock::LayerFE>()};
+ Output::ReleasedLayers layers;
+ layers.push_back(releasedLayer1);
+ layers.push_back(releasedLayer2);
+ layers.push_back(releasedLayer3);
+ mOutput.setReleasedLayers(std::move(layers));
+
+ // Set up a fake present fence
+ sp<Fence> presentFence = new Fence();
+ Output::FrameFences frameFences;
+ frameFences.presentFence = presentFence;
+
+ EXPECT_CALL(*mRenderSurface, flip());
+ EXPECT_CALL(mOutput, presentAndGetFrameFences()).WillOnce(Return(frameFences));
+ EXPECT_CALL(*mRenderSurface, onPresentDisplayCompleted());
+
+ // Each released layer should be given the presentFence.
+ EXPECT_CALL(*releasedLayer1,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+ EXPECT_CALL(*releasedLayer2,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+ EXPECT_CALL(*releasedLayer3,
+ onLayerDisplayed(Property(&sp<Fence>::get, Eq(presentFence.get()))));
+
+ mOutput.postFramebuffer();
+
+ // After the call the list of released layers should have been cleared.
+ EXPECT_TRUE(mOutput.getReleasedLayersForTest().empty());
+}
/*
* Output::composeSurfaces()
@@ -695,11 +2677,11 @@
static const mat4 kDefaultColorTransformMat;
struct OutputPartialMock : public OutputPartialMockBase {
- // Sets up the helper functions called by composeSurfaces to use a mock
- // implementations.
+ // Sets up the helper functions called by the function under test to use
+ // mock implementations.
MOCK_CONST_METHOD0(getSkipColorTransform, bool());
- MOCK_METHOD2(generateClientCompositionRequests,
- std::vector<renderengine::LayerSettings>(bool, Region&));
+ MOCK_METHOD3(generateClientCompositionRequests,
+ std::vector<renderengine::LayerSettings>(bool, Region&, ui::Dataspace));
MOCK_METHOD2(appendRegionFlashRequests,
void(const Region&, std::vector<renderengine::LayerSettings>&));
MOCK_METHOD1(setExpensiveRenderingExpected, void(bool));
@@ -729,10 +2711,14 @@
.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()));
}
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;
@@ -777,7 +2763,7 @@
EXPECT_CALL(*mRenderSurface, dequeueBuffer(_)).WillOnce(Return(mOutputBuffer));
EXPECT_CALL(mOutput, getSkipColorTransform()).WillOnce(Return(false));
- EXPECT_CALL(mOutput, generateClientCompositionRequests(false, _)).Times(1);
+ 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);
@@ -794,9 +2780,10 @@
struct OutputPartialMock : public OutputPartialMockBase {
// compositionengine::Output overrides
std::vector<renderengine::LayerSettings> generateClientCompositionRequests(
- bool supportsProtectedContent, Region& clearRegion) override {
+ bool supportsProtectedContent, Region& clearRegion,
+ ui::Dataspace dataspace) override {
return impl::Output::generateClientCompositionRequests(supportsProtectedContent,
- clearRegion);
+ clearRegion, dataspace);
}
};
@@ -855,6 +2842,8 @@
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));
@@ -864,6 +2853,8 @@
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));
@@ -887,8 +2878,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
ASSERT_EQ(2u, requests.size());
EXPECT_EQ(leftLayerColor, requests[0].source.solidColor);
@@ -937,8 +2928,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
EXPECT_EQ(0u, requests.size());
}
@@ -1015,8 +3006,8 @@
constexpr bool supportsProtectedContent = false;
Region clearRegion;
- auto requests =
- mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion);
+ auto requests = mOutput.generateClientCompositionRequests(supportsProtectedContent, clearRegion,
+ mOutput.getState().targetDataspace);
const half3 clearColor{0.f, 0.f, 0.f};
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 89123df..84ec597 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -132,11 +132,11 @@
}
// ----------------------------------------------------------------------------
-void DisplayDevice::setActiveConfig(int mode) {
+void DisplayDevice::setActiveConfig(HwcConfigIndexType mode) {
mActiveConfig = mode;
}
-int DisplayDevice::getActiveConfig() const {
+HwcConfigIndexType DisplayDevice::getActiveConfig() const {
return mActiveConfig;
}
@@ -285,7 +285,7 @@
result.append(" ");
StringAppendF(&result, "powerMode=%d, ", mPowerMode);
- StringAppendF(&result, "activeConfig=%d, ", mActiveConfig);
+ StringAppendF(&result, "activeConfig=%d, ", mActiveConfig.value());
getCompositionDisplay()->dump(result);
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index ce4e1e6..79a1185 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -43,6 +43,7 @@
#include "DisplayHardware/DisplayIdentification.h"
#include "DisplayHardware/PowerAdvisor.h"
#include "RenderArea.h"
+#include "Scheduler/HwcStrongTypes.h"
namespace android {
@@ -141,8 +142,8 @@
/* ------------------------------------------------------------------------
* Display active config management.
*/
- int getActiveConfig() const;
- void setActiveConfig(int mode);
+ HwcConfigIndexType getActiveConfig() const;
+ void setActiveConfig(HwcConfigIndexType mode);
// release HWC resources (if any) for removable displays
void disconnect();
@@ -186,7 +187,7 @@
// Current power mode
int mPowerMode;
// Current active config
- int mActiveConfig;
+ HwcConfigIndexType mActiveConfig;
// TODO(b/74619554): Remove special cases for primary display.
const bool mIsPrimary;
@@ -246,6 +247,7 @@
uint32_t reqHeight, ui::Dataspace reqDataSpace,
ui::Transform::orientation_flags rotation, bool allowSecureLayers = true)
: RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace,
+ device->getViewport(),
getDisplayRotation(rotation, device->getInstallOrientation())),
mDevice(device),
mSourceCrop(sourceCrop),
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
index aef1c75..dc71128 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -95,7 +95,7 @@
// assume NO_RESOURCES when Status::isOk returns false
constexpr Error kDefaultError = Error::NO_RESOURCES;
-constexpr V2_4::Error kDefaultError_2_4 = V2_4::Error::NO_RESOURCES;
+constexpr V2_4::Error kDefaultError_2_4 = static_cast<V2_4::Error>(kDefaultError);
template<typename T, typename U>
T unwrapRet(Return<T>& ret, const U& default_val)
@@ -247,7 +247,12 @@
void Composer::registerCallback(const sp<IComposerCallback>& callback)
{
android::hardware::setMinSchedulerPolicy(callback, SCHED_FIFO, 2);
- auto ret = mClient->registerCallback(callback);
+ auto ret = [&]() {
+ if (mClient_2_4) {
+ return mClient_2_4->registerCallback_2_4(callback);
+ }
+ return mClient->registerCallback(callback);
+ }();
if (!ret.isOk()) {
ALOGE("failed to register IComposerCallback");
}
@@ -413,15 +418,28 @@
IComposerClient::Attribute attribute, int32_t* outValue)
{
Error error = kDefaultError;
- mClient->getDisplayAttribute(display, config, attribute,
- [&](const auto& tmpError, const auto& tmpValue) {
- error = tmpError;
- if (error != Error::NONE) {
- return;
- }
+ if (mClient_2_4) {
+ mClient_2_4->getDisplayAttribute_2_4(display, config, attribute,
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = static_cast<Error>(tmpError);
+ if (error != Error::NONE) {
+ return;
+ }
- *outValue = tmpValue;
- });
+ *outValue = tmpValue;
+ });
+ } else {
+ mClient->getDisplayAttribute(display, config,
+ static_cast<V2_1::IComposerClient::Attribute>(attribute),
+ [&](const auto& tmpError, const auto& tmpValue) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outValue = tmpValue;
+ });
+ }
return error;
}
@@ -1200,13 +1218,14 @@
return static_cast<Error>(error);
}
-Error Composer::getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) {
+V2_4::Error Composer::getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) {
+ using Error = V2_4::Error;
if (!mClient_2_4) {
return Error::UNSUPPORTED;
}
- V2_4::Error error = kDefaultError_2_4;
+ Error error = kDefaultError_2_4;
mClient_2_4->getDisplayConnectionType(display, [&](const auto& tmpError, const auto& tmpType) {
error = tmpError;
if (error != V2_4::Error::NONE) {
@@ -1216,7 +1235,50 @@
*outType = tmpType;
});
- return static_cast<V2_1::Error>(error);
+ return error;
+}
+
+V2_4::Error Composer::getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->getDisplayVsyncPeriod(display,
+ [&](const auto& tmpError, const auto& tmpVsyncPeriod) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outVsyncPeriod = tmpVsyncPeriod;
+ });
+
+ return error;
+}
+
+V2_4::Error Composer::setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) {
+ using Error = V2_4::Error;
+ if (!mClient_2_4) {
+ return Error::UNSUPPORTED;
+ }
+
+ Error error = kDefaultError_2_4;
+ mClient_2_4->setActiveConfigWithConstraints(display, config, vsyncPeriodChangeConstraints,
+ [&](const auto& tmpError, const auto& tmpTimeline) {
+ error = tmpError;
+ if (error != Error::NONE) {
+ return;
+ }
+
+ *outTimeline = tmpTimeline;
+ });
+
+ return error;
}
CommandReader::~CommandReader()
diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h
index e743e59..336fdd8 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -62,12 +62,14 @@
using V2_1::Config;
using V2_1::Display;
using V2_1::Error;
-using V2_1::IComposerCallback;
using V2_1::Layer;
using V2_3::CommandReaderBase;
using V2_3::CommandWriterBase;
using V2_4::IComposer;
+using V2_4::IComposerCallback;
using V2_4::IComposerClient;
+using V2_4::VsyncPeriodChangeTimeline;
+using V2_4::VsyncPeriodNanos;
using DisplayCapability = IComposerClient::DisplayCapability;
using PerFrameMetadata = IComposerClient::PerFrameMetadata;
using PerFrameMetadataKey = IComposerClient::PerFrameMetadataKey;
@@ -208,10 +210,17 @@
virtual Error setDisplayBrightness(Display display, float brightness) = 0;
// Composer HAL 2.4
+ virtual bool isVsyncPeriodSwitchSupported() = 0;
virtual Error getDisplayCapabilities(Display display,
std::vector<DisplayCapability>* outCapabilities) = 0;
- virtual Error getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) = 0;
+ virtual V2_4::Error getDisplayConnectionType(
+ Display display, IComposerClient::DisplayConnectionType* outType) = 0;
+ virtual V2_4::Error getDisplayVsyncPeriod(Display display,
+ VsyncPeriodNanos* outVsyncPeriod) = 0;
+ virtual V2_4::Error setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) = 0;
};
namespace impl {
@@ -423,10 +432,16 @@
Error setDisplayBrightness(Display display, float brightness) override;
// Composer HAL 2.4
+ bool isVsyncPeriodSwitchSupported() override { return mClient_2_4 != nullptr; }
Error getDisplayCapabilities(Display display,
std::vector<DisplayCapability>* outCapabilities) override;
- Error getDisplayConnectionType(Display display,
- IComposerClient::DisplayConnectionType* outType) override;
+ V2_4::Error getDisplayConnectionType(Display display,
+ IComposerClient::DisplayConnectionType* outType) override;
+ V2_4::Error getDisplayVsyncPeriod(Display display, VsyncPeriodNanos* outVsyncPeriod) override;
+ V2_4::Error setActiveConfigWithConstraints(
+ Display display, Config config,
+ const IComposerClient::VsyncPeriodChangeConstraints& vsyncPeriodChangeConstraints,
+ VsyncPeriodChangeTimeline* outTimeline) override;
private:
#if defined(USE_VR_COMPOSER) && USE_VR_COMPOSER
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp
index 6f7428a..34254e0 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -81,7 +81,26 @@
Return<void> onVsync(Hwc2::Display display, int64_t timestamp) override
{
- mCallback->onVsyncReceived(mSequenceId, display, timestamp);
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp, std::nullopt);
+ return Void();
+ }
+
+ Return<void> onVsync_2_4(Hwc2::Display display, int64_t timestamp,
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos) override {
+ // TODO(b/140201379): use vsyncPeriodNanos in the new DispSync
+ mCallback->onVsyncReceived(mSequenceId, display, timestamp,
+ std::make_optional(vsyncPeriodNanos));
+ return Void();
+ }
+
+ Return<void> onVsyncPeriodTimingChanged(
+ Hwc2::Display display,
+ const Hwc2::VsyncPeriodChangeTimeline& updatedTimeline) override {
+ hwc_vsync_period_change_timeline_t timeline;
+ timeline.newVsyncAppliedTimeNanos = updatedTimeline.newVsyncAppliedTimeNanos;
+ timeline.refreshRequired = updatedTimeline.refreshRequired;
+ timeline.refreshTimeNanos = updatedTimeline.refreshTimeNanos;
+ mCallback->onVsyncPeriodTimingChangedReceived(mSequenceId, display, timeline);
return Void();
}
@@ -330,6 +349,36 @@
return Error::None;
}
+bool Display::isVsyncPeriodSwitchSupported() const {
+ ALOGV("[%" PRIu64 "] isVsyncPeriodSwitchSupported()", mId);
+
+ return mComposer.isVsyncPeriodSwitchSupported();
+}
+
+Error Display::getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const {
+ ALOGV("[%" PRIu64 "] getDisplayVsyncPeriod", mId);
+
+ Error error;
+
+ if (isVsyncPeriodSwitchSupported()) {
+ Hwc2::VsyncPeriodNanos vsyncPeriodNanos = 0;
+ auto intError = mComposer.getDisplayVsyncPeriod(mId, &vsyncPeriodNanos);
+ error = static_cast<Error>(intError);
+ *outVsyncPeriod = static_cast<nsecs_t>(vsyncPeriodNanos);
+ } else {
+ // Get the default vsync period
+ hwc2_config_t configId = 0;
+ auto intError_2_1 = mComposer.getActiveConfig(mId, &configId);
+ error = static_cast<Error>(intError_2_1);
+ if (error == Error::None) {
+ auto config = mConfigs.at(configId);
+ *outVsyncPeriod = config->getVsyncPeriod();
+ }
+ }
+
+ return error;
+}
+
Error Display::getActiveConfigIndex(int* outIndex) const {
ALOGV("[%" PRIu64 "] getActiveConfigIndex", mId);
hwc2_config_t configId = 0;
@@ -345,6 +394,7 @@
auto pos = mConfigs.find(configId);
if (pos != mConfigs.end()) {
*outIndex = std::distance(mConfigs.begin(), pos);
+ ALOGV("[%" PRIu64 "] index = %d", mId, *outIndex);
} else {
ALOGE("[%" PRIu64 "] getActiveConfig returned unknown config %u", mId, configId);
// Return no error, but the caller needs to check for a negative index
@@ -582,6 +632,46 @@
return Error::None;
}
+Error Display::setActiveConfigWithConstraints(
+ const std::shared_ptr<const HWC2::Display::Config>& config,
+ const VsyncPeriodChangeConstraints& constraints, VsyncPeriodChangeTimeline* outTimeline) {
+ ALOGV("[%" PRIu64 "] setActiveConfigWithConstraints", mId);
+ if (config->getDisplayId() != mId) {
+ ALOGE("setActiveConfigWithConstraints received config %u for the wrong display %" PRIu64
+ " (expected %" PRIu64 ")",
+ config->getId(), config->getDisplayId(), mId);
+ return Error::BadConfig;
+ }
+
+ if (isVsyncPeriodSwitchSupported()) {
+ Hwc2::IComposerClient::VsyncPeriodChangeConstraints hwc2Constraints;
+ hwc2Constraints.desiredTimeNanos = constraints.desiredTimeNanos;
+ hwc2Constraints.seamlessRequired = constraints.seamlessRequired;
+
+ Hwc2::VsyncPeriodChangeTimeline vsyncPeriodChangeTimeline = {};
+ auto intError =
+ mComposer.setActiveConfigWithConstraints(mId, config->getId(), hwc2Constraints,
+ &vsyncPeriodChangeTimeline);
+ outTimeline->newVsyncAppliedTimeNanos = vsyncPeriodChangeTimeline.newVsyncAppliedTimeNanos;
+ outTimeline->refreshRequired = vsyncPeriodChangeTimeline.refreshRequired;
+ outTimeline->refreshTimeNanos = vsyncPeriodChangeTimeline.refreshTimeNanos;
+ return static_cast<Error>(intError);
+ }
+
+ // Use legacy setActiveConfig instead
+ ALOGV("fallback to legacy setActiveConfig");
+ const auto now = systemTime();
+ if (constraints.desiredTimeNanos > now || constraints.seamlessRequired) {
+ ALOGE("setActiveConfigWithConstraints received constraints that can't be satisfied");
+ }
+
+ auto intError_2_4 = mComposer.setActiveConfig(mId, config->getId());
+ outTimeline->newVsyncAppliedTimeNanos = std::max(now, constraints.desiredTimeNanos);
+ outTimeline->refreshRequired = true;
+ outTimeline->refreshTimeNanos = now;
+ return static_cast<Error>(intError_2_4);
+}
+
Error Display::setActiveConfig(const std::shared_ptr<const Config>& config)
{
if (config->getDisplayId() != mId) {
@@ -742,12 +832,13 @@
ALOGV("[%" PRIu64 "] loadConfig(%u)", mId, configId);
auto config = Config::Builder(*this, configId)
- .setWidth(getAttribute(configId, Attribute::Width))
- .setHeight(getAttribute(configId, Attribute::Height))
- .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
- .setDpiX(getAttribute(configId, Attribute::DpiX))
- .setDpiY(getAttribute(configId, Attribute::DpiY))
- .build();
+ .setWidth(getAttribute(configId, Attribute::Width))
+ .setHeight(getAttribute(configId, Attribute::Height))
+ .setVsyncPeriod(getAttribute(configId, Attribute::VsyncPeriod))
+ .setDpiX(getAttribute(configId, Attribute::DpiX))
+ .setDpiY(getAttribute(configId, Attribute::DpiY))
+ .setConfigGroup(getAttribute(configId, Attribute::ConfigGroup))
+ .build();
mConfigs.emplace(configId, std::move(config));
}
diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h
index b7cdf7f..81ae3b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -38,6 +38,8 @@
#include <unordered_set>
#include <vector>
+#include "../Scheduler/StrongTyping.h"
+
namespace android {
struct DisplayedFrameStats;
class Fence;
@@ -54,6 +56,8 @@
class Display;
class Layer;
+using VsyncPeriodChangeConstraints = hwc_vsync_period_change_constraints_t;
+using VsyncPeriodChangeTimeline = hwc_vsync_period_change_timeline_t;
// Implement this interface to receive hardware composer events.
//
@@ -70,8 +74,12 @@
Connection connection) = 0;
virtual void onRefreshReceived(int32_t sequenceId,
hwc2_display_t display) = 0;
- virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display,
- int64_t timestamp) = 0;
+ virtual void onVsyncReceived(int32_t sequenceId, hwc2_display_t display, int64_t timestamp,
+ std::optional<hwc2_vsync_period_t> vsyncPeriod) = 0;
+ virtual void onVsyncPeriodTimingChangedReceived(
+ int32_t sequenceId, hwc2_display_t display,
+ const hwc_vsync_period_change_timeline_t& updatedTimeline) = 0;
+
virtual ~ComposerCallback() = default;
};
@@ -170,6 +178,10 @@
}
return *this;
}
+ Builder& setConfigGroup(int32_t configGroup) {
+ mConfig->mConfigGroup = configGroup;
+ return *this;
+ }
private:
float getDefaultDensity();
@@ -184,6 +196,7 @@
nsecs_t getVsyncPeriod() const { return mVsyncPeriod; }
float getDpiX() const { return mDpiX; }
float getDpiY() const { return mDpiY; }
+ int32_t getConfigGroup() const { return mConfigGroup; }
private:
Config(Display& display, hwc2_config_t id);
@@ -196,12 +209,14 @@
nsecs_t mVsyncPeriod;
float mDpiX;
float mDpiY;
+ int32_t mConfigGroup;
};
virtual hwc2_display_t getId() const = 0;
virtual bool isConnected() const = 0;
virtual void setConnected(bool connected) = 0; // For use by Device only
virtual const std::unordered_set<DisplayCapability>& getCapabilities() const = 0;
+ virtual bool isVsyncPeriodSwitchSupported() const = 0;
[[clang::warn_unused_result]] virtual Error acceptChanges() = 0;
[[clang::warn_unused_result]] virtual Error createLayer(Layer** outLayer) = 0;
@@ -264,6 +279,12 @@
uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0;
[[clang::warn_unused_result]] virtual Error setDisplayBrightness(float brightness) const = 0;
+ [[clang::warn_unused_result]] virtual Error getDisplayVsyncPeriod(
+ nsecs_t* outVsyncPeriod) const = 0;
+ [[clang::warn_unused_result]] virtual Error setActiveConfigWithConstraints(
+ const std::shared_ptr<const HWC2::Display::Config>& config,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) = 0;
};
namespace impl {
@@ -323,6 +344,10 @@
Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests,
android::sp<android::Fence>* outPresentFence, uint32_t* state) override;
Error setDisplayBrightness(float brightness) const override;
+ Error getDisplayVsyncPeriod(nsecs_t* outVsyncPeriod) const override;
+ Error setActiveConfigWithConstraints(const std::shared_ptr<const HWC2::Display::Config>& config,
+ const VsyncPeriodChangeConstraints& constraints,
+ VsyncPeriodChangeTimeline* outTimeline) override;
// Other Display methods
hwc2_display_t getId() const override { return mId; }
@@ -331,6 +356,7 @@
const std::unordered_set<DisplayCapability>& getCapabilities() const override {
return mDisplayCapabilities;
};
+ virtual bool isVsyncPeriodSwitchSupported() const override;
private:
int32_t getAttribute(hwc2_config_t configId, Attribute attribute);
@@ -355,7 +381,9 @@
bool mIsConnected;
DisplayType mType;
std::unordered_map<hwc2_layer_t, std::unique_ptr<Layer>> mLayers;
+
std::unordered_map<hwc2_config_t, std::shared_ptr<const Config>> mConfigs;
+
std::once_flag mDisplayCapabilityQueryFlag;
std::unordered_set<DisplayCapability> mDisplayCapabilities;
};
diff --git a/services/surfaceflinger/FrameTracer/FrameTracer.cpp b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
index 6f91843..4418116 100644
--- a/services/surfaceflinger/FrameTracer/FrameTracer.cpp
+++ b/services/surfaceflinger/FrameTracer/FrameTracer.cpp
@@ -21,6 +21,7 @@
#include "FrameTracer.h"
#include <android-base/stringprintf.h>
+#include <perfetto/trace/clock_snapshot.pbzero.h>
#include <algorithm>
#include <mutex>
@@ -29,6 +30,7 @@
namespace android {
+using Clock = perfetto::protos::pbzero::ClockSnapshot::Clock;
void FrameTracer::initialize() {
std::call_once(mInitializationFlag, [this]() {
perfetto::TracingInitArgs args;
@@ -130,6 +132,7 @@
uint64_t bufferID, uint64_t frameNumber, nsecs_t timestamp,
FrameEvent::BufferEventType type, nsecs_t duration) {
auto packet = ctx.NewTracePacket();
+ packet->set_timestamp_clock_id(Clock::MONOTONIC);
packet->set_timestamp(timestamp);
auto* event = packet->set_graphics_frame_event()->set_buffer_event();
event->set_buffer_id(static_cast<uint32_t>(bufferID));
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index ce9aab5..35fc4be 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -571,6 +571,53 @@
return layerSettings;
}
+std::optional<renderengine::LayerSettings> Layer::prepareShadowClientComposition(
+ const renderengine::LayerSettings& casterLayerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) {
+ renderengine::ShadowSettings shadow = getShadowSettings(displayViewport);
+ if (shadow.length <= 0.f) {
+ return {};
+ }
+
+ const float casterAlpha = casterLayerSettings.alpha;
+ const bool casterIsOpaque = ((casterLayerSettings.source.buffer.buffer != nullptr) &&
+ casterLayerSettings.source.buffer.isOpaque);
+
+ renderengine::LayerSettings shadowLayer = casterLayerSettings;
+ shadowLayer.shadow = shadow;
+
+ // 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.
+ shadowLayer.shadow.casterIsTranslucent = !casterIsOpaque || (casterAlpha < 1.0f);
+ shadowLayer.shadow.ambientColor *= casterAlpha;
+ shadowLayer.shadow.spotColor *= casterAlpha;
+ shadowLayer.sourceDataspace = outputDataspace;
+ shadowLayer.source.buffer.buffer = nullptr;
+
+ if (shadowLayer.shadow.ambientColor.a <= 0.f && shadowLayer.shadow.spotColor.a <= 0.f) {
+ return {};
+ }
+
+ float casterCornerRadius = shadowLayer.geometry.roundedCornersRadius;
+ const FloatRect& cornerRadiusCropRect = casterLayerSettings.geometry.roundedCornersCrop;
+ const FloatRect& casterRect = shadowLayer.geometry.boundaries;
+
+ // crop used to set the corner radius may be larger than the content rect. Adjust the corner
+ // radius accordingly.
+ if (casterCornerRadius > 0.f) {
+ float cropRectOffset = std::max(std::abs(cornerRadiusCropRect.top - casterRect.top),
+ std::abs(cornerRadiusCropRect.left - casterRect.left));
+ if (cropRectOffset > casterCornerRadius) {
+ casterCornerRadius = 0;
+ } else {
+ casterCornerRadius -= cropRectOffset;
+ }
+ shadowLayer.geometry.roundedCornersRadius = casterCornerRadius;
+ }
+
+ return shadowLayer;
+}
+
Hwc2::IComposerClient::Composition Layer::getCompositionType(
const sp<const DisplayDevice>& display) const {
const auto outputLayer = findOutputLayerForDisplay(display);
@@ -1800,6 +1847,18 @@
: RoundedCornerState();
}
+renderengine::ShadowSettings Layer::getShadowSettings(const Rect& viewport) const {
+ renderengine::ShadowSettings state = mFlinger->mDrawingState.globalShadowSettings;
+
+ // Shift the spot light x-position to the middle of the display and then
+ // offset it by casting layer's screen pos.
+ state.lightPos.x = (viewport.width() / 2.f) - mScreenBounds.left;
+ state.lightPos.y -= mScreenBounds.top;
+
+ state.length = mEffectiveShadowRadius;
+ return state;
+}
+
void Layer::commitChildList() {
for (size_t i = 0; i < mCurrentChildren.size(); i++) {
const auto& child = mCurrentChildren[i];
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 286311b..843d3ae 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -216,6 +216,9 @@
std::deque<sp<CallbackHandle>> callbackHandles;
bool colorSpaceAgnostic;
nsecs_t desiredPresentTime = -1;
+
+ // Length of the cast shadow. If the radius is > 0, a shadow of length shadowRadius will
+ // be rendered around the layer.
float shadowRadius;
};
@@ -507,6 +510,9 @@
void latchCursorCompositionState(compositionengine::LayerFECompositionState&) const override;
std::optional<renderengine::LayerSettings> prepareClientComposition(
compositionengine::LayerFE::ClientCompositionTargetSettings&) override;
+ std::optional<renderengine::LayerSettings> prepareShadowClientComposition(
+ const renderengine::LayerSettings& layerSettings, const Rect& displayViewport,
+ ui::Dataspace outputDataspace) override;
void onLayerDisplayed(const sp<Fence>& releaseFence) override;
const char* getDebugName() const override;
@@ -601,8 +607,6 @@
virtual sp<GraphicBuffer> getBuffer() const { return nullptr; }
- virtual uint64_t getCurrentFrameNumber() const { return mCurrentFrameNumber; }
-
/*
* Returns if a frame is ready
*/
@@ -652,6 +656,8 @@
// ignored.
RoundedCornerState getRoundedCornerState() const;
+ renderengine::ShadowSettings getShadowSettings(const Rect& viewport) const;
+
void traverseInReverseZOrder(LayerVector::StateSet stateSet,
const LayerVector::Visitor& visitor);
void traverseInZOrder(LayerVector::StateSet stateSet, const LayerVector::Visitor& visitor);
@@ -664,6 +670,15 @@
const LayerVector::Visitor& visitor);
size_t getChildrenCount() const;
+
+ // ONLY CALL THIS FROM THE LAYER DTOR!
+ // See b/141111965. We need to add current children to offscreen layers in
+ // the layer dtor so as not to dangle layers. Since the layer has not
+ // committed its transaction when the layer is destroyed, we must add
+ // current children. This is safe in the dtor as we will no longer update
+ // the current state, but should not be called anywhere else!
+ LayerVector& getCurrentChildren() { return mCurrentChildren; }
+
void addChild(const sp<Layer>& layer);
// Returns index if removed, or negative value otherwise
// for symmetry with Vector::remove
@@ -701,6 +716,14 @@
Region debugGetVisibleRegionOnDefaultDisplay() const;
+ /**
+ * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
+ * INVALID_RECT if the layer has no buffer and no crop.
+ * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
+ * bounds are constrained by its parent bounds.
+ */
+ Rect getCroppedBufferSize(const Layer::State& s) const;
+
protected:
// constant
sp<SurfaceFlinger> mFlinger;
@@ -909,13 +932,6 @@
const LayerVector::Visitor& visitor);
LayerVector makeChildrenTraversalList(LayerVector::StateSet stateSet,
const std::vector<Layer*>& layersInTree);
- /**
- * Returns the cropped buffer size or the layer crop if the layer has no buffer. Return
- * INVALID_RECT if the layer has no buffer and no crop.
- * A layer with an invalid buffer size and no crop is considered to be boundless. The layer
- * bounds are constrained by its parent bounds.
- */
- Rect getCroppedBufferSize(const Layer::State& s) const;
// Cached properties computed from drawing state
// Effective transform taking into account parent transforms and any parent scaling.
diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp
index 976fedb..38a80a7 100644
--- a/services/surfaceflinger/RefreshRateOverlay.cpp
+++ b/services/surfaceflinger/RefreshRateOverlay.cpp
@@ -20,8 +20,6 @@
namespace android {
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
-
RefreshRateOverlay::RefreshRateOverlay(SurfaceFlinger& flinger)
: mFlinger(flinger), mClient(new Client(&mFlinger)) {
createLayer();
@@ -51,8 +49,8 @@
return true;
}
-void RefreshRateOverlay::changeRefreshRate(RefreshRateType type) {
- const half3& color = (type == RefreshRateType::PERFORMANCE) ? GREEN : RED;
+void RefreshRateOverlay::changeRefreshRate(const RefreshRate& refreshRate) {
+ const half3& color = (refreshRate.fps > 65.0f) ? GREEN : RED;
mLayer->setColor(color);
mFlinger.mTransactionFlags.fetch_or(eTransactionMask);
}
diff --git a/services/surfaceflinger/RefreshRateOverlay.h b/services/surfaceflinger/RefreshRateOverlay.h
index ce29bc3..414bc47 100644
--- a/services/surfaceflinger/RefreshRateOverlay.h
+++ b/services/surfaceflinger/RefreshRateOverlay.h
@@ -19,13 +19,13 @@
namespace android {
-using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
class RefreshRateOverlay {
public:
RefreshRateOverlay(SurfaceFlinger& flinger);
- void changeRefreshRate(RefreshRateType type);
+ void changeRefreshRate(const RefreshRate& refreshRate);
private:
bool createLayer();
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index edc6442..532572f 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -22,13 +22,14 @@
static float getCaptureFillValue(CaptureFill captureFill);
RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
- ui::Dataspace reqDataSpace,
+ ui::Dataspace reqDataSpace, const Rect& displayViewport,
ui::Transform::orientation_flags rotation = ui::Transform::ROT_0)
: mReqWidth(reqWidth),
mReqHeight(reqHeight),
mReqDataSpace(reqDataSpace),
mCaptureFill(captureFill),
- mRotationFlags(rotation) {}
+ mRotationFlags(rotation),
+ mDisplayViewport(displayViewport) {}
virtual ~RenderArea() = default;
@@ -80,12 +81,16 @@
virtual const sp<const DisplayDevice> getDisplayDevice() const = 0;
+ // Returns the source display viewport.
+ const Rect& getDisplayViewport() const { return mDisplayViewport; }
+
private:
const uint32_t mReqWidth;
const uint32_t mReqHeight;
const ui::Dataspace mReqDataSpace;
const CaptureFill mCaptureFill;
const ui::Transform::orientation_flags mRotationFlags;
+ const Rect mDisplayViewport;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp
index 8d9adc8..ff800c3 100644
--- a/services/surfaceflinger/Scheduler/EventThread.cpp
+++ b/services/surfaceflinger/Scheduler/EventThread.cpp
@@ -36,6 +36,7 @@
#include <utils/Trace.h>
#include "EventThread.h"
+#include "HwcStrongTypes.h"
using namespace std::chrono_literals;
@@ -101,10 +102,11 @@
return event;
}
-DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+DisplayEventReceiver::Event makeConfigChanged(PhysicalDisplayId displayId,
+ HwcConfigIndexType configId) {
DisplayEventReceiver::Event event;
event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()};
- event.config.configId = configId;
+ event.config.configId = configId.value();
return event;
}
@@ -290,7 +292,7 @@
mCondition.notify_all();
}
-void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) {
+void EventThread::onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) {
std::lock_guard<std::mutex> lock(mMutex);
mPendingEvents.push_back(makeConfigChanged(displayId, configId));
diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h
index a029586..a42546c 100644
--- a/services/surfaceflinger/Scheduler/EventThread.h
+++ b/services/surfaceflinger/Scheduler/EventThread.h
@@ -33,6 +33,7 @@
#include <private/gui/BitTube.h>
#include <utils/Errors.h>
+#include "HwcStrongTypes.h"
// ---------------------------------------------------------------------------
namespace android {
@@ -109,7 +110,7 @@
virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0;
// called when SF changes the active config and apps needs to be notified about the change
- virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0;
+ virtual void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) = 0;
virtual void dump(std::string& result) const = 0;
@@ -146,7 +147,7 @@
void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override;
- void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override;
+ void onConfigChanged(PhysicalDisplayId displayId, HwcConfigIndexType configId) override;
void dump(std::string& result) const override;
diff --git a/services/surfaceflinger/Scheduler/HwcStrongTypes.h b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
new file mode 100644
index 0000000..cfbbdfe
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/HwcStrongTypes.h
@@ -0,0 +1,27 @@
+/*
+ * 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 "StrongTyping.h"
+
+namespace android {
+
+// Strong types for the different indexes as they are referring to a different base.
+using HwcConfigIndexType = StrongTyping<int, struct HwcConfigIndexTypeTag, Compare, Add, Hash>;
+using HwcConfigGroupType = StrongTyping<int, struct HwcConfigGroupTypeTag, Compare>;
+
+} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.cpp b/services/surfaceflinger/Scheduler/LayerHistory.cpp
index 8b71728..146ec1b 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.cpp
+++ b/services/surfaceflinger/Scheduler/LayerHistory.cpp
@@ -39,7 +39,7 @@
namespace {
bool isLayerActive(const Layer& layer, const LayerInfo& info, nsecs_t threshold) {
- return layer.isVisible() && (info.isHDR() || info.getLastUpdatedTime() >= threshold);
+ return layer.isVisible() && info.getLastUpdatedTime() >= threshold;
}
bool traceEnabled() {
@@ -69,7 +69,7 @@
mLayerInfos.emplace_back(layer, std::move(info));
}
-void LayerHistory::record(Layer* layer, nsecs_t presentTime, bool isHDR, nsecs_t now) {
+void LayerHistory::record(Layer* layer, nsecs_t presentTime, nsecs_t now) {
std::lock_guard lock(mLock);
const auto it = std::find_if(mLayerInfos.begin(), mLayerInfos.end(),
@@ -78,7 +78,6 @@
const auto& info = it->second;
info->setLastPresentTime(presentTime, now);
- info->setIsHDR(isHDR);
// Activate layer if inactive.
if (const auto end = activeLayers().end(); it >= end) {
@@ -89,7 +88,6 @@
LayerHistory::Summary LayerHistory::summarize(nsecs_t now) {
float maxRefreshRate = 0;
- bool isHDR = false;
std::lock_guard lock(mLock);
@@ -108,13 +106,12 @@
trace(layer, std::round(refreshRate));
}
}
- isHDR |= info->isHDR();
}
if (CC_UNLIKELY(mTraceEnabled)) {
- ALOGD("%s: maxRefreshRate=%.2f, isHDR=%d", __FUNCTION__, maxRefreshRate, isHDR);
+ ALOGD("%s: maxRefreshRate=%.2f", __FUNCTION__, maxRefreshRate);
}
- return {maxRefreshRate, isHDR};
+ return {maxRefreshRate};
}
void LayerHistory::partitionLayers(nsecs_t now) {
diff --git a/services/surfaceflinger/Scheduler/LayerHistory.h b/services/surfaceflinger/Scheduler/LayerHistory.h
index bd9aca1..745c4c1 100644
--- a/services/surfaceflinger/Scheduler/LayerHistory.h
+++ b/services/surfaceflinger/Scheduler/LayerHistory.h
@@ -46,11 +46,10 @@
void registerLayer(Layer*, float lowRefreshRate, float highRefreshRate);
// Marks the layer as active, and records the given state to its history.
- void record(Layer*, nsecs_t presentTime, bool isHDR, nsecs_t now);
+ void record(Layer*, nsecs_t presentTime, nsecs_t now);
struct Summary {
float maxRefreshRate; // Maximum refresh rate among recently active layers.
- bool isHDR; // True if any recently active layer has HDR content.
};
// Rebuilds sets of active/inactive layers, and accumulates stats for active layers.
diff --git a/services/surfaceflinger/Scheduler/LayerInfo.h b/services/surfaceflinger/Scheduler/LayerInfo.h
index b86709f..cb81ca2 100644
--- a/services/surfaceflinger/Scheduler/LayerInfo.h
+++ b/services/surfaceflinger/Scheduler/LayerInfo.h
@@ -140,9 +140,6 @@
// updated time, the updated time is the present time.
void setLastPresentTime(nsecs_t lastPresentTime, nsecs_t now);
- bool isHDR() const { return mIsHDR; }
- void setIsHDR(bool isHDR) { mIsHDR = isHDR; }
-
bool isRecentlyActive(nsecs_t now) const { return mPresentTimeHistory.isRecentlyActive(now); }
bool isFrequent(nsecs_t now) const { return mPresentTimeHistory.isFrequent(now); }
@@ -167,7 +164,6 @@
nsecs_t mLastPresentTime = 0;
RefreshRateHistory mRefreshRateHistory{mHighRefreshRate};
PresentTimeHistory mPresentTimeHistory;
- bool mIsHDR = false;
};
} // namespace scheduler
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
index 6be88f8..12832a6 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp
@@ -48,16 +48,18 @@
getProperty("debug.sf.phase_offset_threshold_for_next_vsync_ns")
.value_or(std::numeric_limits<nsecs_t>::max());
- const Offsets defaultOffsets = getDefaultOffsets(thresholdForNextVsync);
- const Offsets highFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
-
- mOffsets.insert({RefreshRateType::DEFAULT, defaultOffsets});
- mOffsets.insert({RefreshRateType::PERFORMANCE, highFpsOffsets});
+ mDefaultOffsets = getDefaultOffsets(thresholdForNextVsync);
+ mHighFpsOffsets = getHighFpsOffsets(thresholdForNextVsync);
}
-PhaseOffsets::Offsets PhaseOffsets::getOffsetsForRefreshRate(
- RefreshRateType refreshRateType) const {
- return mOffsets.at(refreshRateType);
+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;
+ }
}
void PhaseOffsets::dump(std::string& result) const {
@@ -80,13 +82,13 @@
const auto earlyAppOffsetNs = getProperty("debug.sf.early_app_phase_offset_ns");
const auto earlyGlAppOffsetNs = getProperty("debug.sf.early_gl_app_phase_offset_ns");
- return {{RefreshRateType::DEFAULT, earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+ return {{earlySfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
earlyAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
- {RefreshRateType::DEFAULT, earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
+ {earlyGlSfOffsetNs.value_or(sfVsyncPhaseOffsetNs),
earlyGlAppOffsetNs.value_or(vsyncPhaseOffsetNs)},
- {RefreshRateType::DEFAULT, sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
+ {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs},
thresholdForNextVsync};
}
@@ -104,13 +106,13 @@
const auto highFpsEarlyGlAppOffsetNs =
getProperty("debug.sf.high_fps_early_gl_app_phase_offset_ns");
- return {{RefreshRateType::PERFORMANCE, highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
+ return {{highFpsEarlySfOffsetNs.value_or(highFpsLateSfOffsetNs),
highFpsEarlyAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
- {RefreshRateType::PERFORMANCE, highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
+ {highFpsEarlyGlSfOffsetNs.value_or(highFpsLateSfOffsetNs),
highFpsEarlyGlAppOffsetNs.value_or(highFpsLateAppOffsetNs)},
- {RefreshRateType::PERFORMANCE, highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
+ {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs},
thresholdForNextVsync};
}
diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.h b/services/surfaceflinger/Scheduler/PhaseOffsets.h
index 2c52432..7747f0c 100644
--- a/services/surfaceflinger/Scheduler/PhaseOffsets.h
+++ b/services/surfaceflinger/Scheduler/PhaseOffsets.h
@@ -32,7 +32,6 @@
class PhaseOffsets {
public:
using Offsets = VSyncModulator::OffsetsConfig;
- using RefreshRateType = RefreshRateConfigs::RefreshRateType;
virtual ~PhaseOffsets();
@@ -43,9 +42,9 @@
}
virtual Offsets getCurrentOffsets() const = 0;
- virtual Offsets getOffsetsForRefreshRate(RefreshRateType) const = 0;
+ virtual Offsets getOffsetsForRefreshRate(float fps) const = 0;
- virtual void setRefreshRateType(RefreshRateType) = 0;
+ virtual void setRefreshRateFps(float fps) = 0;
virtual void dump(std::string& result) const = 0;
};
@@ -57,18 +56,14 @@
PhaseOffsets();
// Returns early, early GL, and late offsets for Apps and SF for a given refresh rate.
- Offsets getOffsetsForRefreshRate(RefreshRateType) const override;
+ Offsets getOffsetsForRefreshRate(float fps) const override;
// Returns early, early GL, and late offsets for Apps and SF.
- Offsets getCurrentOffsets() const override {
- return getOffsetsForRefreshRate(mRefreshRateType);
- }
+ 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 setRefreshRateType(RefreshRateType refreshRateType) override {
- mRefreshRateType = refreshRateType;
- }
+ void setRefreshRateFps(float fps) override { mRefreshRateFps = fps; }
// Returns current offsets in human friendly format.
void dump(std::string& result) const override;
@@ -77,9 +72,10 @@
static Offsets getDefaultOffsets(nsecs_t thresholdForNextVsync);
static Offsets getHighFpsOffsets(nsecs_t thresholdForNextVsync);
- std::atomic<RefreshRateType> mRefreshRateType = RefreshRateType::DEFAULT;
+ std::atomic<float> mRefreshRateFps = 0;
- std::unordered_map<RefreshRateType, Offsets> mOffsets;
+ Offsets mDefaultOffsets;
+ Offsets mHighFpsOffsets;
};
} // namespace impl
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
new file mode 100644
index 0000000..23fb96a
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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_NDEBUG 0
+#include "RefreshRateConfigs.h"
+
+namespace android::scheduler {
+
+using AllRefreshRatesMapType = RefreshRateConfigs::AllRefreshRatesMapType;
+using RefreshRate = RefreshRateConfigs::RefreshRate;
+
+// Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
+// from multiple threads. This can only be called if refreshRateSwitching() returns true.
+// TODO(b/122916473): Get this information from configs prepared by vendors, instead of
+// baking them in.
+const RefreshRate& RefreshRateConfigs::getRefreshRateForContent(float contentFramerate) const {
+ std::lock_guard lock(mLock);
+ // Find the appropriate refresh rate with minimal error
+ auto iter = min_element(mAvailableRefreshRates.cbegin(), mAvailableRefreshRates.cend(),
+ [contentFramerate](const auto& lhs, const auto& rhs) -> bool {
+ return std::abs(lhs->fps - contentFramerate) <
+ std::abs(rhs->fps - contentFramerate);
+ });
+
+ // Some content aligns better on higher refresh rate. For example for 45fps we should choose
+ // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
+ // align well with both
+ const RefreshRate* bestSoFar = *iter;
+ constexpr float MARGIN = 0.05f;
+ float ratio = (*iter)->fps / contentFramerate;
+ if (std::abs(std::round(ratio) - ratio) > MARGIN) {
+ while (iter != mAvailableRefreshRates.cend()) {
+ ratio = (*iter)->fps / contentFramerate;
+
+ if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
+ bestSoFar = *iter;
+ break;
+ }
+ ++iter;
+ }
+ }
+
+ return *bestSoFar;
+}
+
+const AllRefreshRatesMapType& RefreshRateConfigs::getAllRefreshRates() const {
+ return mRefreshRates;
+}
+
+const RefreshRate& RefreshRateConfigs::getMinRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.front();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getMaxRefreshRateByPolicy() const {
+ std::lock_guard lock(mLock);
+ if (!mRefreshRateSwitching) {
+ return *mCurrentRefreshRate;
+ } else {
+ return *mAvailableRefreshRates.back();
+ }
+}
+
+const RefreshRate& RefreshRateConfigs::getCurrentRefreshRate() const {
+ std::lock_guard lock(mLock);
+ return *mCurrentRefreshRate;
+}
+
+void RefreshRateConfigs::setCurrentConfigId(HwcConfigIndexType configId) {
+ std::lock_guard lock(mLock);
+ mCurrentRefreshRate = &mRefreshRates.at(configId);
+}
+
+RefreshRateConfigs::RefreshRateConfigs(bool refreshRateSwitching,
+ const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig)
+ : mRefreshRateSwitching(refreshRateSwitching) {
+ init(configs, currentHwcConfig);
+}
+
+RefreshRateConfigs::RefreshRateConfigs(
+ bool refreshRateSwitching,
+ const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
+ HwcConfigIndexType currentConfigId)
+ : mRefreshRateSwitching(refreshRateSwitching) {
+ std::vector<InputConfig> inputConfigs;
+ for (auto configId = HwcConfigIndexType(0); configId < HwcConfigIndexType(configs.size());
+ ++configId) {
+ auto configGroup = HwcConfigGroupType(configs[configId.value()]->getConfigGroup());
+ inputConfigs.push_back(
+ {configId, configGroup, configs[configId.value()]->getVsyncPeriod()});
+ }
+ init(inputConfigs, currentConfigId);
+}
+
+void RefreshRateConfigs::setPolicy(HwcConfigIndexType defaultConfigId, float minRefreshRate,
+ float maxRefreshRate) {
+ std::lock_guard lock(mLock);
+ mCurrentGroupId = mRefreshRates.at(defaultConfigId).configGroup;
+ mMinRefreshRateFps = minRefreshRate;
+ mMaxRefreshRateFps = maxRefreshRate;
+ constructAvailableRefreshRates();
+}
+
+void RefreshRateConfigs::getSortedRefreshRateList(
+ const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+ std::vector<const RefreshRate*>* outRefreshRates) {
+ outRefreshRates->clear();
+ outRefreshRates->reserve(mRefreshRates.size());
+ for (const auto& [type, refreshRate] : mRefreshRates) {
+ if (shouldAddRefreshRate(refreshRate)) {
+ ALOGV("getSortedRefreshRateList: config %d added to list policy",
+ refreshRate.configId.value());
+ outRefreshRates->push_back(&refreshRate);
+ }
+ }
+
+ std::sort(outRefreshRates->begin(), outRefreshRates->end(),
+ [](const auto refreshRate1, const auto refreshRate2) {
+ return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
+ });
+}
+
+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);
+ getSortedRefreshRateList(
+ [this](const RefreshRate& refreshRate) REQUIRES(mLock) {
+ return refreshRate.configGroup == mCurrentGroupId &&
+ refreshRate.fps >= mMinRefreshRateFps &&
+ refreshRate.fps <= mMaxRefreshRateFps;
+ },
+ &mAvailableRefreshRates);
+}
+
+// NO_THREAD_SAFETY_ANALYSIS since this is called from the constructor
+void RefreshRateConfigs::init(const std::vector<InputConfig>& configs,
+ HwcConfigIndexType currentHwcConfig) NO_THREAD_SAFETY_ANALYSIS {
+ LOG_ALWAYS_FATAL_IF(configs.empty());
+ LOG_ALWAYS_FATAL_IF(currentHwcConfig.value() >= configs.size());
+
+ auto buildRefreshRate = [&](InputConfig config) -> RefreshRate {
+ const float fps = 1e9f / config.vsyncPeriod;
+ return RefreshRate(config.configId, config.vsyncPeriod, config.configGroup,
+ base::StringPrintf("%2.ffps", fps), fps);
+ };
+
+ for (const auto& config : configs) {
+ 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);
+ mMinSupportedRefreshRate = sortedConfigs.front();
+ mMaxSupportedRefreshRate = sortedConfigs.back();
+ constructAvailableRefreshRates();
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
index 2fd100f..fb14dc7 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h
@@ -23,7 +23,9 @@
#include <type_traits>
#include "DisplayHardware/HWComposer.h"
+#include "HwcStrongTypes.h"
#include "Scheduler/SchedulerUtils.h"
+#include "Scheduler/StrongTyping.h"
namespace android::scheduler {
@@ -41,172 +43,123 @@
*/
class RefreshRateConfigs {
public:
- // Enum to indicate which vsync rate to run at. Default is the old 60Hz, and performance
- // is the new 90Hz. Eventually we want to have a way for vendors to map these in the configs.
- enum class RefreshRateType { DEFAULT, PERFORMANCE };
-
struct RefreshRate {
+ RefreshRate(HwcConfigIndexType configId, nsecs_t vsyncPeriod,
+ HwcConfigGroupType configGroup, std::string name, float fps)
+ : configId(configId),
+ vsyncPeriod(vsyncPeriod),
+ configGroup(configGroup),
+ name(std::move(name)),
+ fps(fps) {}
// This config ID corresponds to the position of the config in the vector that is stored
// on the device.
- int configId;
- // Human readable name of the refresh rate.
- std::string name;
- // Refresh rate in frames per second, rounded to the nearest integer.
- uint32_t fps = 0;
+ const HwcConfigIndexType configId;
// Vsync period in nanoseconds.
- nsecs_t vsyncPeriod;
- // Hwc config Id (returned from HWC2::Display::Config::getId())
- hwc2_config_t hwcId;
+ const nsecs_t vsyncPeriod;
+ // This configGroup for the config.
+ const HwcConfigGroupType configGroup;
+ // Human readable name of the refresh rate.
+ const std::string name;
+ // Refresh rate in frames per second
+ const float fps = 0;
+
+ bool operator!=(const RefreshRate& other) const {
+ return configId != other.configId || vsyncPeriod != other.vsyncPeriod ||
+ configGroup != other.configGroup;
+ }
+
+ bool operator==(const RefreshRate& other) const { return !(*this != other); }
};
+ 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);
+
// Returns true if this device is doing refresh rate switching. This won't change at runtime.
- bool refreshRateSwitchingSupported() const { return mRefreshRateSwitchingSupported; }
+ bool refreshRateSwitchingSupported() const { return mRefreshRateSwitching; }
- // Returns the refresh rate map. This map won't be modified at runtime, so it's safe to access
- // from multiple threads. This can only be called if refreshRateSwitching() returns true.
- // TODO(b/122916473): Get this information from configs prepared by vendors, instead of
- // baking them in.
- const std::map<RefreshRateType, RefreshRate>& getRefreshRateMap() const {
- LOG_ALWAYS_FATAL_IF(!mRefreshRateSwitchingSupported);
- return mRefreshRateMap;
- }
+ // Returns all available refresh rates according to the current policy.
+ const RefreshRate& getRefreshRateForContent(float contentFramerate) const EXCLUDES(mLock);
- const RefreshRate& getRefreshRateFromType(RefreshRateType type) const {
- if (!mRefreshRateSwitchingSupported) {
- return getCurrentRefreshRate().second;
- } else {
- auto refreshRate = mRefreshRateMap.find(type);
- LOG_ALWAYS_FATAL_IF(refreshRate == mRefreshRateMap.end());
- return refreshRate->second;
- }
- }
+ // Returns all the refresh rates supported by the device. This won't change at runtime.
+ const AllRefreshRatesMapType& getAllRefreshRates() const EXCLUDES(mLock);
- std::pair<RefreshRateType, const RefreshRate&> getCurrentRefreshRate() const {
- int currentConfig = mCurrentConfig;
- if (mRefreshRateSwitchingSupported) {
- for (const auto& [type, refresh] : mRefreshRateMap) {
- if (refresh.configId == currentConfig) {
- return {type, refresh};
- }
- }
- LOG_ALWAYS_FATAL();
- }
- return {RefreshRateType::DEFAULT, mRefreshRates[currentConfig]};
- }
+ // Returns the lowest refresh rate supported by the device. This won't change at runtime.
+ const RefreshRate& getMinRefreshRate() const { return *mMinSupportedRefreshRate; }
- const RefreshRate& getRefreshRateFromConfigId(int configId) const {
- LOG_ALWAYS_FATAL_IF(configId >= mRefreshRates.size());
- return mRefreshRates[configId];
- }
+ // Returns the lowest refresh rate according to the current policy. May change in runtime.
+ const RefreshRate& getMinRefreshRateByPolicy() const EXCLUDES(mLock);
- RefreshRateType getRefreshRateTypeFromHwcConfigId(hwc2_config_t hwcId) const {
- if (!mRefreshRateSwitchingSupported) return RefreshRateType::DEFAULT;
+ // Returns the highest refresh rate supported by the device. This won't change at runtime.
+ const RefreshRate& getMaxRefreshRate() const { return *mMaxSupportedRefreshRate; }
- for (const auto& [type, refreshRate] : mRefreshRateMap) {
- if (refreshRate.hwcId == hwcId) {
- return type;
- }
- }
+ // Returns the highest refresh rate according to the current policy. May change in runtime.
+ const RefreshRate& getMaxRefreshRateByPolicy() const EXCLUDES(mLock);
- return RefreshRateType::DEFAULT;
- }
+ // Returns the current refresh rate
+ const RefreshRate& getCurrentRefreshRate() const EXCLUDES(mLock);
- void setCurrentConfig(int config) {
- LOG_ALWAYS_FATAL_IF(config >= mRefreshRates.size());
- mCurrentConfig = config;
- }
+ // Returns the refresh rate that corresponds to a HwcConfigIndexType. This won't change at
+ // runtime.
+ const RefreshRate& getRefreshRateFromConfigId(HwcConfigIndexType configId) const {
+ return mRefreshRates.at(configId);
+ };
+
+ // Stores the current configId the device operates at
+ void setCurrentConfigId(HwcConfigIndexType configId) EXCLUDES(mLock);
struct InputConfig {
- hwc2_config_t hwcId = 0;
+ HwcConfigIndexType configId = HwcConfigIndexType(0);
+ HwcConfigGroupType configGroup = HwcConfigGroupType(0);
nsecs_t vsyncPeriod = 0;
};
RefreshRateConfigs(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig) {
- init(refreshRateSwitching, configs, currentConfig);
- }
-
+ HwcConfigIndexType currentHwcConfig);
RefreshRateConfigs(bool refreshRateSwitching,
const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs,
- int currentConfig) {
- std::vector<InputConfig> inputConfigs;
- for (const auto& config : configs) {
- inputConfigs.push_back({config->getId(), config->getVsyncPeriod()});
- }
- init(refreshRateSwitching, inputConfigs, currentConfig);
- }
+ HwcConfigIndexType currentConfigId);
private:
- void init(bool refreshRateSwitching, const std::vector<InputConfig>& configs,
- int currentConfig) {
- mRefreshRateSwitchingSupported = refreshRateSwitching;
- LOG_ALWAYS_FATAL_IF(configs.empty());
- LOG_ALWAYS_FATAL_IF(currentConfig >= configs.size());
- mCurrentConfig = currentConfig;
+ void init(const std::vector<InputConfig>& configs, HwcConfigIndexType currentHwcConfig);
- auto buildRefreshRate = [&](int configId) -> RefreshRate {
- const nsecs_t vsyncPeriod = configs[configId].vsyncPeriod;
- const float fps = 1e9 / vsyncPeriod;
- return {configId, base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps),
- vsyncPeriod, configs[configId].hwcId};
- };
+ void constructAvailableRefreshRates() REQUIRES(mLock);
- for (int i = 0; i < configs.size(); ++i) {
- mRefreshRates.push_back(buildRefreshRate(i));
- }
+ void getSortedRefreshRateList(
+ const std::function<bool(const RefreshRate&)>& shouldAddRefreshRate,
+ std::vector<const RefreshRate*>* outRefreshRates);
- if (!mRefreshRateSwitchingSupported) return;
-
- auto findDefaultAndPerfConfigs = [&]() -> std::optional<std::pair<int, int>> {
- if (configs.size() < 2) {
- return {};
- }
-
- std::vector<const RefreshRate*> sortedRefreshRates;
- for (const auto& refreshRate : mRefreshRates) {
- sortedRefreshRates.push_back(&refreshRate);
- }
- std::sort(sortedRefreshRates.begin(), sortedRefreshRates.end(),
- [](const RefreshRate* refreshRate1, const RefreshRate* refreshRate2) {
- return refreshRate1->vsyncPeriod > refreshRate2->vsyncPeriod;
- });
-
- // When the configs are ordered by the resync rate, we assume that
- // the first one is DEFAULT and the second one is PERFORMANCE,
- // i.e. the higher rate.
- if (sortedRefreshRates[0]->vsyncPeriod == 0 ||
- sortedRefreshRates[1]->vsyncPeriod == 0) {
- return {};
- }
-
- return std::pair<int, int>(sortedRefreshRates[0]->configId,
- sortedRefreshRates[1]->configId);
- };
-
- auto defaultAndPerfConfigs = findDefaultAndPerfConfigs();
- if (!defaultAndPerfConfigs) {
- mRefreshRateSwitchingSupported = false;
- return;
- }
-
- mRefreshRateMap[RefreshRateType::DEFAULT] = mRefreshRates[defaultAndPerfConfigs->first];
- mRefreshRateMap[RefreshRateType::PERFORMANCE] =
- mRefreshRates[defaultAndPerfConfigs->second];
- }
-
- // Whether this device is doing refresh rate switching or not. This must not change after this
- // object is initialized.
- bool mRefreshRateSwitchingSupported;
// The list of refresh rates, indexed by display config ID. This must not change after this
// object is initialized.
- std::vector<RefreshRate> mRefreshRates;
- // The mapping of refresh rate type to RefreshRate. This must not change after this object is
- // initialized.
- std::map<RefreshRateType, RefreshRate> mRefreshRateMap;
- // The ID of the current 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, so it's
- // atomic.
- std::atomic<int> mCurrentConfig;
+ AllRefreshRatesMapType mRefreshRates;
+
+ // The list of refresh rates which are available in the current policy, ordered by vsyncPeriod
+ // (the first element is the lowest refresh rate)
+ std::vector<const RefreshRate*> mAvailableRefreshRates GUARDED_BY(mLock);
+
+ // The current 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.
+ const RefreshRate* mCurrentRefreshRate GUARDED_BY(mLock);
+
+ // The current config group. 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);
+
+ // The min and max FPS allowed by the policy. This will change at runtime and set by
+ // SurfaceFlinger on the main thread.
+ float mMinRefreshRateFps GUARDED_BY(mLock) = 0;
+ float mMaxRefreshRateFps GUARDED_BY(mLock) = std::numeric_limits<float>::max();
+
+ // The min and max refresh rates supported by the device.
+ // This will not change at runtime.
+ const RefreshRate* mMinSupportedRefreshRate;
+ const RefreshRate* mMaxSupportedRefreshRate;
+
+ const bool mRefreshRateSwitching;
+
+ mutable std::mutex mLock;
};
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h
index 8afc93e..a384dbe 100644
--- a/services/surfaceflinger/Scheduler/RefreshRateStats.h
+++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h
@@ -25,8 +25,7 @@
#include "android-base/stringprintf.h"
#include "utils/Timers.h"
-namespace android {
-namespace scheduler {
+namespace android::scheduler {
/**
* Class to encapsulate statistics about refresh rates that the display is using. When the power
@@ -42,10 +41,10 @@
public:
RefreshRateStats(const RefreshRateConfigs& refreshRateConfigs, TimeStats& timeStats,
- int currentConfigMode, int currentPowerMode)
+ HwcConfigIndexType currentConfigId, int currentPowerMode)
: mRefreshRateConfigs(refreshRateConfigs),
mTimeStats(timeStats),
- mCurrentConfigMode(currentConfigMode),
+ mCurrentConfigMode(currentConfigId),
mCurrentPowerMode(currentPowerMode) {}
// Sets power mode.
@@ -59,12 +58,12 @@
// Sets config mode. If the mode has changed, it records how much time was spent in the previous
// mode.
- void setConfigMode(int mode) {
- if (mCurrentConfigMode == mode) {
+ void setConfigMode(HwcConfigIndexType configId) {
+ if (mCurrentConfigMode == configId) {
return;
}
flushTime();
- mCurrentConfigMode = mode;
+ mCurrentConfigMode = configId;
}
// Returns a map between human readable refresh rate and number of seconds the device spent in
@@ -78,11 +77,11 @@
std::unordered_map<std::string, int64_t> totalTime;
// Multiple configs may map to the same name, e.g. "60fps". Add the
// times for such configs together.
- for (const auto& [config, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] = 0;
+ for (const auto& [configId, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] = 0;
}
- for (const auto& [config, time] : mConfigModesTotalTime) {
- totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(config).name] += time;
+ for (const auto& [configId, time] : mConfigModesTotalTime) {
+ totalTime[mRefreshRateConfigs.getRefreshRateFromConfigId(configId).name] += time;
}
totalTime["ScreenOff"] = mScreenOffTime;
return totalTime;
@@ -139,14 +138,14 @@
// Aggregate refresh rate statistics for telemetry.
TimeStats& mTimeStats;
- int mCurrentConfigMode;
+ HwcConfigIndexType mCurrentConfigMode;
int32_t mCurrentPowerMode;
- std::unordered_map<int /* config */, int64_t /* duration in ms */> mConfigModesTotalTime;
+ std::unordered_map<HwcConfigIndexType /* configId */, int64_t /* duration in ms */>
+ mConfigModesTotalTime;
int64_t mScreenOffTime = 0;
nsecs_t mPreviousRecordedTime = systemTime();
};
-} // namespace scheduler
-} // namespace android
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index 55fd603..1d50fe1 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -182,7 +182,7 @@
}
void Scheduler::onConfigChanged(ConnectionHandle handle, PhysicalDisplayId displayId,
- int32_t configId) {
+ HwcConfigIndexType configId) {
RETURN_IF_INVALID_HANDLE(handle);
mConnections[handle].thread->onConfigChanged(displayId, configId);
}
@@ -280,8 +280,7 @@
const nsecs_t last = mLastResyncTime.exchange(now);
if (now - last > kIgnoreDelay) {
- resyncToHardwareVsync(false,
- mRefreshRateConfigs.getCurrentRefreshRate().second.vsyncPeriod);
+ resyncToHardwareVsync(false, mRefreshRateConfigs.getCurrentRefreshRate().vsyncPeriod);
}
}
@@ -332,53 +331,49 @@
void Scheduler::registerLayer(Layer* layer) {
if (!mLayerHistory) return;
- const auto type = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
- ? RefreshRateType::DEFAULT
- : RefreshRateType::PERFORMANCE;
-
- const auto lowFps = mRefreshRateConfigs.getRefreshRateFromType(RefreshRateType::DEFAULT).fps;
- const auto highFps = mRefreshRateConfigs.getRefreshRateFromType(type).fps;
+ const auto lowFps = mRefreshRateConfigs.getMinRefreshRate().fps;
+ const auto highFps = layer->getWindowType() == InputWindowInfo::TYPE_WALLPAPER
+ ? lowFps
+ : mRefreshRateConfigs.getMaxRefreshRate().fps;
mLayerHistory->registerLayer(layer, lowFps, highFps);
}
-void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime, bool isHDR) {
+void Scheduler::recordLayerHistory(Layer* layer, nsecs_t presentTime) {
if (mLayerHistory) {
- mLayerHistory->record(layer, presentTime, isHDR, systemTime());
+ mLayerHistory->record(layer, presentTime, systemTime());
}
}
void Scheduler::chooseRefreshRateForContent() {
if (!mLayerHistory) return;
- auto [refreshRate, isHDR] = mLayerHistory->summarize(systemTime());
+ auto [refreshRate] = mLayerHistory->summarize(systemTime());
const uint32_t refreshRateRound = std::round(refreshRate);
- RefreshRateType newRefreshRateType;
+ HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- if (mFeatures.contentRefreshRate == refreshRateRound && mFeatures.isHDRContent == isHDR) {
+ if (mFeatures.contentRefreshRate == refreshRateRound) {
return;
}
mFeatures.contentRefreshRate = refreshRateRound;
ATRACE_INT("ContentFPS", refreshRateRound);
- mFeatures.isHDRContent = isHDR;
- ATRACE_INT("ContentHDR", isHDR);
-
mFeatures.contentDetection =
refreshRateRound > 0 ? ContentDetectionState::On : ContentDetectionState::Off;
- newRefreshRateType = calculateRefreshRateType();
- if (mFeatures.refreshRateType == newRefreshRateType) {
+ newConfigId = calculateRefreshRateType();
+ if (mFeatures.configId == newConfigId) {
return;
}
- mFeatures.refreshRateType = newRefreshRateType;
- }
- changeRefreshRate(newRefreshRateType, ConfigEvent::Changed);
+ mFeatures.configId = newConfigId;
+ };
+ auto newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ changeRefreshRate(newRefreshRate, ConfigEvent::Changed);
}
-void Scheduler::setChangeRefreshRateCallback(ChangeRefreshRateCallback&& callback) {
+void Scheduler::setSchedulerCallback(android::Scheduler::ISchedulerCallback* callback) {
std::lock_guard<std::mutex> lock(mCallbackLock);
- mChangeRefreshRateCallback = std::move(callback);
+ mSchedulerCallback = callback;
}
void Scheduler::resetIdleTimer() {
@@ -423,13 +418,16 @@
void Scheduler::kernelIdleTimerCallback(TimerState state) {
ATRACE_INT("ExpiredKernelIdleTimer", static_cast<int>(state));
+ // TODO(145561154): cleanup the kernel idle timer implementation and the refresh rate
+ // magic number
const auto refreshRate = mRefreshRateConfigs.getCurrentRefreshRate();
- if (state == TimerState::Reset && refreshRate.first == RefreshRateType::PERFORMANCE) {
+ constexpr float FPS_THRESHOLD_FOR_KERNEL_TIMER = 65.0f;
+ if (state == TimerState::Reset && refreshRate.fps > FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// If we're not in performance mode then the kernel timer shouldn't do
// anything, as the refresh rate during DPU power collapse will be the
// same.
- resyncToHardwareVsync(true /* makeAvailable */, refreshRate.second.vsyncPeriod);
- } else if (state == TimerState::Expired && refreshRate.first != RefreshRateType::PERFORMANCE) {
+ resyncToHardwareVsync(true /* makeAvailable */, refreshRate.vsyncPeriod);
+ } else if (state == TimerState::Expired && refreshRate.fps <= FPS_THRESHOLD_FOR_KERNEL_TIMER) {
// Disable HW VSYNC if the timer expired, as we don't need it enabled if
// we're not pushing frames, and if we're in PERFORMANCE mode then we'll
// need to update the DispSync model anyway.
@@ -471,96 +469,67 @@
template <class T>
void Scheduler::handleTimerStateChanged(T* currentState, T newState, bool eventOnContentDetection) {
ConfigEvent event = ConfigEvent::None;
- RefreshRateType newRefreshRateType;
+ HwcConfigIndexType newConfigId;
{
std::lock_guard<std::mutex> lock(mFeatureStateLock);
if (*currentState == newState) {
return;
}
*currentState = newState;
- newRefreshRateType = calculateRefreshRateType();
- if (mFeatures.refreshRateType == newRefreshRateType) {
+ newConfigId = calculateRefreshRateType();
+ if (mFeatures.configId == newConfigId) {
return;
}
- mFeatures.refreshRateType = newRefreshRateType;
+ mFeatures.configId = newConfigId;
if (eventOnContentDetection && mFeatures.contentDetection == ContentDetectionState::On) {
event = ConfigEvent::Changed;
}
}
- changeRefreshRate(newRefreshRateType, event);
+ const RefreshRate& newRefreshRate = mRefreshRateConfigs.getRefreshRateFromConfigId(newConfigId);
+ changeRefreshRate(newRefreshRate, event);
}
-Scheduler::RefreshRateType Scheduler::calculateRefreshRateType() {
+HwcConfigIndexType Scheduler::calculateRefreshRateType() {
if (!mRefreshRateConfigs.refreshRateSwitchingSupported()) {
- return RefreshRateType::DEFAULT;
- }
-
- // HDR content is not supported on PERFORMANCE mode
- if (mForceHDRContentToDefaultRefreshRate && mFeatures.isHDRContent) {
- return RefreshRateType::DEFAULT;
+ return mRefreshRateConfigs.getCurrentRefreshRate().configId;
}
// If Display Power is not in normal operation we want to be in performance mode.
// When coming back to normal mode, a grace period is given with DisplayPowerTimer
if (!mFeatures.isDisplayPowerStateNormal || mFeatures.displayPowerTimer == TimerState::Reset) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// As long as touch is active we want to be in performance mode
if (mFeatures.touch == TouchState::Active) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// If timer has expired as it means there is no new content on the screen
if (mFeatures.idleTimer == TimerState::Expired) {
- return RefreshRateType::DEFAULT;
+ return mRefreshRateConfigs.getMinRefreshRateByPolicy().configId;
}
// If content detection is off we choose performance as we don't know the content fps
if (mFeatures.contentDetection == ContentDetectionState::Off) {
- return RefreshRateType::PERFORMANCE;
+ return mRefreshRateConfigs.getMaxRefreshRateByPolicy().configId;
}
// Content detection is on, find the appropriate refresh rate with minimal error
- // TODO(b/139751853): Scan allowed refresh rates only (SurfaceFlinger::mAllowedDisplayConfigs)
- const float rate = static_cast<float>(mFeatures.contentRefreshRate);
- auto iter = min_element(mRefreshRateConfigs.getRefreshRateMap().cbegin(),
- mRefreshRateConfigs.getRefreshRateMap().cend(),
- [rate](const auto& lhs, const auto& rhs) -> bool {
- return std::abs(lhs.second.fps - rate) <
- std::abs(rhs.second.fps - rate);
- });
- RefreshRateType currRefreshRateType = iter->first;
-
- // Some content aligns better on higher refresh rate. For example for 45fps we should choose
- // 90Hz config. However we should still prefer a lower refresh rate if the content doesn't
- // align well with both
- constexpr float MARGIN = 0.05f;
- float ratio = mRefreshRateConfigs.getRefreshRateFromType(currRefreshRateType).fps / rate;
- if (std::abs(std::round(ratio) - ratio) > MARGIN) {
- while (iter != mRefreshRateConfigs.getRefreshRateMap().cend()) {
- ratio = iter->second.fps / rate;
-
- if (std::abs(std::round(ratio) - ratio) <= MARGIN) {
- currRefreshRateType = iter->first;
- break;
- }
- ++iter;
- }
- }
-
- return currRefreshRateType;
+ return mRefreshRateConfigs
+ .getRefreshRateForContent(static_cast<float>(mFeatures.contentRefreshRate))
+ .configId;
}
-Scheduler::RefreshRateType Scheduler::getPreferredRefreshRateType() {
+std::optional<HwcConfigIndexType> Scheduler::getPreferredConfigId() {
std::lock_guard<std::mutex> lock(mFeatureStateLock);
- return mFeatures.refreshRateType;
+ return mFeatures.configId;
}
-void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
+void Scheduler::changeRefreshRate(const RefreshRate& refreshRate, ConfigEvent configEvent) {
std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mChangeRefreshRateCallback) {
- mChangeRefreshRateCallback(refreshRateType, configEvent);
+ if (mSchedulerCallback) {
+ mSchedulerCallback->changeRefreshRate(refreshRate, configEvent);
}
}
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index 346896c..04a8390 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -34,6 +34,8 @@
namespace android {
+using namespace std::chrono_literals;
+
class DispSync;
class FenceTime;
class InjectVSyncSource;
@@ -41,10 +43,14 @@
class Scheduler {
public:
- using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
using ConfigEvent = scheduler::RefreshRateConfigEvent;
- using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>;
+ 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 };
@@ -67,7 +73,7 @@
sp<EventThreadConnection> getEventConnection(ConnectionHandle);
void onHotplugReceived(ConnectionHandle, PhysicalDisplayId, bool connected);
- void onConfigChanged(ConnectionHandle, PhysicalDisplayId, int32_t configId);
+ void onConfigChanged(ConnectionHandle, PhysicalDisplayId, HwcConfigIndexType configId);
void onScreenAcquired(ConnectionHandle);
void onScreenReleased(ConnectionHandle);
@@ -103,13 +109,13 @@
// Layers are registered on creation, and unregistered when the weak reference expires.
void registerLayer(Layer*);
- void recordLayerHistory(Layer*, nsecs_t presentTime, bool isHDR);
+ void recordLayerHistory(Layer*, nsecs_t presentTime);
// Detects content using layer history, and selects a matching refresh rate.
void chooseRefreshRateForContent();
- // Called by Scheduler to change refresh rate.
- void setChangeRefreshRateCallback(ChangeRefreshRateCallback&&);
+ // Called by Scheduler to control SurfaceFlinger operations.
+ void setSchedulerCallback(ISchedulerCallback*);
bool isIdleTimerEnabled() const { return mIdleTimer.has_value(); }
void resetIdleTimer();
@@ -122,8 +128,8 @@
void dump(std::string&) const;
void dump(ConnectionHandle, std::string&) const;
- // Get the appropriate refresh type for current conditions.
- RefreshRateType getPreferredRefreshRateType();
+ // Get the appropriate refresh for current conditions.
+ std::optional<HwcConfigIndexType> getPreferredConfigId();
private:
friend class TestableScheduler;
@@ -158,9 +164,9 @@
void setVsyncPeriod(nsecs_t period);
- RefreshRateType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
+ HwcConfigIndexType calculateRefreshRateType() REQUIRES(mFeatureStateLock);
// Acquires a lock and calls the ChangeRefreshRateCallback with given parameters.
- void changeRefreshRate(RefreshRateType, ConfigEvent);
+ void changeRefreshRate(const RefreshRate&, ConfigEvent);
// Stores EventThread associated with a given VSyncSource, and an initial EventThreadConnection.
struct Connection {
@@ -198,7 +204,7 @@
std::optional<scheduler::OneShotTimer> mDisplayPowerTimer;
std::mutex mCallbackLock;
- ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+ ISchedulerCallback* mSchedulerCallback GUARDED_BY(mCallbackLock) = nullptr;
// 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.
@@ -210,17 +216,13 @@
TouchState touch = TouchState::Inactive;
TimerState displayPowerTimer = TimerState::Expired;
- RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ std::optional<HwcConfigIndexType> configId;
uint32_t contentRefreshRate = 0;
- bool isHDRContent = false;
bool isDisplayPowerStateNormal = true;
} mFeatures GUARDED_BY(mFeatureStateLock);
const scheduler::RefreshRateConfigs& mRefreshRateConfigs;
-
- // Global config to force HDR content to work on DEFAULT refreshRate
- static constexpr bool mForceHDRContentToDefaultRefreshRate = false;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/StrongTyping.h b/services/surfaceflinger/Scheduler/StrongTyping.h
index 02db022..e8ca0ba 100644
--- a/services/surfaceflinger/Scheduler/StrongTyping.h
+++ b/services/surfaceflinger/Scheduler/StrongTyping.h
@@ -51,13 +51,22 @@
inline bool operator>(T const& other) const { return !(*this < other || *this == other); }
};
+template <typename T>
+struct Hash : Ability<T, Hash> {
+ [[nodiscard]] std::size_t hash() const {
+ return std::hash<typename std::remove_const<
+ typename std::remove_reference<decltype(this->base().value())>::type>::type>{}(
+ this->base().value());
+ }
+};
+
template <typename T, typename W, template <typename> class... Ability>
struct StrongTyping : Ability<StrongTyping<T, W, Ability...>>... {
StrongTyping() : mValue(0) {}
explicit StrongTyping(T const& value) : mValue(value) {}
StrongTyping(StrongTyping const&) = default;
StrongTyping& operator=(StrongTyping const&) = default;
- inline operator T() const { return mValue; }
+ explicit inline operator T() const { return mValue; }
T const& value() const { return mValue; }
T& value() { return mValue; }
@@ -65,3 +74,12 @@
T mValue;
};
} // namespace android
+
+namespace std {
+template <typename T, typename W, template <typename> class... Ability>
+struct hash<android::StrongTyping<T, W, Ability...>> {
+ std::size_t operator()(android::StrongTyping<T, W, Ability...> const& k) const {
+ return k.hash();
+ }
+};
+} // namespace std
diff --git a/services/surfaceflinger/Scheduler/TimeKeeper.h b/services/surfaceflinger/Scheduler/TimeKeeper.h
index 699cd50..38f0708 100644
--- a/services/surfaceflinger/Scheduler/TimeKeeper.h
+++ b/services/surfaceflinger/Scheduler/TimeKeeper.h
@@ -21,10 +21,24 @@
namespace android::scheduler {
+class Clock {
+public:
+ virtual ~Clock();
+ /*
+ * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
+ */
+ virtual nsecs_t now() const = 0;
+
+protected:
+ Clock() = default;
+ Clock(Clock const&) = delete;
+ Clock& operator=(Clock const&) = delete;
+};
+
/*
* TimeKeeper is the interface for a single-shot timer primitive.
*/
-class TimeKeeper {
+class TimeKeeper : public Clock {
public:
virtual ~TimeKeeper();
@@ -39,11 +53,6 @@
*/
virtual void alarmCancel() = 0;
- /*
- * Returns the SYSTEM_TIME_MONOTONIC, used by testing infra to stub time.
- */
- virtual nsecs_t now() const = 0;
-
protected:
TimeKeeper(TimeKeeper const&) = delete;
TimeKeeper& operator=(TimeKeeper const&) = delete;
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatch.h b/services/surfaceflinger/Scheduler/VSyncDispatch.h
index 4a4bef8..e001080 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatch.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatch.h
@@ -34,7 +34,7 @@
*/
class VSyncDispatch {
public:
- using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare>;
+ using CallbackToken = StrongTyping<size_t, class CallbackTokenTag, Compare, Hash>;
virtual ~VSyncDispatch();
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
index 7922484..a79fe98 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.cpp
@@ -47,22 +47,26 @@
return {mArmedInfo->mActualWakeupTime};
}
-nsecs_t VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
- VSyncTracker& tracker, nsecs_t now) {
+ScheduleResult VSyncDispatchTimerQueueEntry::schedule(nsecs_t workDuration, nsecs_t earliestVsync,
+ VSyncTracker& tracker, nsecs_t now) {
+ auto const nextVsyncTime =
+ tracker.nextAnticipatedVSyncTimeFrom(std::max(earliestVsync, now + workDuration));
+ if (mLastDispatchTime >= nextVsyncTime) { // already dispatched a callback for this vsync
+ return ScheduleResult::CannotSchedule;
+ }
+
+ auto const nextWakeupTime = nextVsyncTime - workDuration;
+ auto result = mArmedInfo ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
mWorkDuration = workDuration;
mEarliestVsync = earliestVsync;
- arm(tracker, now);
- return mArmedInfo->mActualWakeupTime;
+ mArmedInfo = {nextWakeupTime, nextVsyncTime};
+ return result;
}
void VSyncDispatchTimerQueueEntry::update(VSyncTracker& tracker, nsecs_t now) {
if (!mArmedInfo) {
return;
}
- arm(tracker, now);
-}
-
-void VSyncDispatchTimerQueueEntry::arm(VSyncTracker& tracker, nsecs_t now) {
auto const nextVsyncTime =
tracker.nextAnticipatedVSyncTimeFrom(std::max(mEarliestVsync, now + mWorkDuration));
mArmedInfo = {nextVsyncTime - mWorkDuration, nextVsyncTime};
@@ -214,16 +218,13 @@
return result;
}
auto& callback = it->second;
- result = callback->wakeupTime() ? ScheduleResult::ReScheduled : ScheduleResult::Scheduled;
-
auto const now = mTimeKeeper->now();
- auto const wakeupTime = callback->schedule(workDuration, earliestVsync, mTracker, now);
-
- if (wakeupTime < now - mTimerSlack || callback->lastExecutedVsyncTarget() > wakeupTime) {
- return ScheduleResult::CannotSchedule;
+ result = callback->schedule(workDuration, earliestVsync, mTracker, now);
+ if (result == ScheduleResult::CannotSchedule) {
+ return result;
}
- if (wakeupTime < mIntendedWakeupTime - mTimerSlack) {
+ if (callback->wakeupTime() < mIntendedWakeupTime - mTimerSlack) {
rearmTimerSkippingUpdateFor(now, it);
}
}
diff --git a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
index f058099..fc78da3 100644
--- a/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
+++ b/services/surfaceflinger/Scheduler/VSyncDispatchTimerQueue.h
@@ -44,8 +44,8 @@
std::optional<nsecs_t> lastExecutedVsyncTarget() const;
// This moves the state from disarmed->armed and will calculate the wakeupTime.
- nsecs_t schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
- nsecs_t now);
+ ScheduleResult schedule(nsecs_t workDuration, nsecs_t earliestVsync, VSyncTracker& tracker,
+ nsecs_t now);
// This will update armed entries with the latest vsync information. Entry remains armed.
void update(VSyncTracker& tracker, nsecs_t now);
@@ -67,7 +67,6 @@
void ensureNotRunning();
private:
- void arm(VSyncTracker& tracker, nsecs_t now);
std::string const mName;
std::function<void(nsecs_t)> const mCallback;
@@ -106,7 +105,8 @@
VSyncDispatchTimerQueue(VSyncDispatchTimerQueue const&) = delete;
VSyncDispatchTimerQueue& operator=(VSyncDispatchTimerQueue const&) = delete;
- using CallbackMap = std::unordered_map<size_t, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
+ using CallbackMap =
+ std::unordered_map<CallbackToken, std::shared_ptr<VSyncDispatchTimerQueueEntry>>;
void timerCallback();
void setTimer(nsecs_t, nsecs_t) REQUIRES(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.cpp b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
index 27fd76c..8de35b1 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.cpp
@@ -134,18 +134,13 @@
return;
}
- const bool isDefault = mOffsets.fpsMode == RefreshRateType::DEFAULT;
- const bool isPerformance = mOffsets.fpsMode == RefreshRateType::PERFORMANCE;
const bool isEarly = &offsets == &mOffsetsConfig.early;
const bool isEarlyGl = &offsets == &mOffsetsConfig.earlyGl;
const bool isLate = &offsets == &mOffsetsConfig.late;
- ATRACE_INT("Vsync-EarlyOffsetsOn", isDefault && isEarly);
- ATRACE_INT("Vsync-EarlyGLOffsetsOn", isDefault && isEarlyGl);
- ATRACE_INT("Vsync-LateOffsetsOn", isDefault && isLate);
- ATRACE_INT("Vsync-HighFpsEarlyOffsetsOn", isPerformance && isEarly);
- ATRACE_INT("Vsync-HighFpsEarlyGLOffsetsOn", isPerformance && isEarlyGl);
- ATRACE_INT("Vsync-HighFpsLateOffsetsOn", isPerformance && isLate);
+ ATRACE_INT("Vsync-EarlyOffsetsOn", isEarly);
+ ATRACE_INT("Vsync-EarlyGLOffsetsOn", isEarlyGl);
+ ATRACE_INT("Vsync-LateOffsetsOn", isLate);
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h
index 727cef2..63c0feb 100644
--- a/services/surfaceflinger/Scheduler/VSyncModulator.h
+++ b/services/surfaceflinger/Scheduler/VSyncModulator.h
@@ -37,13 +37,10 @@
// switch in and out of gl composition.
static constexpr int MIN_EARLY_GL_FRAME_COUNT_TRANSACTION = 2;
- using RefreshRateType = RefreshRateConfigs::RefreshRateType;
-
public:
// Wrapper for a collection of surfaceflinger/app offsets for a particular
// configuration.
struct Offsets {
- RefreshRateType fpsMode;
nsecs_t sf;
nsecs_t app;
};
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
index 643c5d2..3894992 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.cpp
@@ -54,6 +54,11 @@
return percent < kOutlierTolerancePercent || percent > (kMaxPercent - kOutlierTolerancePercent);
}
+nsecs_t VSyncPredictor::currentPeriod() const {
+ std::lock_guard<std::mutex> lk(mMutex);
+ return std::get<0>(mRateMap.find(mIdealPeriod)->second);
+}
+
void VSyncPredictor::addVsyncTimestamp(nsecs_t timestamp) {
std::lock_guard<std::mutex> lk(mMutex);
diff --git a/services/surfaceflinger/Scheduler/VSyncPredictor.h b/services/surfaceflinger/Scheduler/VSyncPredictor.h
index 1590f49..4210b3c 100644
--- a/services/surfaceflinger/Scheduler/VSyncPredictor.h
+++ b/services/surfaceflinger/Scheduler/VSyncPredictor.h
@@ -39,6 +39,7 @@
void addVsyncTimestamp(nsecs_t timestamp) final;
nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final;
+ nsecs_t currentPeriod() const final;
/*
* Inform the model that the period is anticipated to change to a new value.
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.cpp b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
new file mode 100644
index 0000000..f2a7791
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 "VSyncReactor.h"
+#include "TimeKeeper.h"
+#include "VSyncDispatch.h"
+#include "VSyncTracker.h"
+
+namespace android::scheduler {
+
+Clock::~Clock() = default;
+
+VSyncReactor::VSyncReactor(std::unique_ptr<Clock> clock, std::unique_ptr<VSyncDispatch> dispatch,
+ std::unique_ptr<VSyncTracker> tracker, size_t pendingFenceLimit)
+ : mClock(std::move(clock)),
+ mDispatch(std::move(dispatch)),
+ mTracker(std::move(tracker)),
+ mPendingLimit(pendingFenceLimit) {}
+
+bool VSyncReactor::addPresentFence(const std::shared_ptr<FenceTime>& fence) {
+ if (!fence) {
+ return false;
+ }
+
+ nsecs_t const signalTime = fence->getCachedSignalTime();
+ if (signalTime == Fence::SIGNAL_TIME_INVALID) {
+ return true;
+ }
+
+ std::lock_guard<std::mutex> lk(mMutex);
+ if (mIgnorePresentFences) {
+ return true;
+ }
+
+ for (auto it = mUnfiredFences.begin(); it != mUnfiredFences.end();) {
+ auto const time = (*it)->getCachedSignalTime();
+ if (time == Fence::SIGNAL_TIME_PENDING) {
+ it++;
+ } else if (time == Fence::SIGNAL_TIME_INVALID) {
+ it = mUnfiredFences.erase(it);
+ } else {
+ mTracker->addVsyncTimestamp(time);
+ it = mUnfiredFences.erase(it);
+ }
+ }
+
+ if (signalTime == Fence::SIGNAL_TIME_PENDING) {
+ if (mPendingLimit == mUnfiredFences.size()) {
+ mUnfiredFences.erase(mUnfiredFences.begin());
+ }
+ mUnfiredFences.push_back(fence);
+ } else {
+ mTracker->addVsyncTimestamp(signalTime);
+ }
+
+ return false; // TODO(b/144707443): add policy for turning on HWVsync.
+}
+
+void VSyncReactor::setIgnorePresentFences(bool ignoration) {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mIgnorePresentFences = ignoration;
+ if (mIgnorePresentFences == true) {
+ mUnfiredFences.clear();
+ }
+}
+
+nsecs_t VSyncReactor::computeNextRefresh(int periodOffset) const {
+ auto const now = mClock->now();
+ auto const currentPeriod = periodOffset ? mTracker->currentPeriod() : 0;
+ return mTracker->nextAnticipatedVSyncTimeFrom(now + periodOffset * currentPeriod);
+}
+
+nsecs_t VSyncReactor::expectedPresentTime() {
+ return mTracker->nextAnticipatedVSyncTimeFrom(mClock->now());
+}
+
+void VSyncReactor::setPeriod(nsecs_t period) {
+ mTracker->setPeriod(period);
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ mPeriodChangeInProgress = true;
+ }
+}
+
+nsecs_t VSyncReactor::getPeriod() {
+ return mTracker->currentPeriod();
+}
+
+void VSyncReactor::beginResync() {}
+
+void VSyncReactor::endResync() {}
+
+bool VSyncReactor::addResyncSample(nsecs_t timestamp, bool* periodFlushed) {
+ assert(periodFlushed);
+ mTracker->addVsyncTimestamp(timestamp);
+ {
+ std::lock_guard<std::mutex> lk(mMutex);
+ *periodFlushed = mPeriodChangeInProgress;
+ mPeriodChangeInProgress = false;
+ }
+ return false;
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncReactor.h b/services/surfaceflinger/Scheduler/VSyncReactor.h
new file mode 100644
index 0000000..786ee98
--- /dev/null
+++ b/services/surfaceflinger/Scheduler/VSyncReactor.h
@@ -0,0 +1,63 @@
+/*
+ * 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 <android-base/thread_annotations.h>
+#include <ui/FenceTime.h>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+namespace android::scheduler {
+
+class Clock;
+class VSyncDispatch;
+class VSyncTracker;
+
+// 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);
+
+ bool addPresentFence(const std::shared_ptr<FenceTime>& fence);
+ void setIgnorePresentFences(bool ignoration);
+
+ nsecs_t computeNextRefresh(int periodOffset) const;
+ nsecs_t expectedPresentTime();
+
+ void setPeriod(nsecs_t period);
+ nsecs_t getPeriod();
+
+ // TODO: (b/145626181) remove begin,endResync functions from DispSync i/f when possible.
+ void beginResync();
+ bool addResyncSample(nsecs_t timestamp, bool* periodFlushed);
+ void endResync();
+
+private:
+ std::unique_ptr<Clock> const mClock;
+ std::unique_ptr<VSyncDispatch> const mDispatch;
+ std::unique_ptr<VSyncTracker> const mTracker;
+ size_t const mPendingLimit;
+
+ std::mutex mMutex;
+ bool mIgnorePresentFences GUARDED_BY(mMutex) = false;
+ std::vector<std::shared_ptr<FenceTime>> mUnfiredFences GUARDED_BY(mMutex);
+ bool mPeriodChangeInProgress GUARDED_BY(mMutex) = false;
+};
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/Scheduler/VSyncTracker.h b/services/surfaceflinger/Scheduler/VSyncTracker.h
index 97b9620..6be63fe 100644
--- a/services/surfaceflinger/Scheduler/VSyncTracker.h
+++ b/services/surfaceflinger/Scheduler/VSyncTracker.h
@@ -47,6 +47,20 @@
*/
virtual nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const = 0;
+ /*
+ * The current period of the vsync signal.
+ *
+ * \return The current period of the vsync signal
+ */
+ virtual nsecs_t currentPeriod() const = 0;
+
+ /*
+ * Inform the tracker that the period is changing and the tracker needs to recalibrate itself.
+ *
+ * \param [in] period The period that the system is changing into.
+ */
+ virtual void setPeriod(nsecs_t period) = 0;
+
protected:
VSyncTracker(VSyncTracker const&) = delete;
VSyncTracker& operator=(VSyncTracker const&) = delete;
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 14a2ab1..9f4dd2b 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -542,14 +542,8 @@
if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
// set the refresh rate according to the policy
- const auto& performanceRefreshRate =
- mRefreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE);
-
- if (isDisplayConfigAllowed(performanceRefreshRate.configId)) {
- setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
- } else {
- setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
- }
+ const auto& performanceRefreshRate = mRefreshRateConfigs->getMaxRefreshRateByPolicy();
+ changeRefreshRateLocked(performanceRefreshRate, Scheduler::ConfigEvent::None);
}
}));
}
@@ -606,6 +600,7 @@
? renderengine::RenderEngine::ContextPriority::HIGH
: renderengine::RenderEngine::ContextPriority::MEDIUM)
.build()));
+ mCompositionEngine->setTimeStats(mTimeStats);
LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay,
"Starting with vr flinger active is not currently supported.");
@@ -821,9 +816,8 @@
info.xdpi = xdpi;
info.ydpi = ydpi;
info.fps = 1e9 / hwConfig->getVsyncPeriod();
- const auto refreshRateType =
- mRefreshRateConfigs->getRefreshRateTypeFromHwcConfigId(hwConfig->getId());
- const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(refreshRateType);
+
+ const auto offset = mPhaseOffsets->getOffsetsForRefreshRate(info.fps);
info.appVsyncOffset = offset.late.app;
// This is how far in advance a buffer must be queued for
@@ -873,17 +867,17 @@
if (display->isPrimary()) {
std::lock_guard<std::mutex> lock(mActiveConfigLock);
if (mDesiredActiveConfigChanged) {
- return mDesiredActiveConfig.configId;
- } else {
- return display->getActiveConfig();
+ return mDesiredActiveConfig.configId.value();
}
- } else {
- return display->getActiveConfig();
}
+
+ return display->getActiveConfig().value();
}
void SurfaceFlinger::setDesiredActiveConfig(const ActiveConfigInfo& info) {
ATRACE_CALL();
+ auto refreshRate = mRefreshRateConfigs->getRefreshRateFromConfigId(info.configId);
+ ALOGV("setDesiredActiveConfig(%s)", refreshRate.name.c_str());
// Don't check against the current mode yet. Worst case we set the desired
// config twice. However event generation config might have changed so we need to update it
@@ -902,13 +896,14 @@
// As we called to set period, we will call to onRefreshRateChangeCompleted once
// DispSync model is locked.
mVSyncModulator->onRefreshRateChangeInitiated();
- mPhaseOffsets->setRefreshRateType(info.type);
+
+ mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
}
mDesiredActiveConfigChanged = true;
if (mRefreshRateOverlay) {
- mRefreshRateOverlay->changeRefreshRate(mDesiredActiveConfig.type);
+ mRefreshRateOverlay->changeRefreshRate(refreshRate);
}
}
@@ -930,14 +925,15 @@
}
std::lock_guard<std::mutex> lock(mActiveConfigLock);
- mRefreshRateConfigs->setCurrentConfig(mUpcomingActiveConfig.configId);
+ mRefreshRateConfigs->setCurrentConfigId(mUpcomingActiveConfig.configId);
mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId);
-
display->setActiveConfig(mUpcomingActiveConfig.configId);
- mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
+ auto refreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(mUpcomingActiveConfig.configId);
+ mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
- ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
+ ATRACE_INT("ActiveConfigFPS", refreshRate.fps);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
@@ -951,12 +947,15 @@
mDesiredActiveConfigChanged = false;
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
- mPhaseOffsets->setRefreshRateType(mUpcomingActiveConfig.type);
+ auto refreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(mDesiredActiveConfig.configId);
+ mPhaseOffsets->setRefreshRateFps(refreshRate.fps);
mVSyncModulator->setPhaseOffsets(mPhaseOffsets->getCurrentOffsets());
}
bool SurfaceFlinger::performSetActiveConfig() {
ATRACE_CALL();
+ ALOGV("performSetActiveConfig");
if (mCheckPendingFence) {
if (previousFrameMissed()) {
// fence has not signaled yet. wait for the next invalidate
@@ -980,6 +979,10 @@
desiredActiveConfig = mDesiredActiveConfig;
}
+ auto refreshRate =
+ mRefreshRateConfigs->getRefreshRateFromConfigId(desiredActiveConfig.configId);
+ ALOGV("performSetActiveConfig changing active config to %d(%s)", refreshRate.configId.value(),
+ refreshRate.name.c_str());
const auto display = getDefaultDisplayDeviceLocked();
if (!display || display->getActiveConfig() == desiredActiveConfig.configId) {
// display is not valid or we are already in the requested mode
@@ -1000,8 +1003,8 @@
const auto displayId = display->getId();
LOG_ALWAYS_FATAL_IF(!displayId);
- ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId);
- getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
+ 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
mCheckPendingFence = true;
@@ -1363,9 +1366,12 @@
}
void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
- int64_t timestamp) {
+ int64_t timestamp,
+ std::optional<hwc2_vsync_period_t> /*vsyncPeriod*/) {
ATRACE_NAME("SF onVsync");
+ // TODO(b/140201379): use vsyncPeriod in the new DispSync
+
Mutex::Autolock lock(mStateLock);
// Ignore any vsyncs from a previous hardware composer.
if (sequenceId != getBE().mComposerSequenceId) {
@@ -1393,11 +1399,12 @@
*compositorTiming = getBE().mCompositorTiming;
}
-bool SurfaceFlinger::isDisplayConfigAllowed(int32_t configId) const {
+bool SurfaceFlinger::isDisplayConfigAllowed(HwcConfigIndexType configId) const {
return mAllowedDisplayConfigs.empty() || mAllowedDisplayConfigs.count(configId);
}
-void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
+void SurfaceFlinger::changeRefreshRateLocked(const RefreshRate& refreshRate,
+ Scheduler::ConfigEvent event) {
const auto display = getDefaultDisplayDeviceLocked();
if (!display || mBootStage != BootStage::FINISHED) {
return;
@@ -1405,15 +1412,13 @@
ATRACE_CALL();
// Don't do any updating if the current fps is the same as the new one.
- const auto& refreshRateConfig = mRefreshRateConfigs->getRefreshRateFromType(refreshRate);
- const int desiredConfigId = refreshRateConfig.configId;
-
- if (!isDisplayConfigAllowed(desiredConfigId)) {
- ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId);
+ if (!isDisplayConfigAllowed(refreshRate.configId)) {
+ ALOGV("Skipping config %d as it is not part of allowed configs",
+ refreshRate.configId.value());
return;
}
- setDesiredActiveConfig({refreshRate, desiredConfigId, event});
+ setDesiredActiveConfig({refreshRate.configId, event});
}
void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
@@ -1442,6 +1447,12 @@
setTransactionFlags(eDisplayTransactionNeeded);
}
+void SurfaceFlinger::onVsyncPeriodTimingChangedReceived(
+ int32_t /*sequenceId*/, hwc2_display_t /*display*/,
+ const hwc_vsync_period_change_timeline_t& /*updatedTimeline*/) {
+ // TODO(b/142753004): use timeline when changing refresh rate
+}
+
void SurfaceFlinger::onRefreshReceived(int sequenceId, hwc2_display_t /*hwcDisplayId*/) {
Mutex::Autolock lock(mStateLock);
if (sequenceId != getBE().mComposerSequenceId) {
@@ -1601,6 +1612,7 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
+ const nsecs_t frameStart = systemTime();
// calculate the expected present time once and use the cached
// value throughout this frame to make sure all layers are
// seeing this same value.
@@ -1665,6 +1677,13 @@
// Signal a refresh if a transaction modified the window state,
// a new buffer was latched, or if HWC has requested a full
// repaint
+ if (mFrameStartTime <= 0) {
+ // We should only use the time of the first invalidate
+ // message that signals a refresh as the beginning of the
+ // frame. Otherwise the real frame time will be
+ // underestimated.
+ mFrameStartTime = frameStart;
+ }
signalRefresh();
}
break;
@@ -1715,12 +1734,7 @@
refreshArgs.layersWithQueuedFrames.reserve(mLayersWithQueuedFrames.size());
for (sp<Layer> layer : mLayersWithQueuedFrames) {
auto compositionLayer = layer->getCompositionLayer();
- if (compositionLayer) {
- refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
- mFrameTracer->traceTimestamp(layer->getSequence(), layer->getCurrentBufferId(),
- layer->getCurrentFrameNumber(), systemTime(),
- FrameTracer::FrameEvent::HWC_COMPOSITION_QUEUED);
- }
+ if (compositionLayer) refreshArgs.layersWithQueuedFrames.push_back(compositionLayer.get());
}
refreshArgs.repaintEverything = mRepaintEverything.exchange(false);
@@ -1748,6 +1762,9 @@
mGeometryInvalid = false;
mCompositionEngine->present(refreshArgs);
+ mTimeStats->recordFrameDuration(mFrameStartTime, systemTime());
+ // Reset the frame start time now that we've recorded this frame.
+ mFrameStartTime = 0;
postFrame();
postComposition();
@@ -2165,7 +2182,8 @@
Dataspace::UNKNOWN});
if (!state.isVirtual()) {
LOG_ALWAYS_FATAL_IF(!displayId);
- display->setActiveConfig(getHwComposer().getActiveConfigIndex(*displayId));
+ auto activeConfigId = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(*displayId));
+ display->setActiveConfig(activeConfigId);
}
display->setLayerStack(state.layerStack);
@@ -2505,6 +2523,12 @@
mCompositionEngine->updateCursorAsync(refreshArgs);
}
+void SurfaceFlinger::changeRefreshRate(const RefreshRate& refreshRate,
+ Scheduler::ConfigEvent event) {
+ Mutex::Autolock lock(mStateLock);
+ changeRefreshRateLocked(refreshRate, event);
+}
+
void SurfaceFlinger::initScheduler(DisplayId primaryDisplayId) {
if (mScheduler) {
// In practice it's not allowed to hotplug in/out the primary display once it's been
@@ -2513,7 +2537,7 @@
return;
}
- int currentConfig = getHwComposer().getActiveConfigIndex(primaryDisplayId);
+ auto currentConfig = HwcConfigIndexType(getHwComposer().getActiveConfigIndex(primaryDisplayId));
mRefreshRateConfigs =
std::make_unique<scheduler::RefreshRateConfigs>(refresh_rate_switching(false),
getHwComposer().getConfigs(
@@ -2547,11 +2571,7 @@
new RegionSamplingThread(*this, *mScheduler,
RegionSamplingThread::EnvironmentTimingTunables());
- mScheduler->setChangeRefreshRateCallback(
- [this](RefreshRateType type, Scheduler::ConfigEvent event) {
- Mutex::Autolock lock(mStateLock);
- setRefreshRateTo(type, event);
- });
+ mScheduler->setSchedulerCallback(this);
}
void SurfaceFlinger::commitTransaction()
@@ -3114,7 +3134,13 @@
listenerCallbacks.insert(listener);
}
- sp<Layer> layer(fromHandle(s.surface));
+ sp<Layer> layer = nullptr;
+ if (s.surface) {
+ layer = fromHandle(s.surface);
+ } else {
+ // The client may provide us a null handle. Treat it as if the layer was removed.
+ ALOGW("Attempt to set client state with a null layer handle");
+ }
if (layer == nullptr) {
for (auto& [listener, callbackIds] : s.listeners) {
mTransactionCompletedThread.registerUnpresentedCallbackHandle(
@@ -3717,9 +3743,6 @@
}
if (currentMode == HWC_POWER_MODE_OFF) {
- // Turn on the display
- // TODO: @vhau temp fix only! See b/141111965
- mTransactionCompletedThread.clearAllPending();
getHwComposer().setPowerMode(*displayId, mode);
if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) {
setVsyncEnabledInHWC(*displayId, mHWCVsyncPendingState);
@@ -3951,10 +3974,17 @@
dispSyncPresentTimeOffset, getVsyncPeriod());
StringAppendF(&result, "Allowed Display Configs: ");
- for (int32_t configId : mAllowedDisplayConfigs) {
+ for (auto configId : mAllowedDisplayConfigs) {
StringAppendF(&result, "%" PRIu32 " Hz, ",
- mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps);
+ static_cast<int32_t>(
+ mRefreshRateConfigs->getRefreshRateFromConfigId(configId).fps));
}
+ StringAppendF(&result,
+ "DesiredDisplayConfigSpecs: default config ID: %" PRIu32
+ ", min: %.2f Hz, max: %.2f Hz",
+ mDesiredDisplayConfigSpecs.defaultModeId,
+ mDesiredDisplayConfigSpecs.minRefreshRate,
+ mDesiredDisplayConfigSpecs.maxRefreshRate);
StringAppendF(&result, "(config override by backdoor: %s)\n\n",
mDebugDisplayConfigSetByBackdoor ? "yes" : "no");
@@ -4384,13 +4414,16 @@
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 INJECT_VSYNC:
case SET_POWER_MODE:
case GET_DISPLAYED_CONTENT_SAMPLING_ATTRIBUTES:
case SET_DISPLAY_CONTENT_SAMPLING_ENABLED:
case GET_DISPLAYED_CONTENT_SAMPLE:
- case NOTIFY_POWER_HINT: {
+ case NOTIFY_POWER_HINT:
+ case SET_GLOBAL_SHADOW_SETTINGS: {
if (!callingThreadHasUnscopedSurfaceFlingerAccess()) {
IPCThreadState* ipc = IPCThreadState::self();
ALOGE("Permission Denial: can't access SurfaceFlinger pid=%d, uid=%d",
@@ -4793,13 +4826,9 @@
n = data.readInt32();
if (n && !mRefreshRateOverlay &&
mRefreshRateConfigs->refreshRateSwitchingSupported()) {
- RefreshRateType type;
- {
- std::lock_guard<std::mutex> lock(mActiveConfigLock);
- type = mDesiredActiveConfig.type;
- }
mRefreshRateOverlay = std::make_unique<RefreshRateOverlay>(*this);
- mRefreshRateOverlay->changeRefreshRate(type);
+ auto current = mRefreshRateConfigs->getCurrentRefreshRate();
+ mRefreshRateOverlay->changeRefreshRate(current);
} else if (!n) {
mRefreshRateOverlay.reset();
}
@@ -4903,8 +4932,12 @@
}
// Couldn't find display by displayId. Try to get display by layerStack since virtual displays
// may not have a displayId.
+ return getDisplayByLayerStack(displayOrLayerStack);
+}
+
+const sp<DisplayDevice> SurfaceFlinger::getDisplayByLayerStack(uint64_t layerStack) {
for (const auto& [token, display] : mDisplays) {
- if (display->getLayerStack() == displayOrLayerStack) {
+ if (display->getLayerStack() == layerStack) {
return display;
}
}
@@ -4960,8 +4993,8 @@
public:
LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
int32_t reqWidth, int32_t reqHeight, Dataspace reqDataSpace,
- bool childrenOnly)
- : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace),
+ bool childrenOnly, const Rect& displayViewport)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR, reqDataSpace, displayViewport),
mLayer(layer),
mCrop(crop),
mNeedsFiltering(false),
@@ -5045,7 +5078,7 @@
sp<Layer> parent;
Rect crop(sourceCrop);
std::unordered_set<sp<Layer>, ISurfaceComposer::SpHash<Layer>> excludeLayers;
-
+ Rect displayViewport;
{
Mutex::Autolock _l(mStateLock);
@@ -5062,14 +5095,21 @@
return PERMISSION_DENIED;
}
+ Rect parentSourceBounds = parent->getCroppedBufferSize(parent->getCurrentState());
if (sourceCrop.width() <= 0) {
crop.left = 0;
- crop.right = parent->getBufferSize(parent->getCurrentState()).getWidth();
+ crop.right = parentSourceBounds.getWidth();
}
if (sourceCrop.height() <= 0) {
crop.top = 0;
- crop.bottom = parent->getBufferSize(parent->getCurrentState()).getHeight();
+ crop.bottom = parentSourceBounds.getHeight();
+ }
+
+ if (crop.isEmpty() || frameScale <= 0.0f) {
+ // Error out if the layer has no source bounds (i.e. they are boundless) and a source
+ // crop was not specified, or an invalid frame scale was provided.
+ return BAD_VALUE;
}
reqWidth = crop.width() * frameScale;
reqHeight = crop.height() * frameScale;
@@ -5083,6 +5123,13 @@
return NAME_NOT_FOUND;
}
}
+
+ auto display = getDisplayByLayerStack(parent->getLayerStack());
+ if (!display) {
+ return BAD_VALUE;
+ }
+
+ displayViewport = display->getViewport();
} // mStateLock
// really small crop or frameScale
@@ -5093,7 +5140,8 @@
reqHeight = 1;
}
- LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly);
+ LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, reqDataspace, childrenOnly,
+ displayViewport);
auto traverseLayers = [parent, childrenOnly,
&excludeLayers](const LayerVector::Visitor& visitor) {
parent->traverseChildrenInZOrder(LayerVector::StateSet::Drawing, [&](Layer* layer) {
@@ -5219,6 +5267,7 @@
const auto rotation = renderArea.getRotationFlags();
const auto transform = renderArea.getTransform();
const auto sourceCrop = renderArea.getSourceCrop();
+ const auto& displayViewport = renderArea.getDisplayViewport();
renderengine::DisplaySettings clientCompositionDisplay;
std::vector<renderengine::LayerSettings> clientCompositionLayers;
@@ -5303,6 +5352,12 @@
};
auto result = layer->prepareClientComposition(targetSettings);
if (result) {
+ std::optional<renderengine::LayerSettings> shadowLayer =
+ layer->prepareShadowClientComposition(*result, displayViewport,
+ clientCompositionDisplay.outputDataspace);
+ if (shadowLayer) {
+ clientCompositionLayers.push_back(*shadowLayer);
+ }
clientCompositionLayers.push_back(*result);
}
});
@@ -5401,28 +5456,48 @@
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
display->getActiveConfig());
- if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
- const auto& type = mScheduler->getPreferredRefreshRateType();
- const auto& config = mRefreshRateConfigs->getRefreshRateFromType(type);
- if (isDisplayConfigAllowed(config.configId)) {
- ALOGV("switching to Scheduler preferred config %d", config.configId);
- setDesiredActiveConfig({type, config.configId, Scheduler::ConfigEvent::Changed});
- } else {
- // Set the highest allowed config by iterating backwards on available refresh rates
- const auto& refreshRates = mRefreshRateConfigs->getRefreshRateMap();
- for (auto iter = refreshRates.crbegin(); iter != refreshRates.crend(); ++iter) {
- if (isDisplayConfigAllowed(iter->second.configId)) {
- ALOGV("switching to allowed config %d", iter->second.configId);
- setDesiredActiveConfig(
- {iter->first, iter->second.configId, Scheduler::ConfigEvent::Changed});
- break;
- }
- }
+ // 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;
}
- } else if (!allowedConfigs.empty()) {
- ALOGV("switching to config %d", allowedConfigs[0]);
- setDesiredActiveConfig(
- {RefreshRateType::DEFAULT, allowedConfigs[0], Scheduler::ConfigEvent::Changed});
+ }
+ mRefreshRateConfigs->setPolicy(defaultModeId, minRefreshRateFps, maxRefreshRateFps);
+
+ if (mRefreshRateConfigs->refreshRateSwitchingSupported()) {
+ auto configId = mScheduler->getPreferredConfigId();
+ auto preferredRefreshRate = configId
+ ? mRefreshRateConfigs->getRefreshRateFromConfigId(*configId)
+ : mRefreshRateConfigs->getMinRefreshRateByPolicy();
+ ALOGV("trying to switch to Scheduler preferred config %d (%s)",
+ preferredRefreshRate.configId.value(), preferredRefreshRate.name.c_str());
+ if (isDisplayConfigAllowed(preferredRefreshRate.configId)) {
+ ALOGV("switching to Scheduler preferred config %d",
+ preferredRefreshRate.configId.value());
+ setDesiredActiveConfig(
+ {preferredRefreshRate.configId, Scheduler::ConfigEvent::Changed});
+ } else {
+ // Set the highest allowed config
+ setDesiredActiveConfig({mRefreshRateConfigs->getMaxRefreshRateByPolicy().configId,
+ Scheduler::ConfigEvent::Changed});
+ }
+ } else {
+ if (!allowedConfigs.empty()) {
+ ALOGV("switching to config %d", allowedConfigs[0]);
+ auto configId = HwcConfigIndexType(allowedConfigs[0]);
+ setDesiredActiveConfig({configId, Scheduler::ConfigEvent::Changed});
+ }
}
}
@@ -5471,7 +5546,70 @@
}
if (display->isPrimary()) {
- outAllowedConfigs->assign(mAllowedDisplayConfigs.begin(), mAllowedDisplayConfigs.end());
+ outAllowedConfigs->reserve(mAllowedDisplayConfigs.size());
+ for (auto configId : mAllowedDisplayConfigs) {
+ outAllowedConfigs->push_back(configId.value());
+ }
+ }
+
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::setDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t defaultModeId, float minRefreshRate,
+ float maxRefreshRate) {
+ ATRACE_CALL();
+
+ if (!displayToken) {
+ return BAD_VALUE;
+ }
+
+ postMessageSync(new LambdaMessage([&]() {
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ ALOGE("Attempt to set desired display configs for invalid display token %p",
+ displayToken.get());
+ } else if (display->isVirtual()) {
+ 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;
+ }
+ }));
+ return NO_ERROR;
+}
+
+status_t SurfaceFlinger::getDesiredDisplayConfigSpecs(const sp<IBinder>& displayToken,
+ int32_t* outDefaultModeId,
+ float* outMinRefreshRate,
+ float* outMaxRefreshRate) {
+ ATRACE_CALL();
+
+ if (!displayToken || !outDefaultModeId || !outMinRefreshRate || !outMaxRefreshRate) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mStateLock);
+ const auto display = getDisplayDeviceLocked(displayToken);
+ if (!display) {
+ return NAME_NOT_FOUND;
+ }
+
+ if (display->isPrimary()) {
+ *outDefaultModeId = mDesiredDisplayConfigSpecs.defaultModeId;
+ *outMinRefreshRate = mDesiredDisplayConfigSpecs.minRefreshRate;
+ *outMaxRefreshRate = mDesiredDisplayConfigSpecs.maxRefreshRate;
}
return NO_ERROR;
@@ -5500,6 +5638,20 @@
void SurfaceFlinger::onLayerDestroyed(Layer* layer) {
mNumLayers--;
+ removeFromOffscreenLayers(layer);
+}
+
+// WARNING: ONLY CALL THIS FROM LAYER DTOR
+// Here we add children in the current state to offscreen layers and remove the
+// layer itself from the offscreen layer list. Since
+// this is the dtor, it is safe to access the current state. This keeps us
+// from dangling children layers such that they are not reachable from the
+// Drawing state nor the offscreen layer list
+// See b/141111965
+void SurfaceFlinger::removeFromOffscreenLayers(Layer* layer) {
+ for (auto& child : layer->getCurrentChildren()) {
+ mOffscreenLayers.emplace(child.get());
+ }
mOffscreenLayers.erase(layer);
}
@@ -5507,6 +5659,22 @@
getRenderEngine().unbindExternalTextureBuffer(clientCacheId.id);
}
+status_t SurfaceFlinger::setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ,
+ float lightRadius) {
+ Mutex::Autolock _l(mStateLock);
+ mCurrentState.globalShadowSettings.ambientColor = vec4(ambientColor);
+ mCurrentState.globalShadowSettings.spotColor = vec4(spotColor);
+ mCurrentState.globalShadowSettings.lightPos.y = lightPosY;
+ mCurrentState.globalShadowSettings.lightPos.z = lightPosZ;
+ mCurrentState.globalShadowSettings.lightRadius = lightRadius;
+
+ // these values are overridden when calculating the shadow settings for a layer.
+ mCurrentState.globalShadowSettings.lightPos.x = 0.f;
+ mCurrentState.globalShadowSettings.length = 0.f;
+ return NO_ERROR;
+}
+
} // namespace android
#if defined(__gl_h_)
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 50b3ae4..ae3f2d7 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -37,6 +37,7 @@
#include <input/ISetInputWindowsListener.h>
#include <layerproto/LayerProtoHeader.h>
#include <math/mat4.h>
+#include <renderengine/LayerSettings.h>
#include <serviceutils/PriorityDumper.h>
#include <system/graphics.h>
#include <ui/FenceTime.h>
@@ -174,7 +175,8 @@
public PriorityDumper,
public ClientCache::ErasedRecipient,
private IBinder::DeathRecipient,
- private HWC2::ComposerCallback {
+ private HWC2::ComposerCallback,
+ private Scheduler::ISchedulerCallback {
public:
SurfaceFlingerBE& getBE() { return mBE; }
const SurfaceFlingerBE& getBE() const { return mBE; }
@@ -313,6 +315,8 @@
void onLayerFirstRef(Layer*);
void onLayerDestroyed(Layer*);
+ void removeFromOffscreenLayers(Layer* layer);
+
TransactionCompletedThread& getTransactionCompletedThread() {
return mTransactionCompletedThread;
}
@@ -365,6 +369,8 @@
if (colorMatrixChanged) {
colorMatrix = other.colorMatrix;
}
+ globalShadowSettings = other.globalShadowSettings;
+
return *this;
}
@@ -375,10 +381,23 @@
bool colorMatrixChanged = true;
mat4 colorMatrix;
+ renderengine::ShadowSettings globalShadowSettings;
+
void traverseInZOrder(const LayerVector::Visitor& visitor) const;
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
*/
@@ -466,11 +485,17 @@
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,
+ float* outMaxRefreshRate) override;
status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken,
bool* outSupport) const override;
status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override;
status_t notifyPowerHint(int32_t hintId) override;
-
+ status_t setGlobalShadowSettings(const half4& ambientColor, const half4& spotColor,
+ float lightPosY, float lightPosZ, float lightRadius) override;
/* ------------------------------------------------------------------------
* DeathRecipient interface
*/
@@ -484,13 +509,20 @@
/* ------------------------------------------------------------------------
* HWC2::ComposerCallback / HWComposer::EventHandler interface
*/
- void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
- int64_t timestamp) override;
+ void onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, int64_t timestamp,
+ std::optional<hwc2_vsync_period_t> vsyncPeriod) override;
void onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId,
HWC2::Connection connection) override;
void onRefreshReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId) override;
+ void onVsyncPeriodTimingChangedReceived(
+ int32_t sequenceId, hwc2_display_t display,
+ const hwc_vsync_period_change_timeline_t& updatedTimeline) override;
/* ------------------------------------------------------------------------
+ * Scheduler::ISchedulerCallback
+ */
+ void changeRefreshRate(const Scheduler::RefreshRate&, Scheduler::ConfigEvent) override;
+ /* ------------------------------------------------------------------------
* Message handling
*/
void waitForEvent();
@@ -500,15 +532,14 @@
void signalLayerUpdate();
void signalRefresh();
- using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType;
+ using RefreshRate = scheduler::RefreshRateConfigs::RefreshRate;
struct ActiveConfigInfo {
- RefreshRateType type = RefreshRateType::DEFAULT;
- int configId = 0;
+ HwcConfigIndexType configId;
Scheduler::ConfigEvent event = Scheduler::ConfigEvent::None;
bool operator!=(const ActiveConfigInfo& other) const {
- return type != other.type || configId != other.configId || event != other.event;
+ return configId != other.configId || event != other.event;
}
};
@@ -661,6 +692,7 @@
const sp<GraphicBuffer>& buffer, bool useIdentityTransform,
bool& outCapturedSecureLayers);
const sp<DisplayDevice> getDisplayByIdOrLayerStack(uint64_t displayOrLayerStack);
+ const sp<DisplayDevice> getDisplayByLayerStack(uint64_t layerStack);
status_t captureScreenImplLocked(const RenderArea& renderArea,
TraverseLayersFunction traverseLayers,
ANativeWindowBuffer* buffer, bool useIdentityTransform,
@@ -783,9 +815,10 @@
// Sets the refresh rate by switching active configs, if they are available for
// the desired refresh rate.
- void setRefreshRateTo(RefreshRateType, Scheduler::ConfigEvent event) REQUIRES(mStateLock);
+ void changeRefreshRateLocked(const RefreshRate&, Scheduler::ConfigEvent event)
+ REQUIRES(mStateLock);
- bool isDisplayConfigAllowed(int32_t configId) const REQUIRES(mStateLock);
+ bool isDisplayConfigAllowed(HwcConfigIndexType configId) const REQUIRES(mStateLock);
bool previousFrameMissed(int graceTimeMs = 0);
@@ -1109,8 +1142,9 @@
std::atomic<nsecs_t> mExpectedPresentTime = 0;
// All configs are allowed if the set is empty.
- using DisplayConfigs = std::set<int32_t>;
+ 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
@@ -1148,6 +1182,9 @@
bool mPendingSyncInputWindows GUARDED_BY(mStateLock);
Hwc2::impl::PowerAdvisor mPowerAdvisor;
+ // This should only be accessed on the main thread.
+ nsecs_t mFrameStartTime = 0;
+
std::unique_ptr<RefreshRateOverlay> mRefreshRateOverlay;
// Flag used to set override allowed display configs from backdoor
diff --git a/services/surfaceflinger/TimeStats/TimeStats.cpp b/services/surfaceflinger/TimeStats/TimeStats.cpp
index 611afce..1895777 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.cpp
+++ b/services/surfaceflinger/TimeStats/TimeStats.cpp
@@ -20,14 +20,13 @@
#include "TimeStats.h"
#include <android-base/stringprintf.h>
-
#include <log/log.h>
-
#include <utils/String8.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
#include <algorithm>
+#include <chrono>
namespace android {
@@ -113,6 +112,46 @@
mTimeStats.clientCompositionFrames++;
}
+static int32_t msBetween(nsecs_t start, nsecs_t end) {
+ int64_t delta = std::chrono::duration_cast<std::chrono::milliseconds>(
+ std::chrono::nanoseconds(end - start))
+ .count();
+ delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
+ return static_cast<int32_t>(delta);
+}
+
+void TimeStats::recordFrameDuration(nsecs_t startTime, nsecs_t endTime) {
+ if (!mEnabled.load()) return;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mPowerTime.powerMode == HWC_POWER_MODE_NORMAL) {
+ mTimeStats.frameDuration.insert(msBetween(startTime, endTime));
+ }
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) {
+ if (!mEnabled.load()) return;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+ ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+ mGlobalRecord.renderEngineDurations.pop_front();
+ }
+ mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
+void TimeStats::recordRenderEngineDuration(nsecs_t startTime,
+ const std::shared_ptr<FenceTime>& endTime) {
+ if (!mEnabled.load()) return;
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (mGlobalRecord.renderEngineDurations.size() == MAX_NUM_TIME_RECORDS) {
+ ALOGE("RenderEngineTimes are already at its maximum size[%zu]", MAX_NUM_TIME_RECORDS);
+ mGlobalRecord.renderEngineDurations.pop_front();
+ }
+ mGlobalRecord.renderEngineDurations.push_back({startTime, endTime});
+}
+
bool TimeStats::recordReadyLocked(int32_t layerId, TimeRecord* timeRecord) {
if (!timeRecord->ready) {
ALOGV("[%d]-[%" PRIu64 "]-presentFence is still not received", layerId,
@@ -149,12 +188,6 @@
return true;
}
-static int32_t msBetween(nsecs_t start, nsecs_t end) {
- int64_t delta = (end - start) / 1000000;
- delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
- return static_cast<int32_t>(delta);
-}
-
void TimeStats::flushAvailableRecordsToStatsLocked(int32_t layerId) {
ATRACE_CALL();
@@ -491,6 +524,31 @@
mGlobalRecord.prevPresentTime = curPresentTime;
mGlobalRecord.presentFences.pop_front();
}
+ while (!mGlobalRecord.renderEngineDurations.empty()) {
+ const auto duration = mGlobalRecord.renderEngineDurations.front();
+ const auto& endTime = duration.endTime;
+
+ nsecs_t endNs = -1;
+
+ if (auto val = std::get_if<nsecs_t>(&endTime)) {
+ endNs = *val;
+ } else {
+ endNs = std::get<std::shared_ptr<FenceTime>>(endTime)->getSignalTime();
+ }
+
+ if (endNs == Fence::SIGNAL_TIME_PENDING) break;
+
+ if (endNs < 0) {
+ ALOGE("RenderEngineTiming is invalid!");
+ mGlobalRecord.renderEngineDurations.pop_front();
+ continue;
+ }
+
+ const int32_t renderEngineMs = msBetween(duration.startTime, endNs);
+ mTimeStats.renderEngineTiming.insert(renderEngineMs);
+
+ mGlobalRecord.renderEngineDurations.pop_front();
+ }
}
void TimeStats::setPresentFenceGlobal(const std::shared_ptr<FenceTime>& presentFence) {
diff --git a/services/surfaceflinger/TimeStats/TimeStats.h b/services/surfaceflinger/TimeStats/TimeStats.h
index 6e71f5a..65e5cf4 100644
--- a/services/surfaceflinger/TimeStats/TimeStats.h
+++ b/services/surfaceflinger/TimeStats/TimeStats.h
@@ -27,6 +27,7 @@
#include <mutex>
#include <optional>
#include <unordered_map>
+#include <variant>
using namespace android::surfaceflinger;
@@ -44,6 +45,20 @@
virtual void incrementMissedFrames() = 0;
virtual void incrementClientCompositionFrames() = 0;
+ // Records the start and end times for a frame.
+ // The start time is the same as the beginning of a SurfaceFlinger
+ // invalidate message.
+ // The end time corresponds to when SurfaceFlinger finishes submitting the
+ // request to HWC to present a frame.
+ virtual void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+ // Records the start time and end times for when RenderEngine begins work.
+ // The start time corresponds to the beginning of RenderEngine::drawLayers.
+ // The end time corresponds to when RenderEngine finishes rendering.
+ virtual void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) = 0;
+ // Same as above, but passes in a fence representing the end time.
+ virtual void recordRenderEngineDuration(nsecs_t startTime,
+ const std::shared_ptr<FenceTime>& readyFence) = 0;
+
virtual void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) = 0;
virtual void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) = 0;
@@ -51,6 +66,8 @@
virtual void setAcquireTime(int32_t layerId, uint64_t frameNumber, nsecs_t acquireTime) = 0;
virtual void setAcquireFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& acquireFence) = 0;
+ // SetPresent{Time, Fence} are not expected to be called in the critical
+ // rendering path, as they flush prior fences if those fences have fired.
virtual void setPresentTime(int32_t layerId, uint64_t frameNumber, nsecs_t presentTime) = 0;
virtual void setPresentFence(int32_t layerId, uint64_t frameNumber,
const std::shared_ptr<FenceTime>& presentFence) = 0;
@@ -100,9 +117,15 @@
nsecs_t prevTime = 0;
};
+ struct RenderEngineDuration {
+ nsecs_t startTime;
+ std::variant<nsecs_t, std::shared_ptr<FenceTime>> endTime;
+ };
+
struct GlobalRecord {
nsecs_t prevPresentTime = 0;
std::deque<std::shared_ptr<FenceTime>> presentFences;
+ std::deque<RenderEngineDuration> renderEngineDurations;
};
public:
@@ -116,6 +139,11 @@
void incrementMissedFrames() override;
void incrementClientCompositionFrames() override;
+ void recordFrameDuration(nsecs_t startTime, nsecs_t endTime) override;
+ void recordRenderEngineDuration(nsecs_t startTime, nsecs_t endTime) override;
+ void recordRenderEngineDuration(nsecs_t startTime,
+ const std::shared_ptr<FenceTime>& readyFence) override;
+
void setPostTime(int32_t layerId, uint64_t frameNumber, const std::string& layerName,
nsecs_t postTime) override;
void setLatchTime(int32_t layerId, uint64_t frameNumber, nsecs_t latchTime) override;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
index 16d2da0..7e43880 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
+++ b/services/surfaceflinger/TimeStats/timestatsproto/TimeStatsHelper.cpp
@@ -111,6 +111,10 @@
StringAppendF(&result, "totalP2PTime = %" PRId64 " ms\n", presentToPresent.totalTime());
StringAppendF(&result, "presentToPresent histogram is as below:\n");
result.append(presentToPresent.toString());
+ StringAppendF(&result, "frameDuration histogram is as below:\n");
+ result.append(frameDuration.toString());
+ StringAppendF(&result, "renderEngineTiming histogram is as below:\n");
+ result.append(renderEngineTiming.toString());
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
result.append(ele->toString());
@@ -158,6 +162,16 @@
histProto->set_time_millis(histEle.first);
histProto->set_frame_count(histEle.second);
}
+ for (const auto& histEle : frameDuration.hist) {
+ SFTimeStatsHistogramBucketProto* histProto = globalProto.add_frame_duration();
+ histProto->set_time_millis(histEle.first);
+ histProto->set_frame_count(histEle.second);
+ }
+ for (const auto& histEle : renderEngineTiming.hist) {
+ SFTimeStatsHistogramBucketProto* histProto = globalProto.add_render_engine_timing();
+ histProto->set_time_millis(histEle.first);
+ histProto->set_frame_count(histEle.second);
+ }
const auto dumpStats = generateDumpStats(maxLayers);
for (const auto& ele : dumpStats) {
SFTimeStatsLayerProto* layerProto = globalProto.add_stats();
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
index f2ac7ff..bd97ecc 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
+++ b/services/surfaceflinger/TimeStats/timestatsproto/include/timestatsproto/TimeStatsHelper.h
@@ -61,6 +61,8 @@
int32_t clientCompositionFrames = 0;
int64_t displayOnTime = 0;
Histogram presentToPresent;
+ Histogram frameDuration;
+ Histogram renderEngineTiming;
std::unordered_map<std::string, TimeStatsLayer> stats;
std::unordered_map<uint32_t, nsecs_t> refreshRateStats;
diff --git a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
index 0dacbeb..5fd4a39 100644
--- a/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
+++ b/services/surfaceflinger/TimeStats/timestatsproto/timestats.proto
@@ -25,7 +25,7 @@
// changes to these messages, and keep google3 side proto messages in sync if
// the end to end pipeline needs to be updated.
-// Next tag: 10
+// Next tag: 12
message SFTimeStatsGlobalProto {
// The stats start time in UTC as seconds since January 1, 1970
optional int64 stats_start = 1;
@@ -43,6 +43,10 @@
repeated SFTimeStatsDisplayConfigBucketProto display_config_stats = 9;
// Present to present histogram.
repeated SFTimeStatsHistogramBucketProto present_to_present = 8;
+ // Frame CPU duration histogram.
+ repeated SFTimeStatsHistogramBucketProto frame_duration = 10;
+ // Frame GPU duration histogram.
+ repeated SFTimeStatsHistogramBucketProto render_engine_timing = 11;
// Stats per layer. Apps could have multiple layers.
repeated SFTimeStatsLayerProto stats = 6;
}
diff --git a/services/surfaceflinger/TransactionCompletedThread.cpp b/services/surfaceflinger/TransactionCompletedThread.cpp
index c15355d..8db03db 100644
--- a/services/surfaceflinger/TransactionCompletedThread.cpp
+++ b/services/surfaceflinger/TransactionCompletedThread.cpp
@@ -189,15 +189,6 @@
return NO_ERROR;
}
-void TransactionCompletedThread::clearAllPending() {
- std::lock_guard lock(mMutex);
- if (!mRunning) {
- return;
- }
- mPendingTransactions.clear();
- mConditionVariable.notify_all();
-}
-
status_t TransactionCompletedThread::registerUnpresentedCallbackHandle(
const sp<CallbackHandle>& handle) {
std::lock_guard lock(mMutex);
diff --git a/services/surfaceflinger/TransactionCompletedThread.h b/services/surfaceflinger/TransactionCompletedThread.h
index cd95bfb..12ea8fe 100644
--- a/services/surfaceflinger/TransactionCompletedThread.h
+++ b/services/surfaceflinger/TransactionCompletedThread.h
@@ -70,8 +70,6 @@
// Notifies the TransactionCompletedThread that a pending CallbackHandle has been presented.
status_t finalizePendingCallbackHandles(const std::deque<sp<CallbackHandle>>& handles);
- void clearAllPending();
-
// Adds the Transaction CallbackHandle from a layer that does not need to be relatched and
// presented this frame.
status_t registerUnpresentedCallbackHandle(const sp<CallbackHandle>& handle);
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 51b20cb..049c872 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -260,7 +260,7 @@
prop {
api_name: "color_space_agnostic_dataspace"
type: Long
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.color_space_agnostic_dataspace"
}
@@ -339,7 +339,7 @@
prop {
api_name: "set_display_power_timer_ms"
type: Integer
- scope: System
+ scope: Public
access: Readonly
prop_name: "ro.surface_flinger.set_display_power_timer_ms"
}
diff --git a/services/surfaceflinger/tests/Android.bp b/services/surfaceflinger/tests/Android.bp
index d021fc2..ca7e18d 100644
--- a/services/surfaceflinger/tests/Android.bp
+++ b/services/surfaceflinger/tests/Android.bp
@@ -18,9 +18,11 @@
test_suites: ["device-tests"],
srcs: [
"BufferGenerator.cpp",
+ "CommonTypes_test.cpp",
"Credentials_test.cpp",
"DereferenceSurfaceControl_test.cpp",
"DisplayActiveConfig_test.cpp",
+ "DisplayConfigs_test.cpp",
"InvalidHandles_test.cpp",
"LayerCallback_test.cpp",
"LayerRenderTypeTransaction_test.cpp",
@@ -41,6 +43,8 @@
"libtrace_proto",
],
shared_libs: [
+ "android.hardware.graphics.common@1.2",
+ "android.hardware.graphics.composer@2.1",
"libandroid",
"libbinder",
"libcutils",
@@ -53,6 +57,7 @@
"libtimestats_proto",
"libui",
"libutils",
+ "vintf-graphics-common-ndk_platform",
]
}
diff --git a/services/surfaceflinger/tests/CommonTypes_test.cpp b/services/surfaceflinger/tests/CommonTypes_test.cpp
new file mode 100644
index 0000000..a3e16f9
--- /dev/null
+++ b/services/surfaceflinger/tests/CommonTypes_test.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 <aidl/android/hardware/graphics/common/BlendMode.h>
+#include <aidl/android/hardware/graphics/common/Dataspace.h>
+
+#include <android/hardware/graphics/common/1.2/types.h>
+#include <android/hardware/graphics/composer/2.1/IComposerClient.h>
+
+using AidlBlendMode = aidl::android::hardware::graphics::common::BlendMode;
+using AidlDataspace = aidl::android::hardware::graphics::common::Dataspace;
+
+using HidlBlendMode = android::hardware::graphics::composer::V2_1::IComposerClient::BlendMode;
+using HidlDataspace = android::hardware::graphics::common::V1_2::Dataspace;
+
+static_assert(static_cast<uint32_t>(AidlBlendMode::INVALID) ==
+ static_cast<uint32_t>(HidlBlendMode::INVALID));
+static_assert(static_cast<uint32_t>(AidlBlendMode::NONE) ==
+ static_cast<uint32_t>(HidlBlendMode::NONE));
+static_assert(static_cast<uint32_t>(AidlBlendMode::PREMULTIPLIED) ==
+ static_cast<uint32_t>(HidlBlendMode::PREMULTIPLIED));
+static_assert(static_cast<uint32_t>(AidlBlendMode::COVERAGE) ==
+ static_cast<uint32_t>(HidlBlendMode::COVERAGE));
+
+static_assert(static_cast<uint32_t>(AidlDataspace::UNKNOWN) ==
+ static_cast<uint32_t>(HidlDataspace::UNKNOWN));
+static_assert(static_cast<uint32_t>(AidlDataspace::ARBITRARY) ==
+ static_cast<uint32_t>(HidlDataspace::ARBITRARY));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_SHIFT) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_MASK) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_UNSPECIFIED) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT709) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT709));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_625_UNADJUSTED) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_625_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT601_525_UNADJUSTED) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT601_525_UNADJUSTED));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT2020_CONSTANT_LUMINANCE));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_BT470M) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_BT470M));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_FILM) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_FILM));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_DCI_P3) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::STANDARD_ADOBE_RGB) ==
+ static_cast<uint32_t>(HidlDataspace::STANDARD_ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SHIFT) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_MASK) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_UNSPECIFIED) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SRGB) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_SMPTE_170M) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_SMPTE_170M));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_2) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_2));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_6) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_6));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_GAMMA2_8) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_GAMMA2_8));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_ST2084) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_ST2084));
+static_assert(static_cast<uint32_t>(AidlDataspace::TRANSFER_HLG) ==
+ static_cast<uint32_t>(HidlDataspace::TRANSFER_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_SHIFT) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_SHIFT));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_MASK) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_MASK));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_UNSPECIFIED) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_UNSPECIFIED));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_FULL) ==
+ static_cast<uint32_t>(HidlDataspace::RANGE_FULL));
+static_assert(static_cast<uint32_t>(AidlDataspace::RANGE_LIMITED) ==
+ 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_cast<uint32_t>(HidlDataspace::V0_SRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SCRGB_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_SRGB) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_SCRGB) ==
+ static_cast<uint32_t>(HidlDataspace::V0_SCRGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_JFIF) ==
+ static_cast<uint32_t>(HidlDataspace::V0_JFIF));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_625) ==
+ static_cast<uint32_t>(HidlDataspace::V0_BT601_625));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_BT601_525) ==
+ static_cast<uint32_t>(HidlDataspace::V0_BT601_525));
+static_assert(static_cast<uint32_t>(AidlDataspace::V0_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));
+static_assert(static_cast<uint32_t>(AidlDataspace::DCI_P3) ==
+ static_cast<uint32_t>(HidlDataspace::DCI_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::DISPLAY_P3_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_P3) ==
+ static_cast<uint32_t>(HidlDataspace::DISPLAY_P3));
+static_assert(static_cast<uint32_t>(AidlDataspace::ADOBE_RGB) ==
+ static_cast<uint32_t>(HidlDataspace::ADOBE_RGB));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_LINEAR) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_LINEAR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_PQ) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::DEPTH) ==
+ static_cast<uint32_t>(HidlDataspace::DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::SENSOR) ==
+ static_cast<uint32_t>(HidlDataspace::SENSOR));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_ITU));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_PQ) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_ITU_PQ));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_ITU_HLG) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_ITU_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::BT2020_HLG) ==
+ static_cast<uint32_t>(HidlDataspace::BT2020_HLG));
+static_assert(static_cast<uint32_t>(AidlDataspace::DISPLAY_BT2020) ==
+ static_cast<uint32_t>(HidlDataspace::DISPLAY_BT2020));
+static_assert(static_cast<uint32_t>(AidlDataspace::DYNAMIC_DEPTH) ==
+ static_cast<uint32_t>(HidlDataspace::DYNAMIC_DEPTH));
+static_assert(static_cast<uint32_t>(AidlDataspace::JPEG_APP_SEGMENTS) ==
+ 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/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index b667a74..b1bb7fd 100644
--- a/services/surfaceflinger/tests/Credentials_test.cpp
+++ b/services/surfaceflinger/tests/Credentials_test.cpp
@@ -274,7 +274,7 @@
sp<GraphicBuffer> outBuffer;
return ScreenshotClient::captureLayers(mBGSurfaceControl->getHandle(),
ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888,
- Rect(), FRAME_SCALE, &outBuffer);
+ Rect(0, 0, 1, 1), FRAME_SCALE, &outBuffer);
};
ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, PERMISSION_DENIED));
}
diff --git a/services/surfaceflinger/tests/DisplayConfigs_test.cpp b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
new file mode 100644
index 0000000..420fb29
--- /dev/null
+++ b/services/surfaceflinger/tests/DisplayConfigs_test.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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());
+
+/**
+ * Test class for setting display configs and passing around refresh rate ranges.
+ */
+class RefreshRateRangeTest : public ::testing::Test {
+protected:
+ void SetUp() override { mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); }
+
+ sp<IBinder> mDisplayToken;
+ int32_t defaultConfigId;
+ float minRefreshRate;
+ float maxRefreshRate;
+};
+
+TEST_F(RefreshRateRangeTest, simpleSetAndGet) {
+ status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
+ EXPECT_EQ(res, NO_ERROR);
+
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
+ &minRefreshRate, &maxRefreshRate);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(defaultConfigId, 1);
+ EXPECT_EQ(minRefreshRate, 45);
+ EXPECT_EQ(maxRefreshRate, 75);
+}
+
+TEST_F(RefreshRateRangeTest, complexSetAndGet) {
+ status_t res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 1, 45, 75);
+ EXPECT_EQ(res, NO_ERROR);
+
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
+ &minRefreshRate, &maxRefreshRate);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(defaultConfigId, 1);
+ EXPECT_EQ(minRefreshRate, 45);
+ EXPECT_EQ(maxRefreshRate, 75);
+
+ // Second call overrides the first one.
+ res = SurfaceComposerClient::setDesiredDisplayConfigSpecs(mDisplayToken, 10, 145, 875);
+ EXPECT_EQ(res, NO_ERROR);
+ res = SurfaceComposerClient::getDesiredDisplayConfigSpecs(mDisplayToken, &defaultConfigId,
+ &minRefreshRate, &maxRefreshRate);
+ EXPECT_EQ(res, NO_ERROR);
+ EXPECT_EQ(defaultConfigId, 10);
+ EXPECT_EQ(minRefreshRate, 145);
+ EXPECT_EQ(maxRefreshRate, 875);
+}
+} // namespace android
diff --git a/services/surfaceflinger/tests/LayerUpdate_test.cpp b/services/surfaceflinger/tests/LayerUpdate_test.cpp
index 73f563d..0ad122b 100644
--- a/services/surfaceflinger/tests/LayerUpdate_test.cpp
+++ b/services/surfaceflinger/tests/LayerUpdate_test.cpp
@@ -1443,6 +1443,61 @@
mCapture->expectColor(Rect(0, 0, 9, 9), {100, 100, 100, 255});
}
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop(0, 0, 10, 10);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundedLayerWithoutSourceCrop) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ Rect layerCrop(0, 0, 10, 10);
+ SurfaceComposerClient::Transaction().setCrop_legacy(child, layerCrop).show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
+TEST_F(ScreenCaptureTest, CaptureBoundlessLayerWithoutSourceCropFails) {
+ sp<SurfaceControl> child = createColorLayer("Child layer", Color::RED, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+}
+
+TEST_F(ScreenCaptureTest, CaptureBufferLayerWithoutBufferFails) {
+ sp<SurfaceControl> child = createSurface(mClient, "Child surface", 10, 10,
+ PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get());
+ SurfaceComposerClient::Transaction().show(child).apply(true);
+
+ sp<ISurfaceComposer> sf(ComposerService::getComposerService());
+ sp<GraphicBuffer> outBuffer;
+ Rect sourceCrop = Rect();
+ ASSERT_EQ(BAD_VALUE, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+
+ TransactionUtils::fillSurfaceRGBA8(child, Color::RED);
+ SurfaceComposerClient::Transaction().apply(true);
+ ASSERT_EQ(NO_ERROR, sf->captureLayers(child->getHandle(), &outBuffer, sourceCrop));
+ ScreenCapture sc(outBuffer);
+ sc.expectColor(Rect(0, 0, 9, 9), Color::RED);
+}
+
// In the following tests we verify successful skipping of a parent layer,
// so we use the same verification logic and only change how we mutate
// the parent layer to verify that various properties are ignored.
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..6d79615 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,32 @@
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;
-}
-
-Error FakeComposerClient::setLayerCursorPosition(Display /*display*/, Layer /*layer*/,
- int32_t /*x*/, int32_t /*y*/) {
- ALOGV("setLayerCursorPosition");
- return Error::NONE;
-}
-
-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;
-}
-
-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;
+ 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 +773,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..2a08b9b 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,178 @@
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;
void setClient(ComposerClient* client);
@@ -150,6 +277,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 +287,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 0c4a752..ccfa6c5 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -57,6 +57,7 @@
"VSyncDispatchTimerQueueTest.cpp",
"VSyncDispatchRealtimeTest.cpp",
"VSyncPredictorTest.cpp",
+ "VSyncReactorTest.cpp",
"mock/DisplayHardware/MockComposer.cpp",
"mock/DisplayHardware/MockDisplay.cpp",
"mock/DisplayHardware/MockPowerAdvisor.cpp",
diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
index 32f997f..04991f9 100644
--- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp
@@ -33,13 +33,13 @@
#include "BufferQueueLayer.h"
#include "ColorLayer.h"
#include "Layer.h"
-
#include "TestableSurfaceFlinger.h"
#include "mock/DisplayHardware/MockComposer.h"
#include "mock/MockDispSync.h"
#include "mock/MockEventControlThread.h"
#include "mock/MockEventThread.h"
#include "mock/MockMessageQueue.h"
+#include "mock/MockTimeStats.h"
#include "mock/system/window/MockNativeWindow.h"
namespace android {
@@ -100,6 +100,7 @@
.WillRepeatedly(DoAll(SetArgPointee<1>(DEFAULT_DISPLAY_HEIGHT), Return(0)));
mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine));
+ mFlinger.setupTimeStats(std::shared_ptr<TimeStats>(mTimeStats));
setupComposer(0);
}
@@ -181,6 +182,7 @@
Hwc2::mock::Composer* mComposer = nullptr;
renderengine::mock::RenderEngine* mRenderEngine = new renderengine::mock::RenderEngine();
+ mock::TimeStats* mTimeStats = new mock::TimeStats();
mock::MessageQueue* mMessageQueue = new mock::MessageQueue();
sp<Fence> mClientTargetAcquireFence = Fence::NO_FENCE;
diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
index b1a4951..76e8171 100644
--- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
+++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp
@@ -469,6 +469,10 @@
getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
IComposerClient::Attribute::DPI_Y, _))
.WillOnce(DoAll(SetArgPointee<3>(DEFAULT_DPI), Return(Error::NONE)));
+ EXPECT_CALL(*test->mComposer,
+ getDisplayAttribute(HWC_DISPLAY_ID, HWC_ACTIVE_CONFIG_ID,
+ IComposerClient::Attribute::CONFIG_GROUP, _))
+ .WillOnce(DoAll(SetArgPointee<3>(-1), Return(Error::NONE)));
if (PhysicalDisplay::HAS_IDENTIFICATION_DATA) {
EXPECT_CALL(*test->mComposer, getDisplayIdentificationData(HWC_DISPLAY_ID, _, _))
@@ -1423,7 +1427,7 @@
// Note: This is not Case::Display::HWC_ACTIVE_CONFIG_ID as the ids are
// remapped, and the test only ever sets up one config. If there were an error
// looking up the remapped index, device->getActiveConfig() would be -1 instead.
- EXPECT_EQ(0, device->getActiveConfig());
+ EXPECT_EQ(0, device->getActiveConfig().value());
EXPECT_EQ(Case::PerFrameMetadataSupport::PER_FRAME_METADATA_KEYS,
device->getSupportedPerFrameMetadata());
}
diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
index 2662f52..80bca02 100644
--- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
+++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp
@@ -26,6 +26,7 @@
#include "AsyncCallRecorder.h"
#include "Scheduler/EventThread.h"
+#include "Scheduler/HwcStrongTypes.h"
using namespace std::chrono_literals;
using namespace std::placeholders;
@@ -34,6 +35,7 @@
using testing::Invoke;
namespace android {
+
namespace {
constexpr PhysicalDisplayId INTERNAL_DISPLAY_ID = 111;
@@ -448,17 +450,17 @@
}
TEST_F(EventThreadTest, postConfigChangedPrimary) {
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 7);
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(7));
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7);
}
TEST_F(EventThreadTest, postConfigChangedExternal) {
- mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, 5);
+ mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, HwcConfigIndexType(5));
expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5);
}
TEST_F(EventThreadTest, postConfigChangedPrimary64bit) {
- mThread->onConfigChanged(DISPLAY_ID_64BIT, 7);
+ mThread->onConfigChanged(DISPLAY_ID_64BIT, HwcConfigIndexType(7));
expectConfigChangedEventReceivedByConnection(DISPLAY_ID_64BIT, 7);
}
@@ -468,7 +470,7 @@
createConnection(suppressConnectionEventRecorder,
ISurfaceComposer::eConfigChangedSuppress);
- mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 9);
+ mThread->onConfigChanged(INTERNAL_DISPLAY_ID, HwcConfigIndexType(9));
expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 9);
auto args = suppressConnectionEventRecorder.waitForCall();
diff --git a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
index 66c7f6b..da4eea0 100644
--- a/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
+++ b/services/surfaceflinger/tests/unittests/FakePhaseOffsets.h
@@ -25,16 +25,16 @@
struct FakePhaseOffsets : PhaseOffsets {
static constexpr nsecs_t FAKE_PHASE_OFFSET_NS = 0;
- Offsets getOffsetsForRefreshRate(RefreshRateType) const override { return getCurrentOffsets(); }
+ Offsets getOffsetsForRefreshRate(float) const override { return getCurrentOffsets(); }
Offsets getCurrentOffsets() const override {
- return {{RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
- {RefreshRateType::DEFAULT, FAKE_PHASE_OFFSET_NS, FAKE_PHASE_OFFSET_NS},
+ 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};
}
- void setRefreshRateType(RefreshRateType) override {}
+ void setRefreshRateFps(float) override {}
void dump(std::string&) const override {}
};
diff --git a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
index e93d31e..d95252b 100644
--- a/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
+++ b/services/surfaceflinger/tests/unittests/LayerHistoryTest.cpp
@@ -44,9 +44,15 @@
auto createLayer() { return sp<mock::MockLayer>(new mock::MockLayer(mFlinger.flinger())); }
RefreshRateConfigs mConfigs{true,
- {RefreshRateConfigs::InputConfig{0, LO_FPS_PERIOD},
- RefreshRateConfigs::InputConfig{1, HI_FPS_PERIOD}},
- 0};
+ {
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(0),
+ HwcConfigGroupType(0),
+ LO_FPS_PERIOD},
+ RefreshRateConfigs::InputConfig{HwcConfigIndexType(1),
+ HwcConfigGroupType(0),
+ HI_FPS_PERIOD},
+ },
+ HwcConfigIndexType(0)};
TestableScheduler* const mScheduler{new TestableScheduler(mConfigs)};
TestableSurfaceFlinger mFlinger;
@@ -57,7 +63,6 @@
TEST_F(LayerHistoryTest, oneLayer) {
const auto layer = createLayer();
- constexpr bool isHDR = false;
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_EQ(1, layerCount());
@@ -69,14 +74,14 @@
// 0 FPS is returned if active layers have insufficient history.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
- history().record(layer.get(), 0, isHDR, mTime);
+ history().record(layer.get(), 0, mTime);
EXPECT_FLOAT_EQ(0, history().summarize(mTime).maxRefreshRate);
EXPECT_EQ(1, activeLayerCount());
}
// High FPS is returned once enough history has been recorded.
for (int i = 0; i < 10; i++) {
- history().record(layer.get(), 0, isHDR, mTime);
+ history().record(layer.get(), 0, mTime);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(mTime).maxRefreshRate);
EXPECT_EQ(1, activeLayerCount());
}
@@ -84,29 +89,25 @@
TEST_F(LayerHistoryTest, oneHDRLayer) {
const auto layer = createLayer();
- constexpr bool isHDR = true;
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_EQ(1, layerCount());
EXPECT_EQ(0, activeLayerCount());
- history().record(layer.get(), 0, isHDR, mTime);
+ history().record(layer.get(), 0, mTime);
auto summary = history().summarize(mTime);
EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- EXPECT_TRUE(summary.isHDR);
EXPECT_EQ(1, activeLayerCount());
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(false));
summary = history().summarize(mTime);
EXPECT_FLOAT_EQ(0, summary.maxRefreshRate);
- EXPECT_FALSE(summary.isHDR);
EXPECT_EQ(0, activeLayerCount());
}
TEST_F(LayerHistoryTest, explicitTimestamp) {
const auto layer = createLayer();
- constexpr bool isHDR = false;
EXPECT_CALL(*layer, isVisible()).WillRepeatedly(Return(true));
EXPECT_EQ(1, layerCount());
@@ -114,7 +115,7 @@
nsecs_t time = mTime;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer.get(), time, isHDR, time);
+ history().record(layer.get(), time, time);
time += LO_FPS_PERIOD;
}
@@ -127,7 +128,6 @@
auto layer1 = createLayer();
auto layer2 = createLayer();
auto layer3 = createLayer();
- constexpr bool isHDR = false;
EXPECT_CALL(*layer1, isVisible()).WillRepeatedly(Return(true));
EXPECT_CALL(*layer2, isVisible()).WillRepeatedly(Return(true));
@@ -141,7 +141,7 @@
// layer1 is active but infrequent.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer1.get(), time, isHDR, time);
+ history().record(layer1.get(), time, time);
time += MAX_FREQUENT_LAYER_PERIOD_NS.count();
}
@@ -151,12 +151,12 @@
// layer2 is frequent and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
time += HI_FPS_PERIOD;
}
// layer1 is still active but infrequent.
- history().record(layer1.get(), time, isHDR, time);
+ history().record(layer1.get(), time, time);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
EXPECT_EQ(2, activeLayerCount());
@@ -165,7 +165,7 @@
// layer1 is no longer active.
// layer2 is frequent and has low refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
time += LO_FPS_PERIOD;
}
@@ -178,10 +178,10 @@
constexpr int RATIO = LO_FPS_PERIOD / HI_FPS_PERIOD;
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE - 1; i++) {
if (i % RATIO == 0) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
}
- history().record(layer3.get(), time, isHDR, time);
+ history().record(layer3.get(), time, time);
time += HI_FPS_PERIOD;
}
@@ -190,7 +190,7 @@
EXPECT_EQ(2, frequentLayerCount(time));
// layer3 becomes recently active.
- history().record(layer3.get(), time, isHDR, time);
+ history().record(layer3.get(), time, time);
EXPECT_FLOAT_EQ(HI_FPS, history().summarize(time).maxRefreshRate);
EXPECT_EQ(2, activeLayerCount());
EXPECT_EQ(2, frequentLayerCount(time));
@@ -205,7 +205,7 @@
// layer2 still has low refresh rate.
// layer3 becomes inactive.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer2.get(), time, isHDR, time);
+ history().record(layer2.get(), time, time);
time += LO_FPS_PERIOD;
}
@@ -222,7 +222,7 @@
// layer3 becomes active and has high refresh rate.
for (int i = 0; i < PRESENT_TIME_HISTORY_SIZE; i++) {
- history().record(layer3.get(), time, isHDR, time);
+ history().record(layer3.get(), time, time);
time += HI_FPS_PERIOD;
}
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
index f315a8a..546e65c 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateConfigsTest.cpp
@@ -30,27 +30,19 @@
namespace android {
namespace scheduler {
-using RefreshRateType = RefreshRateConfigs::RefreshRateType;
using RefreshRate = RefreshRateConfigs::RefreshRate;
class RefreshRateConfigsTest : public testing::Test {
protected:
- static constexpr int CONFIG_ID_60 = 0;
- static constexpr hwc2_config_t HWC2_CONFIG_ID_60 = 0;
- static constexpr int CONFIG_ID_90 = 1;
- static constexpr hwc2_config_t HWC2_CONFIG_ID_90 = 1;
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_60 = HwcConfigIndexType(0);
+ static inline const HwcConfigIndexType HWC_CONFIG_ID_90 = HwcConfigIndexType(1);
+ static inline const HwcConfigGroupType HWC_GROUP_ID_0 = HwcConfigGroupType(0);
+ static inline const HwcConfigGroupType HWC_GROUP_ID_1 = HwcConfigGroupType(1);
static constexpr int64_t VSYNC_60 = 16666667;
static constexpr int64_t VSYNC_90 = 11111111;
RefreshRateConfigsTest();
~RefreshRateConfigsTest();
-
- void assertRatesEqual(const RefreshRate& left, const RefreshRate& right) {
- ASSERT_EQ(left.configId, right.configId);
- ASSERT_EQ(left.name, right.name);
- ASSERT_EQ(left.fps, right.fps);
- ASSERT_EQ(left.vsyncPeriod, right.vsyncPeriod);
- }
};
RefreshRateConfigsTest::RefreshRateConfigsTest() {
@@ -69,40 +61,173 @@
/* ------------------------------------------------------------------------
* Test cases
*/
-TEST_F(RefreshRateConfigsTest, oneDeviceConfig_isRejected) {
- std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60}};
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingSupported) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfig=*/0);
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+}
+
+TEST_F(RefreshRateConfigsTest, oneDeviceConfig_SwitchingNotSupported) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
ASSERT_FALSE(refreshRateConfigs->refreshRateSwitchingSupported());
}
TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap) {
- std::vector<RefreshRateConfigs::InputConfig> configs{{HWC2_CONFIG_ID_60, VSYNC_60},
- {HWC2_CONFIG_ID_90, VSYNC_90}};
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
auto refreshRateConfigs =
std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
- /*currentConfig=*/0);
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
- const auto& rates = refreshRateConfigs->getRefreshRateMap();
- ASSERT_EQ(2, rates.size());
- const auto& defaultRate = rates.find(RefreshRateType::DEFAULT);
- const auto& performanceRate = rates.find(RefreshRateType::PERFORMANCE);
- ASSERT_NE(rates.end(), defaultRate);
- ASSERT_NE(rates.end(), performanceRate);
- RefreshRate expectedDefaultConfig = {CONFIG_ID_60, "60fps", 60, VSYNC_60, HWC2_CONFIG_ID_60};
- assertRatesEqual(expectedDefaultConfig, defaultRate->second);
- RefreshRate expectedPerformanceConfig = {CONFIG_ID_90, "90fps", 90, VSYNC_90,
- HWC2_CONFIG_ID_90};
- assertRatesEqual(expectedPerformanceConfig, performanceRate->second);
+ const auto minRate = refreshRateConfigs->getMinRefreshRate();
+ const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
- assertRatesEqual(expectedDefaultConfig,
- refreshRateConfigs->getRefreshRateFromType(RefreshRateType::DEFAULT));
- assertRatesEqual(expectedPerformanceConfig,
- refreshRateConfigs->getRefreshRateFromType(RefreshRateType::PERFORMANCE));
+ RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ ASSERT_EQ(expectedDefaultConfig, minRate);
+ RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps",
+ 90};
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+
+ const auto minRateByPolicy = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRateByPolicy = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ ASSERT_EQ(minRateByPolicy, minRate);
+ ASSERT_EQ(performanceRateByPolicy, performanceRate);
}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_storesFullRefreshRateMap_differentGroups) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_1, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ const auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRate = refreshRateConfigs->getMaxRefreshRate();
+ const auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+ RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ ASSERT_EQ(expectedDefaultConfig, minRate);
+ ASSERT_EQ(expectedDefaultConfig, minRate60);
+ ASSERT_EQ(expectedDefaultConfig, performanceRate60);
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_90, 60, 90);
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ const auto minRate90 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ const auto performanceRate90 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+ RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_1, "90fps",
+ 90};
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+ ASSERT_EQ(expectedPerformanceConfig, minRate90);
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate90);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_policyChange) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+ auto minRate = refreshRateConfigs->getMinRefreshRateByPolicy();
+ auto performanceRate = refreshRateConfigs->getMaxRefreshRateByPolicy();
+
+ RefreshRate expectedDefaultConfig = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ ASSERT_EQ(expectedDefaultConfig, minRate);
+ RefreshRate expectedPerformanceConfig = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps",
+ 90};
+ ASSERT_EQ(expectedPerformanceConfig, performanceRate);
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ auto minRate60 = refreshRateConfigs->getMinRefreshRateByPolicy();
+ auto performanceRate60 = refreshRateConfigs->getMaxRefreshRateByPolicy();
+ ASSERT_EQ(expectedDefaultConfig, minRate60);
+ ASSERT_EQ(expectedDefaultConfig, performanceRate60);
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getCurrentRefreshRate) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+ {
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.configId, HWC_CONFIG_ID_60);
+ }
+
+ refreshRateConfigs->setCurrentConfigId(HWC_CONFIG_ID_90);
+ {
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
+ }
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 90, 90);
+ {
+ auto current = refreshRateConfigs->getCurrentRefreshRate();
+ EXPECT_EQ(current.configId, HWC_CONFIG_ID_90);
+ }
+}
+
+TEST_F(RefreshRateConfigsTest, twoDeviceConfigs_getRefreshRateForContent) {
+ std::vector<RefreshRateConfigs::InputConfig> configs{
+ {{HWC_CONFIG_ID_60, HWC_GROUP_ID_0, VSYNC_60},
+ {HWC_CONFIG_ID_90, HWC_GROUP_ID_0, VSYNC_90}}};
+ auto refreshRateConfigs =
+ std::make_unique<RefreshRateConfigs>(/*refreshRateSwitching=*/true, configs,
+ /*currentConfigId=*/HWC_CONFIG_ID_60);
+
+ ASSERT_TRUE(refreshRateConfigs->refreshRateSwitchingSupported());
+
+ RefreshRate expected60Config = {HWC_CONFIG_ID_60, VSYNC_60, HWC_GROUP_ID_0, "60fps", 60};
+ RefreshRate expected90Config = {HWC_CONFIG_ID_90, VSYNC_90, HWC_GROUP_ID_0, "90fps", 90};
+
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+
+ refreshRateConfigs->setPolicy(HWC_CONFIG_ID_60, 60, 60);
+ 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_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_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(90.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(60.0f));
+ ASSERT_EQ(expected90Config, refreshRateConfigs->getRefreshRateForContent(45.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(30.0f));
+ ASSERT_EQ(expected60Config, refreshRateConfigs->getRefreshRateForContent(24.0f));
+}
+
} // namespace
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
index cec0b32..ef4699f 100644
--- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp
@@ -33,8 +33,9 @@
class RefreshRateStatsTest : public testing::Test {
protected:
- static constexpr int CONFIG_ID_90 = 0;
- static constexpr int CONFIG_ID_60 = 1;
+ static inline const auto CONFIG_ID_0 = HwcConfigIndexType(0);
+ static inline const auto CONFIG_ID_1 = HwcConfigIndexType(1);
+ static inline const auto CONFIG_GROUP_0 = HwcConfigGroupType(0);
static constexpr int64_t VSYNC_90 = 11111111;
static constexpr int64_t VSYNC_60 = 16666667;
@@ -43,10 +44,10 @@
void init(const std::vector<RefreshRateConfigs::InputConfig>& configs) {
mRefreshRateConfigs = std::make_unique<RefreshRateConfigs>(
- /*refreshRateSwitching=*/true, configs, /*currentConfig=*/0);
+ /*refreshRateSwitching=*/true, configs, /*currentConfig=*/CONFIG_ID_0);
mRefreshRateStats =
std::make_unique<RefreshRateStats>(*mRefreshRateConfigs, mTimeStats,
- /*currentConfig=*/0,
+ /*currentConfigId=*/CONFIG_ID_0,
/*currentPowerMode=*/HWC_POWER_MODE_OFF);
}
@@ -72,7 +73,7 @@
* Test cases
*/
TEST_F(RefreshRateStatsTest, oneConfigTest) {
- init({{CONFIG_ID_90, VSYNC_90}});
+ init({{{CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90}}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(90, _)).Times(AtLeast(1));
@@ -91,7 +92,7 @@
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(0u, times.count("90fps"));
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
@@ -107,7 +108,7 @@
EXPECT_LT(screenOff, times["ScreenOff"]);
EXPECT_EQ(ninety, times["90fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -118,7 +119,7 @@
}
TEST_F(RefreshRateStatsTest, twoConfigsTest) {
- init({{CONFIG_ID_90, VSYNC_90}, {CONFIG_ID_60, VSYNC_60}});
+ init({{{CONFIG_ID_0, CONFIG_GROUP_0, VSYNC_90}, {CONFIG_ID_1, CONFIG_GROUP_0, VSYNC_60}}});
EXPECT_CALL(mTimeStats, recordRefreshRate(0, _)).Times(AtLeast(1));
EXPECT_CALL(mTimeStats, recordRefreshRate(60, _)).Times(AtLeast(1));
@@ -137,7 +138,7 @@
times = mRefreshRateStats->getTotalTimes();
EXPECT_LT(screenOff, times["ScreenOff"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
mRefreshRateStats->setPowerMode(HWC_POWER_MODE_NORMAL);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
@@ -147,7 +148,7 @@
EXPECT_LT(0, times["90fps"]);
// When power mode is normal, time for configs updates.
- mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_1);
int ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -156,7 +157,7 @@
ASSERT_EQ(1u, times.count("60fps"));
EXPECT_LT(0, times["60fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
int sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -164,7 +165,7 @@
EXPECT_LT(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_1);
ninety = mRefreshRateStats->getTotalTimes()["90fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -175,7 +176,7 @@
// Because the power mode is not HWC_POWER_MODE_NORMAL, switching the config
// does not update refresh rates that come from the config.
mRefreshRateStats->setPowerMode(HWC_POWER_MODE_DOZE);
- mRefreshRateStats->setConfigMode(CONFIG_ID_90);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_0);
sixty = mRefreshRateStats->getTotalTimes()["60fps"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
@@ -183,7 +184,7 @@
EXPECT_EQ(ninety, times["90fps"]);
EXPECT_EQ(sixty, times["60fps"]);
- mRefreshRateStats->setConfigMode(CONFIG_ID_60);
+ mRefreshRateStats->setConfigMode(CONFIG_ID_1);
screenOff = mRefreshRateStats->getTotalTimes()["ScreenOff"];
std::this_thread::sleep_for(std::chrono::milliseconds(2));
times = mRefreshRateStats->getTotalTimes();
diff --git a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
index b4cc1e1..40536ab 100644
--- a/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
+++ b/services/surfaceflinger/tests/unittests/SchedulerTest.cpp
@@ -50,10 +50,11 @@
::testing::UnitTest::GetInstance()->current_test_info();
ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name());
- std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
- mRefreshRateConfigs =
- std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
- /*currentConfig=*/0);
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
+ {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
+ mRefreshRateConfigs = std::make_unique<
+ scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/HwcConfigIndexType(0));
mScheduler = std::make_unique<TestableScheduler>(*mRefreshRateConfigs);
diff --git a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
index b9ddcd7..5406879 100644
--- a/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
+++ b/services/surfaceflinger/tests/unittests/StrongTypingTest.cpp
@@ -56,18 +56,18 @@
EXPECT_THAT(f1 + f2, Eq(FunkyType(32)));
EXPECT_THAT(f2 + f1, Eq(FunkyType(32)));
- EXPECT_THAT(++f1, Eq(11));
- EXPECT_THAT(f1, Eq(11));
- EXPECT_THAT(f1++, Eq(11));
- EXPECT_THAT(f1++, Eq(12));
- EXPECT_THAT(f1, Eq(13));
+ EXPECT_THAT(++f1.value(), Eq(11));
+ EXPECT_THAT(f1.value(), Eq(11));
+ EXPECT_THAT(f1++.value(), Eq(11));
+ EXPECT_THAT(f1++.value(), Eq(12));
+ EXPECT_THAT(f1.value(), Eq(13));
auto f3 = f1;
EXPECT_THAT(f1, Eq(f3));
EXPECT_THAT(f1, Lt(f2));
f3 += f1;
- EXPECT_THAT(f1, Eq(13));
- EXPECT_THAT(f3, Eq(26));
+ EXPECT_THAT(f1.value(), Eq(13));
+ EXPECT_THAT(f3.value(), Eq(26));
}
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 94fc5f7..b2210ec 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -189,19 +189,23 @@
std::make_unique<impl::HWComposer>(std::move(composer)));
}
+ void setupTimeStats(const std::shared_ptr<TimeStats>& timeStats) {
+ mFlinger->mCompositionEngine->setTimeStats(timeStats);
+ }
+
void setupScheduler(std::unique_ptr<DispSync> primaryDispSync,
std::unique_ptr<EventControlThread> eventControlThread,
std::unique_ptr<EventThread> appEventThread,
std::unique_ptr<EventThread> sfEventThread) {
- std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{{/*hwcId=*/0, 16666667}};
- mFlinger->mRefreshRateConfigs =
- std::make_unique<scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false,
- configs, /*currentConfig=*/0);
- mFlinger->mRefreshRateStats =
- std::make_unique<scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs,
- *mFlinger->mTimeStats,
- /*currentConfig=*/0,
- /*powerMode=*/HWC_POWER_MODE_OFF);
+ std::vector<scheduler::RefreshRateConfigs::InputConfig> configs{
+ {{HwcConfigIndexType(0), HwcConfigGroupType(0), 16666667}}};
+ mFlinger->mRefreshRateConfigs = std::make_unique<
+ scheduler::RefreshRateConfigs>(/*refreshRateSwitching=*/false, configs,
+ /*currentConfig=*/HwcConfigIndexType(0));
+ mFlinger->mRefreshRateStats = std::make_unique<
+ scheduler::RefreshRateStats>(*mFlinger->mRefreshRateConfigs, *mFlinger->mTimeStats,
+ /*currentConfig=*/HwcConfigIndexType(0),
+ /*powerMode=*/HWC_POWER_MODE_OFF);
mScheduler =
new TestableScheduler(std::move(primaryDispSync), std::move(eventControlThread),
@@ -429,6 +433,7 @@
static constexpr int32_t DEFAULT_WIDTH = 1920;
static constexpr int32_t DEFAULT_HEIGHT = 1280;
static constexpr int32_t DEFAULT_REFRESH_RATE = 16'666'666;
+ static constexpr int32_t DEFAULT_CONFIG_GROUP = 7;
static constexpr int32_t DEFAULT_DPI = 320;
static constexpr int32_t DEFAULT_ACTIVE_CONFIG = 0;
static constexpr int32_t DEFAULT_POWER_MODE = 2;
@@ -452,7 +457,7 @@
return *this;
}
- auto& setRefreshRate(int32_t refreshRate) {
+ auto& setRefreshRate(uint32_t refreshRate) {
mRefreshRate = refreshRate;
return *this;
}
@@ -499,6 +504,7 @@
config.setVsyncPeriod(mRefreshRate);
config.setDpiX(mDpiX);
config.setDpiY(mDpiY);
+ config.setConfigGroup(mConfigGroup);
display->mutableConfigs().emplace(mActiveConfig, config.build());
display->mutableIsConnected() = true;
display->setPowerMode(static_cast<HWC2::PowerMode>(mPowerMode));
@@ -522,8 +528,9 @@
hwc2_display_t mHwcDisplayId = DEFAULT_HWC_DISPLAY_ID;
int32_t mWidth = DEFAULT_WIDTH;
int32_t mHeight = DEFAULT_HEIGHT;
- int32_t mRefreshRate = DEFAULT_REFRESH_RATE;
+ uint32_t mRefreshRate = DEFAULT_REFRESH_RATE;
int32_t mDpiX = DEFAULT_DPI;
+ int32_t mConfigGroup = DEFAULT_CONFIG_GROUP;
int32_t mDpiY = DEFAULT_DPI;
int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG;
int32_t mPowerMode = DEFAULT_POWER_MODE;
diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
index 7b60fa2..68e4c58 100644
--- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
+++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp
@@ -20,11 +20,11 @@
#include <TimeStats/TimeStats.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-
#include <log/log.h>
#include <utils/String16.h>
#include <utils/Vector.h>
+#include <chrono>
#include <random>
#include <unordered_set>
@@ -278,6 +278,69 @@
EXPECT_EQ(2, histogramProto.time_millis());
}
+TEST_F(TimeStatsTest, canInsertGlobalFrameDuration) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ using namespace std::chrono_literals;
+
+ mTimeStats->setPowerMode(HWC_POWER_MODE_OFF);
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(5ms)
+ .count());
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats
+ ->recordFrameDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(3ms).count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
+
+ SFTimeStatsGlobalProto globalProto;
+ ASSERT_TRUE(globalProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, globalProto.frame_duration_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto = globalProto.frame_duration().Get(0);
+ EXPECT_EQ(1, histogramProto.frame_count());
+ EXPECT_EQ(3, histogramProto.time_millis());
+}
+
+TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) {
+ EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
+
+ using namespace std::chrono_literals;
+
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(1ms)
+ .count(),
+ std::make_shared<FenceTime>(
+ std::chrono::duration_cast<
+ std::chrono::nanoseconds>(3ms)
+ .count()));
+
+ mTimeStats->recordRenderEngineDuration(std::chrono::duration_cast<std::chrono::nanoseconds>(4ms)
+ .count(),
+ std::chrono::duration_cast<std::chrono::nanoseconds>(6ms)
+ .count());
+
+ // First verify that flushing RenderEngine durations did not occur yet.
+ SFTimeStatsGlobalProto preFlushProto;
+ ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+ ASSERT_EQ(0, preFlushProto.render_engine_timing_size());
+
+ // Push a dummy present fence to trigger flushing the RenderEngine timings.
+ mTimeStats->setPowerMode(HWC_POWER_MODE_NORMAL);
+ mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>(
+ std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count()));
+
+ // Now we can verify that RenderEngine durations were flushed now.
+ SFTimeStatsGlobalProto postFlushProto;
+ ASSERT_TRUE(postFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO)));
+
+ ASSERT_EQ(1, postFlushProto.render_engine_timing_size());
+ const SFTimeStatsHistogramBucketProto& histogramProto =
+ postFlushProto.render_engine_timing().Get(0);
+ EXPECT_EQ(2, histogramProto.frame_count());
+ EXPECT_EQ(2, histogramProto.time_millis());
+}
+
TEST_F(TimeStatsTest, canInsertOneLayerTimeStats) {
EXPECT_TRUE(inputCommand(InputCommand::ENABLE, FMT_STRING).empty());
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
index c012616..484947d 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchRealtimeTest.cpp
@@ -47,6 +47,10 @@
return timePoint - floor + mPeriod;
}
+ nsecs_t currentPeriod() const final { return mPeriod; }
+
+ void setPeriod(nsecs_t) final {}
+
private:
nsecs_t const mPeriod;
};
@@ -73,6 +77,13 @@
mBase = last_known;
}
+ nsecs_t currentPeriod() const final {
+ std::lock_guard<decltype(mMutex)> lk(mMutex);
+ return mPeriod;
+ }
+
+ void setPeriod(nsecs_t) final {}
+
private:
std::mutex mutable mMutex;
nsecs_t mPeriod;
diff --git a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
index 82950b5..d668a41 100644
--- a/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
+++ b/services/surfaceflinger/tests/unittests/VSyncDispatchTimerQueueTest.cpp
@@ -39,6 +39,8 @@
MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+ MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+ MOCK_METHOD1(setPeriod, void(nsecs_t));
nsecs_t nextVSyncTime(nsecs_t timePoint) const {
if (timePoint % mPeriod == 0) {
@@ -551,6 +553,32 @@
EXPECT_EQ(mDispatch.schedule(cb0, 100, 1000), ScheduleResult::ReScheduled);
}
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleNegativeOffsetAgainstDifferentPeriods) {
+ CountingCallback cb0(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ EXPECT_EQ(mDispatch.schedule(cb0, 1100, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, canScheduleLargeNegativeOffset) {
+ Sequence seq;
+ EXPECT_CALL(mMockClock, alarmIn(_, 500)).InSequence(seq);
+ EXPECT_CALL(mMockClock, alarmIn(_, 600)).InSequence(seq);
+ CountingCallback cb0(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb0, 500, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ EXPECT_EQ(mDispatch.schedule(cb0, 1900, 2000), ScheduleResult::Scheduled);
+}
+
+TEST_F(VSyncDispatchTimerQueueTest, cannotScheduleDoesNotAffectSchedulingState) {
+ EXPECT_CALL(mMockClock, alarmIn(_, 600));
+
+ CountingCallback cb(mDispatch);
+ EXPECT_EQ(mDispatch.schedule(cb, 400, 1000), ScheduleResult::Scheduled);
+ advanceToNextCallback();
+ EXPECT_EQ(mDispatch.schedule(cb, 100, 1000), ScheduleResult::CannotSchedule);
+}
+
TEST_F(VSyncDispatchTimerQueueTest, helperMove) {
EXPECT_CALL(mMockClock, alarmIn(_, 500)).Times(1);
EXPECT_CALL(mMockClock, alarmCancel()).Times(1);
@@ -599,11 +627,10 @@
VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
EXPECT_FALSE(entry.wakeupTime());
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(wakeup));
- EXPECT_THAT(*queried, Eq(900));
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(900));
entry.disarm();
EXPECT_FALSE(entry.wakeupTime());
@@ -619,11 +646,10 @@
VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
EXPECT_FALSE(entry.wakeupTime());
- auto const wakeup = entry.schedule(500, 994, mStubTracker, now);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(wakeup));
- EXPECT_THAT(*queried, Eq(9500));
+ EXPECT_THAT(entry.schedule(500, 994, mStubTracker, now), Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(9500));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, runCallback) {
@@ -634,8 +660,10 @@
calledTime = time;
});
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
- EXPECT_THAT(wakeup, Eq(900));
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(900));
entry.callback(entry.executing());
@@ -659,23 +687,40 @@
entry.update(mStubTracker, 0);
EXPECT_FALSE(entry.wakeupTime());
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ auto wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
EXPECT_THAT(wakeup, Eq(900));
entry.update(mStubTracker, 0);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(920));
+ wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(920));
}
TEST_F(VSyncDispatchTimerQueueEntryTest, skipsUpdateIfJustScheduled) {
VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
- auto const wakeup = entry.schedule(100, 500, mStubTracker, 0);
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
entry.update(mStubTracker, 0);
- auto const queried = entry.wakeupTime();
- ASSERT_TRUE(queried);
- EXPECT_THAT(*queried, Eq(wakeup));
+ auto const wakeup = entry.wakeupTime();
+ ASSERT_TRUE(wakeup);
+ EXPECT_THAT(*wakeup, Eq(wakeup));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsCannotScheduleIfMissedOpportunity) {
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ 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));
+ EXPECT_THAT(entry.schedule(200, 1001, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+}
+
+TEST_F(VSyncDispatchTimerQueueEntryTest, reportsReScheduleIfStillTime) {
+ VSyncDispatchTimerQueueEntry entry("test", [](auto) {});
+ EXPECT_THAT(entry.schedule(100, 500, mStubTracker, 0), Eq(ScheduleResult::Scheduled));
+ EXPECT_THAT(entry.schedule(200, 500, mStubTracker, 0), Eq(ScheduleResult::ReScheduled));
}
} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
new file mode 100644
index 0000000..84df019
--- /dev/null
+++ b/services/surfaceflinger/tests/unittests/VSyncReactorTest.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "LibSurfaceFlingerUnittests"
+#define LOG_NDEBUG 0
+
+#include "Scheduler/TimeKeeper.h"
+#include "Scheduler/VSyncDispatch.h"
+#include "Scheduler/VSyncReactor.h"
+#include "Scheduler/VSyncTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <ui/Fence.h>
+#include <ui/FenceTime.h>
+#include <array>
+
+using namespace testing;
+using namespace std::literals;
+namespace android::scheduler {
+
+class MockVSyncTracker : public VSyncTracker {
+public:
+ MOCK_METHOD1(addVsyncTimestamp, void(nsecs_t));
+ MOCK_CONST_METHOD1(nextAnticipatedVSyncTimeFrom, nsecs_t(nsecs_t));
+ MOCK_CONST_METHOD0(currentPeriod, nsecs_t());
+ MOCK_METHOD1(setPeriod, void(nsecs_t));
+};
+
+class VSyncTrackerWrapper : public VSyncTracker {
+public:
+ VSyncTrackerWrapper(std::shared_ptr<VSyncTracker> const& tracker) : mTracker(tracker) {}
+
+ void addVsyncTimestamp(nsecs_t timestamp) final { mTracker->addVsyncTimestamp(timestamp); }
+ nsecs_t nextAnticipatedVSyncTimeFrom(nsecs_t timePoint) const final {
+ return mTracker->nextAnticipatedVSyncTimeFrom(timePoint);
+ }
+ nsecs_t currentPeriod() const final { return mTracker->currentPeriod(); }
+ void setPeriod(nsecs_t period) { mTracker->setPeriod(period); }
+
+private:
+ std::shared_ptr<VSyncTracker> const mTracker;
+};
+
+class MockClock : public Clock {
+public:
+ MOCK_CONST_METHOD0(now, nsecs_t());
+};
+
+class ClockWrapper : public Clock {
+public:
+ ClockWrapper(std::shared_ptr<Clock> const& clock) : mClock(clock) {}
+
+ nsecs_t now() const { return mClock->now(); }
+
+private:
+ std::shared_ptr<Clock> const mClock;
+};
+
+class MockVSyncDispatch : public VSyncDispatch {
+public:
+ MOCK_METHOD2(registerCallback, CallbackToken(std::function<void(nsecs_t)> const&, std::string));
+ MOCK_METHOD1(unregisterCallback, void(CallbackToken));
+ MOCK_METHOD3(schedule, ScheduleResult(CallbackToken, nsecs_t, nsecs_t));
+ MOCK_METHOD1(cancel, CancelResult(CallbackToken token));
+};
+
+class VSyncDispatchWrapper : public VSyncDispatch {
+public:
+ VSyncDispatchWrapper(std::shared_ptr<VSyncDispatch> const& dispatch) : mDispatch(dispatch) {}
+ CallbackToken registerCallback(std::function<void(nsecs_t)> const& callbackFn,
+ std::string callbackName) final {
+ return mDispatch->registerCallback(callbackFn, callbackName);
+ }
+
+ void unregisterCallback(CallbackToken token) final { mDispatch->unregisterCallback(token); }
+
+ ScheduleResult schedule(CallbackToken token, nsecs_t workDuration,
+ nsecs_t earliestVsync) final {
+ return mDispatch->schedule(token, workDuration, earliestVsync);
+ }
+
+ CancelResult cancel(CallbackToken token) final { return mDispatch->cancel(token); }
+
+private:
+ std::shared_ptr<VSyncDispatch> const mDispatch;
+};
+
+std::shared_ptr<FenceTime> generateInvalidFence() {
+ sp<Fence> fence = new Fence();
+ return std::make_shared<FenceTime>(fence);
+}
+
+std::shared_ptr<FenceTime> generatePendingFence() {
+ sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+ return std::make_shared<FenceTime>(fence);
+}
+
+void signalFenceWithTime(std::shared_ptr<FenceTime> const& fence, nsecs_t time) {
+ FenceTime::Snapshot snap(time);
+ fence->applyTrustedSnapshot(snap);
+}
+
+std::shared_ptr<FenceTime> generateSignalledFenceWithTime(nsecs_t time) {
+ sp<Fence> fence = new Fence(dup(fileno(tmpfile())));
+ std::shared_ptr<FenceTime> ft = std::make_shared<FenceTime>(fence);
+ signalFenceWithTime(ft, time);
+ return ft;
+}
+
+class VSyncReactorTest : public testing::Test {
+protected:
+ VSyncReactorTest()
+ : mMockDispatch(std::make_shared<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::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;
+ VSyncReactor mReactor;
+};
+
+TEST_F(VSyncReactorTest, addingNullFenceCheck) {
+ EXPECT_FALSE(mReactor.addPresentFence(nullptr));
+}
+
+TEST_F(VSyncReactorTest, addingInvalidFenceSignalsNeedsMoreInfo) {
+ EXPECT_TRUE(mReactor.addPresentFence(generateInvalidFence()));
+}
+
+TEST_F(VSyncReactorTest, addingSignalledFenceAddsToTracker) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+TEST_F(VSyncReactorTest, addingPendingFenceAddsSignalled) {
+ nsecs_t anotherDummyTime = 2919019201;
+
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(_)).Times(0);
+ auto pendingFence = generatePendingFence();
+ EXPECT_FALSE(mReactor.addPresentFence(pendingFence));
+ Mock::VerifyAndClearExpectations(mMockTracker.get());
+
+ signalFenceWithTime(pendingFence, dummyTime);
+
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime));
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(anotherDummyTime));
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(anotherDummyTime)));
+}
+
+TEST_F(VSyncReactorTest, limitsPendingFences) {
+ std::array<std::shared_ptr<FenceTime>, kPendingLimit * 2> fences;
+ std::array<nsecs_t, fences.size()> fakeTimes;
+ std::generate(fences.begin(), fences.end(), [] { return generatePendingFence(); });
+ std::generate(fakeTimes.begin(), fakeTimes.end(), [i = 10]() mutable {
+ i++;
+ return i * i;
+ });
+
+ for (auto const& fence : fences) {
+ mReactor.addPresentFence(fence);
+ }
+
+ for (auto i = fences.size() - kPendingLimit; i < fences.size(); i++) {
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimes[i]));
+ }
+
+ for (auto i = 0u; i < fences.size(); i++) {
+ signalFenceWithTime(fences[i], fakeTimes[i]);
+ }
+ mReactor.addPresentFence(generatePendingFence());
+}
+
+TEST_F(VSyncReactorTest, ignoresPresentFencesWhenToldTo) {
+ static constexpr size_t aFewTimes = 8;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(dummyTime)).Times(1);
+
+ mReactor.setIgnorePresentFences(true);
+ for (auto i = 0; i < aFewTimes; i++) {
+ mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime));
+ }
+
+ mReactor.setIgnorePresentFences(false);
+ EXPECT_FALSE(mReactor.addPresentFence(generateSignalledFenceWithTime(dummyTime)));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshNow) {
+ nsecs_t const fakeTimestamp = 4839;
+ EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+ .Times(1)
+ .WillOnce(Return(fakeTimestamp));
+
+ EXPECT_THAT(mReactor.computeNextRefresh(0), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForExpectedPresentTime) {
+ nsecs_t const fakeTimestamp = 4839;
+ EXPECT_CALL(*mMockTracker, currentPeriod()).Times(0);
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(_))
+ .Times(1)
+ .WillOnce(Return(fakeTimestamp));
+
+ EXPECT_THAT(mReactor.expectedPresentTime(), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, queriesTrackerForNextRefreshFuture) {
+ nsecs_t const fakeTimestamp = 4839;
+ nsecs_t const fakePeriod = 1010;
+ nsecs_t const fakeNow = 2214;
+ int const numPeriodsOut = 3;
+ EXPECT_CALL(*mMockClock, now()).WillOnce(Return(fakeNow));
+ EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+ EXPECT_CALL(*mMockTracker, nextAnticipatedVSyncTimeFrom(fakeNow + numPeriodsOut * fakePeriod))
+ .WillOnce(Return(fakeTimestamp));
+ EXPECT_THAT(mReactor.computeNextRefresh(numPeriodsOut), Eq(fakeTimestamp));
+}
+
+TEST_F(VSyncReactorTest, getPeriod) {
+ nsecs_t const fakePeriod = 1010;
+ EXPECT_CALL(*mMockTracker, currentPeriod()).WillOnce(Return(fakePeriod));
+ EXPECT_THAT(mReactor.getPeriod(), Eq(fakePeriod));
+}
+
+TEST_F(VSyncReactorTest, setPeriod) {
+ nsecs_t const fakePeriod = 4098;
+ EXPECT_CALL(*mMockTracker, setPeriod(fakePeriod));
+ mReactor.setPeriod(fakePeriod);
+}
+
+TEST_F(VSyncReactorTest, addResyncSampleTypical) {
+ nsecs_t const fakeTimestamp = 3032;
+ bool periodFlushed = false;
+
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+ EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+ EXPECT_FALSE(periodFlushed);
+}
+
+TEST_F(VSyncReactorTest, addResyncSamplePeriodChanges) {
+ bool periodFlushed = false;
+ nsecs_t const fakeTimestamp = 4398;
+ nsecs_t const newPeriod = 3490;
+ EXPECT_CALL(*mMockTracker, addVsyncTimestamp(fakeTimestamp));
+ mReactor.setPeriod(newPeriod);
+ EXPECT_FALSE(mReactor.addResyncSample(fakeTimestamp, &periodFlushed));
+ EXPECT_TRUE(periodFlushed);
+}
+
+} // namespace android::scheduler
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index 98c6aa0..2453ccb 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -39,8 +39,8 @@
using android::hardware::graphics::composer::V2_1::Display;
using android::hardware::graphics::composer::V2_1::Error;
using android::hardware::graphics::composer::V2_1::IComposer;
-using android::hardware::graphics::composer::V2_1::IComposerCallback;
using android::hardware::graphics::composer::V2_1::Layer;
+using android::hardware::graphics::composer::V2_4::IComposerCallback;
using android::hardware::graphics::composer::V2_4::IComposerClient;
class Composer : public Hwc2::Composer {
@@ -120,8 +120,16 @@
MOCK_METHOD3(setLayerPerFrameMetadataBlobs,
Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&));
MOCK_METHOD2(setDisplayBrightness, Error(Display, float));
+ MOCK_METHOD0(isVsyncPeriodSwitchSupported, bool());
MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*));
- MOCK_METHOD2(getDisplayConnectionType, Error(Display, IComposerClient::DisplayConnectionType*));
+ MOCK_METHOD2(getDisplayConnectionType,
+ V2_4::Error(Display, IComposerClient::DisplayConnectionType*));
+ MOCK_METHOD3(getSupportedDisplayVsyncPeriods,
+ V2_4::Error(Display, Config, std::vector<VsyncPeriodNanos>*));
+ MOCK_METHOD2(getDisplayVsyncPeriod, V2_4::Error(Display, VsyncPeriodNanos*));
+ MOCK_METHOD4(setActiveConfigWithConstraints,
+ V2_4::Error(Display, Config, const IComposerClient::VsyncPeriodChangeConstraints&,
+ VsyncPeriodChangeTimeline*));
};
} // namespace mock
diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
index ed35ebf..f7c3804 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h
@@ -33,7 +33,7 @@
MOCK_METHOD0(onScreenReleased, void());
MOCK_METHOD0(onScreenAcquired, void());
MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool));
- MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, int32_t));
+ MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, HwcConfigIndexType));
MOCK_CONST_METHOD1(dump, void(std::string&));
MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset));
MOCK_METHOD1(registerDisplayEventConnection,
diff --git a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
index b1634a8..ec74a42 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockTimeStats.h
@@ -34,6 +34,9 @@
MOCK_METHOD0(incrementTotalFrames, void());
MOCK_METHOD0(incrementMissedFrames, void());
MOCK_METHOD0(incrementClientCompositionFrames, void());
+ MOCK_METHOD2(recordFrameDuration, void(nsecs_t, nsecs_t));
+ MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, nsecs_t));
+ MOCK_METHOD2(recordRenderEngineDuration, void(nsecs_t, const std::shared_ptr<FenceTime>&));
MOCK_METHOD4(setPostTime, void(int32_t, uint64_t, const std::string&, nsecs_t));
MOCK_METHOD3(setLatchTime, void(int32_t, uint64_t, nsecs_t));
MOCK_METHOD3(setDesiredTime, void(int32_t, uint64_t, nsecs_t));
diff --git a/services/surfaceflinger/tests/utils/TransactionUtils.h b/services/surfaceflinger/tests/utils/TransactionUtils.h
index 22df255..2c63da0 100644
--- a/services/surfaceflinger/tests/utils/TransactionUtils.h
+++ b/services/surfaceflinger/tests/utils/TransactionUtils.h
@@ -136,6 +136,11 @@
}
}
+ static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, const Color& color,
+ bool unlock = true) {
+ fillSurfaceRGBA8(sc, color.r, color.g, color.b, unlock);
+ }
+
// Fill an RGBA_8888 formatted surface with a single color.
static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, uint8_t b,
bool unlock = true) {
diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp
index 8f4667e..a020e74 100644
--- a/vulkan/libvulkan/swapchain.cpp
+++ b/vulkan/libvulkan/swapchain.cpp
@@ -1311,7 +1311,14 @@
&img.dequeue_fence);
if (err != 0) {
ALOGE("dequeueBuffer[%u] failed: %s (%d)", i, strerror(-err), err);
- result = VK_ERROR_SURFACE_LOST_KHR;
+ switch (-err) {
+ case ENOMEM:
+ result = VK_ERROR_OUT_OF_DEVICE_MEMORY;
+ break;
+ default:
+ result = VK_ERROR_SURFACE_LOST_KHR;
+ break;
+ }
break;
}
img.buffer = buffer;
diff --git a/vulkan/vkjson/Android.bp b/vulkan/vkjson/Android.bp
index a626e48..8528898 100644
--- a/vulkan/vkjson/Android.bp
+++ b/vulkan/vkjson/Android.bp
@@ -1,4 +1,4 @@
-cc_library_static {
+cc_library_shared {
name: "libvkjson",
srcs: [
"vkjson.cc",
@@ -15,11 +15,14 @@
export_include_dirs: [
".",
],
+ shared_libs: [
+ "libvulkan",
+ ],
whole_static_libs: [
"libjsoncpp",
],
- header_libs: [
- "vulkan_headers",
+ export_shared_lib_headers: [
+ "libvulkan",
],
}