diff options
72 files changed, 2347 insertions, 642 deletions
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index ea266e5f75..738c20fefc 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -131,6 +131,19 @@ static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); +#define RETURN_IF_USER_DENIED_CONSENT() \ + if (ds.IsUserConsentDenied()) { \ + MYLOGE("Returning early as user denied consent to share bugreport with calling app."); \ + return Dumpstate::RunStatus::USER_CONSENT_DENIED; \ + } + +// Runs func_ptr, but checks user consent before and after running it. Returns USER_CONSENT_DENIED +// if consent is found to be denied. +#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(func_ptr, ...) \ + RETURN_IF_USER_DENIED_CONSENT(); \ + func_ptr(__VA_ARGS__); \ + RETURN_IF_USER_DENIED_CONSENT(); + namespace android { namespace os { namespace { @@ -1054,9 +1067,9 @@ static void DumpIpAddrAndRules() { RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"}); } -static void RunDumpsysTextByPriority(const std::string& title, int priority, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { auto start = std::chrono::steady_clock::now(); sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); @@ -1064,6 +1077,7 @@ static void RunDumpsysTextByPriority(const std::string& title, int priority, Dumpsys::setServiceArgs(args, /* asProto = */ false, priority); Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ false); for (const String16& service : services) { + RETURN_IF_USER_DENIED_CONSENT(); std::string path(title); path.append(" - ").append(String8(service).c_str()); DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); @@ -1089,6 +1103,7 @@ static void RunDumpsysTextByPriority(const std::string& title, int priority, break; } } + return Dumpstate::RunStatus::OK; } static void RunDumpsysText(const std::string& title, int priority, @@ -1101,24 +1116,27 @@ static void RunDumpsysText(const std::string& title, int priority, } /* Dump all services registered with Normal or Default priority. */ -static void RunDumpsysTextNormalPriority(const std::string& title, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysTextNormalPriority(const std::string& title, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { DurationReporter duration_reporter(title); dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str()); fsync(STDOUT_FILENO); RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, timeout, service_timeout); - RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, - service_timeout); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, + service_timeout); } -static void RunDumpsysProto(const std::string& title, int priority, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { if (!ds.IsZipping()) { MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str()); - return; + return Dumpstate::RunStatus::OK; } sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); @@ -1129,6 +1147,7 @@ static void RunDumpsysProto(const std::string& title, int priority, auto start = std::chrono::steady_clock::now(); Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ true); for (const String16& service : services) { + RETURN_IF_USER_DENIED_CONSENT(); std::string path(kProtoPath); path.append(String8(service).c_str()); if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { @@ -1157,35 +1176,54 @@ static void RunDumpsysProto(const std::string& title, int priority, break; } } + return Dumpstate::RunStatus::OK; } // Runs dumpsys on services that must dump first and will take less than 100ms to dump. -static void RunDumpsysCritical() { +static Dumpstate::RunStatus RunDumpsysCritical() { RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, /* timeout= */ 5s, /* service_timeout= */ 500ms); - RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, - /* timeout= */ 5s, /* service_timeout= */ 500ms); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, + /* timeout= */ 5s, /* service_timeout= */ 500ms); } // Runs dumpsys on services that must dump first but can take up to 250ms to dump. -static void RunDumpsysHigh() { +static Dumpstate::RunStatus RunDumpsysHigh() { // TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both // high priority. Reduce timeout once they are able to dump in a shorter time or // moved to a parallel task. RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, /* timeout= */ 90s, /* service_timeout= */ 30s); - RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, - /* timeout= */ 5s, /* service_timeout= */ 1s); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, + /* timeout= */ 5s, /* service_timeout= */ 1s); } // Runs dumpsys on services that must dump but can take up to 10s to dump. -static void RunDumpsysNormal() { +static Dumpstate::RunStatus RunDumpsysNormal() { RunDumpsysTextNormalPriority("DUMPSYS", /* timeout= */ 90s, /* service_timeout= */ 10s); - RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, - /* timeout= */ 90s, /* service_timeout= */ 10s); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, + /* timeout= */ 90s, /* service_timeout= */ 10s); } static void DumpHals() { + if (!ds.IsZipping()) { + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + return; + } + DurationReporter duration_reporter("DUMP HALS"); + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, + CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); + using android::hidl::manager::V1_0::IServiceManager; using android::hardware::defaultServiceManager; @@ -1235,9 +1273,16 @@ static void DumpHals() { } } -static void dumpstate() { +// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent +// via the consent they are shown. Ignores other errors that occur while running various +// commands. The consent checking is currently done around long running tasks, which happen to +// be distributed fairly evenly throughout the function. +static Dumpstate::RunStatus dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); + // Dump various things. Note that anything that takes "long" (i.e. several seconds) should + // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped + // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK). dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version"); RunCommand("UPTIME", {"uptime"}); DumpBlockStatFiles(); @@ -1245,7 +1290,9 @@ static void dumpstate() { DumpFile("MEMORY INFO", "/proc/meminfo"); RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"}); - RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20); + DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat"); DumpFile("VMALLOC INFO", "/proc/vmallocinfo"); DumpFile("SLAB INFO", "/proc/slabinfo"); @@ -1260,16 +1307,11 @@ static void dumpstate() { RunCommand("PROCESSES AND THREADS", {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); - RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT); - if (ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, - CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); - DumpHals(); - } else { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); - } + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"}, + CommandOptions::AS_ROOT); + + DumpHals(); RunCommand("PRINTENV", {"printenv"}); RunCommand("NETSTAT", {"netstat", "-nW"}); @@ -1288,7 +1330,9 @@ static void dumpstate() { } RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); - for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES"); + for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); @@ -1326,7 +1370,7 @@ static void dumpstate() { RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"}); RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"}); - RunDumpsysHigh(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh); RunCommand("SYSTEM PROPERTIES", {"getprop"}); @@ -1349,7 +1393,7 @@ static void dumpstate() { ds.AddDir(WMTRACE_DATA_DIR, false); } - ds.DumpstateBoard(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard); /* Migrate the ril_dumpstate to a device specific dumpstate? */ int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0); @@ -1369,14 +1413,16 @@ static void dumpstate() { printf("== Android Framework Services\n"); printf("========================================================\n"); - RunDumpsysNormal(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal); printf("========================================================\n"); printf("== Checkins\n"); printf("========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); - RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"}); + RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}); RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}); RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}); @@ -1440,19 +1486,27 @@ static void dumpstate() { printf("========================================================\n"); // This differs from the usual dumpsys stats, which is the stats report data. RunDumpsys("STATSDSTATS", {"stats", "--metadata"}); + return Dumpstate::RunStatus::OK; } -/* Dumps state for the default case. Returns true if everything went fine. */ -static bool DumpstateDefault() { +/* + * Dumps state for the default case; drops root after it's no longer necessary. + * + * Returns RunStatus::OK if everything went fine. + * Returns RunStatus::ERROR if there was an error. + * Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport + * with the caller. + */ +static Dumpstate::RunStatus DumpstateDefault() { // Try to dump anrd trace if the daemon is running. dump_anrd_trace(); - // Invoking the following dumpsys calls before dump_traces() to try and + // Invoking the following dumpsys calls before DumpTraces() to try and // keep the system stats as close to its initial state as possible. - RunDumpsysCritical(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical); /* collect stack traces from Dalvik and native processes (needs root) */ - dump_traces_path = ds.DumpTraces(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path); /* Run some operations that require root. */ ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping()); @@ -1488,11 +1542,11 @@ static bool DumpstateDefault() { } if (!DropRootUser()) { - return false; + return Dumpstate::RunStatus::ERROR; } - dumpstate(); - return true; + RETURN_IF_USER_DENIED_CONSENT(); + return dumpstate(); } // This method collects common dumpsys for telephony and wifi @@ -1575,21 +1629,14 @@ static void DumpstateWifiOnly() { RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - if (ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, - CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); - DumpHals(); - } else { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); - } + DumpHals(); printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); } -const char* Dumpstate::DumpTraces() { +Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) { DurationReporter duration_reporter("DUMP TRACES"); const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX"; @@ -1605,7 +1652,7 @@ const char* Dumpstate::DumpTraces() { android::base::unique_fd fd(mkostemp(file_name_buf.get(), O_APPEND | O_CLOEXEC)); if (fd < 0) { MYLOGE("mkostemp on pattern %s: %s\n", file_name_buf.get(), strerror(errno)); - return nullptr; + return RunStatus::OK; } // Nobody should have access to this temporary file except dumpstate, but we @@ -1615,13 +1662,13 @@ const char* Dumpstate::DumpTraces() { const int chmod_ret = fchmod(fd, 0666); if (chmod_ret < 0) { MYLOGE("fchmod on %s failed: %s\n", file_name_buf.get(), strerror(errno)); - return nullptr; + return RunStatus::OK; } std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir); if (proc.get() == nullptr) { MYLOGE("opendir /proc failed: %s\n", strerror(errno)); - return nullptr; + return RunStatus::OK; } // Number of times process dumping has timed out. If we encounter too many @@ -1633,6 +1680,7 @@ const char* Dumpstate::DumpTraces() { struct dirent* d; while ((d = readdir(proc.get()))) { + RETURN_IF_USER_DENIED_CONSENT(); int pid = atoi(d->d_name); if (pid <= 0) { continue; @@ -1694,7 +1742,8 @@ const char* Dumpstate::DumpTraces() { MYLOGE("Warning: no Dalvik processes found to dump stacks\n"); } - return file_name_buf.release(); + *path = file_name_buf.release(); + return RunStatus::OK; } void Dumpstate::DumpstateBoard() { @@ -1735,6 +1784,7 @@ void Dumpstate::DumpstateBoard() { return; } + // TODO(128270426): Check for consent in between? for (size_t i = 0; i < paths.size(); i++) { MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); @@ -2592,9 +2642,12 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, DumpstateWifiOnly(); } else { // Dump state for the default case. This also drops root. - if (!DumpstateDefault()) { - // Something went wrong. - return RunStatus::ERROR; + RunStatus s = DumpstateDefault(); + if (s != RunStatus::OK) { + if (s == RunStatus::USER_CONSENT_TIMED_OUT) { + HandleUserConsentDenied(); + } + return s; } } @@ -2631,9 +2684,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, MYLOGI( "Did not receive user consent yet." " Will not copy the bugreport artifacts to caller.\n"); - // TODO(b/111441001): - // 1. cancel outstanding requests - // 2. check for result more frequently + // TODO(b/111441001): cancel outstanding requests } } @@ -2688,6 +2739,11 @@ void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& c } } +bool Dumpstate::IsUserConsentDenied() const { + return ds.consent_callback_ != nullptr && + ds.consent_callback_->getResult() == UserConsentResult::DENIED; +} + void Dumpstate::CleanupFiles() { android::os::UnlinkAndLogOnError(tmp_path_); android::os::UnlinkAndLogOnError(screenshot_path_); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 603af710ff..d02ec759a7 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -291,8 +291,11 @@ class Dumpstate { // TODO: temporary method until Dumpstate object is properly set void SetProgress(std::unique_ptr<Progress> progress); - // Dumps Dalvik and native stack traces, return the trace file location (nullptr if none). - const char* DumpTraces(); + // Dumps Dalvik and native stack traces, sets the trace file location to path + // if it succeeded. + // Note that it returns early if user consent is denied with status USER_CONSENT_DENIED. + // Returns OK in all other cases. + RunStatus DumpTraces(const char** path); void DumpstateBoard(); @@ -331,6 +334,13 @@ class Dumpstate { void SetOptions(std::unique_ptr<DumpOptions> options); /* + * Returns true if user consent is necessary and has been denied. + * Consent is only necessary if the caller has asked to copy over the bugreport to a file they + * provided. + */ + bool IsUserConsentDenied() const; + + /* * Structure to hold options that determine the behavior of dumpstate. */ struct DumpOptions { diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 8b9298820d..0bb80dcfba 100644 --- a/cmds/dumpstate/utils.cpp +++ b/cmds/dumpstate/utils.cpp @@ -102,14 +102,16 @@ DurationReporter::DurationReporter(const std::string& title, bool logcat_only) DurationReporter::~DurationReporter() { if (!title_.empty()) { - uint64_t elapsed = Nanotime() - started_; - MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC); + float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC; + if (elapsed < .5f) { + return; + } + MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed); if (logcat_only_) { return; } // Use "Yoda grammar" to make it easier to grep|sort sections. - printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC, - title_.c_str()); + printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str()); } } @@ -278,6 +280,12 @@ static void __for_each_pid(void (*helper)(int, const char *, void *), const char if (header) printf("\n------ %s ------\n", header); while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } int pid; int fd; char cmdpath[255]; @@ -350,6 +358,12 @@ static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { func(pid, pid, cmdline); while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } int tid; int fd; char commpath[255]; diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index 5d4f176a24..56739181bb 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -3,6 +3,8 @@ set noparent agampe@google.com calin@google.com jsharkey@android.com +maco@google.com mathieuc@google.com +narayan@google.com ngeoffray@google.com toddke@google.com diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index ffd1191501..72571cf622 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -608,7 +608,7 @@ static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t f // the app uid. If we cannot do that, there's no point in returning the fd // since dex2oat/profman will fail with SElinux denials. if (fchown(fd.get(), uid, uid) < 0) { - PLOG(ERROR) << "Could not chwon profile " << profile; + PLOG(ERROR) << "Could not chown profile " << profile; return invalid_unique_fd(); } return fd; diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index 670abea616..0fdc9d6cbb 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -41,23 +41,6 @@ using android::base::StringPrintf; namespace android { namespace installd { -// Configuration for bind-mounted Bionic artifacts. - -static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker"; -static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker"; - -static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/"; -static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/"; - -static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64"; -static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64"; - -static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/"; -static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/"; - -static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"}; - - static void CloseDescriptor(int fd) { if (fd >= 0) { int result = close(fd); @@ -94,43 +77,6 @@ static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_pac } } -// Copied from system/core/init/mount_namespace.cpp. -static bool BindMount(const std::string& source, const std::string& mount_point, - bool recursive = false) { - unsigned long mountflags = MS_BIND; - if (recursive) { - mountflags |= MS_REC; - } - if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) { - PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point; - return false; - } - return true; -} - -// Copied from system/core/init/mount_namespace.cpp and and adjusted (bind -// mounts are not made private, as the /postinstall is already private (see -// `android::installd::otapreopt_chroot`). -static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source, - const std::string& linker_mount_point, - const std::string& lib_mount_dir) { - if (access(linker_source.c_str(), F_OK) != 0) { - PLOG(INFO) << linker_source << " does not exist. Skipping mounting Bionic there."; - return true; - } - if (!BindMount(linker_source, linker_mount_point)) { - return false; - } - for (const auto& libname : kBionicLibFileNames) { - std::string mount_point = lib_mount_dir + libname; - std::string source = lib_dir_source + libname; - if (!BindMount(source, mount_point)) { - return false; - } - } - return true; -} - // Entry for otapreopt_chroot. Expected parameters are: // [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] // The file descriptor denoted by status-fd will be closed. The rest of the parameters will @@ -274,23 +220,6 @@ static int otapreopt_chroot(const int argc, char **arg) { // the Android Runtime APEX, as it is required by otapreopt to run dex2oat. std::vector<apex::ApexFile> active_packages = ActivateApexPackages(); - // Bind-mount Bionic artifacts from the Runtime APEX. - // This logic is copied and adapted from system/core/init/mount_namespace.cpp. - if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint, - kBionicLibsMountPointDir)) { - LOG(ERROR) << "Failed to mount 32-bit Bionic artifacts from the Runtime APEX."; - // Clean up and exit. - DeactivateApexPackages(active_packages); - exit(215); - } - if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64, - kBionicLibsMountPointDir64)) { - LOG(ERROR) << "Failed to mount 64-bit Bionic artifacts from the Runtime APEX."; - // Clean up and exit. - DeactivateApexPackages(active_packages); - exit(216); - } - // Now go on and run otapreopt. // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index 34a3fdc420..d5dc6b741d 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -232,7 +232,6 @@ int main(int argc, char* const argv[]) else if (strcmp(key, "categories") == 0) { char* context2 = nullptr; - int categoryCount = 0; categories[categoryCount] = strtok_r(value, ",", &context2); while (categories[categoryCount] != nullptr) diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h index 3c81f0f286..853f0c9fde 100644 --- a/include/powermanager/IPowerManager.h +++ b/include/powermanager/IPowerManager.h @@ -45,7 +45,7 @@ public: IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11, IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13, - SET_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 14, + SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14, REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17, REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18, SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19, diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 0dd795a676..202d6d2e6a 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -133,7 +133,7 @@ struct AIBinder_DeathRecipient { // binderDied receipt only gives us information about the IBinder. struct TransferDeathRecipient : ::android::IBinder::DeathRecipient { TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie, - const AIBinder_DeathRecipient_onBinderDied& onDied) + const AIBinder_DeathRecipient_onBinderDied onDied) : mWho(who), mCookie(cookie), mOnDied(onDied) {} void binderDied(const ::android::wp<::android::IBinder>& who) override; @@ -144,7 +144,7 @@ struct AIBinder_DeathRecipient { private: ::android::wp<::android::IBinder> mWho; void* mCookie; - const AIBinder_DeathRecipient_onBinderDied& mOnDied; + const AIBinder_DeathRecipient_onBinderDied mOnDied; }; explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied); diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp index 0b0bf445ae..1dc1c0e4e9 100644 --- a/libs/graphicsenv/IGpuService.cpp +++ b/libs/graphicsenv/IGpuService.cpp @@ -67,6 +67,29 @@ public: outStats->clear(); return reply.readParcelableVector(outStats); } + + virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const { + if (!outStats) return UNEXPECTED_NULL; + + Parcel data, reply; + status_t status; + + if ((status = data.writeInterfaceToken(IGpuService::getInterfaceDescriptor())) != OK) { + return status; + } + + if ((status = remote()->transact(BnGpuService::GET_GPU_STATS_APP_INFO, data, &reply)) != + OK) { + return status; + } + + int32_t result = 0; + if ((status = reply.readInt32(&result)) != OK) return status; + if (result != OK) return result; + + outStats->clear(); + return reply.readParcelableVector(outStats); + } }; IMPLEMENT_META_INTERFACE(GpuService, "android.graphicsenv.IGpuService"); @@ -123,6 +146,19 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep return OK; } + case GET_GPU_STATS_APP_INFO: { + CHECK_INTERFACE(IGpuService, data, reply); + + std::vector<GpuStatsAppInfo> stats; + const status_t result = getGpuStatsAppInfo(&stats); + + if ((status = reply->writeInt32(result)) != OK) return status; + if (result != OK) return result; + + if ((status = reply->writeParcelableVector(stats)) != OK) return status; + + return OK; + } case SHELL_COMMAND_TRANSACTION: { int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h index e7cdb3802e..ac022b5d65 100644 --- a/libs/graphicsenv/include/graphicsenv/IGpuService.h +++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h @@ -42,6 +42,9 @@ public: // get GPU global stats from GpuStats module. virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const = 0; + + // get GPU app stats from GpuStats module. + virtual status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const = 0; }; class BnGpuService : public BnInterface<IGpuService> { @@ -49,6 +52,7 @@ public: enum IGpuServiceTag { SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION, GET_GPU_STATS_GLOBAL_INFO, + GET_GPU_STATS_APP_INFO, // Always append new enum to the end. }; diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index 400daf0512..247dc8d9d2 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -860,6 +860,59 @@ public: } return reply.readInt32(); } + + virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to write interface token: %d", error); + return error; + } + error = data.writeStrongBinder(displayToken); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to write display token: %d", error); + return error; + } + error = remote()->transact(BnSurfaceComposer::GET_DISPLAY_BRIGHTNESS_SUPPORT, data, &reply); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to transact: %d", error); + return error; + } + bool support; + error = reply.readBool(&support); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to read support: %d", error); + return error; + } + *outSupport = support; + return NO_ERROR; + } + + virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const { + Parcel data, reply; + status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to write interface token: %d", error); + return error; + } + error = data.writeStrongBinder(displayToken); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to write display token: %d", error); + return error; + } + error = data.writeFloat(brightness); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to write brightness: %d", error); + return error; + } + error = remote()->transact(BnSurfaceComposer::SET_DISPLAY_BRIGHTNESS, data, &reply); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to transact: %d", error); + return error; + } + return NO_ERROR; + } }; // Out-of-line virtual method definition to trigger vtable emission in this @@ -1390,6 +1443,35 @@ status_t BnSurfaceComposer::onTransact( reply->writeInt32(result); return result; } + case GET_DISPLAY_BRIGHTNESS_SUPPORT: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken; + status_t error = data.readNullableStrongBinder(&displayToken); + if (error != NO_ERROR) { + ALOGE("getDisplayBrightnessSupport: failed to read display token: %d", error); + return error; + } + bool support = false; + error = getDisplayBrightnessSupport(displayToken, &support); + reply->writeBool(support); + return error; + } + case SET_DISPLAY_BRIGHTNESS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken; + status_t error = data.readNullableStrongBinder(&displayToken); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to read display token: %d", error); + return error; + } + float brightness = -1.0f; + error = data.readFloat(&brightness); + if (error != NO_ERROR) { + ALOGE("setDisplayBrightness: failed to read brightness: %d", error); + return error; + } + return setDisplayBrightness(displayToken, brightness); + } default: { return BBinder::onTransact(code, data, reply, flags); } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 6460325582..93b41914bf 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -816,7 +816,7 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { // The consumer doesn't send it back to prevent us from having two // file descriptors of the same fence. mFrameEventHistory->updateAcquireFence(mNextFrameNumber, - std::make_shared<FenceTime>(std::move(fence))); + std::make_shared<FenceTime>(fence)); // Cache timestamps of signaled fences so we can close their file // descriptors. diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index d6708ab706..b0e827536d 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -1503,6 +1503,17 @@ status_t SurfaceComposerClient::removeRegionSamplingListener( return ComposerService::getComposerService()->removeRegionSamplingListener(listener); } +bool SurfaceComposerClient::getDisplayBrightnessSupport(const sp<IBinder>& displayToken) { + bool support = false; + ComposerService::getComposerService()->getDisplayBrightnessSupport(displayToken, &support); + return support; +} + +status_t SurfaceComposerClient::setDisplayBrightness(const sp<IBinder>& displayToken, + float brightness) { + return ComposerService::getComposerService()->setDisplayBrightness(displayToken, brightness); +} + // ---------------------------------------------------------------------------- status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 0e576ca958..3dffa8f8bc 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -376,6 +376,37 @@ public: */ virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, std::vector<int32_t>* outAllowedConfigs) = 0; + /* + * Gets whether brightness operations are supported on a display. + * + * displayToken + * The token of the display. + * outSupport + * An output parameter for whether brightness operations are supported. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display is invalid, or + * BAD_VALUE if the output parameter is invalid. + */ + virtual status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const = 0; + + /* + * Sets the brightness of a display. + * + * displayToken + * The token of the display whose brightness is set. + * brightness + * A number between 0.0f (minimum brightness) and 1.0 (maximum brightness), or -1.0f to + * turn the backlight off. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display is invalid, or + * BAD_VALUE if the brightness is invalid, or + * INVALID_OPERATION if brightness operations are not supported. + */ + virtual status_t setDisplayBrightness(const sp<IBinder>& displayToken, + float brightness) const = 0; }; // ---------------------------------------------------------------------------- @@ -425,6 +456,8 @@ public: REMOVE_REGION_SAMPLING_LISTENER, SET_ALLOWED_DISPLAY_CONFIGS, GET_ALLOWED_DISPLAY_CONFIGS, + GET_DISPLAY_BRIGHTNESS_SUPPORT, + SET_DISPLAY_BRIGHTNESS, // Always append new enum to the end. }; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 48c978f20f..39d6d13682 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -168,6 +168,32 @@ public: // Queries whether a given display is wide color display. static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay); + /* + * Returns whether brightness operations are supported on a display. + * + * displayToken + * The token of the display. + * + * Returns whether brightness operations are supported on a display or not. + */ + static bool getDisplayBrightnessSupport(const sp<IBinder>& displayToken); + + /* + * Sets the brightness of a display. + * + * displayToken + * The token of the display whose brightness is set. + * brightness + * A number between 0.0 (minimum brightness) and 1.0 (maximum brightness), or -1.0f to + * turn the backlight off. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display handle is invalid, or + * BAD_VALUE if the brightness value is invalid, or + * INVALID_OPERATION if brightness operaetions are not supported. + */ + static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness); + // ------------------------------------------------------------------------ // surface creation / destruction diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index a7599e0787..06fe86c0cf 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -669,6 +669,14 @@ public: status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; } status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; } + status_t getDisplayBrightnessSupport(const sp<IBinder>& /*displayToken*/, + bool* /*outSupport*/) const override { + return NO_ERROR; + } + status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/, + float /*brightness*/) const override { + return NO_ERROR; + } status_t addRegionSamplingListener(const Rect& /*samplingArea*/, const sp<IBinder>& /*stopLayerHandle*/, diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 5e5cf35987..59fa1c0c33 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -67,6 +67,14 @@ status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outS return OK; } +status_t GpuService::getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const { + ATRACE_CALL(); + + mGpuStats->pullAppStats(outStats); + + return OK; +} + status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) { ATRACE_CALL(); diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h index b984e0f25e..7a9b2d4dbf 100644 --- a/services/gpuservice/GpuService.h +++ b/services/gpuservice/GpuService.h @@ -48,6 +48,7 @@ private: const std::string& appPackageName, GraphicsEnv::Driver driver, bool isDriverLoaded, int64_t driverLoadingTime) override; status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const override; + status_t getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const override; /* * IBinder interface diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp index 146e2c29b1..618530523a 100644 --- a/services/gpuservice/gpustats/GpuStats.cpp +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -53,10 +53,12 @@ static void addLoadingTime(GraphicsEnv::Driver driver, int64_t driverLoadingTime switch (driver) { case GraphicsEnv::Driver::GL: case GraphicsEnv::Driver::GL_UPDATED: + if (outAppInfo->glDriverLoadingTime.size() >= GpuStats::MAX_NUM_LOADING_TIMES) break; outAppInfo->glDriverLoadingTime.emplace_back(driverLoadingTime); break; case GraphicsEnv::Driver::VULKAN: case GraphicsEnv::Driver::VULKAN_UPDATED: + if (outAppInfo->vkDriverLoadingTime.size() >= GpuStats::MAX_NUM_LOADING_TIMES) break; outAppInfo->vkDriverLoadingTime.emplace_back(driverLoadingTime); break; default: @@ -198,4 +200,18 @@ void GpuStats::pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats) { mGlobalStats.clear(); } +void GpuStats::pullAppStats(std::vector<GpuStatsAppInfo>* outStats) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mLock); + outStats->clear(); + outStats->reserve(mAppStats.size()); + + for (const auto& ele : mAppStats) { + outStats->emplace_back(ele.second); + } + + mAppStats.clear(); +} + } // namespace android diff --git a/services/gpuservice/gpustats/GpuStats.h b/services/gpuservice/gpustats/GpuStats.h index 9cdcd957b1..d942154cbb 100644 --- a/services/gpuservice/gpustats/GpuStats.h +++ b/services/gpuservice/gpustats/GpuStats.h @@ -41,6 +41,11 @@ public: void dump(const Vector<String16>& args, std::string* result); // Pull gpu global stats void pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats); + // Pull gpu app stats + void pullAppStats(std::vector<GpuStatsAppInfo>* outStats); + + // This limits the worst case number of loading times tracked. + static const size_t MAX_NUM_LOADING_TIMES = 50; private: // Dump global stats @@ -48,9 +53,9 @@ private: // Dump app stats void dumpAppLocked(std::string* result); - // This limits the memory usage of GpuStats to be less than 30KB. This is - // the maximum atom size statsd could afford. - static const size_t MAX_NUM_APP_RECORDS = 300; + // Below limits the memory usage of GpuStats to be less than 10KB. This is + // the preferred number for statsd while maintaining nice data quality. + static const size_t MAX_NUM_APP_RECORDS = 100; // GpuStats access should be guarded by mLock. std::mutex mLock; // Key is driver version code. diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 3905ed05b0..b4db338687 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -40,6 +40,7 @@ using android::base::StringPrintf; using android::hardware::hidl_bitfield; using android::hardware::hidl_vec; +using android::hardware::Return; using namespace android::hardware::input; namespace android { @@ -548,23 +549,28 @@ void MotionClassifier::callInputClassifierHal() { ensureHalThread(__func__); while (true) { ClassifierEvent event = mEvents.pop(); + bool halResponseOk = true; switch (event.type) { case ClassifierEventType::MOTION: { NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get()); common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs); - common::V1_0::Classification halClassification = mService->classify(motionEvent); - updateClassification(motionArgs->deviceId, motionArgs->eventTime, - getMotionClassification(halClassification)); + Return<common::V1_0::Classification> response = mService->classify(motionEvent); + halResponseOk = response.isOk(); + if (halResponseOk) { + common::V1_0::Classification halClassification = response; + updateClassification(motionArgs->deviceId, motionArgs->eventTime, + getMotionClassification(halClassification)); + } break; } case ClassifierEventType::DEVICE_RESET: { const int32_t deviceId = *(event.getDeviceId()); - mService->resetDevice(deviceId); + halResponseOk = mService->resetDevice(deviceId).isOk(); setClassification(deviceId, MotionClassification::NONE); break; } case ClassifierEventType::HAL_RESET: { - mService->reset(); + halResponseOk = mService->reset().isOk(); clearClassifications(); break; } @@ -573,6 +579,21 @@ void MotionClassifier::callInputClassifierHal() { return; } } + if (!halResponseOk) { + ALOGE("Error communicating with InputClassifier HAL. " + "Exiting MotionClassifier HAL thread"); + clearClassifications(); + return; + } + } +} + +void MotionClassifier::enqueueEvent(ClassifierEvent&& event) { + bool eventAdded = mEvents.push(std::move(event)); + if (!eventAdded) { + // If the queue is full, suspect the HAL is slow in processing the events. + ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime); + reset(); } } @@ -617,22 +638,12 @@ void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) { } MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) { - if (!mService) { - // If HAL is not present, do nothing - return MotionClassification::NONE; - } if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) { updateLastDownTime(args.deviceId, args.downTime); } ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args)); - bool elementAdded = mEvents.push(std::move(event)); - if (!elementAdded) { - // Queue should not ever overfill. Suspect HAL is slow. - ALOGE("Dropped element with eventTime %" PRIu64, args.eventTime); - reset(); - return MotionClassification::NONE; - } + enqueueEvent(std::move(event)); return getClassification(args.deviceId); } @@ -652,7 +663,7 @@ void MotionClassifier::reset(const NotifyDeviceResetArgs& args) { std::optional<int32_t> eventDeviceId = event.getDeviceId(); return eventDeviceId && (*eventDeviceId == deviceId); }); - mEvents.push(std::make_unique<NotifyDeviceResetArgs>(args)); + enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args)); } void MotionClassifier::dump(std::string& dump) { @@ -679,20 +690,39 @@ void MotionClassifier::dump(std::string& dump) { } } + // --- InputClassifier --- InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : mListener(listener) { - if (deepPressEnabled()) { - sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = - classifier::V1_0::IInputClassifier::getService(); - if (service) { - mMotionClassifier = std::make_unique<MotionClassifier>(service); - } else { - ALOGI("Could not obtain InputClassifier HAL"); - } + // The rest of the initialization is done in onFirstRef, because we need to obtain + // an sp to 'this' in order to register for HAL death notifications +} + +void InputClassifier::onFirstRef() { + std::scoped_lock lock(mLock); + if (!deepPressEnabled()) { + // If feature is not enabled, the InputClassifier will just be in passthrough + // mode, and will forward all events to the next InputListener, unmodified + return; + } + + sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = + classifier::V1_0::IInputClassifier::getService(); + if (!service) { + // Not really an error, maybe the device does not have this HAL, + // but somehow the feature flag is flipped + ALOGI("Could not obtain InputClassifier HAL"); + return; + } + const bool linked = service->linkToDeath(this, 0 /* cookie */).withDefault(false); + if (!linked) { + ALOGE("Could not link android::InputClassifier to the HAL death"); + return; } -}; + + mMotionClassifier = std::make_unique<MotionClassifier>(service); +} void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through @@ -705,12 +735,17 @@ void InputClassifier::notifyKey(const NotifyKeyArgs* args) { } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { - NotifyMotionArgs copyArgs = NotifyMotionArgs(*args); - if (mMotionClassifier && isTouchEvent(*args)) { - // We only cover touch events, for now. - copyArgs.classification = mMotionClassifier->classify(copyArgs); + std::scoped_lock lock(mLock); + // MotionClassifier is only used for touch events, for now + const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); + if (!sendToMotionClassifier) { + mListener->notifyMotion(args); + return; } - mListener->notifyMotion(©Args); + + NotifyMotionArgs newArgs(*args); + newArgs.classification = mMotionClassifier->classify(newArgs); + mListener->notifyMotion(&newArgs); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { @@ -719,6 +754,7 @@ void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { } void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + std::scoped_lock lock(mLock); if (mMotionClassifier) { mMotionClassifier->reset(*args); } @@ -726,7 +762,19 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); } +void InputClassifier::serviceDied(uint64_t /*cookie*/, + const wp<android::hidl::base::V1_0::IBase>& who) { + std::scoped_lock lock(mLock); + ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null"); + mMotionClassifier = nullptr; + sp<android::hidl::base::V1_0::IBase> service = who.promote(); + if (service) { + service->unlinkToDeath(this); + } +} + void InputClassifier::dump(std::string& dump) { + std::scoped_lock lock(mLock); dump += "Input Classifier State:\n"; dump += INDENT1 "Motion Classifier:\n"; diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index b97357dfd7..0b1483fdd5 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -69,7 +69,13 @@ public: * provide a MotionClassification for the current gesture. */ virtual MotionClassification classify(const NotifyMotionArgs& args) = 0; + /** + * Reset all internal HAL state. + */ virtual void reset() = 0; + /** + * Reset HAL state for a specific device. + */ virtual void reset(const NotifyDeviceResetArgs& args) = 0; /** @@ -107,6 +113,9 @@ protected: */ class MotionClassifier final : public MotionClassifierInterface { public: + /** + * The provided pointer to the service cannot be null. + */ MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service); ~MotionClassifier(); /** @@ -128,6 +137,10 @@ private: // The events that need to be sent to the HAL. BlockingQueue<ClassifierEvent> mEvents; /** + * Add an event to the queue mEvents. + */ + void enqueueEvent(ClassifierEvent&& event); + /** * Thread that will communicate with InputClassifier HAL. * This should be the only thread that communicates with InputClassifier HAL, * because this thread is allowed to block on the HAL calls. @@ -143,9 +156,9 @@ private: */ void callInputClassifierHal(); /** - * Access to the InputClassifier HAL + * Access to the InputClassifier HAL. Can always be safely dereferenced. */ - sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService; + const sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService; std::mutex mLock; /** * Per-device input classifications. Should only be accessed using the @@ -184,15 +197,19 @@ private: void requestExit(); }; + /** * Implementation of the InputClassifierInterface. * Represents a separate stage of input processing. All of the input events go through this stage. * Acts as a passthrough for all input events except for motion events. * The events of motion type are sent to MotionClassifier. */ -class InputClassifier : public InputClassifierInterface { +class InputClassifier : public InputClassifierInterface, + public android::hardware::hidl_death_recipient { public: explicit InputClassifier(const sp<InputListenerInterface>& listener); + // Some of the constructor logic is finished in onFirstRef + virtual void onFirstRef() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -200,10 +217,15 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + virtual void serviceDied(uint64_t cookie, + const wp<android::hidl::base::V1_0::IBase>& who) override; + virtual void dump(std::string& dump) override; private: - std::unique_ptr<MotionClassifierInterface> mMotionClassifier = nullptr; + // Protect access to mMotionClassifier, since it may become null via a hidl callback + std::mutex mLock; + std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); // The next stage to pass input events to sp<InputListenerInterface> mListener; }; diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index 7884362570..4751e5f122 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -105,6 +105,10 @@ bool BufferLayer::isFixedSize() const { return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE; } +bool BufferLayer::usesSourceCrop() const { + return true; +} + static constexpr mat4 inverseOrientation(uint32_t transform) { const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); @@ -204,8 +208,16 @@ bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& } const Rect win{getBounds()}; - const float bufferWidth = getBufferSize(s).getWidth(); - const float bufferHeight = getBufferSize(s).getHeight(); + float bufferWidth = getBufferSize(s).getWidth(); + float bufferHeight = getBufferSize(s).getHeight(); + + // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has + // been set and there is no parent layer bounds. In that case, the scale is meaningless so + // ignore them. + if (!getBufferSize(s).isValid()) { + bufferWidth = float(win.right) - float(win.left); + bufferHeight = float(win.bottom) - float(win.top); + } const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight; const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth; diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index 8149cba0cc..dc103cbada 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -78,6 +78,8 @@ public: // isFixedSize - true if content has a fixed size bool isFixedSize() const override; + bool usesSourceCrop() const override; + bool isHdrY410() const override; void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, @@ -181,6 +183,8 @@ private: // main thread. bool mBufferLatched{false}; // TODO: Use mActiveBuffer? + // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame + // and its parent layer is not bounded Rect getBufferSize(const State& s) const override; std::shared_ptr<compositionengine::Layer> mCompositionLayer; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index b2383ad8a7..1ca0b029df 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -156,9 +156,22 @@ bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInver } bool BufferStateLayer::setCrop(const Rect& crop) { - if (mCurrentState.crop == crop) return false; + Rect c = crop; + if (c.left < 0) { + c.left = 0; + } + if (c.top < 0) { + c.top = 0; + } + // If the width and/or height are < 0, make it [0, 0, -1, -1] so the equality comparision below + // treats all invalid rectangles the same. + if (!c.isValid()) { + c.makeInvalid(); + } + + if (mCurrentState.crop == c) return false; mCurrentState.sequence++; - mCurrentState.crop = crop; + mCurrentState.crop = c; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); return true; @@ -327,10 +340,6 @@ Rect BufferStateLayer::getBufferSize(const State& s) const { } } - // if there is no parent layer, use the buffer's bounds as the buffer size - if (s.buffer) { - return s.buffer->getBounds(); - } return Rect::INVALID_RECT; } @@ -345,6 +354,14 @@ FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) c return parentBounds; } +void BufferStateLayer::setPostTime(nsecs_t postTime) { + mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime); +} + +void BufferStateLayer::setDesiredPresentTime(nsecs_t desiredPresentTime) { + mDesiredPresentTime = desiredPresentTime; +} + // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- @@ -359,8 +376,7 @@ bool BufferStateLayer::fenceHasSignaled() const { } nsecs_t BufferStateLayer::getDesiredPresentTime() { - // TODO(marissaw): support an equivalent to desiredPresentTime for timestats metrics - return 0; + return mDesiredPresentTime; } std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const { @@ -532,8 +548,6 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse } } - // TODO(marissaw): properly support mTimeStats - mFlinger->mTimeStats->setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime); mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime()); mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime); diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index 97662e8ff7..668830a3fe 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -89,6 +89,9 @@ public: Rect getBufferSize(const State& s) const override; FloatRect computeSourceBounds(const FloatRect& parentBounds) const override; + + void setPostTime(nsecs_t postTime) override; + void setDesiredPresentTime(nsecs_t desiredPresentTime) override; // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- @@ -149,6 +152,8 @@ private: bool mReleasePreviousBuffer = false; nsecs_t mCallbackHandleAcquireTime = -1; + nsecs_t mDesiredPresentTime = -1; + // TODO(marissaw): support sticky transform for LEGACY camera mode }; diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp index a2692bc592..3364399b35 100644 --- a/services/surfaceflinger/ColorLayer.cpp +++ b/services/surfaceflinger/ColorLayer.cpp @@ -79,6 +79,18 @@ bool ColorLayer::setColor(const half3& color) { return true; } +bool ColorLayer::setDataspace(ui::Dataspace dataspace) { + if (mCurrentState.dataspace == dataspace) { + return false; + } + + mCurrentState.sequence++; + mCurrentState.dataspace = dataspace; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, const Rect& viewport, int32_t /* supportedPerFrameMetadata */, @@ -148,6 +160,11 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display, layerCompositionState.surfaceDamage = surfaceDamageRegion; } +void ColorLayer::commitTransaction(const State& stateToCommit) { + Layer::commitTransaction(stateToCommit); + mCurrentDataSpace = mDrawingState.dataspace; +} + std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const { return mCompositionLayer; } diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h index 9a72b4026f..53d5b5b605 100644 --- a/services/surfaceflinger/ColorLayer.h +++ b/services/surfaceflinger/ColorLayer.h @@ -35,18 +35,21 @@ public: bool setColor(const half3& color) override; + bool setDataspace(ui::Dataspace dataspace) override; + void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, const Rect& viewport, int32_t supportedPerFrameMetadata, const ui::Dataspace targetDataspace) override; + void commitTransaction(const State& stateToCommit) override; + bool onPreComposition(nsecs_t /*refreshStartTime*/) override { return false; } protected: - FloatRect computeCrop(const sp<const DisplayDevice>& /*display*/) const override { return {}; } - bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, - bool useIdentityTransform, Region& clearRegion, - const bool supportProtectedContent, - renderengine::LayerSettings& layer) override; + virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, + bool useIdentityTransform, Region& clearRegion, + const bool supportProtectedContent, + renderengine::LayerSettings& layer); private: std::shared_ptr<compositionengine::Layer> mCompositionLayer; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index 6cc87ba79f..9f635b9a35 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -24,12 +24,21 @@ class Fence; namespace compositionengine { +struct LayerFECompositionState; + // Defines the interface used by the CompositionEngine to make requests // of the front-end layer class LayerFE : public virtual RefBase { public: + // Latches the output-independent state. If includeGeometry is false, the + // geometry state can be skipped. + virtual void latchCompositionState(LayerFECompositionState&, bool includeGeometry) const = 0; + // Called after the layer is displayed to update the presentation fence virtual void onLayerDisplayed(const sp<Fence>&) = 0; + + // Gets some kind of identifier for the layer for debug purposes. + virtual const char* getDebugName() const = 0; }; } // namespace compositionengine diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 785a6d7fbe..e6ee078624 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -41,6 +41,22 @@ struct LayerFECompositionState { Region geomVisibleRegion; /* + * Geometry state + */ + + bool isSecure{false}; + bool geomUsesSourceCrop{false}; + bool geomBufferUsesDisplayInverseTransform{false}; + uint32_t geomBufferTransform{0}; + ui::Transform geomLayerTransform; + ui::Transform geomInverseLayerTransform; + Rect geomBufferSize; + Rect geomContentCrop; + Rect geomCrop; + Region geomActiveTransparentRegion; + FloatRect geomLayerBounds; + + /* * Presentation */ diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index e7a17c474e..cd63b57d86 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -64,6 +64,15 @@ public: // TODO(lpique): Make this protected once it is only internally called. virtual CompositionState& editState() = 0; + // Recalculates the state of the output layer from the output-independent + // layer. If includeGeometry is false, the geometry state can be skipped. + virtual void updateCompositionState(bool includeGeometry) = 0; + + // Writes the geometry state to the HWC, or does nothing if this layer does + // not use the HWC. If includeGeometry is false, the geometry state can be + // skipped. + virtual void writeStateToHWC(bool includeGeometry) const = 0; + // Debugging virtual void dump(std::string& result) const = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index d8f0cdd5e8..6a4818f10f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -21,6 +21,8 @@ #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> +#include <ui/FloatRect.h> +#include <ui/Rect.h> #include "DisplayHardware/DisplayIdentification.h" @@ -41,9 +43,18 @@ public: const OutputLayerCompositionState& getState() const override; OutputLayerCompositionState& editState() override; + void updateCompositionState(bool) override; + void writeStateToHWC(bool) const override; + void dump(std::string& result) const override; + virtual FloatRect calculateOutputSourceCrop() const; + virtual Rect calculateOutputDisplayFrame() const; + virtual uint32_t calculateOutputRelativeBufferTransform() const; + private: + Rect calculateInitialCrop() const; + const compositionengine::Output& mOutput; std::shared_ptr<compositionengine::Layer> mLayer; sp<compositionengine::LayerFE> mLayerFE; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index a0c2a63a73..aab18db3c9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -17,6 +17,7 @@ #pragma once #include <compositionengine/LayerFE.h> +#include <compositionengine/LayerFECompositionState.h> #include <gmock/gmock.h> #include <ui/Fence.h> @@ -29,7 +30,10 @@ public: LayerFE(); virtual ~LayerFE(); + MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool)); MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&)); + + MOCK_CONST_METHOD0(getDebugName, const char*()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 54c7407a98..29cd08a681 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -38,6 +38,9 @@ public: MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&()); MOCK_METHOD0(editState, impl::OutputLayerCompositionState&()); + MOCK_METHOD1(updateCompositionState, void(bool)); + MOCK_CONST_METHOD1(writeStateToHWC, void(bool)); + MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp index c49701315b..959843050c 100644 --- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp @@ -24,7 +24,7 @@ namespace android::compositionengine::impl { using android::base::StringAppendF; void dumpVal(std::string& out, const char* name, bool value) { - StringAppendF(&out, "%s=%c ", name, value ? 'T' : 'F'); + StringAppendF(&out, "%s=%s ", name, value ? "true" : "false"); } void dumpVal(std::string& out, const char* name, const void* value) { @@ -56,7 +56,7 @@ void dumpVal(std::string& out, const char* name, const std::string& value) { } void dumpVal(std::string& out, const char* name, const char* valueName, int value) { - StringAppendF(&out, "%s=%s (%d)", name, valueName, value); + StringAppendF(&out, "%s=%s (%d) ", name, valueName, value); } void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) { diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp index 109e9f8438..96e9731768 100644 --- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp @@ -52,7 +52,9 @@ LayerCompositionState& Layer::editState() { } void Layer::dump(std::string& out) const { - android::base::StringAppendF(&out, " Layer %p\n", this); + auto layerFE = getLayerFE(); + android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this, + layerFE ? layerFE->getDebugName() : "<unknown>"); mState.dump(out); } diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp index 517b641594..40c4da97a8 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp @@ -31,6 +31,25 @@ void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color va void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) { out.append(" "); + dumpVal(out, "isSecure", state.isSecure); + dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop); + dumpVal(out, "geomBufferUsesDisplayInverseTransform", + state.geomBufferUsesDisplayInverseTransform); + dumpVal(out, "geomLayerTransform", state.geomLayerTransform); + + out.append("\n "); + dumpVal(out, "geomBufferSize", state.geomBufferSize); + dumpVal(out, "geomContentCrop", state.geomContentCrop); + dumpVal(out, "geomCrop", state.geomCrop); + dumpVal(out, "geomBufferTransform", state.geomBufferTransform); + + out.append("\n "); + dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion); + + out.append(" "); + dumpVal(out, "geomLayerBounds", state.geomLayerBounds); + + out.append("\n "); dumpVal(out, "blend", toString(state.blendMode), state.blendMode); dumpVal(out, "alpha", state.alpha); @@ -61,7 +80,7 @@ void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) { } // namespace void LayerCompositionState::dump(std::string& out) const { - out.append(" frontend:\n"); + out.append(" frontend:\n"); dumpFrontEnd(out, frontEnd); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index ad4c7bf501..d22bdaf625 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -147,7 +147,7 @@ void Output::dumpBase(std::string& out) const { out.append(" No render surface!\n"); } - out.append("\n %d Layers", mOutputLayersOrderedByZ.size()); + android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size()); for (const auto& outputLayer : mOutputLayersOrderedByZ) { if (!outputLayer) { continue; diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 78807ffa9f..9549054bd6 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -20,6 +20,7 @@ namespace android::compositionengine::impl { void OutputCompositionState::dump(std::string& out) const { + out.append(" "); dumpVal(out, "isEnabled", isEnabled); dumpVal(out, "isSecure", isSecure); @@ -37,7 +38,7 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "scissor", scissor); dumpVal(out, "needsFiltering", needsFiltering); - out.append("\n"); + out.append("\n "); dumpVal(out, "colorMode", toString(colorMode), colorMode); dumpVal(out, "renderIntent", toString(renderIntent), renderIntent); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 10da49dc5c..379ad87922 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -19,7 +19,10 @@ #include <compositionengine/Layer.h> #include <compositionengine/LayerFE.h> #include <compositionengine/Output.h> +#include <compositionengine/impl/LayerCompositionState.h> +#include <compositionengine/impl/OutputCompositionState.h> #include <compositionengine/impl/OutputLayer.h> +#include <compositionengine/impl/OutputLayerCompositionState.h> #include "DisplayHardware/HWComposer.h" @@ -29,6 +32,18 @@ OutputLayer::~OutputLayer() = default; namespace impl { +namespace { + +FloatRect reduce(const FloatRect& win, const Region& exclude) { + if (CC_LIKELY(exclude.isEmpty())) { + return win; + } + // Convert through Rect (by rounding) for lack of FloatRegion + return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect(); +} + +} // namespace + std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId, const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer, @@ -77,10 +92,296 @@ OutputLayerCompositionState& OutputLayer::editState() { return mState; } +Rect OutputLayer::calculateInitialCrop() const { + const auto& layerState = mLayer->getState().frontEnd; + + // apply the projection's clipping to the window crop in + // layerstack space, and convert-back to layer space. + // if there are no window scaling involved, this operation will map to full + // pixels in the buffer. + + FloatRect activeCropFloat = + reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion); + + const Rect& viewport = mOutput.getState().viewport; + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; + // Transform to screen space. + activeCropFloat = layerTransform.transform(activeCropFloat); + activeCropFloat = activeCropFloat.intersect(viewport.toFloatRect()); + // Back to layer space to work with the content crop. + activeCropFloat = inverseLayerTransform.transform(activeCropFloat); + + // This needs to be here as transform.transform(Rect) computes the + // transformed rect and then takes the bounding box of the result before + // returning. This means + // transform.inverse().transform(transform.transform(Rect)) != Rect + // in which case we need to make sure the final rect is clipped to the + // display bounds. + Rect activeCrop{activeCropFloat}; + if (!activeCrop.intersect(layerState.geomBufferSize, &activeCrop)) { + activeCrop.clear(); + } + return activeCrop; +} + +FloatRect OutputLayer::calculateOutputSourceCrop() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + if (!layerState.geomUsesSourceCrop) { + return {}; + } + + // the content crop is the area of the content that gets scaled to the + // layer's size. This is in buffer space. + FloatRect crop = layerState.geomContentCrop.toFloatRect(); + + // In addition there is a WM-specified crop we pull from our drawing state. + Rect activeCrop = calculateInitialCrop(); + const Rect& bufferSize = layerState.geomBufferSize; + + int winWidth = bufferSize.getWidth(); + int winHeight = bufferSize.getHeight(); + + // The bufferSize for buffer state layers can be unbounded ([0, 0, -1, -1]) + // if display frame hasn't been set and the parent is an unbounded layer. + if (winWidth < 0 && winHeight < 0) { + return crop; + } + + // Transform the window crop to match the buffer coordinate system, + // which means using the inverse of the current transform set on the + // SurfaceFlingerConsumer. + uint32_t invTransform = layerState.geomBufferTransform; + if (layerState.geomBufferUsesDisplayInverseTransform) { + /* + * the code below applies the primary display's inverse transform to the + * buffer + */ + uint32_t invTransformOrient = outputState.orientation; + // calculate the inverse transform + if (invTransformOrient & HAL_TRANSFORM_ROT_90) { + invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + // and apply to the current transform + invTransform = + (ui::Transform(invTransformOrient) * ui::Transform(invTransform)).getOrientation(); + } + + if (invTransform & HAL_TRANSFORM_ROT_90) { + // If the activeCrop has been rotate the ends are rotated but not + // the space itself so when transforming ends back we can't rely on + // a modification of the axes of rotation. To account for this we + // need to reorient the inverse rotation in terms of the current + // axes of rotation. + bool is_h_flipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0; + bool is_v_flipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0; + if (is_h_flipped == is_v_flipped) { + invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + std::swap(winWidth, winHeight); + } + const Rect winCrop = + activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight()); + + // below, crop is intersected with winCrop expressed in crop's coordinate space + float xScale = crop.getWidth() / float(winWidth); + float yScale = crop.getHeight() / float(winHeight); + + float insetL = winCrop.left * xScale; + float insetT = winCrop.top * yScale; + float insetR = (winWidth - winCrop.right) * xScale; + float insetB = (winHeight - winCrop.bottom) * yScale; + + crop.left += insetL; + crop.top += insetT; + crop.right -= insetR; + crop.bottom -= insetB; + + return crop; +} + +Rect OutputLayer::calculateOutputDisplayFrame() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + // apply the layer's transform, followed by the display's global transform + // here we're guaranteed that the layer's transform preserves rects + Region activeTransparentRegion = layerState.geomActiveTransparentRegion; + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; + const Rect& bufferSize = layerState.geomBufferSize; + Rect activeCrop = layerState.geomCrop; + if (!activeCrop.isEmpty() && bufferSize.isValid()) { + activeCrop = layerTransform.transform(activeCrop); + if (!activeCrop.intersect(outputState.viewport, &activeCrop)) { + activeCrop.clear(); + } + activeCrop = inverseLayerTransform.transform(activeCrop, true); + // This needs to be here as transform.transform(Rect) computes the + // transformed rect and then takes the bounding box of the result before + // returning. This means + // transform.inverse().transform(transform.transform(Rect)) != Rect + // in which case we need to make sure the final rect is clipped to the + // display bounds. + if (!activeCrop.intersect(bufferSize, &activeCrop)) { + activeCrop.clear(); + } + // mark regions outside the crop as transparent + activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top)); + activeTransparentRegion.orSelf( + Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight())); + activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); + activeTransparentRegion.orSelf( + Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom)); + } + + // reduce uses a FloatRect to provide more accuracy during the + // transformation. We then round upon constructing 'frame'. + Rect frame{ + layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))}; + if (!frame.intersect(outputState.viewport, &frame)) { + frame.clear(); + } + const ui::Transform displayTransform{outputState.transform}; + + return displayTransform.transform(frame); +} + +uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + /* + * Transformations are applied in this order: + * 1) buffer orientation/flip/mirror + * 2) state transformation (window manager) + * 3) layer orientation (screen orientation) + * (NOTE: the matrices are multiplied in reverse order) + */ + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform displayTransform{outputState.orientation}; + const ui::Transform bufferTransform{layerState.geomBufferTransform}; + ui::Transform transform(displayTransform * layerTransform * bufferTransform); + + if (layerState.geomBufferUsesDisplayInverseTransform) { + /* + * the code below applies the primary display's inverse transform to the + * buffer + */ + uint32_t invTransform = outputState.orientation; + // calculate the inverse transform + if (invTransform & HAL_TRANSFORM_ROT_90) { + invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + + /* + * Here we cancel out the orientation component of the WM transform. + * The scaling and translate components are already included in our bounds + * computation so it's enough to just omit it in the composition. + * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why. + */ + transform = + ui::Transform(invTransform) * displayTransform * layerTransform * bufferTransform; + } + + // this gives us only the "orientation" component of the transform + return transform.getOrientation(); +} // namespace impl + +void OutputLayer::updateCompositionState(bool includeGeometry) { + if (includeGeometry) { + mState.displayFrame = calculateOutputDisplayFrame(); + mState.sourceCrop = calculateOutputSourceCrop(); + mState.bufferTransform = + static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform()); + + if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) || + (mState.bufferTransform & ui::Transform::ROT_INVALID)) { + mState.forceClientComposition = true; + } + } +} + +void OutputLayer::writeStateToHWC(bool includeGeometry) const { + // Skip doing this if there is no HWC interface + if (!mState.hwc) { + return; + } + + auto& hwcLayer = (*mState.hwc).hwcLayer; + if (!hwcLayer) { + ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s", + mLayerFE->getDebugName(), mOutput.getName().c_str()); + return; + } + + if (includeGeometry) { + // Output dependent state + + if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", + mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top, + mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " + "%s (%d)", + mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top, + mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z, + to_string(error).c_str(), static_cast<int32_t>(error)); + } + + if (auto error = + hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(), + toString(mState.bufferTransform).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + // Output independent state + + const auto& outputIndependentState = mLayer->getState().frontEnd; + + if (auto error = hwcLayer->setBlendMode( + static_cast<HWC2::BlendMode>(outputIndependentState.blendMode)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(), + toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(), + outputIndependentState.alpha, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = + hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + } +} + void OutputLayer::dump(std::string& out) const { using android::base::StringAppendF; - StringAppendF(&out, " Output Layer %p\n", this); + StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(), + mLayerFE->getDebugName()); mState.dump(out); } diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp index 10f27b86f9..861ea5757b 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp @@ -46,7 +46,7 @@ void OutputLayerCompositionState::dump(std::string& out) const { dumpVal(out, "clearClientTarget", clearClientTarget); dumpVal(out, "displayFrame", displayFrame); dumpVal(out, "sourceCrop", sourceCrop); - dumpVal(out, "bufferTransform%", toString(bufferTransform), bufferTransform); + dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform); dumpVal(out, "z-index", z); if (hwc) { diff --git a/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h new file mode 100644 index 0000000000..6741cc9b7a --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/FloatRectMatcher.h @@ -0,0 +1,48 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> + +#include <android-base/stringprintf.h> +#include <gmock/gmock.h> + +namespace { + +using android::base::StringAppendF; +using FloatRect = android::FloatRect; + +void dumpFloatRect(const FloatRect& rect, std::string& result, const char* name) { + StringAppendF(&result, "%s (%f %f %f %f) ", name, rect.left, rect.top, rect.right, rect.bottom); +} + +// Checks for a region match +MATCHER_P(FloatRectEq, expected, "") { + std::string buf; + buf.append("FloatRects are not equal\n"); + dumpFloatRect(expected, buf, "expected rect"); + dumpFloatRect(arg, buf, "actual rect"); + *result_listener << buf; + + const float TOLERANCE = 1e-3f; + return (std::fabs(expected.left - arg.left) < TOLERANCE) && + (std::fabs(expected.top - arg.top) < TOLERANCE) && + (std::fabs(expected.right - arg.right) < TOLERANCE) && + (std::fabs(expected.bottom - arg.bottom) < TOLERANCE); +} + +} // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 885cdd4ec4..94349dea14 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -65,6 +65,8 @@ public: MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t)); MOCK_METHOD4(getDisplayedContentSample, status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*)); + MOCK_METHOD2(setDisplayBrightness, status_t(DisplayId, float)); + MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*)); MOCK_METHOD2(onHotplug, std::optional<DisplayIdentificationInfo>(hwc2_display_t, HWC2::Connection)); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index f7dcf6f08e..2504fa62ea 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -21,18 +21,40 @@ #include <compositionengine/mock/Output.h> #include <gtest/gtest.h> +#include "FloatRectMatcher.h" #include "MockHWC2.h" #include "MockHWComposer.h" +#include "RectMatcher.h" namespace android::compositionengine { namespace { +using testing::_; +using testing::Return; +using testing::ReturnRef; using testing::StrictMock; constexpr DisplayId DEFAULT_DISPLAY_ID = DisplayId{42}; +constexpr auto TR_IDENT = 0u; +constexpr auto TR_FLP_H = HAL_TRANSFORM_FLIP_H; +constexpr auto TR_FLP_V = HAL_TRANSFORM_FLIP_V; +constexpr auto TR_ROT_90 = HAL_TRANSFORM_ROT_90; +constexpr auto TR_ROT_180 = TR_FLP_H | TR_FLP_V; +constexpr auto TR_ROT_270 = TR_ROT_90 | TR_ROT_180; + +const std::string kOutputName{"Test Output"}; + class OutputLayerTest : public testing::Test { public: + OutputLayerTest() { + EXPECT_CALL(*mLayerFE, getDebugName()).WillRepeatedly(Return("Test LayerFE")); + EXPECT_CALL(mOutput, getName()).WillRepeatedly(ReturnRef(kOutputName)); + + EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState)); + EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState)); + } + ~OutputLayerTest() override = default; compositionengine::mock::Output mOutput; @@ -41,15 +63,18 @@ public: sp<compositionengine::mock::LayerFE> mLayerFE{ new StrictMock<compositionengine::mock::LayerFE>()}; impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE}; + + impl::LayerCompositionState mLayerState; + impl::OutputCompositionState mOutputState; }; -/* ------------------------------------------------------------------------ +/* * Basic construction */ TEST_F(OutputLayerTest, canInstantiateOutputLayer) {} -/* ------------------------------------------------------------------------ +/* * OutputLayer::initialize() */ @@ -71,15 +96,481 @@ TEST_F(OutputLayerTest, initializingOutputLayerWithHwcDisplayCreatesHwcLayer) { mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID); - const auto& state = mOutputLayer.getState(); - ASSERT_TRUE(state.hwc); + const auto& outputLayerState = mOutputLayer.getState(); + ASSERT_TRUE(outputLayerState.hwc); - const auto& hwcState = *state.hwc; + const auto& hwcState = *outputLayerState.hwc; EXPECT_EQ(&hwcLayer, hwcState.hwcLayer.get()); EXPECT_CALL(hwc, destroyLayer(DEFAULT_DISPLAY_ID, &hwcLayer)); mOutputLayer.editState().hwc.reset(); } +/* + * OutputLayer::calculateOutputSourceCrop() + */ + +struct OutputLayerSourceCropTest : public OutputLayerTest { + OutputLayerSourceCropTest() { + // Set reasonable default values for a simple case. Each test will + // set one specific value to something different. + mLayerState.frontEnd.geomUsesSourceCrop = true; + mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomActiveTransparentRegion = Region{}; + mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; + mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT}; + mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomBufferTransform = TR_IDENT; + + mOutputState.viewport = Rect{0, 0, 1920, 1080}; + } + + FloatRect calculateOutputSourceCrop() { + mLayerState.frontEnd.geomInverseLayerTransform = + mLayerState.frontEnd.geomLayerTransform.inverse(); + + return mOutputLayer.calculateOutputSourceCrop(); + } +}; + +TEST_F(OutputLayerSourceCropTest, computesEmptyIfSourceCropNotUsed) { + mLayerState.frontEnd.geomUsesSourceCrop = false; + + const FloatRect expected{}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, correctForSimpleDefaultCase) { + const FloatRect expected{0.f, 0.f, 1920.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewport) { + mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f}; + + const FloatRect expected{0.f, 0.f, 1920.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, handlesBoundsOutsideViewportRotated) { + mLayerState.frontEnd.geomLayerBounds = FloatRect{-2000.f, -2000.f, 2000.f, 2000.f}; + mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); + + const FloatRect expected{0.f, 0.f, 1080.f, 1080.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, calculateOutputSourceCropWorksWithATransformedBuffer) { + struct Entry { + uint32_t bufferInvDisplay; + uint32_t buffer; + uint32_t display; + FloatRect expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 12> testData = { + // clang-format off + // inv buffer display expected + /* 0 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 1 */ Entry{false, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 2 */ Entry{false, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 3 */ Entry{false, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + /* 4 */ Entry{true, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 5 */ Entry{true, TR_IDENT, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 6 */ Entry{true, TR_IDENT, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 7 */ Entry{true, TR_IDENT, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + /* 8 */ Entry{false, TR_IDENT, TR_IDENT, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 9 */ Entry{false, TR_ROT_90, TR_ROT_90, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 10 */ Entry{false, TR_ROT_180, TR_ROT_180, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + /* 11 */ Entry{false, TR_ROT_270, TR_ROT_270, FloatRect{0.f, 0.f, 1920.f, 1080.f}}, + + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = entry.bufferInvDisplay; + mLayerState.frontEnd.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(entry.expected)) << "entry " << i; + } +} + +TEST_F(OutputLayerSourceCropTest, geomContentCropAffectsCrop) { + mLayerState.frontEnd.geomContentCrop = Rect{0, 0, 960, 540}; + + const FloatRect expected{0.f, 0.f, 960.f, 540.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +TEST_F(OutputLayerSourceCropTest, viewportAffectsCrop) { + mOutputState.viewport = Rect{0, 0, 960, 540}; + + const FloatRect expected{0.f, 0.f, 960.f, 540.f}; + EXPECT_THAT(calculateOutputSourceCrop(), FloatRectEq(expected)); +} + +/* + * OutputLayer::calculateOutputDisplayFrame() + */ + +struct OutputLayerDisplayFrameTest : public OutputLayerTest { + OutputLayerDisplayFrameTest() { + // Set reasonable default values for a simple case. Each test will + // set one specific value to something different. + + mLayerState.frontEnd.geomActiveTransparentRegion = Region{}; + mLayerState.frontEnd.geomLayerTransform = ui::Transform{TR_IDENT}; + mLayerState.frontEnd.geomBufferSize = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false; + mLayerState.frontEnd.geomCrop = Rect{0, 0, 1920, 1080}; + mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 1920.f, 1080.f}; + + mOutputState.viewport = Rect{0, 0, 1920, 1080}; + mOutputState.transform = ui::Transform{TR_IDENT}; + } + + Rect calculateOutputDisplayFrame() { + mLayerState.frontEnd.geomInverseLayerTransform = + mLayerState.frontEnd.geomLayerTransform.inverse(); + + return mOutputLayer.calculateOutputDisplayFrame(); + } +}; + +TEST_F(OutputLayerDisplayFrameTest, correctForSimpleDefaultCase) { + const Rect expected{0, 0, 1920, 1080}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, fullActiveTransparentRegionReturnsEmptyFrame) { + mLayerState.frontEnd.geomActiveTransparentRegion = Region{Rect{0, 0, 1920, 1080}}; + const Rect expected{0, 0, 0, 0}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrame) { + mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500}; + const Rect expected{100, 200, 300, 500}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, cropAffectsDisplayFrameRotated) { + mLayerState.frontEnd.geomCrop = Rect{100, 200, 300, 500}; + mLayerState.frontEnd.geomLayerTransform.set(HAL_TRANSFORM_ROT_90, 1920, 1080); + const Rect expected{1420, 100, 1720, 300}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, emptyGeomCropIsNotUsedToComputeFrame) { + mLayerState.frontEnd.geomCrop = Rect{}; + const Rect expected{0, 0, 1920, 1080}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, geomLayerBoundsAffectsFrame) { + mLayerState.frontEnd.geomLayerBounds = FloatRect{0.f, 0.f, 960.f, 540.f}; + const Rect expected{0, 0, 960, 540}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, viewportAffectsFrame) { + mOutputState.viewport = Rect{0, 0, 960, 540}; + const Rect expected{0, 0, 960, 540}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +TEST_F(OutputLayerDisplayFrameTest, outputTransformAffectsDisplayFrame) { + mOutputState.transform = ui::Transform{HAL_TRANSFORM_ROT_90}; + const Rect expected{-1080, 0, 0, 1920}; + EXPECT_THAT(calculateOutputDisplayFrame(), RectEq(expected)); +} + +/* + * OutputLayer::calculateOutputRelativeBufferTransform() + */ + +TEST_F(OutputLayerTest, calculateOutputRelativeBufferTransformTestsNeeded) { + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = false; + + struct Entry { + uint32_t layer; + uint32_t buffer; + uint32_t display; + uint32_t expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 24> testData = { + // clang-format off + // layer buffer display expected + /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT}, + /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_ROT_90}, + /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_ROT_180}, + /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_ROT_270}, + + /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_IDENT}, + /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_90}, + /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_180}, + /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_ROT_270}, + + /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V}, + /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_180}, + /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_IDENT}, + /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_180}, + + /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_90}, + /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_180}, + /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT ^ TR_ROT_270}, + /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_IDENT}, + + /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_ROT_180}, + /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT ^ TR_ROT_270}, + /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_IDENT}, + /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_90}, + + /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_270}, + /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_IDENT}, + /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_90}, + /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_180}, + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerState.frontEnd.geomLayerTransform.set(entry.layer, 1920, 1080); + mLayerState.frontEnd.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(); + EXPECT_EQ(entry.expected, actual) << "entry " << i; + } +} + +TEST_F(OutputLayerTest, + calculateOutputRelativeBufferTransformTestWithOfBufferUsesDisplayInverseTransform) { + mLayerState.frontEnd.geomBufferUsesDisplayInverseTransform = true; + + struct Entry { + uint32_t layer; + uint32_t buffer; + uint32_t display; + uint32_t expected; + }; + // Not an exhaustive list of cases, but hopefully enough. + const std::array<Entry, 24> testData = { + // clang-format off + // layer buffer display expected + /* 0 */ Entry{TR_IDENT, TR_IDENT, TR_IDENT, TR_IDENT}, + /* 1 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_90, TR_IDENT}, + /* 2 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_180, TR_IDENT}, + /* 3 */ Entry{TR_IDENT, TR_IDENT, TR_ROT_270, TR_IDENT}, + + /* 4 */ Entry{TR_IDENT, TR_FLP_H, TR_IDENT, TR_FLP_H}, + /* 5 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_90, TR_FLP_H}, + /* 6 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_180, TR_FLP_H}, + /* 7 */ Entry{TR_IDENT, TR_FLP_H, TR_ROT_270, TR_FLP_H}, + + /* 8 */ Entry{TR_IDENT, TR_FLP_V, TR_IDENT, TR_FLP_V}, + /* 9 */ Entry{TR_IDENT, TR_ROT_90, TR_ROT_90, TR_ROT_90}, + /* 10 */ Entry{TR_IDENT, TR_ROT_180, TR_ROT_180, TR_ROT_180}, + /* 11 */ Entry{TR_IDENT, TR_ROT_270, TR_ROT_270, TR_ROT_270}, + + /* 12 */ Entry{TR_ROT_90, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_90}, + /* 13 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_90}, + /* 14 */ Entry{TR_ROT_90, TR_IDENT, TR_ROT_180, TR_IDENT ^ TR_ROT_90}, + /* 15 */ Entry{TR_ROT_90, TR_FLP_H, TR_ROT_270, TR_FLP_H ^ TR_ROT_90}, + + /* 16 */ Entry{TR_ROT_180, TR_FLP_H, TR_IDENT, TR_FLP_H ^ TR_ROT_180}, + /* 17 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_90, TR_IDENT ^ TR_ROT_180}, + /* 18 */ Entry{TR_ROT_180, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_180}, + /* 19 */ Entry{TR_ROT_180, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_180}, + + /* 20 */ Entry{TR_ROT_270, TR_IDENT, TR_IDENT, TR_IDENT ^ TR_ROT_270}, + /* 21 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_90, TR_FLP_H ^ TR_ROT_270}, + /* 22 */ Entry{TR_ROT_270, TR_FLP_H, TR_ROT_180, TR_FLP_H ^ TR_ROT_270}, + /* 23 */ Entry{TR_ROT_270, TR_IDENT, TR_ROT_270, TR_IDENT ^ TR_ROT_270}, + // clang-format on + }; + + for (size_t i = 0; i < testData.size(); i++) { + const auto& entry = testData[i]; + + mLayerState.frontEnd.geomLayerTransform = ui::Transform{entry.layer}; + mLayerState.frontEnd.geomBufferTransform = entry.buffer; + mOutputState.orientation = entry.display; + + auto actual = mOutputLayer.calculateOutputRelativeBufferTransform(); + EXPECT_EQ(entry.expected, actual) << "entry " << i; + } +} + +/* + * OutputLayer::updateCompositionState() + */ + +struct OutputLayerPartialMockForUpdateCompositionState : public impl::OutputLayer { + OutputLayerPartialMockForUpdateCompositionState(const compositionengine::Output& output, + std::shared_ptr<compositionengine::Layer> layer, + sp<compositionengine::LayerFE> layerFE) + : impl::OutputLayer(output, layer, layerFE) {} + // Mock everything called by updateCompositionState to simplify testing it. + MOCK_CONST_METHOD0(calculateOutputSourceCrop, FloatRect()); + MOCK_CONST_METHOD0(calculateOutputDisplayFrame, Rect()); + MOCK_CONST_METHOD0(calculateOutputRelativeBufferTransform, uint32_t()); +}; + +struct OutputLayerUpdateCompositionStateTest : public OutputLayerTest { +public: + OutputLayerUpdateCompositionStateTest() { + EXPECT_CALL(*mLayer, getState()).WillRepeatedly(ReturnRef(mLayerState)); + EXPECT_CALL(mOutput, getState()).WillRepeatedly(ReturnRef(mOutputState)); + } + + ~OutputLayerUpdateCompositionStateTest() = default; + + void setupGeometryChildCallValues() { + EXPECT_CALL(mOutputLayer, calculateOutputSourceCrop()).WillOnce(Return(kSourceCrop)); + EXPECT_CALL(mOutputLayer, calculateOutputDisplayFrame()).WillOnce(Return(kDisplayFrame)); + EXPECT_CALL(mOutputLayer, calculateOutputRelativeBufferTransform()) + .WillOnce(Return(mBufferTransform)); + } + + void validateComputedGeometryState() { + const auto& state = mOutputLayer.getState(); + EXPECT_EQ(kSourceCrop, state.sourceCrop); + EXPECT_EQ(kDisplayFrame, state.displayFrame); + EXPECT_EQ(static_cast<Hwc2::Transform>(mBufferTransform), state.bufferTransform); + } + + const FloatRect kSourceCrop{1.f, 2.f, 3.f, 4.f}; + const Rect kDisplayFrame{11, 12, 13, 14}; + uint32_t mBufferTransform{21}; + + using OutputLayer = OutputLayerPartialMockForUpdateCompositionState; + StrictMock<OutputLayer> mOutputLayer{mOutput, mLayer, mLayerFE}; +}; + +TEST_F(OutputLayerUpdateCompositionStateTest, setsStateNormally) { + mLayerState.frontEnd.isSecure = true; + mOutputState.isSecure = true; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true); + + validateComputedGeometryState(); + + EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + alsoSetsForceCompositionIfSecureLayerOnNonsecureOutput) { + mLayerState.frontEnd.isSecure = true; + mOutputState.isSecure = false; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true); + + validateComputedGeometryState(); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, + alsoSetsForceCompositionIfUnsupportedBufferTransform) { + mLayerState.frontEnd.isSecure = true; + mOutputState.isSecure = true; + + mBufferTransform = ui::Transform::ROT_INVALID; + + setupGeometryChildCallValues(); + + mOutputLayer.updateCompositionState(true); + + validateComputedGeometryState(); + + EXPECT_EQ(true, mOutputLayer.getState().forceClientComposition); +} + +TEST_F(OutputLayerUpdateCompositionStateTest, doesNotRecomputeGeometryIfNotRequested) { + mOutputLayer.updateCompositionState(false); + + EXPECT_EQ(false, mOutputLayer.getState().forceClientComposition); +} + +/* + * OutputLayer::writeStateToHWC() + */ + +struct OutputLayerWriteStateToHWCTest : public OutputLayerTest { + static constexpr HWC2::Error kError = HWC2::Error::Unsupported; + static constexpr FloatRect kSourceCrop{11.f, 12.f, 13.f, 14.f}; + static constexpr uint32_t kZOrder = 21u; + static constexpr Hwc2::Transform kBufferTransform = static_cast<Hwc2::Transform>(31); + static constexpr Hwc2::IComposerClient::BlendMode kBlendMode = + static_cast<Hwc2::IComposerClient::BlendMode>(41); + static constexpr float kAlpha = 51.f; + static constexpr uint32_t kType = 61u; + static constexpr uint32_t kAppId = 62u; + + static const Rect kDisplayFrame; + + OutputLayerWriteStateToHWCTest() { + auto& outputLayerState = mOutputLayer.editState(); + outputLayerState.hwc = impl::OutputLayerCompositionState::Hwc(mHwcLayer); + + outputLayerState.displayFrame = kDisplayFrame; + outputLayerState.sourceCrop = kSourceCrop; + outputLayerState.z = kZOrder; + outputLayerState.bufferTransform = static_cast<Hwc2::Transform>(kBufferTransform); + + mLayerState.frontEnd.blendMode = kBlendMode; + mLayerState.frontEnd.alpha = kAlpha; + mLayerState.frontEnd.type = kType; + mLayerState.frontEnd.appId = kAppId; + } + + void expectGeometryCommonCalls() { + EXPECT_CALL(*mHwcLayer, setDisplayFrame(kDisplayFrame)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setSourceCrop(kSourceCrop)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setZOrder(kZOrder)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setTransform(static_cast<HWC2::Transform>(kBufferTransform))) + .WillOnce(Return(kError)); + + EXPECT_CALL(*mHwcLayer, setBlendMode(static_cast<HWC2::BlendMode>(kBlendMode))) + .WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setPlaneAlpha(kAlpha)).WillOnce(Return(kError)); + EXPECT_CALL(*mHwcLayer, setInfo(kType, kAppId)).WillOnce(Return(kError)); + } + + std::shared_ptr<HWC2::mock::Layer> mHwcLayer{std::make_shared<StrictMock<HWC2::mock::Layer>>()}; +}; + +const Rect OutputLayerWriteStateToHWCTest::kDisplayFrame{1001, 1002, 1003, 10044}; + +TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCState) { + mOutputLayer.editState().hwc.reset(); + + mOutputLayer.writeStateToHWC(true); +} + +TEST_F(OutputLayerWriteStateToHWCTest, doesNothingIfNoHWCLayer) { + mOutputLayer.editState().hwc = impl::OutputLayerCompositionState::Hwc(nullptr); + + mOutputLayer.writeStateToHWC(true); +} + +TEST_F(OutputLayerWriteStateToHWCTest, canSetsAllState) { + expectGeometryCommonCalls(); + + mOutputLayer.writeStateToHWC(true); +} + } // namespace } // namespace android::compositionengine diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h new file mode 100644 index 0000000000..d4c76bc7e8 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h @@ -0,0 +1,45 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> + +#include <android-base/stringprintf.h> +#include <gmock/gmock.h> + +namespace { + +using android::base::StringAppendF; +using Rect = android::Rect; + +void dumpRect(const Rect& rect, std::string& result, const char* name) { + StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom); +} + +// Checks for a region match +MATCHER_P(RectEq, expected, "") { + std::string buf; + buf.append("Rects are not equal\n"); + dumpRect(expected, buf, "expected rect"); + dumpRect(arg, buf, "actual rect"); + *result_listener << buf; + + return (expected.left == arg.left) && (expected.top == arg.top) && + (expected.right == arg.right) && (expected.bottom == arg.bottom); +} + +} // namespace diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index 8343e5ab91..9cb43bccfa 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -214,7 +214,6 @@ std::vector<IComposer::Capability> Composer::getCapabilities() [&](const auto& tmpCapabilities) { capabilities = tmpCapabilities; }); - return capabilities; } @@ -1159,6 +1158,30 @@ Error Composer::setLayerPerFrameMetadataBlobs( return Error::NONE; } +Error Composer::getDisplayBrightnessSupport(Display display, bool* outSupport) { + if (!mClient_2_3) { + return Error::UNSUPPORTED; + } + Error error = kDefaultError; + mClient_2_3->getDisplayBrightnessSupport(display, + [&](const auto& tmpError, const auto& tmpSupport) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + *outSupport = tmpSupport; + }); + return error; +} + +Error Composer::setDisplayBrightness(Display display, float brightness) { + if (!mClient_2_3) { + return Error::UNSUPPORTED; + } + return mClient_2_3->setDisplayBrightness(display, brightness); +} + CommandReader::~CommandReader() { resetData(); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 542e840251..e24db152e9 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -203,6 +203,8 @@ public: std::vector<DisplayCapability>* outCapabilities) = 0; virtual Error setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0; + virtual Error getDisplayBrightnessSupport(Display display, bool* outSupport) = 0; + virtual Error setDisplayBrightness(Display display, float brightness) = 0; }; namespace impl { @@ -414,6 +416,8 @@ public: Error setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override; + Error getDisplayBrightnessSupport(Display display, bool* outSupport) override; + Error setDisplayBrightness(Display display, float brightness) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 910a5275e9..62073b6ef4 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -277,6 +277,11 @@ Display::Display(android::Hwc2::Composer& composer, if (error == Error::None && dozeSupport) { mDisplayCapabilities.emplace(DisplayCapability::Doze); } + bool brightnessSupport = false; + error = static_cast<Error>(mComposer.getDisplayBrightnessSupport(mId, &brightnessSupport)); + if (error == Error::None && brightnessSupport) { + mDisplayCapabilities.emplace(DisplayCapability::Brightness); + } } ALOGV("Created display %" PRIu64, id); } @@ -710,6 +715,11 @@ Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests return error; } +Error Display::setDisplayBrightness(float brightness) const { + auto intError = mComposer.setDisplayBrightness(mId, brightness); + return static_cast<Error>(intError); +} + // For use by Device void Display::setConnected(bool connected) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index f96614fc96..4209e45175 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -264,6 +264,7 @@ public: [[clang::warn_unused_result]] virtual Error presentOrValidate( 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; }; namespace impl { @@ -322,6 +323,7 @@ public: Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override; Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests, android::sp<android::Fence>* outPresentFence, uint32_t* state) override; + Error setDisplayBrightness(float brightness) const override; // Other Display methods hwc2_display_t getId() const override { return mId; } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 3b9e0e6c83..1099041b4b 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -784,6 +784,19 @@ status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t max return NO_ERROR; } +status_t HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->setDisplayBrightness(brightness); + if (error == HWC2::Error::Unsupported) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + if (error == HWC2::Error::BadParameter) { + RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); + } + RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); + return NO_ERROR; +} + bool HWComposer::isUsingVrComposer() const { return getComposer()->isUsingVrComposer(); } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index ca59a2645a..de863b8d6c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -140,6 +140,9 @@ public: uint64_t timestamp, DisplayedFrameStats* outStats) = 0; + // Sets the brightness of a display. + virtual status_t setDisplayBrightness(DisplayId displayId, float brightness) = 0; + // Events handling --------------------------------------------------------- // Returns stable display ID (and display name on connection of new or previously disconnected @@ -271,6 +274,7 @@ public: uint8_t componentMask, uint64_t maxFrames) override; status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) override; + status_t setDisplayBrightness(DisplayId displayId, float brightness) override; // Events handling --------------------------------------------------------- diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 5c3fb05744..ef9418c952 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -29,6 +29,7 @@ #include <android-base/stringprintf.h> #include <compositionengine/Display.h> #include <compositionengine/Layer.h> +#include <compositionengine/LayerFECompositionState.h> #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/LayerCompositionState.h> #include <compositionengine/impl/OutputLayerCompositionState.h> @@ -104,6 +105,7 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.api = -1; mCurrentState.hasColorTransform = false; mCurrentState.colorSpaceAgnostic = false; + mCurrentState.metadata = args.metadata; // drawing state & current state are identical mDrawingState = mCurrentState; @@ -286,6 +288,14 @@ ui::Transform Layer::getTransformWithScale() const { return mEffectiveTransform; } + // If the layer is a buffer state layer, the active width and height + // could be infinite. In that case, return the effective transform. + const uint32_t activeWidth = getActiveWidth(getDrawingState()); + const uint32_t activeHeight = getActiveHeight(getDrawingState()); + if (activeWidth >= UINT32_MAX && activeHeight >= UINT32_MAX) { + return mEffectiveTransform; + } + int bufferWidth; int bufferHeight; if ((mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) { @@ -295,8 +305,9 @@ ui::Transform Layer::getTransformWithScale() const { bufferHeight = mActiveBuffer->getWidth(); bufferWidth = mActiveBuffer->getHeight(); } - float sx = getActiveWidth(getDrawingState()) / static_cast<float>(bufferWidth); - float sy = getActiveHeight(getDrawingState()) / static_cast<float>(bufferHeight); + float sx = activeWidth / static_cast<float>(bufferWidth); + float sy = activeHeight / static_cast<float>(bufferHeight); + ui::Transform extraParentScaling; extraParentScaling.set(sx, 0, 0, sy); return mEffectiveTransform * extraParentScaling; @@ -329,8 +340,6 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) } } - - Rect Layer::getCroppedBufferSize(const State& s) const { Rect size = getBufferSize(s); Rect crop = getCrop(s); @@ -342,36 +351,6 @@ Rect Layer::getCroppedBufferSize(const State& s) const { return size; } -Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const { - // the crop is the area of the window that gets cropped, but not - // scaled in any ways. - const State& s(getDrawingState()); - - // apply the projection's clipping to the window crop in - // layerstack space, and convert-back to layer space. - // if there are no window scaling involved, this operation will map to full - // pixels in the buffer. - - FloatRect activeCropFloat = getBounds(); - ui::Transform t = getTransform(); - // Transform to screen space. - activeCropFloat = t.transform(activeCropFloat); - activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect()); - // Back to layer space to work with the content crop. - activeCropFloat = t.inverse().transform(activeCropFloat); - // This needs to be here as transform.transform(Rect) computes the - // transformed rect and then takes the bounding box of the result before - // returning. This means - // transform.inverse().transform(transform.transform(Rect)) != Rect - // in which case we need to make sure the final rect is clipped to the - // display bounds. - Rect activeCrop{activeCropFloat}; - if (!activeCrop.intersect(getBufferSize(s), &activeCrop)) { - activeCrop.clear(); - } - return activeCrop; -} - void Layer::setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const { // Translate win by the rounded corners rect coordinates, to have all values in @@ -389,182 +368,17 @@ void Layer::setupRoundedCornersCropCoordinates(Rect win, cropCoords[3] = vec2(win.right, win.top); } -FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const { - // the content crop is the area of the content that gets scaled to the - // layer's size. This is in buffer space. - FloatRect crop = getContentCrop().toFloatRect(); - - // In addition there is a WM-specified crop we pull from our drawing state. - const State& s(getDrawingState()); - - Rect activeCrop = computeInitialCrop(display); - Rect bufferSize = getBufferSize(s); - - // Transform the window crop to match the buffer coordinate system, - // which means using the inverse of the current transform set on the - // SurfaceFlingerConsumer. - uint32_t invTransform = mCurrentTransform; - if (getTransformToDisplayInverse()) { - /* - * the code below applies the primary display's inverse transform to the - * buffer - */ - uint32_t invTransformOrient = DisplayDevice::getPrimaryDisplayOrientationTransform(); - // calculate the inverse transform - if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) { - invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } - // and apply to the current transform - invTransform = (ui::Transform(invTransformOrient) * - ui::Transform(invTransform)).getOrientation(); - } - - int winWidth = bufferSize.getWidth(); - int winHeight = bufferSize.getHeight(); - if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - // If the activeCrop has been rotate the ends are rotated but not - // the space itself so when transforming ends back we can't rely on - // a modification of the axes of rotation. To account for this we - // need to reorient the inverse rotation in terms of the current - // axes of rotation. - bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0; - bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0; - if (is_h_flipped == is_v_flipped) { - invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } - std::swap(winWidth, winHeight); - } - const Rect winCrop = - activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight()); - - // below, crop is intersected with winCrop expressed in crop's coordinate space - float xScale = crop.getWidth() / float(winWidth); - float yScale = crop.getHeight() / float(winHeight); - - float insetL = winCrop.left * xScale; - float insetT = winCrop.top * yScale; - float insetR = (winWidth - winCrop.right) * xScale; - float insetB = (winHeight - winCrop.bottom) * yScale; - - crop.left += insetL; - crop.top += insetT; - crop.right -= insetR; - crop.bottom -= insetB; - - return crop; -} - -void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); - LOG_FATAL_IF(!outputLayer->getState().hwc); - auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer; - - if (!hasHwcLayer(display)) { - ALOGE("[%s] failed to setGeometry: no HWC layer found (%s)", mName.string(), - display->getDebugName().c_str()); - return; - } - - LOG_FATAL_IF(!getCompositionLayer()); - auto& commonCompositionState = getCompositionLayer()->editState().frontEnd; - auto& compositionState = outputLayer->editState(); - - // enable this layer - compositionState.forceClientComposition = false; - - if (isSecure() && !display->isSecure()) { - compositionState.forceClientComposition = true; - } - - // this gives us only the "orientation" component of the transform - const State& s(getDrawingState()); - const Rect bufferSize = getBufferSize(s); +void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const { + const auto& drawingState{getDrawingState()}; + auto alpha = static_cast<float>(getAlpha()); auto blendMode = HWC2::BlendMode::None; - if (!isOpaque(s) || getAlpha() != 1.0f) { + if (!isOpaque(drawingState) || alpha != 1.0f) { blendMode = mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage; } - auto error = hwcLayer->setBlendMode(blendMode); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set blend mode %s:" - " %s (%d)", - mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - commonCompositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); - // apply the layer's transform, followed by the display's global transform - // here we're guaranteed that the layer's transform preserves rects - Region activeTransparentRegion(getActiveTransparentRegion(s)); - ui::Transform t = getTransform(); - Rect activeCrop = getCrop(s); - if (!activeCrop.isEmpty() && bufferSize.isValid()) { - activeCrop = t.transform(activeCrop); - if (!activeCrop.intersect(display->getViewport(), &activeCrop)) { - activeCrop.clear(); - } - activeCrop = t.inverse().transform(activeCrop, true); - // This needs to be here as transform.transform(Rect) computes the - // transformed rect and then takes the bounding box of the result before - // returning. This means - // transform.inverse().transform(transform.transform(Rect)) != Rect - // in which case we need to make sure the final rect is clipped to the - // display bounds. - if (!activeCrop.intersect(bufferSize, &activeCrop)) { - activeCrop.clear(); - } - // mark regions outside the crop as transparent - activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top)); - activeTransparentRegion.orSelf( - Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight())); - activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); - activeTransparentRegion.orSelf( - Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom)); - } - - // getBounds returns a FloatRect to provide more accuracy during the - // transformation. We then round upon constructing 'frame'. - Rect frame{t.transform(getBounds(activeTransparentRegion))}; - if (!frame.intersect(display->getViewport(), &frame)) { - frame.clear(); - } - const ui::Transform& tr = display->getTransform(); - Rect transformedFrame = tr.transform(frame); - error = hwcLayer->setDisplayFrame(transformedFrame); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", mName.string(), - transformedFrame.left, transformedFrame.top, transformedFrame.right, - transformedFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error)); - } else { - compositionState.displayFrame = transformedFrame; - } - - FloatRect sourceCrop = computeCrop(display); - error = hwcLayer->setSourceCrop(sourceCrop); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " - "%s (%d)", - mName.string(), sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom, - to_string(error).c_str(), static_cast<int32_t>(error)); - } else { - compositionState.sourceCrop = sourceCrop; - } - - float alpha = static_cast<float>(getAlpha()); - error = hwcLayer->setPlaneAlpha(alpha); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set plane alpha %.3f: " - "%s (%d)", - mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error)); - commonCompositionState.alpha = alpha; - - error = hwcLayer->setZOrder(z); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z, - to_string(error).c_str(), static_cast<int32_t>(error)); - compositionState.z = z; - - int type = s.metadata.getInt32(METADATA_WINDOW_TYPE, 0); - int appId = s.metadata.getInt32(METADATA_OWNER_UID, 0); + int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0); + int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0); sp<Layer> parent = mDrawingParent.promote(); if (parent.get()) { auto& parentState = parent->getDrawingState(); @@ -576,62 +390,35 @@ void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { } } - error = hwcLayer->setInfo(type, appId); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(), - static_cast<int32_t>(error)); + compositionState.geomLayerTransform = getTransform(); + compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse(); + compositionState.geomBufferSize = getBufferSize(drawingState); + compositionState.geomContentCrop = getContentCrop(); + compositionState.geomCrop = getCrop(drawingState); + compositionState.geomBufferTransform = mCurrentTransform; + compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); + compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState); + compositionState.geomLayerBounds = mBounds; + compositionState.geomUsesSourceCrop = usesSourceCrop(); + compositionState.isSecure = isSecure(); - commonCompositionState.type = type; - commonCompositionState.appId = appId; - - /* - * Transformations are applied in this order: - * 1) buffer orientation/flip/mirror - * 2) state transformation (window manager) - * 3) layer orientation (screen orientation) - * (NOTE: the matrices are multiplied in reverse order) - */ - - const ui::Transform bufferOrientation(mCurrentTransform); - ui::Transform transform(tr * t * bufferOrientation); - - if (getTransformToDisplayInverse()) { - /* - * the code below applies the primary display's inverse transform to the - * buffer - */ - uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); - // calculate the inverse transform - if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } + compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); + compositionState.alpha = alpha; + compositionState.type = type; + compositionState.appId = appId; +} - /* - * Here we cancel out the orientation component of the WM transform. - * The scaling and translate components are already included in our bounds - * computation so it's enough to just omit it in the composition. - * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why. - */ - transform = ui::Transform(invTransform) * tr * bufferOrientation; - } - - // this gives us only the "orientation" component of the transform - const uint32_t orientation = transform.getOrientation(); - if (orientation & ui::Transform::ROT_INVALID) { - // we can only handle simple transformation - compositionState.forceClientComposition = true; - (*compositionState.hwc).hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT; - } else { - auto transform = static_cast<HWC2::Transform>(orientation); - auto error = hwcLayer->setTransform(transform); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set transform %s: " - "%s (%d)", - mName.string(), to_string(transform).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - compositionState.bufferTransform = static_cast<Hwc2::Transform>(transform); +void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState, + bool includeGeometry) const { + if (includeGeometry) { + latchGeometry(compositionState); } } +const char* Layer::getDebugName() const { + return mName.string(); +} + void Layer::forceClientComposition(const sp<DisplayDevice>& display) { const auto outputLayer = findOutputLayerForDisplay(display); LOG_FATAL_IF(!outputLayer); @@ -1250,8 +1037,8 @@ bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace da // create background color layer if one does not yet exist uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor; const String8& name = mName + "BackgroundColorLayer"; - mCurrentState.bgColorLayer = - new ColorLayer(LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags)); + mCurrentState.bgColorLayer = new ColorLayer( + LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata())); // add to child list addChild(mCurrentState.bgColorLayer); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 1afb9173f3..78377d7133 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -70,6 +70,7 @@ class LayerDebugInfo; namespace compositionengine { class Layer; class OutputLayer; +struct LayerFECompositionState; } namespace impl { @@ -80,8 +81,9 @@ class SurfaceInterceptor; struct LayerCreationArgs { LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags) - : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags) {} + uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata) + : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags), + metadata(std::move(metadata)) {} SurfaceFlinger* flinger; const sp<Client>& client; @@ -89,6 +91,7 @@ struct LayerCreationArgs { uint32_t w; uint32_t h; uint32_t flags; + LayerMetadata metadata; }; class Layer : public virtual compositionengine::LayerFE { @@ -182,6 +185,9 @@ public: bool inputInfoChanged; InputWindowInfo inputInfo; + // dataspace is only used by BufferStateLayer and ColorLayer + ui::Dataspace dataspace; + // The fields below this point are only used by BufferStateLayer Geometry active; @@ -193,7 +199,6 @@ public: sp<GraphicBuffer> buffer; sp<Fence> acquireFence; - ui::Dataspace dataspace; HdrMetadata hdrMetadata; Region surfaceDamageRegion; int32_t api; @@ -409,6 +414,11 @@ public: */ virtual bool isFixedSize() const { return true; } + /* + * usesSourceCrop - true if content should use a source crop + */ + virtual bool usesSourceCrop() const { return false; } + // Most layers aren't created from the main thread, and therefore need to // grab the SF state lock to access HWC, but ContainerLayer does, so we need // to avoid grabbing the lock again to avoid deadlock @@ -432,6 +442,9 @@ public: } virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; } + virtual void setPostTime(nsecs_t /*postTime*/) {} + virtual void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/) {} + protected: virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform, Region& clearRegion, @@ -442,13 +455,19 @@ public: /* * compositionengine::LayerFE overrides */ + void latchCompositionState(compositionengine::LayerFECompositionState&, + bool includeGeometry) const override; void onLayerDisplayed(const sp<Fence>& releaseFence) override; + const char* getDebugName() const override; +protected: + void latchGeometry(compositionengine::LayerFECompositionState& outState) const; + +public: virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {} virtual bool isHdrY410() const { return false; } - void setGeometry(const sp<const DisplayDevice>& display, uint32_t z); void forceClientComposition(const sp<DisplayDevice>& display); bool getForceClientComposition(const sp<DisplayDevice>& display); virtual void setPerFrameData(const sp<const DisplayDevice>& display, @@ -691,16 +710,10 @@ protected: // For unit tests friend class TestableSurfaceFlinger; - void commitTransaction(const State& stateToCommit); + virtual void commitTransaction(const State& stateToCommit); uint32_t getEffectiveUsage(uint32_t usage) const; - virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const; - // Compute the initial crop as specified by parent layers and the - // SurfaceControl for this layer. Does not include buffer crop from the - // IGraphicBufferProducer client, as that should not affect child clipping. - // Returns in screen space. - Rect computeInitialCrop(const sp<const DisplayDevice>& display) const; /** * Setup rounded corners coordinates of this layer, taking into account the layer bounds and * crop coordinates, transforming them into layer space. diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 04e8796314..c25c418dc5 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -95,6 +95,9 @@ void LayerProtoHelper::writeToProto(const ui::Transform& transform, const uint32_t type = transform.getType() | (transform.getOrientation() << 8); transformProto->set_type(type); + // Rotations that are 90/180/270 have their own type so the transform matrix can be + // reconstructed later. All other rotation have the type UKNOWN so we need to save the transform + // values in that case. if (type & (ui::Transform::SCALE | ui::Transform::UNKNOWN)) { transformProto->set_dsdx(transform[0][0]); transformProto->set_dtdx(transform[0][1]); diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp index 4f0b3bbbcf..718e996dae 100644 --- a/services/surfaceflinger/RegionSamplingThread.cpp +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -101,7 +101,7 @@ struct SamplingOffsetCallback : DispSync::Callback { mPhaseIntervalSetting = Phase::ZERO; mScheduler.withPrimaryDispSync([this](android::DispSync& sync) { - sync.addEventListener("SamplingThreadDispSyncListener", 0, this); + sync.addEventListener("SamplingThreadDispSyncListener", 0, this, mLastCallbackTime); }); mVsyncListening = true; } @@ -115,8 +115,9 @@ private: void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ { if (!mVsyncListening) return; - mScheduler.withPrimaryDispSync( - [this](android::DispSync& sync) { sync.removeEventListener(this); }); + mScheduler.withPrimaryDispSync([this](android::DispSync& sync) { + sync.removeEventListener(this, &mLastCallbackTime); + }); mVsyncListening = false; } @@ -147,6 +148,7 @@ private: Scheduler& mScheduler; const std::chrono::nanoseconds mTargetSamplingOffset; mutable std::mutex mMutex; + nsecs_t mLastCallbackTime = 0; enum class Phase { ZERO, SAMPLING diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp index 517883627d..5296da9652 100644 --- a/services/surfaceflinger/Scheduler/DispSync.cpp +++ b/services/surfaceflinger/Scheduler/DispSync.cpp @@ -79,7 +79,7 @@ public: mPhase = phase; mReferenceTime = referenceTime; - if (mPeriod != period && mReferenceTime != 0) { + if (mPeriod != 0 && mPeriod != period && mReferenceTime != 0) { // Inflate the reference time to be the most recent predicted // vsync before the current time. const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); @@ -185,7 +185,8 @@ public: return false; } - status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) { + status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback, + nsecs_t lastCallbackTime) { if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); @@ -201,8 +202,34 @@ public: listener.mCallback = callback; // We want to allow the firstmost future event to fire without - // allowing any past events to fire - listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency; + // allowing any past events to fire. To do this extrapolate from + // mReferenceTime the most recent hardware vsync, and pin the + // last event time there. + const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (mPeriod != 0) { + const nsecs_t baseTime = now - mReferenceTime; + const nsecs_t numPeriodsSinceReference = baseTime / mPeriod; + const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod; + const nsecs_t phaseCorrection = mPhase + listener.mPhase; + const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection; + if (predictedLastEventTime >= now) { + // Make sure that the last event time does not exceed the current time. + // If it would, then back the last event time by a period. + listener.mLastEventTime = predictedLastEventTime - mPeriod; + } else { + listener.mLastEventTime = predictedLastEventTime; + } + } else { + listener.mLastEventTime = now + mPhase - mWakeupLatency; + } + + if (lastCallbackTime <= 0) { + // If there is no prior callback time, try to infer one based on the + // logical last event time. + listener.mLastCallbackTime = listener.mLastEventTime + mWakeupLatency; + } else { + listener.mLastCallbackTime = lastCallbackTime; + } mEventListeners.push_back(listener); @@ -211,13 +238,14 @@ public: return NO_ERROR; } - status_t removeEventListener(DispSync::Callback* callback) { + status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) { if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); for (std::vector<EventListener>::iterator it = mEventListeners.begin(); it != mEventListeners.end(); ++it) { if (it->mCallback == callback) { + *outLastCallback = it->mLastCallbackTime; mEventListeners.erase(it); mCond.signal(); return NO_ERROR; @@ -259,6 +287,7 @@ private: const char* mName; nsecs_t mPhase; nsecs_t mLastEventTime; + nsecs_t mLastCallbackTime; DispSync::Callback* mCallback; }; @@ -283,6 +312,13 @@ private: return nextEventTime; } + // Sanity check that the duration is close enough in length to a period without + // falling into double-rate vsyncs. + bool isCloseToPeriod(nsecs_t duration) { + // Ratio of 3/5 is arbitrary, but it must be greater than 1/2. + return duration < (3 * mPeriod) / 5; + } + std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) { if (mTraceDetailedInfo) ATRACE_CALL(); ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now)); @@ -294,12 +330,21 @@ private: nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo); if (t < now) { + if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) { + eventListener.mLastEventTime = t; + eventListener.mLastCallbackTime = now; + ALOGV("[%s] [%s] Skipping event due to model error", mName, + eventListener.mName); + continue; + } CallbackInvocation ci; ci.mCallback = eventListener.mCallback; ci.mEventTime = t; - ALOGV("[%s] [%s] Preparing to fire", mName, eventListener.mName); + ALOGV("[%s] [%s] Preparing to fire, latency: %" PRId64, mName, eventListener.mName, + t - eventListener.mLastEventTime); callbackInvocations.push_back(ci); eventListener.mLastEventTime = t; + eventListener.mLastCallbackTime = now; } } @@ -346,7 +391,7 @@ private: // Check that it's been slightly more than half a period since the last // event so that we don't accidentally fall into double-rate vsyncs - if (t - listener.mLastEventTime < (3 * mPeriod / 5)) { + if (isCloseToPeriod(t - listener.mLastEventTime)) { t += mPeriod; ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t)); } @@ -408,7 +453,10 @@ DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) { mThread = new DispSyncThread(name, mTraceDetailedInfo); } -DispSync::~DispSync() {} +DispSync::~DispSync() { + mThread->stop(); + mThread->requestExitAndWait(); +} void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { mIgnorePresentFences = !hasSyncFramework; @@ -427,7 +475,7 @@ void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { if (mTraceDetailedInfo && kEnableZeroPhaseTracer) { mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>(); - addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get()); + addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0); } } @@ -521,9 +569,10 @@ bool DispSync::addResyncSample(nsecs_t timestamp) { void DispSync::endResync() {} -status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback) { +status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) { Mutex::Autolock lock(mMutex); - return mThread->addEventListener(name, phase, callback); + return mThread->addEventListener(name, phase, callback, lastCallbackTime); } void DispSync::setRefreshSkipCount(int count) { @@ -533,9 +582,9 @@ void DispSync::setRefreshSkipCount(int count) { updateModelLocked(); } -status_t DispSync::removeEventListener(Callback* callback) { +status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) { Mutex::Autolock lock(mMutex); - return mThread->removeEventListener(callback); + return mThread->removeEventListener(callback, outLastCallbackTime); } status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h index 6f3bd00992..de2b8749c7 100644 --- a/services/surfaceflinger/Scheduler/DispSync.h +++ b/services/surfaceflinger/Scheduler/DispSync.h @@ -54,8 +54,9 @@ public: virtual void setPeriod(nsecs_t period) = 0; virtual nsecs_t getPeriod() = 0; virtual void setRefreshSkipCount(int count) = 0; - virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0; - virtual status_t removeEventListener(Callback* callback) = 0; + virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) = 0; + virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0; virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0; virtual nsecs_t computeNextRefresh(int periodOffset) const = 0; virtual void setIgnorePresentFences(bool ignore) = 0; @@ -139,12 +140,21 @@ public: // given phase offset from the hardware vsync events. The callback is // called from a separate thread and it should return reasonably quickly // (i.e. within a few hundred microseconds). - status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) override; + // If the callback was previously registered, and the last clock time the + // callback was invoked was known to the caller (e.g. via removeEventListener), + // then the caller may pass that through to lastCallbackTime, so that + // callbacks do not accidentally double-fire if they are unregistered and + // reregistered in rapid succession. + status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) override; // removeEventListener removes an already-registered event callback. Once // this method returns that callback will no longer be called by the // DispSync object. - status_t removeEventListener(Callback* callback) override; + // outLastCallbackTime will contain the last time that the callback was invoked. + // If the caller wishes to reregister the same callback, they should pass the + // callback time back into lastCallbackTime (see addEventListener). + status_t removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) override; // changePhaseOffset changes the phase offset of an already-registered event callback. The // method will make sure that there is no skipping or double-firing on the listener per frame, diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp index d848c976dd..6e89648bdd 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -40,13 +40,15 @@ void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); if (enable) { status_t err = mDispSync->addEventListener(mName, mPhaseOffset, - static_cast<DispSync::Callback*>(this)); + static_cast<DispSync::Callback*>(this), + mLastCallbackTime); if (err != NO_ERROR) { ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err); } // ATRACE_INT(mVsyncOnLabel.c_str(), 1); } else { - status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this)); + status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this), + &mLastCallbackTime); if (err != NO_ERROR) { ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err); } diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h index 5e3d181c4f..2858678108 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.h +++ b/services/surfaceflinger/Scheduler/DispSyncSource.h @@ -45,6 +45,7 @@ private: const bool mTraceVsync; const std::string mVsyncOnLabel; const std::string mVsyncEventLabel; + nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0; DispSync* mDispSync; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 1083d0865a..83e53fa869 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1308,6 +1308,33 @@ status_t SurfaceFlinger::removeRegionSamplingListener(const sp<IRegionSamplingLi mRegionSamplingThread->removeListener(listener); return NO_ERROR; } + +status_t SurfaceFlinger::getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const { + if (!displayToken || !outSupport) { + return BAD_VALUE; + } + const auto displayId = getPhysicalDisplayIdLocked(displayToken); + if (!displayId) { + return NAME_NOT_FOUND; + } + *outSupport = + getHwComposer().hasDisplayCapability(displayId, HWC2::DisplayCapability::Brightness); + return NO_ERROR; +} + +status_t SurfaceFlinger::setDisplayBrightness(const sp<IBinder>& displayToken, + float brightness) const { + if (!displayToken) { + return BAD_VALUE; + } + const auto displayId = getPhysicalDisplayIdLocked(displayToken); + if (!displayId) { + return NAME_NOT_FOUND; + } + return getHwComposer().setDisplayBrightness(*displayId, brightness); +} + // ---------------------------------------------------------------------------- sp<IDisplayEventConnection> SurfaceFlinger::createDisplayEventConnection( @@ -1589,6 +1616,14 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { mTimeStats->incrementMissedFrames(); } + if (hwcFrameMissed) { + mHwcFrameMissedCount++; + } + + if (gpuFrameMissed) { + mGpuFrameMissedCount++; + } + if (mUseSmart90ForVideo) { // This call is made each time SF wakes up and creates a new frame. It is part // of video detection feature. @@ -1723,24 +1758,30 @@ void SurfaceFlinger::calculateWorkingSet() { mGeometryInvalid = false; for (const auto& [token, displayDevice] : mDisplays) { auto display = displayDevice->getCompositionDisplay(); - const auto displayId = display->getId(); - if (!displayId) { - continue; - } - const Vector<sp<Layer>>& currentLayers = displayDevice->getVisibleLayersSortedByZ(); - for (size_t i = 0; i < currentLayers.size(); i++) { - const auto& layer = currentLayers[i]; + uint32_t zOrder = 0; - if (!layer->hasHwcLayer(displayDevice)) { - layer->forceClientComposition(displayDevice); - continue; + for (auto& layer : display->getOutputLayersOrderedByZ()) { + auto& compositionState = layer->editState(); + compositionState.forceClientComposition = false; + if (!compositionState.hwc || mDebugDisableHWC || mDebugRegion) { + compositionState.forceClientComposition = true; } - layer->setGeometry(displayDevice, i); - if (mDebugDisableHWC || mDebugRegion) { - layer->forceClientComposition(displayDevice); - } + // The output Z order is set here based on a simple counter. + compositionState.z = zOrder++; + + // Update the display independent composition state. This goes + // to the general composition layer state structure. + // TODO: Do this once per compositionengine::CompositionLayer. + layer->getLayerFE().latchCompositionState(layer->getLayer().editState().frontEnd, + true); + + // Recalculate the geometry state of the output layer. + layer->updateCompositionState(true); + + // Write the updated geometry state to the HWC + layer->writeStateToHWC(true); } } } @@ -3453,12 +3494,13 @@ bool SurfaceFlinger::flushTransactionQueues() { auto& [applyToken, transactionQueue] = *it; while (!transactionQueue.empty()) { - const auto& [states, displays, flags, desiredPresentTime, privileged] = + const auto& [states, displays, flags, desiredPresentTime, postTime, privileged] = transactionQueue.front(); if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) { break; } - applyTransactionState(states, displays, flags, mPendingInputWindowCommands, privileged); + applyTransactionState(states, displays, flags, mPendingInputWindowCommands, + desiredPresentTime, postTime, privileged); transactionQueue.pop(); } @@ -3518,6 +3560,8 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, int64_t desiredPresentTime) { ATRACE_CALL(); + const int64_t postTime = systemTime(); + bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess(); Mutex::Autolock _l(mStateLock); @@ -3530,17 +3574,19 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() || !transactionIsReadyToBeApplied(desiredPresentTime, states)) { mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime, - privileged); + postTime, privileged); setTransactionFlags(eTransactionNeeded); return; } - applyTransactionState(states, displays, flags, inputWindowCommands, privileged); + applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime, + postTime, privileged); } void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, + const int64_t desiredPresentTime, const int64_t postTime, bool privileged) { uint32_t transactionFlags = 0; @@ -3566,7 +3612,7 @@ void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, uint32_t clientStateFlags = 0; for (const ComposerState& state : states) { - clientStateFlags |= setClientStateLocked(state, privileged); + clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged); } // If the state doesn't require a traversal and there are callbacks, send them now if (!(clientStateFlags & eTraversalNeeded)) { @@ -3679,7 +3725,8 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess() { } uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState, - bool privileged) { + int64_t desiredPresentTime, int64_t postTime, + bool privileged) { const layer_state_t& s = composerState.state; sp<Client> client(static_cast<Client*>(composerState.client.get())); @@ -3921,7 +3968,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState sp<GraphicBuffer> buffer = BufferStateLayerCache::getInstance().get(s.cachedBuffer.token, s.cachedBuffer.bufferId); - if (layer->setBuffer(buffer)) flags |= eTraversalNeeded; + if (layer->setBuffer(buffer)) { + flags |= eTraversalNeeded; + layer->setPostTime(postTime); + layer->setDesiredPresentTime(desiredPresentTime); + } } if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded; // Do not put anything that updates layer state or modifies flags after @@ -3959,14 +4010,27 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie String8 uniqueName = getUniqueLayerName(name); + bool primaryDisplayOnly = false; + + // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java + // TODO b/64227542 + if (metadata.has(METADATA_WINDOW_TYPE)) { + int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0); + if (windowType == 441731) { + metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL + primaryDisplayOnly = true; + } + } + switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: - result = createBufferQueueLayer(client, uniqueName, w, h, flags, format, handle, gbp, - &layer); + result = createBufferQueueLayer(client, uniqueName, w, h, flags, std::move(metadata), + format, handle, gbp, &layer); break; case ISurfaceComposerClient::eFXSurfaceBufferState: - result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer); + result = createBufferStateLayer(client, uniqueName, w, h, flags, std::move(metadata), + handle, &layer); break; case ISurfaceComposerClient::eFXSurfaceColor: // check if buffer size is set for color layer. @@ -3976,9 +4040,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return BAD_VALUE; } - result = createColorLayer(client, - uniqueName, w, h, flags, - handle, &layer); + result = createColorLayer(client, uniqueName, w, h, flags, std::move(metadata), handle, + &layer); break; case ISurfaceComposerClient::eFXSurfaceContainer: // check if buffer size is set for container layer. @@ -3987,9 +4050,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie int(w), int(h)); return BAD_VALUE; } - result = createContainerLayer(client, - uniqueName, w, h, flags, - handle, &layer); + result = createContainerLayer(client, uniqueName, w, h, flags, std::move(metadata), + handle, &layer); break; default: result = BAD_VALUE; @@ -4000,18 +4062,10 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return result; } - // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java - // TODO b/64227542 - if (metadata.has(METADATA_WINDOW_TYPE)) { - int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0); - if (windowType == 441731) { - metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL - layer->setPrimaryDisplayOnly(); - } + if (primaryDisplayOnly) { + layer->setPrimaryDisplayOnly(); } - layer->setMetadata(metadata); - bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess(); result = addClientLayer(client, *handle, *gbp, layer, *parent, addToCurrentState); @@ -4054,7 +4108,8 @@ String8 SurfaceFlinger::getUniqueLayerName(const String8& name) status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, - PixelFormat& format, sp<IBinder>* handle, + LayerMetadata metadata, PixelFormat& format, + sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) { // initialize the surfaces @@ -4068,8 +4123,8 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const break; } - sp<BufferQueueLayer> layer = - getFactory().createBufferQueueLayer(LayerCreationArgs(this, client, name, w, h, flags)); + sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); status_t err = layer->setDefaultBufferProperties(w, h, format); if (err == NO_ERROR) { *handle = layer->getHandle(); @@ -4083,30 +4138,31 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) { - sp<BufferStateLayer> layer = - getFactory().createBufferStateLayer(LayerCreationArgs(this, client, name, w, h, flags)); + LayerMetadata metadata, sp<IBinder>* handle, + sp<Layer>* outLayer) { + sp<BufferStateLayer> layer = getFactory().createBufferStateLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = layer->getHandle(); *outLayer = layer; return NO_ERROR; } -status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) -{ - *outLayer = getFactory().createColorLayer(LayerCreationArgs(this, client, name, w, h, flags)); +status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* handle, sp<Layer>* outLayer) { + *outLayer = getFactory().createColorLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = (*outLayer)->getHandle(); return NO_ERROR; } -status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) -{ - *outLayer = - getFactory().createContainerLayer(LayerCreationArgs(this, client, name, w, h, flags)); +status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags, + LayerMetadata metadata, sp<IBinder>* handle, + sp<Layer>* outLayer) { + *outLayer = getFactory().createContainerLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = (*outLayer)->getHandle(); return NO_ERROR; } @@ -4650,7 +4706,9 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co dumpStaticScreenStats(result); result.append("\n"); - StringAppendF(&result, "Missed frame count: %u\n\n", mFrameMissedCount.load()); + StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load()); + StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load()); + StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load()); dumpBufferingStats(result); @@ -4670,6 +4728,14 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n"); } + { + StringAppendF(&result, "Composition layers\n"); + mDrawingState.traverseInZOrder([&](Layer* layer) { + auto compositionLayer = layer->getCompositionLayer(); + if (compositionLayer) compositionLayer->dump(result); + }); + } + /* * Dump Display state */ @@ -4821,7 +4887,6 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case CREATE_DISPLAY: case DESTROY_DISPLAY: case ENABLE_VSYNC_INJECTIONS: - case GET_ACTIVE_COLOR_MODE: case GET_ANIMATION_FRAME_STATS: case GET_HDR_CAPABILITIES: case SET_ACTIVE_CONFIG: @@ -4857,6 +4922,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { // request necessary permissions. However, they do not expose any secret // information, so it is OK to pass them. case AUTHENTICATE_SURFACE: + case GET_ACTIVE_COLOR_MODE: case GET_ACTIVE_CONFIG: case GET_PHYSICAL_DISPLAY_IDS: case GET_PHYSICAL_DISPLAY_TOKEN: @@ -4872,7 +4938,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case GET_COLOR_MANAGEMENT: case GET_COMPOSITION_PREFERENCE: case GET_PROTECTED_CONTENT_SUPPORT: - case IS_WIDE_COLOR_DISPLAY: { + case IS_WIDE_COLOR_DISPLAY: + case GET_DISPLAY_BRIGHTNESS_SUPPORT: + case SET_DISPLAY_BRIGHTNESS: { return OK; } case CAPTURE_LAYERS: @@ -5347,7 +5415,8 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, Rect bounds = getBounds(); screenshotParentLayer = mFlinger->getFactory().createContainerLayer( LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"), - bounds.getWidth(), bounds.getHeight(), 0)); + bounds.getWidth(), bounds.getHeight(), 0, + LayerMetadata())); ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop); drawLayers(); diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 55949433bc..0d39cb58dd 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -494,6 +494,9 @@ private: const std::vector<int32_t>& allowedConfigs) override; status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, std::vector<int32_t>* outAllowedConfigs) override; + status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const override; + status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override; /* ------------------------------------------------------------------------ * DeathRecipient interface @@ -574,6 +577,7 @@ private: void applyTransactionState(const Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, + const int64_t desiredPresentTime, const int64_t postTime, bool privileged) REQUIRES(mStateLock); bool flushTransactionQueues(); uint32_t getTransactionFlags(uint32_t flags); @@ -586,8 +590,8 @@ private: bool containsAnyInvalidClientState(const Vector<ComposerState>& states); bool transactionIsReadyToBeApplied(int64_t desiredPresentTime, const Vector<ComposerState>& states); - uint32_t setClientStateLocked(const ComposerState& composerState, bool privileged) - REQUIRES(mStateLock); + uint32_t setClientStateLocked(const ComposerState& composerState, int64_t desiredPresentTime, + int64_t postTime, bool privileged) REQUIRES(mStateLock); uint32_t setDisplayStateLocked(const DisplayState& s) REQUIRES(mStateLock); uint32_t addInputWindowCommands(const InputWindowCommands& inputWindowCommands) REQUIRES(mStateLock); @@ -600,21 +604,21 @@ private: sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent); status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags, PixelFormat& format, - sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, - sp<Layer>* outLayer); + uint32_t h, uint32_t flags, LayerMetadata metadata, + PixelFormat& format, sp<IBinder>* outHandle, + sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer); status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* outHandle, sp<Layer>* outLayer); - status_t createColorLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + status_t createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, + uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle, + sp<Layer>* outLayer); - status_t createContainerLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + status_t createContainerLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* outHandle, sp<Layer>* outLayer); String8 getUniqueLayerName(const String8& name); @@ -1019,6 +1023,8 @@ private: std::shared_ptr<TimeStats> mTimeStats; bool mUseHwcVirtualDisplays = false; std::atomic<uint32_t> mFrameMissedCount{0}; + std::atomic<uint32_t> mHwcFrameMissedCount{0}; + std::atomic<uint32_t> mGpuFrameMissedCount{0}; TransactionCompletedThread mTransactionCompletedThread; @@ -1052,18 +1058,19 @@ private: struct TransactionState { TransactionState(const Vector<ComposerState>& composerStates, const Vector<DisplayState>& displayStates, uint32_t transactionFlags, - int64_t desiredPresentTime, - bool privileged) + int64_t desiredPresentTime, int64_t postTime, bool privileged) : states(composerStates), displays(displayStates), flags(transactionFlags), - time(desiredPresentTime), + desiredPresentTime(desiredPresentTime), + postTime(postTime), privileged(privileged) {} Vector<ComposerState> states; Vector<DisplayState> displays; uint32_t flags; - int64_t time; + const int64_t desiredPresentTime; + const int64_t postTime; bool privileged; }; std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues; diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp index 61d09daed2..b667a74db0 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -12,7 +12,6 @@ #include <private/android_filesystem_config.h> #include <private/gui/ComposerService.h> - #include <ui/DisplayInfo.h> #include <utils/String8.h> @@ -356,4 +355,11 @@ TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) { ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR)); } +TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) { + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + ColorMode colorMode = SurfaceComposerClient::getActiveColorMode(display); + ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode); +} + } // namespace android diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp index 319e01cbf6..bc5642ecd3 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -32,8 +32,9 @@ #include <gui/LayerState.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> -#include <private/gui/ComposerService.h> +#include <hardware/hwcomposer_defs.h> #include <private/android_filesystem_config.h> +#include <private/gui/ComposerService.h> #include <ui/ColorSpace.h> #include <ui/DisplayInfo.h> @@ -2022,8 +2023,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) { Transaction().setCrop(layer, crop).apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) { @@ -2053,13 +2054,13 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) { { SCOPED_TRACE("empty rect"); Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); } { SCOPED_TRACE("negative rect"); Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); } } @@ -2076,37 +2077,36 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) { TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", mDisplayWidth, mDisplayHeight / 2, - ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(mDisplayWidth, mDisplayHeight / 2, PIXEL_FORMAT_RGBA_8888, 1, + new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY, "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE); - fillGraphicBufferColor(buffer, Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2), - Color::RED); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE); + fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED); + + Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply(); Transaction().setBuffer(layer, buffer).apply(); // Partially out of bounds in the negative (upper left) direction - Transaction().setCrop(layer, Rect(-128, -128, mDisplayWidth, mDisplayHeight / 4)).apply(); + Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply(); { SCOPED_TRACE("out of bounds, negative (upper left) direction"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLUE); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK); + shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); } // Partially out of bounds in the positive (lower right) direction - Transaction() - .setCrop(layer, Rect(0, mDisplayHeight / 4, mDisplayWidth + 1, mDisplayHeight)) - .apply(); + Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply(); { SCOPED_TRACE("out of bounds, positive (lower right) direction"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK); + shot->expectColor(Rect(0, 0, 64, 64), Color::RED); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); } // Fully out of buffer space bounds @@ -2114,9 +2114,9 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) { { SCOPED_TRACE("Fully out of bounds"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE); - shot->expectColor(Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2), - Color::RED); + shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE); + shot->expectColor(Rect(0, 16, 64, 64), Color::RED); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); } } @@ -2293,8 +2293,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) { // A parentless layer will default to a frame with the same size as the buffer auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 10, 10), Color::RED); - shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) { @@ -2377,8 +2377,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) { ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { @@ -2391,8 +2391,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { { SCOPED_TRACE("set buffer 1"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); @@ -2400,8 +2400,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { { SCOPED_TRACE("set buffer 2"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); @@ -2409,8 +2409,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { { SCOPED_TRACE("set buffer 3"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } } @@ -2492,8 +2492,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) { Color color = colors[idx % colors.size()]; auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), color); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } idx++; } @@ -2528,8 +2528,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_Buffer Color color = colors[idx % colors.size()]; auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), color); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } idx++; } @@ -2564,8 +2564,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferSt Color color = colors[idx % colors.size()]; auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), color); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } if (idx == 0) { buffers[0].clear(); @@ -2663,8 +2663,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) { std::this_thread::sleep_for(200ms); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { @@ -2687,8 +2687,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) { @@ -2709,8 +2709,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) { @@ -2733,8 +2733,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) { @@ -2757,8 +2757,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) { @@ -2779,8 +2779,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) { @@ -5523,4 +5523,64 @@ TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) { sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255}); } +class DisplayActiveConfigTest : public ::testing::Test { +protected: + void SetUp() override { + mDisplayToken = SurfaceComposerClient::getInternalDisplayToken(); + SurfaceComposerClient::getDisplayConfigs(mDisplayToken, &mDisplayconfigs); + EXPECT_GT(mDisplayconfigs.size(), 0); + + // set display power to on to make sure config can be changed + SurfaceComposerClient::setDisplayPowerMode(mDisplayToken, HWC_POWER_MODE_NORMAL); + } + + sp<IBinder> mDisplayToken; + Vector<DisplayInfo> mDisplayconfigs; +}; + +TEST_F(DisplayActiveConfigTest, allConfigsAllowed) { + std::vector<int32_t> allowedConfigs; + + // Add all configs to the allowed configs + for (int i = 0; i < mDisplayconfigs.size(); i++) { + allowedConfigs.push_back(i); + } + + status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs); + EXPECT_EQ(res, NO_ERROR); + + std::vector<int32_t> outConfigs; + res = SurfaceComposerClient::getAllowedDisplayConfigs(mDisplayToken, &outConfigs); + EXPECT_EQ(res, NO_ERROR); + EXPECT_EQ(allowedConfigs, outConfigs); +} + +TEST_F(DisplayActiveConfigTest, changeAllowedConfig) { + // we need at least 2 configs available for this test + if (mDisplayconfigs.size() <= 1) return; + + int activeConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken); + + // We want to set the allowed config to everything but the active config + std::vector<int32_t> allowedConfigs; + for (int i = 0; i < mDisplayconfigs.size(); i++) { + if (i != activeConfig) { + allowedConfigs.push_back(i); + } + } + + status_t res = SurfaceComposerClient::setAllowedDisplayConfigs(mDisplayToken, allowedConfigs); + EXPECT_EQ(res, NO_ERROR); + + // Allow some time for the config change + std::this_thread::sleep_for(200ms); + + int newActiveConfig = SurfaceComposerClient::getActiveConfig(mDisplayToken); + EXPECT_NE(activeConfig, newActiveConfig); + + // Make sure the new config is part of allowed config + EXPECT_TRUE(std::find(allowedConfigs.begin(), allowedConfigs.end(), newActiveConfig) != + allowedConfigs.end()); +} + } // namespace android diff --git a/services/surfaceflinger/tests/unittests/AllowedDisplayConfigsTest.cpp b/services/surfaceflinger/tests/unittests/AllowedDisplayConfigsTest.cpp new file mode 100644 index 0000000000..42742543ac --- /dev/null +++ b/services/surfaceflinger/tests/unittests/AllowedDisplayConfigsTest.cpp @@ -0,0 +1,96 @@ +/* + * 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 <memory> +#include <vector> + +#include <gtest/gtest.h> + +#include <log/log.h> + +#include "AllowedDisplayConfigs.h" + +namespace android { +namespace { + +class AllowedDisplayConfigsTest : public testing::Test { +protected: + AllowedDisplayConfigsTest(); + ~AllowedDisplayConfigsTest() override; + + void buildAllowedConfigs(); + + const std::vector<int32_t> expectedConfigs = {0, 2, 7, 129}; + constexpr static int32_t notAllowedConfig = 5; + std::unique_ptr<const AllowedDisplayConfigs> mAllowedConfigs; +}; + +AllowedDisplayConfigsTest::AllowedDisplayConfigsTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +AllowedDisplayConfigsTest::~AllowedDisplayConfigsTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +void AllowedDisplayConfigsTest::buildAllowedConfigs() { + AllowedDisplayConfigs::Builder builder; + for (int config : expectedConfigs) { + builder.addConfig(config); + } + mAllowedConfigs = builder.build(); +} + +/* ------------------------------------------------------------------------ + * Test cases + */ + +TEST_F(AllowedDisplayConfigsTest, checkConfigs) { + buildAllowedConfigs(); + + // Check that all expected configs are allowed + for (int config : expectedConfigs) { + EXPECT_TRUE(mAllowedConfigs->isConfigAllowed(config)); + } + + // Check that all the allowed configs are expected + std::vector<int32_t> allowedConfigVector; + mAllowedConfigs->getAllowedConfigs(&allowedConfigVector); + EXPECT_EQ(allowedConfigVector, expectedConfigs); + + // Check that notAllowedConfig is indeed not allowed + EXPECT_TRUE(std::find(expectedConfigs.begin(), expectedConfigs.end(), notAllowedConfig) == + expectedConfigs.end()); + EXPECT_FALSE(mAllowedConfigs->isConfigAllowed(notAllowedConfig)); +} + +TEST_F(AllowedDisplayConfigsTest, getAllowedConfigsNullptr) { + buildAllowedConfigs(); + + // No other expectations rather than no crash + mAllowedConfigs->getAllowedConfigs(nullptr); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index b1d45f39b0..25ce4ac605 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -35,7 +35,9 @@ cc_test { srcs: [ ":libsurfaceflinger_sources", "libsurfaceflinger_unittest_main.cpp", + "AllowedDisplayConfigsTest.cpp", "CompositionTest.cpp", + "DispSyncSourceTest.cpp", "DisplayIdentificationTest.cpp", "DisplayTransactionTest.cpp", "EventControlThreadTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 30ae7647c0..ea2818d532 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -22,6 +22,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/IProducerListener.h> +#include <gui/LayerMetadata.h> #include <log/log.h> #include <renderengine/mock/Framebuffer.h> #include <renderengine/mock/Image.h> @@ -806,7 +807,7 @@ struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> { return new ColorLayer(LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), String8("test-layer"), LayerProperties::WIDTH, LayerProperties::HEIGHT, - LayerProperties::LAYER_FLAGS)); + LayerProperties::LAYER_FLAGS, LayerMetadata())); }); auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); @@ -847,7 +848,7 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), String8("test-layer"), LayerProperties::WIDTH, LayerProperties::HEIGHT, - LayerProperties::LAYER_FLAGS)); + LayerProperties::LAYER_FLAGS, LayerMetadata())); }); LayerProperties::setupLayerState(test, layer); diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp new file mode 100644 index 0000000000..92bdebd400 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp @@ -0,0 +1,164 @@ +/* + * 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 <inttypes.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include "AsyncCallRecorder.h" +#include "Scheduler/DispSyncSource.h" +#include "mock/MockDispSync.h" + +namespace android { +namespace { + +using namespace std::chrono_literals; +using testing::Return; + +class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback { +protected: + DispSyncSourceTest(); + ~DispSyncSourceTest() override; + + void createDispSync(); + void createDispSyncSource(); + + void onVSyncEvent(nsecs_t when) override; + + std::unique_ptr<mock::DispSync> mDispSync; + std::unique_ptr<DispSyncSource> mDispSyncSource; + + AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder; + + static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms; + static constexpr int mIterations = 100; +}; + +DispSyncSourceTest::DispSyncSourceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +DispSyncSourceTest::~DispSyncSourceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +void DispSyncSourceTest::onVSyncEvent(nsecs_t when) { + ALOGD("onVSyncEvent: %" PRId64, when); + + mVSyncEventCallRecorder.recordCall(when); +} + +void DispSyncSourceTest::createDispSync() { + mDispSync = std::make_unique<mock::DispSync>(); +} + +void DispSyncSourceTest::createDispSyncSource() { + createDispSync(); + mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true, + "DispSyncSourceTest"); + mDispSyncSource->setCallback(this); +} + +/* ------------------------------------------------------------------------ + * Test cases + */ + +TEST_F(DispSyncSourceTest, createDispSync) { + createDispSync(); + EXPECT_TRUE(mDispSync); +} + +TEST_F(DispSyncSourceTest, createDispSyncSource) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); +} + +TEST_F(DispSyncSourceTest, noCallbackAfterInit) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + // DispSyncSource starts with Vsync disabled + mDispSync->triggerCallback(); + EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); +} + +TEST_F(DispSyncSourceTest, waitForCallbacks) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + mDispSyncSource->setVSyncEnabled(true); + EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); + + for (int i = 0; i < mIterations; i++) { + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + } +} + +TEST_F(DispSyncSourceTest, waitForCallbacksWithPhaseChange) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + mDispSyncSource->setVSyncEnabled(true); + EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); + + for (int i = 0; i < mIterations; i++) { + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + } + + EXPECT_CALL(*mDispSync, getPeriod()).Times(1).WillOnce(Return(16666666)); + mDispSyncSource->setPhaseOffset((mPhaseOffset / 2).count()); + + EXPECT_EQ(mDispSync->getCallbackPhase(), (mPhaseOffset / 2).count()); + + for (int i = 0; i < mIterations; i++) { + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + } +} + +TEST_F(DispSyncSourceTest, pauseCallbacks) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + mDispSyncSource->setVSyncEnabled(true); + EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + + mDispSyncSource->pauseVsyncCallback(true); + mDispSync->triggerCallback(); + EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); + + mDispSyncSource->pauseVsyncCallback(false); + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index e6f1a0652d..bb9202054f 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -121,6 +121,8 @@ public: MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*)); MOCK_METHOD3(setLayerPerFrameMetadataBlobs, Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&)); + MOCK_METHOD2(setDisplayBrightness, Error(Display, float)); + MOCK_METHOD2(getDisplayBrightnessSupport, Error(Display, bool*)); }; } // namespace mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h index d7e20c4fc8..6dc28bcb73 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h @@ -79,8 +79,9 @@ public: MOCK_METHOD2(validate, Error(uint32_t*, uint32_t*)); MOCK_METHOD4(presentOrValidate, Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*)); + MOCK_CONST_METHOD1(setDisplayBrightness, Error(float)); }; } // namespace mock } // namespace Hwc2 -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp index 2f7e5ead0f..f6c4f62022 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp @@ -15,6 +15,7 @@ */ #include "mock/MockDispSync.h" +#include <thread> namespace android { namespace mock { @@ -23,5 +24,39 @@ namespace mock { DispSync::DispSync() = default; DispSync::~DispSync() = default; +status_t DispSync::addEventListener(const char* /*name*/, nsecs_t phase, Callback* callback, + nsecs_t /*lastCallbackTime*/) { + if (mCallback.callback != nullptr) { + return BAD_VALUE; + } + + mCallback = {callback, phase}; + return NO_ERROR; +} +status_t DispSync::removeEventListener(Callback* callback, nsecs_t* /*outLastCallback*/) { + if (mCallback.callback != callback) { + return BAD_VALUE; + } + + mCallback = {nullptr, 0}; + return NO_ERROR; +} + +status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { + if (mCallback.callback != callback) { + return BAD_VALUE; + } + + mCallback.phase = phase; + return NO_ERROR; +} + +void DispSync::triggerCallback() { + if (mCallback.callback == nullptr) return; + + mCallback.callback->onDispSyncEvent( + std::chrono::steady_clock::now().time_since_epoch().count()); +} + } // namespace mock } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h index 9213ae5fdf..afcda5bc47 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h @@ -36,14 +36,27 @@ public: MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(getPeriod, nsecs_t()); MOCK_METHOD1(setRefreshSkipCount, void(int)); - MOCK_METHOD3(addEventListener, status_t(const char*, nsecs_t, Callback*)); - MOCK_METHOD1(removeEventListener, status_t(Callback*)); - MOCK_METHOD2(changePhaseOffset, status_t(Callback*, nsecs_t)); MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int)); MOCK_METHOD1(setIgnorePresentFences, void(bool)); MOCK_METHOD0(expectedPresentTime, nsecs_t()); MOCK_CONST_METHOD1(dump, void(std::string&)); + + status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) override; + status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) override; + status_t changePhaseOffset(Callback* callback, nsecs_t phase) override; + + nsecs_t getCallbackPhase() { return mCallback.phase; } + + void triggerCallback(); + +private: + struct CallbackType { + Callback* callback = nullptr; + nsecs_t phase; + }; + CallbackType mCallback; }; } // namespace mock |