Merge "Input: Override touchable region bounds with surface bounds 2/2"
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 21de4db..738c20f 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -54,7 +54,9 @@
#include <android/os/IIncidentCompanion.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
+#include <debuggerd/client.h>
#include <dumpsys.h>
+#include <dumputils/dump_utils.h>
#include <hidl/ServiceManagement.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
@@ -129,6 +131,19 @@
// 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 {
@@ -1052,9 +1067,9 @@
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());
@@ -1062,6 +1077,7 @@
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_);
@@ -1087,6 +1103,7 @@
break;
}
}
+ return Dumpstate::RunStatus::OK;
}
static void RunDumpsysText(const std::string& title, int priority,
@@ -1099,24 +1116,27 @@
}
/* 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());
@@ -1127,6 +1147,7 @@
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) {
@@ -1155,35 +1176,54 @@
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;
@@ -1233,9 +1273,16 @@
}
}
-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();
@@ -1243,7 +1290,9 @@
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");
@@ -1258,16 +1307,11 @@
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"});
@@ -1286,7 +1330,9 @@
}
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)");
@@ -1324,7 +1370,7 @@
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"});
@@ -1347,7 +1393,7 @@
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);
@@ -1367,14 +1413,16 @@
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"});
@@ -1438,19 +1486,27 @@
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 = dump_traces();
+ 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());
@@ -1486,11 +1542,11 @@
}
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
@@ -1573,20 +1629,123 @@
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");
}
+Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
+ DurationReporter duration_reporter("DUMP TRACES");
+
+ const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
+ const size_t buf_size = temp_file_pattern.length() + 1;
+ std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
+ memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
+
+ // Create a new, empty file to receive all trace dumps.
+ //
+ // TODO: This can be simplified once we remove support for the old style
+ // dumps. We can have a file descriptor passed in to dump_traces instead
+ // of creating a file, closing it and then reopening it again.
+ 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 RunStatus::OK;
+ }
+
+ // Nobody should have access to this temporary file except dumpstate, but we
+ // temporarily grant 'read' to 'others' here because this file is created
+ // when tombstoned is still running as root, but dumped after dropping. This
+ // can go away once support for old style dumping has.
+ 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 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 RunStatus::OK;
+ }
+
+ // Number of times process dumping has timed out. If we encounter too many
+ // failures, we'll give up.
+ int timeout_failures = 0;
+ bool dalvik_found = false;
+
+ const std::set<int> hal_pids = get_interesting_hal_pids();
+
+ struct dirent* d;
+ while ((d = readdir(proc.get()))) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ int pid = atoi(d->d_name);
+ if (pid <= 0) {
+ continue;
+ }
+
+ const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
+ std::string exe;
+ if (!android::base::Readlink(link_name, &exe)) {
+ continue;
+ }
+
+ bool is_java_process;
+ if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
+ // Don't bother dumping backtraces for the zygote.
+ if (IsZygote(pid)) {
+ continue;
+ }
+
+ dalvik_found = true;
+ is_java_process = true;
+ } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) {
+ is_java_process = false;
+ } else {
+ // Probably a native process we don't care about, continue.
+ continue;
+ }
+
+ // If 3 backtrace dumps fail in a row, consider debuggerd dead.
+ if (timeout_failures == 3) {
+ dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n");
+ break;
+ }
+
+ const uint64_t start = Nanotime();
+ const int ret = dump_backtrace_to_file_timeout(
+ pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace,
+ is_java_process ? 5 : 20, fd);
+
+ if (ret == -1) {
+ // For consistency, the header and footer to this message match those
+ // dumped by debuggerd in the success case.
+ dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
+ dprintf(fd, "Dump failed, likely due to a timeout.\n");
+ dprintf(fd, "---- end %d ----", pid);
+ timeout_failures++;
+ continue;
+ }
+
+ // We've successfully dumped stack traces, reset the failure count
+ // and write a summary of the elapsed time to the file and continue with the
+ // next process.
+ timeout_failures = 0;
+
+ dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native",
+ pid, (float)(Nanotime() - start) / NANOS_PER_SEC);
+ }
+
+ if (!dalvik_found) {
+ MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
+ }
+
+ *path = file_name_buf.release();
+ return RunStatus::OK;
+}
+
void Dumpstate::DumpstateBoard() {
DurationReporter duration_reporter("dumpstate_board()");
printf("========================================================\n");
@@ -1625,6 +1784,7 @@
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());
@@ -2482,9 +2642,12 @@
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;
}
}
@@ -2521,9 +2684,7 @@
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
}
}
@@ -2578,6 +2739,11 @@
}
}
+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 c326bb6..d02ec75 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -73,13 +73,13 @@
*/
class DurationReporter {
public:
- explicit DurationReporter(const std::string& title, bool log_only = false);
+ explicit DurationReporter(const std::string& title, bool logcat_only = false);
~DurationReporter();
private:
std::string title_;
- bool log_only_;
+ bool logcat_only_;
uint64_t started_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
@@ -291,6 +291,12 @@
// TODO: temporary method until Dumpstate object is properly set
void SetProgress(std::unique_ptr<Progress> progress);
+ // 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();
/*
@@ -328,6 +334,13 @@
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 {
@@ -543,9 +556,6 @@
/* create leading directories, if necessary */
void create_parent_dirs(const char *path);
-/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
-const char *dump_traces();
-
/* for each process in the system, run the specified function */
void for_each_pid(for_each_pid_func func, const char *header);
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 2a5516d..0bb80dc 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -50,8 +50,6 @@
#include <android-base/unique_fd.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
-#include <debuggerd/client.h>
-#include <dumputils/dump_utils.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -95,8 +93,8 @@
return singleton_;
}
-DurationReporter::DurationReporter(const std::string& title, bool log_only)
- : title_(title), log_only_(log_only) {
+DurationReporter::DurationReporter(const std::string& title, bool logcat_only)
+ : title_(title), logcat_only_(logcat_only) {
if (!title_.empty()) {
started_ = Nanotime();
}
@@ -104,16 +102,16 @@
DurationReporter::~DurationReporter() {
if (!title_.empty()) {
- uint64_t elapsed = Nanotime() - started_;
- if (log_only_) {
- MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
- } else {
- // TODO(124089395): Remove or rewrite when bugreport latency is fixed.
- MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
- // 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());
+ 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", elapsed, title_.c_str());
}
}
@@ -282,6 +280,12 @@
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];
@@ -354,6 +358,12 @@
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];
@@ -795,115 +805,6 @@
return _redirect_to_file(redirect, path, O_APPEND);
}
-// Dump Dalvik and native stack traces, return the trace file location (nullptr if none).
-const char* dump_traces() {
- DurationReporter duration_reporter("DUMP TRACES");
-
- const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
- const size_t buf_size = temp_file_pattern.length() + 1;
- std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
- memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
-
- // Create a new, empty file to receive all trace dumps.
- //
- // TODO: This can be simplified once we remove support for the old style
- // dumps. We can have a file descriptor passed in to dump_traces instead
- // of creating a file, closing it and then reopening it again.
- 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;
- }
-
- // Nobody should have access to this temporary file except dumpstate, but we
- // temporarily grant 'read' to 'others' here because this file is created
- // when tombstoned is still running as root, but dumped after dropping. This
- // can go away once support for old style dumping has.
- 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;
- }
-
- std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
- if (proc.get() == nullptr) {
- MYLOGE("opendir /proc failed: %s\n", strerror(errno));
- return nullptr;
- }
-
- // Number of times process dumping has timed out. If we encounter too many
- // failures, we'll give up.
- int timeout_failures = 0;
- bool dalvik_found = false;
-
- const std::set<int> hal_pids = get_interesting_hal_pids();
-
- struct dirent* d;
- while ((d = readdir(proc.get()))) {
- int pid = atoi(d->d_name);
- if (pid <= 0) {
- continue;
- }
-
- const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
- std::string exe;
- if (!android::base::Readlink(link_name, &exe)) {
- continue;
- }
-
- bool is_java_process;
- if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
- // Don't bother dumping backtraces for the zygote.
- if (IsZygote(pid)) {
- continue;
- }
-
- dalvik_found = true;
- is_java_process = true;
- } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) {
- is_java_process = false;
- } else {
- // Probably a native process we don't care about, continue.
- continue;
- }
-
- // If 3 backtrace dumps fail in a row, consider debuggerd dead.
- if (timeout_failures == 3) {
- dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n");
- break;
- }
-
- const uint64_t start = Nanotime();
- const int ret = dump_backtrace_to_file_timeout(
- pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace,
- is_java_process ? 5 : 20, fd);
-
- if (ret == -1) {
- // For consistency, the header and footer to this message match those
- // dumped by debuggerd in the success case.
- dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
- dprintf(fd, "Dump failed, likely due to a timeout.\n");
- dprintf(fd, "---- end %d ----", pid);
- timeout_failures++;
- continue;
- }
-
- // We've successfully dumped stack traces, reset the failure count
- // and write a summary of the elapsed time to the file and continue with the
- // next process.
- timeout_failures = 0;
-
- dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native",
- pid, (float)(Nanotime() - start) / NANOS_PER_SEC);
- }
-
- if (!dalvik_found) {
- MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
- }
-
- return file_name_buf.release();
-}
-
void dump_route_tables() {
DurationReporter duration_reporter("DUMP ROUTE TABLES");
if (PropertiesHelper::IsDryRun()) return;
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index 5d4f176..5673918 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -3,6 +3,8 @@
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 ffd1191..72571cf 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -608,7 +608,7 @@
// 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/globals.cpp b/cmds/installd/globals.cpp
index f52c2e7..1394701 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -44,7 +44,7 @@
static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under
// ANDROID_DATA
-static constexpr const char* STAGING_SUBDIR = "pkg_staging/"; // sub-directory under ANDROID_DATA
+static constexpr const char* STAGING_SUBDIR = "app-staging/"; // sub-directory under ANDROID_DATA
std::string android_app_dir;
std::string android_app_ephemeral_dir;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 670abea..0fdc9d6 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -41,23 +41,6 @@
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 @@
}
}
-// 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 @@
// 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 34a3fdc..d5dc6b7 100644
--- a/cmds/service/service.cpp
+++ b/cmds/service/service.cpp
@@ -232,7 +232,6 @@
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/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml
index 6cbe4ae..5c1cab6 100644
--- a/data/etc/car_core_hardware.xml
+++ b/data/etc/car_core_hardware.xml
@@ -40,7 +40,6 @@
<feature name="android.software.voice_recognizers" notLowRam="true" />
<feature name="android.software.backup" />
<feature name="android.software.home_screen" />
- <feature name="android.software.input_methods" />
<feature name="android.software.print" />
<feature name="android.software.companion_device_setup" />
<feature name="android.software.autofill" />
diff --git a/include/android/choreographer.h b/include/android/choreographer.h
index d75de1e..44883cc 100644
--- a/include/android/choreographer.h
+++ b/include/android/choreographer.h
@@ -26,6 +26,7 @@
#ifndef ANDROID_CHOREOGRAPHER_H
#define ANDROID_CHOREOGRAPHER_H
+#include <stdint.h>
#include <sys/cdefs.h>
__BEGIN_DECLS
@@ -43,6 +44,16 @@
*/
typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data);
+/**
+ * Prototype of the function that is called when a new frame is being rendered.
+ * It's passed the time that the frame is being rendered as nanoseconds in the
+ * CLOCK_MONOTONIC time base, as well as the data pointer provided by the
+ * application that registered a callback. All callbacks that run as part of
+ * rendering a frame will observe the same frame time, so it should be used
+ * whenever events need to be synchronized (e.g. animations).
+ */
+typedef void (*AChoreographer_frameCallback64)(int64_t frameTimeNanos, void* data);
+
#if __ANDROID_API__ >= 24
/**
@@ -52,23 +63,39 @@
AChoreographer* AChoreographer_getInstance() __INTRODUCED_IN(24);
/**
- * Post a callback to be run on the next frame. The data pointer provided will
- * be passed to the callback function when it's called.
+ * Deprecated: Use AChoreographer_postFrameCallback64 instead.
*/
void AChoreographer_postFrameCallback(AChoreographer* choreographer,
- AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24);
+ AChoreographer_frameCallback callback, void* data) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
/**
- * Post a callback to be run on the frame following the specified delay. The
- * data pointer provided will be passed to the callback function when it's
- * called.
+ * Deprecated: Use AChoreographer_postFrameCallbackDelayed64 instead.
*/
void AChoreographer_postFrameCallbackDelayed(AChoreographer* choreographer,
AChoreographer_frameCallback callback, void* data,
- long delayMillis) __INTRODUCED_IN(24);
+ long delayMillis) __INTRODUCED_IN(24) __DEPRECATED_IN(29);
#endif /* __ANDROID_API__ >= 24 */
+#if __ANDROID_API__ >= 29
+
+/**
+ * Power a callback to be run on the next frame. The data pointer provided will
+ * be passed to the callback function when it's called.
+ */
+void AChoreographer_postFrameCallback64(AChoreographer* chroreographer,
+ AChoreographer_frameCallback64 callback, void* data) __INTRODUCED_IN(29);
+
+/**
+ * Post a callback to be run on the frame following the specified delay. The
+ * data pointer provided will be passed to the callback function when it's
+ * called.
+ */
+void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer,
+ AChoreographer_frameCallback64 callback, void* data, uint32_t delayMillis) __INTRODUCED_IN(29);
+
+#endif /* __ANDROID_API__ >= 29 */
+
__END_DECLS
#endif // ANDROID_CHOREOGRAPHER_H
diff --git a/include/android/surface_control.h b/include/android/surface_control.h
index 0e79239..ef2ad99 100644
--- a/include/android/surface_control.h
+++ b/include/android/surface_control.h
@@ -40,9 +40,9 @@
struct ASurfaceControl;
/**
- * The SurfaceControl API can be used to provide a heirarchy of surfaces for
+ * The SurfaceControl API can be used to provide a hierarchy of surfaces for
* composition to the system compositor. ASurfaceControl represents a content node in
- * this heirarchy.
+ * this hierarchy.
*/
typedef struct ASurfaceControl ASurfaceControl;
@@ -112,7 +112,7 @@
* the callback.
*
* |stats| is an opaque handle that can be passed to ASurfaceTransactionStats functions to query
- * information about the transaction. The handle is only valid during the the callback.
+ * information about the transaction. The handle is only valid during the callback.
*
* THREADING
* The transaction completed callback can be invoked on any thread.
diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h
index 3c81f0f..853f0c9 100644
--- a/include/powermanager/IPowerManager.h
+++ b/include/powermanager/IPowerManager.h
@@ -45,7 +45,7 @@
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 0dd795a..202d6d2 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -133,7 +133,7 @@
// 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 @@
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/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index b82141c..83a1048 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -106,28 +106,37 @@
virtual bool isRemote() = 0;
/**
- * Dumps information about the interface.
+ * Dumps information about the interface. By default, dumps nothing.
*/
- virtual binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/) {
- return STATUS_OK;
- }
+ virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
+
+ /**
+ * Interprets this binder as this underlying interface if this has stored an ICInterface in the
+ * binder's user data.
+ *
+ * This does not do type checking and should only be used when the binder is known to originate
+ * from ICInterface. Most likely, you want to use I*::fromBinder.
+ */
+ static inline std::shared_ptr<ICInterface> asInterface(AIBinder* binder);
/**
* Helper method to create a class
*/
- static AIBinder_Class* defineClass(const char* interfaceDescriptor,
- AIBinder_Class_onCreate onCreate,
- AIBinder_Class_onDestroy onDestroy,
- AIBinder_Class_onTransact onTransact,
- AIBinder_onDump onDump = nullptr) {
- AIBinder_Class* clazz =
- AIBinder_Class_define(interfaceDescriptor, onCreate, onDestroy, onTransact);
- if (clazz == nullptr) {
- return nullptr;
- }
- AIBinder_Class_setOnDump(clazz, onDump);
- return clazz;
- }
+ static inline AIBinder_Class* defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact);
+
+ private:
+ class ICInterfaceData {
+ public:
+ std::shared_ptr<ICInterface> interface;
+
+ static inline std::shared_ptr<ICInterface> getInterface(AIBinder* binder);
+
+ static inline void* onCreate(void* args);
+ static inline void onDestroy(void* userData);
+ static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args,
+ uint32_t numArgs);
+ };
};
/**
@@ -141,7 +150,7 @@
SpAIBinder asBinder() override;
- bool isRemote() override { return true; }
+ bool isRemote() override { return false; }
protected:
/**
@@ -176,6 +185,55 @@
SpAIBinder mBinder;
};
+// END OF CLASS DECLARATIONS
+
+binder_status_t ICInterface::dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/) {
+ return STATUS_OK;
+}
+
+std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) {
+ return ICInterfaceData::getInterface(binder);
+}
+
+AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact) {
+ AIBinder_Class* clazz = AIBinder_Class_define(interfaceDescriptor, ICInterfaceData::onCreate,
+ ICInterfaceData::onDestroy, onTransact);
+ if (clazz == nullptr) {
+ return nullptr;
+ }
+
+ // We can't know if this method is overriden by a subclass interface, so we must register
+ // ourselves. The default (nothing to dump) is harmless.
+ AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
+ return clazz;
+}
+
+std::shared_ptr<ICInterface> ICInterface::ICInterfaceData::getInterface(AIBinder* binder) {
+ if (binder == nullptr) return nullptr;
+
+ void* userData = AIBinder_getUserData(binder);
+ if (userData == nullptr) return nullptr;
+
+ return static_cast<ICInterfaceData*>(userData)->interface;
+}
+
+void* ICInterface::ICInterfaceData::onCreate(void* args) {
+ std::shared_ptr<ICInterface> interface = static_cast<ICInterface*>(args)->ref<ICInterface>();
+ ICInterfaceData* data = new ICInterfaceData{interface};
+ return static_cast<void*>(data);
+}
+
+void ICInterface::ICInterfaceData::onDestroy(void* userData) {
+ delete static_cast<ICInterfaceData*>(userData);
+}
+
+binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args,
+ uint32_t numArgs) {
+ std::shared_ptr<ICInterface> interface = getInterface(binder);
+ return interface->dump(fd, args, numArgs);
+}
+
template <typename INTERFACE>
SpAIBinder BnCInterface<INTERFACE>::asBinder() {
std::lock_guard<std::mutex> l(mMutex);
diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp
index 0b0bf44..1dc1c0e 100644
--- a/libs/graphicsenv/IGpuService.cpp
+++ b/libs/graphicsenv/IGpuService.cpp
@@ -67,6 +67,29 @@
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 @@
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 e7cdb38..ac022b5 100644
--- a/libs/graphicsenv/include/graphicsenv/IGpuService.h
+++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h
@@ -42,6 +42,9 @@
// 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 @@
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 400daf0..247dc8d 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -860,6 +860,59 @@
}
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 @@
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 3affa23..93b4191 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -20,6 +20,11 @@
#include <gui/Surface.h>
+#include <condition_variable>
+#include <deque>
+#include <mutex>
+#include <thread>
+
#include <inttypes.h>
#include <android/native_window.h>
@@ -450,6 +455,82 @@
return NO_ERROR;
}
+class FenceMonitor {
+public:
+ explicit FenceMonitor(const char* name) : mName(name), mFencesQueued(0), mFencesSignaled(0) {
+ std::thread thread(&FenceMonitor::loop, this);
+ pthread_setname_np(thread.native_handle(), mName);
+ thread.detach();
+ }
+
+ void queueFence(const sp<Fence>& fence) {
+ char message[64];
+
+ std::lock_guard<std::mutex> lock(mMutex);
+ if (fence->getSignalTime() != Fence::SIGNAL_TIME_PENDING) {
+ snprintf(message, sizeof(message), "%s fence %u has signaled", mName, mFencesQueued);
+ ATRACE_NAME(message);
+ // Need an increment on both to make the trace number correct.
+ mFencesQueued++;
+ mFencesSignaled++;
+ return;
+ }
+ snprintf(message, sizeof(message), "Trace %s fence %u", mName, mFencesQueued);
+ ATRACE_NAME(message);
+
+ mQueue.push_back(fence);
+ mCondition.notify_one();
+ mFencesQueued++;
+ ATRACE_INT(mName, int32_t(mQueue.size()));
+ }
+
+private:
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmissing-noreturn"
+ void loop() {
+ while (true) {
+ threadLoop();
+ }
+ }
+#pragma clang diagnostic pop
+
+ void threadLoop() {
+ sp<Fence> fence;
+ uint32_t fenceNum;
+ {
+ std::unique_lock<std::mutex> lock(mMutex);
+ while (mQueue.empty()) {
+ mCondition.wait(lock);
+ }
+ fence = mQueue[0];
+ fenceNum = mFencesSignaled;
+ }
+ {
+ char message[64];
+ snprintf(message, sizeof(message), "waiting for %s %u", mName, fenceNum);
+ ATRACE_NAME(message);
+
+ status_t result = fence->waitForever(message);
+ if (result != OK) {
+ ALOGE("Error waiting for fence: %d", result);
+ }
+ }
+ {
+ std::lock_guard<std::mutex> lock(mMutex);
+ mQueue.pop_front();
+ mFencesSignaled++;
+ ATRACE_INT(mName, int32_t(mQueue.size()));
+ }
+ }
+
+ const char* mName;
+ uint32_t mFencesQueued;
+ uint32_t mFencesSignaled;
+ std::deque<sp<Fence>> mQueue;
+ std::condition_variable mCondition;
+ std::mutex mMutex;
+};
+
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("Surface::dequeueBuffer");
@@ -519,6 +600,11 @@
// this should never happen
ALOGE_IF(fence == nullptr, "Surface::dequeueBuffer: received null Fence! buf=%d", buf);
+ if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
+ static FenceMonitor hwcReleaseThread("HWC release");
+ hwcReleaseThread.queueFence(fence);
+ }
+
if (result & IGraphicBufferProducer::RELEASE_ALL_BUFFERS) {
freeAllBuffers();
}
@@ -730,7 +816,7 @@
// 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.
@@ -761,6 +847,11 @@
mQueueBufferCondition.broadcast();
+ if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
+ static FenceMonitor gpuCompletionThread("GPU completion");
+ gpuCompletionThread.queueFence(fence);
+ }
+
return err;
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0a4ad46..b0e8275 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1490,6 +1490,30 @@
outIsWideColorDisplay);
}
+status_t SurfaceComposerClient::addRegionSamplingListener(
+ const Rect& samplingArea, const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener) {
+ return ComposerService::getComposerService()->addRegionSamplingListener(samplingArea,
+ stopLayerHandle,
+ listener);
+}
+
+status_t SurfaceComposerClient::removeRegionSamplingListener(
+ const sp<IRegionSamplingListener>& listener) {
+ 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 0e576ca..3dffa8f 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -376,6 +376,37 @@
*/
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 @@
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 2c483ee..39d6d13 100644
--- a/libs/gui/include/gui/SurfaceComposerClient.h
+++ b/libs/gui/include/gui/SurfaceComposerClient.h
@@ -50,6 +50,7 @@
class HdrCapabilities;
class ISurfaceComposerClient;
class IGraphicBufferProducer;
+class IRegionSamplingListener;
class Region;
// ---------------------------------------------------------------------------
@@ -167,6 +168,32 @@
// 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
@@ -449,6 +476,10 @@
static status_t getDisplayedContentSample(const sp<IBinder>& display, uint64_t maxFrames,
uint64_t timestamp, DisplayedFrameStats* outStats);
+ static status_t addRegionSamplingListener(const Rect& samplingArea,
+ const sp<IBinder>& stopLayerHandle,
+ const sp<IRegionSamplingListener>& listener);
+ static status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener);
private:
virtual void onFirstRef();
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index a7599e0..06fe86c 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -669,6 +669,14 @@
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/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 3c67542..a106451 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -66,60 +66,60 @@
EXPECT_NEAR_BY_FRACTION(actual, target, COEFFICIENT_TOLERANCE);
}
-void failWithMessage(std::string message) {
+static void failWithMessage(std::string message) {
FAIL() << message; // cannot do this directly from a non-void function
}
struct Position {
- nsecs_t time;
- float x;
- float y;
+ nsecs_t time;
+ float x;
+ float y;
};
-
-MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSamples) {
+static std::unique_ptr<MotionEvent> createSimpleMotionEvent(
+ const std::vector<Position>& positions) {
/**
* Only populate the basic fields of a MotionEvent, such as time and a single axis
* Designed for use with manually-defined tests.
- * Create a new MotionEvent on the heap, caller responsible for destroying the object.
*/
- if (numSamples < 1) {
- failWithMessage(StringPrintf("Need at least 1 sample to create a MotionEvent."
- " Received numSamples=%zu", numSamples));
+ if (positions.empty()) {
+ failWithMessage("Need at least 1 sample to create a MotionEvent. Received empty vector.");
}
- MotionEvent* event = new MotionEvent();
- PointerCoords coords;
- constexpr size_t pointerCount = 1;
- PointerProperties properties[pointerCount];
+ std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>();
+ constexpr size_t pointerCount = 1;
+ PointerCoords coords[pointerCount];
+ coords[0].clear();
+
+ PointerProperties properties[pointerCount];
properties[0].id = DEFAULT_POINTER_ID;
properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
// First sample added separately with initialize
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x);
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y);
event->initialize(0 /*deviceId*/, AINPUT_SOURCE_TOUCHSCREEN, DISPLAY_ID,
AMOTION_EVENT_ACTION_MOVE, 0 /*actionButton*/, 0 /*flags*/,
AMOTION_EVENT_EDGE_FLAG_NONE, AMETA_NONE, 0 /*buttonState*/, MotionClassification::NONE,
0 /*xOffset*/, 0 /*yOffset*/, 0 /*xPrecision*/, 0 /*yPrecision*/,
- 0 /*downTime*/, positions[0].time, pointerCount, properties, &coords);
+ 0 /*downTime*/, positions[0].time, pointerCount, properties, coords);
- for (size_t i = 1; i < numSamples; i++) {
- coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
- coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y);
- event->addSample(positions[i].time, &coords);
+ for (size_t i = 1; i < positions.size(); i++) {
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x);
+ coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y);
+ event->addSample(positions[i].time, coords);
}
return event;
}
-static void computeAndCheckVelocity(const Position* positions, size_t numSamples,
+static void computeAndCheckVelocity(const std::vector<Position>& positions,
int32_t axis, float targetVelocity) {
VelocityTracker vt(nullptr);
float Vx, Vy;
- MotionEvent* event = createSimpleMotionEvent(positions, numSamples);
- vt.addMovement(event);
+ std::unique_ptr<MotionEvent> event = createSimpleMotionEvent(positions);
+ vt.addMovement(event.get());
vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy);
@@ -133,14 +133,13 @@
default:
FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y";
}
- delete event;
}
-static void computeAndCheckQuadraticEstimate(const Position* positions, size_t numSamples,
+static void computeAndCheckQuadraticEstimate(const std::vector<Position>& positions,
const std::array<float, 3>& coefficients) {
VelocityTracker vt("lsq2");
- MotionEvent* event = createSimpleMotionEvent(positions, numSamples);
- vt.addMovement(event);
+ std::unique_ptr<MotionEvent> event = createSimpleMotionEvent(positions);
+ vt.addMovement(event.get());
VelocityTracker::Estimator estimator;
EXPECT_TRUE(vt.getEstimator(0, &estimator));
for (size_t i = 0; i< coefficients.size(); i++) {
@@ -157,35 +156,32 @@
// Same coordinate is reported 2 times in a row
// It is difficult to determine the correct answer here, but at least the direction
// of the reported velocity should be positive.
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 273, NAN },
{ 12585000, 293, NAN },
{ 14730000, 293, NAN },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1600);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1600);
}
TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) {
// Same coordinate is reported 3 times in a row
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 293, NAN },
{ 6132000, 293, NAN },
{ 11283000, 293, NAN },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 0);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 0);
}
TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) {
// Fixed velocity at 5 points per 10 milliseconds
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 0, NAN },
{ 10000000, 5, NAN },
{ 20000000, 10, NAN },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 500);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 500);
}
@@ -203,7 +199,7 @@
// @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy
TEST_F(VelocityTrackerTest, DISABLED_SwordfishFlingDown) {
// Recording of a fling on Swordfish that could cause a fling in the wrong direction
- Position values[] = {
+ std::vector<Position> values = {
{ 0, 271, 96 },
{ 16071042, 269.786346, 106.922775 },
{ 35648403, 267.983063, 156.660034 },
@@ -212,9 +208,8 @@
{ 85639375, 274.79245, 428.113159 },
{ 96948871, 274.79245, 428.113159 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 623.577637);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 8523.348633);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 623.577637);
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 8523.348633);
}
// --------------- Recorded by hand on sailfish, generated by a script -----------------------------
@@ -236,7 +231,7 @@
TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) {
// Sailfish - fling up - slow - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235089067457000, 528.00, 983.00 },
{ 235089084684000, 527.00, 981.00 },
{ 235089093349000, 527.00, 977.00 },
@@ -254,17 +249,16 @@
{ 235089160784000, 559.00, 851.00 },
{ 235089162955851, 560.66, 843.82 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 872.794617); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 872.794617); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) {
// Sailfish - fling up - slow - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235110560704000, 522.00, 1107.00 },
{ 235110575764000, 522.00, 1107.00 },
{ 235110584385000, 522.00, 1107.00 },
@@ -283,15 +277,14 @@
{ 235110655089581, 525.54, 1000.19 },
{ 235110660368000, 530.00, 980.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) {
// Sailfish - fling up - slow - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 792536237000, 580.00, 1317.00 },
{ 792541538987, 580.63, 1311.94 },
{ 792544613000, 581.00, 1309.00 },
@@ -311,17 +304,16 @@
{ 792625653873, 617.32, 1121.73 },
{ 792629200000, 619.00, 1115.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 574.33429); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 574.33429); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) {
// Sailfish - fling up - faster - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235160420675000, 610.00, 1042.00 },
{ 235160428220000, 609.00, 1026.00 },
{ 235160436544000, 609.00, 1024.00 },
@@ -341,17 +333,16 @@
{ 235160512603000, 670.00, 837.00 },
{ 235160520366000, 679.00, 814.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) {
// Sailfish - fling up - faster - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 847153808000, 576.00, 1264.00 },
{ 847171174000, 576.00, 1262.00 },
{ 847179640000, 576.00, 1257.00 },
@@ -367,15 +358,14 @@
{ 847235701400, 607.56, 1103.83 },
{ 847237986000, 610.00, 1095.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) {
// Sailfish - fling up - faster - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 235200532789000, 507.00, 1084.00 },
{ 235200549221000, 507.00, 1083.00 },
{ 235200557841000, 507.00, 1081.00 },
@@ -391,15 +381,14 @@
{ 235200608527086, 563.02, 910.94 },
{ 235200616933000, 590.00, 844.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) {
// Sailfish - fling up - fast - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 920922149000, 561.00, 1412.00 },
{ 920930185000, 559.00, 1377.00 },
{ 920930262463, 558.98, 1376.66 },
@@ -414,17 +403,16 @@
{ 920980906000, 672.00, 993.00 },
{ 920989261000, 715.00, 903.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) {
// Sailfish - fling up - fast - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235247153233000, 518.00, 1168.00 },
{ 235247170452000, 517.00, 1167.00 },
{ 235247178908000, 515.00, 1159.00 },
@@ -437,15 +425,14 @@
{ 235247213222491, 574.72, 778.45 },
{ 235247220736000, 620.00, 641.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) {
// Sailfish - fling up - fast - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 235302568736000, 529.00, 1167.00 },
{ 235302576644000, 523.00, 1140.00 },
{ 235302579395063, 520.91, 1130.61 },
@@ -456,15 +443,14 @@
{ 235302610545000, 652.00, 605.00 },
{ 235302613019881, 679.26, 526.73 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) {
// Sailfish - fling down - slow - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235655749552755, 582.00, 432.49 },
{ 235655750638000, 582.00, 433.00 },
{ 235655758865000, 582.00, 440.00 },
@@ -484,17 +470,16 @@
{ 235655834541000, 566.00, 623.00 },
{ 235655842893000, 563.00, 649.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -419.749695); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -419.749695); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) {
// Sailfish - fling down - slow - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235671152083370, 485.24, 558.28 },
{ 235671154126000, 485.00, 559.00 },
{ 235671162497000, 484.00, 566.00 },
@@ -514,17 +499,16 @@
{ 235671238098000, 472.00, 765.00 },
{ 235671246532000, 470.00, 799.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -262.80426); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -262.80426); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) {
// Sailfish - fling down - slow - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 170983201000, 557.00, 533.00 },
{ 171000668000, 556.00, 534.00 },
{ 171007359750, 554.73, 535.27 },
@@ -537,17 +521,16 @@
{ 171043147000, 541.00, 571.00 },
{ 171051052000, 536.00, 586.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -723.413513); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -723.413513); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) {
// Sailfish - fling down - faster - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235695280333000, 558.00, 451.00 },
{ 235695283971237, 558.43, 454.45 },
{ 235695289038000, 559.00, 462.00 },
@@ -567,15 +550,14 @@
{ 235695368118682, 562.24, 722.52 },
{ 235695373403000, 564.00, 744.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) {
// Sailfish - fling down - faster - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235709624766000, 535.00, 579.00 },
{ 235709642256000, 534.00, 580.00 },
{ 235709643350278, 533.94, 580.06 },
@@ -592,17 +574,16 @@
{ 235709709830000, 512.00, 739.00 },
{ 235709710626776, 511.72, 741.85 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -430.440247); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -430.440247); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) {
// Sailfish - fling down - faster - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 235727628927000, 540.00, 440.00 },
{ 235727636810000, 537.00, 454.00 },
{ 235727646176000, 536.00, 454.00 },
@@ -621,15 +602,14 @@
{ 235727720880776, 516.33, 655.36 },
{ 235727721580000, 516.00, 658.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) {
// Sailfish - fling down - fast - 1
- Position values[] = {
+ std::vector<Position> values = {
{ 235762352849000, 467.00, 286.00 },
{ 235762360250000, 443.00, 344.00 },
{ 235762362787412, 434.77, 363.89 },
@@ -640,15 +620,14 @@
{ 235762394133000, 406.00, 648.00 },
{ 235762396429369, 404.37, 680.67 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) {
// Sailfish - fling down - fast - 2
- Position values[] = {
+ std::vector<Position> values = {
{ 235772487188000, 576.00, 204.00 },
{ 235772495159000, 553.00, 236.00 },
{ 235772503568000, 551.00, 240.00 },
@@ -659,15 +638,14 @@
{ 235772529174000, 498.00, 451.00 },
{ 235772537635000, 484.00, 589.00 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2
}
TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) {
// Sailfish - fling down - fast - 3
- Position values[] = {
+ std::vector<Position> values = {
{ 507650295000, 628.00, 233.00 },
{ 507658234000, 605.00, 269.00 },
{ 507666784000, 601.00, 274.00 },
@@ -679,11 +657,10 @@
{ 507700707000, 454.00, 792.00 },
{ 507703352649, 443.71, 857.77 },
};
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse
- computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse
+ computeAndCheckVelocity(values, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2
}
/*
@@ -710,7 +687,7 @@
* In the test, we would convert these coefficients to (0*(1E3)^0, 0*(1E3)^1, 1*(1E3)^2).
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constant) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 1, 1 }, // 0 s
{ 1000000, 1, 1 }, // 0.001 s
{ 2000000, 1, 1 }, // 0.002 s
@@ -720,15 +697,14 @@
// -0.002, 1
// -0.001, 1
// -0.000, 1
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({1, 0, 0}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({1, 0, 0}));
}
/*
* Straight line y = x :: the constant and quadratic coefficients are zero.
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, -2, -2 },
{ 1000000, -1, -1 },
{ 2000000, -0, -0 },
@@ -738,15 +714,14 @@
// -0.002, -2
// -0.001, -1
// -0.000, 0
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({0, 1E3, 0}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({0, 1E3, 0}));
}
/*
* Parabola
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 1, 1 },
{ 1000000, 4, 4 },
{ 2000000, 8, 8 },
@@ -756,15 +731,14 @@
// -0.002, 1
// -0.001, 4
// -0.000, 8
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({8, 4.5E3, 0.5E6}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({8, 4.5E3, 0.5E6}));
}
/*
* Parabola
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic2) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 1, 1 },
{ 1000000, 4, 4 },
{ 2000000, 9, 9 },
@@ -774,15 +748,14 @@
// -0.002, 1
// -0.001, 4
// -0.000, 9
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({9, 6E3, 1E6}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({9, 6E3, 1E6}));
}
/*
* Parabola :: y = x^2 :: the constant and linear coefficients are zero.
*/
TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabolic3) {
- Position values[] = {
+ std::vector<Position> values = {
{ 0000000, 4, 4 },
{ 1000000, 1, 1 },
{ 2000000, 0, 0 },
@@ -792,8 +765,7 @@
// -0.002, 4
// -0.001, 1
// -0.000, 0
- size_t count = sizeof(values) / sizeof(Position);
- computeAndCheckQuadraticEstimate(values, count, std::array<float, 3>({0, 0E3, 1E6}));
+ computeAndCheckQuadraticEstimate(values, std::array<float, 3>({0, 0E3, 1E6}));
}
} // namespace android
diff --git a/services/bufferhub/tests/Android.bp b/services/bufferhub/tests/Android.bp
index 8d29923..3033e70 100644
--- a/services/bufferhub/tests/Android.bp
+++ b/services/bufferhub/tests/Android.bp
@@ -11,6 +11,7 @@
"-Wall",
"-Werror",
],
+ compile_multilib: "first",
header_libs: [
"libdvr_headers",
"libnativewindow_headers",
diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp
index 5e5cf35..59fa1c0 100644
--- a/services/gpuservice/GpuService.cpp
+++ b/services/gpuservice/GpuService.cpp
@@ -67,6 +67,14 @@
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 b984e0f..7a9b2d4 100644
--- a/services/gpuservice/GpuService.h
+++ b/services/gpuservice/GpuService.h
@@ -48,6 +48,7 @@
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 146e2c2..6185305 100644
--- a/services/gpuservice/gpustats/GpuStats.cpp
+++ b/services/gpuservice/gpustats/GpuStats.cpp
@@ -53,10 +53,12 @@
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 @@
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 9cdcd95..d942154 100644
--- a/services/gpuservice/gpustats/GpuStats.h
+++ b/services/gpuservice/gpustats/GpuStats.h
@@ -41,6 +41,11 @@
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 @@
// 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/BlockingQueue.h b/services/inputflinger/BlockingQueue.h
index 4b16027..db9f26e 100644
--- a/services/inputflinger/BlockingQueue.h
+++ b/services/inputflinger/BlockingQueue.h
@@ -43,7 +43,7 @@
* Blocks execution while queue is empty.
*/
T pop() {
- std::unique_lock<std::mutex> lock(mLock);
+ std::unique_lock lock(mLock);
android::base::ScopedLockAssertion assumeLock(mLock);
mHasElements.wait(lock, [this]{
android::base::ScopedLockAssertion assumeLock(mLock);
diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp
index 3905ed0..b4db338 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 @@
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 @@
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 @@
}
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 @@
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 @@
}
}
+
// --- 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::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::notifyDeviceReset(const NotifyDeviceResetArgs* args) {
+ std::scoped_lock lock(mLock);
if (mMotionClassifier) {
mMotionClassifier->reset(*args);
}
@@ -726,7 +762,19 @@
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 b97357d..0b1483f 100644
--- a/services/inputflinger/InputClassifier.h
+++ b/services/inputflinger/InputClassifier.h
@@ -69,7 +69,13 @@
* 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 @@
*/
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 @@
// 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 @@
*/
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 @@
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 @@
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/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp
index d288736..bcb1ec5 100644
--- a/services/inputflinger/InputDispatcher.cpp
+++ b/services/inputflinger/InputDispatcher.cpp
@@ -46,6 +46,7 @@
#include "InputDispatcher.h"
#include <errno.h>
+#include <inttypes.h>
#include <limits.h>
#include <sstream>
#include <stddef.h>
@@ -262,7 +263,7 @@
InputDispatcher::~InputDispatcher() {
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
resetKeyRepeatLocked();
releasePendingEventLocked();
@@ -277,8 +278,8 @@
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LONG_LONG_MAX;
{ // acquire lock
- AutoMutex _l(mLock);
- mDispatcherIsAliveCondition.broadcast();
+ std::scoped_lock _l(mLock);
+ mDispatcherIsAlive.notify_all();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
@@ -697,7 +698,7 @@
#if DEBUG_DISPATCH_CYCLE
ALOGD("Injected inbound event was dropped.");
#endif
- setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED);
+ setInjectionResult(entry, INPUT_EVENT_INJECTION_FAILED);
}
if (entry == mNextUnblockedEvent) {
mNextUnblockedEvent = nullptr;
@@ -851,7 +852,7 @@
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
mReporter->reportDroppedKey(entry->sequenceNum);
return true;
@@ -865,7 +866,7 @@
return false;
}
- setInjectionResultLocked(entry, injectionResult);
+ setInjectionResult(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
return true;
}
@@ -901,7 +902,7 @@
// Clean up if dropping the event.
if (*dropReason != DROP_REASON_NOT_DROPPED) {
- setInjectionResultLocked(entry, *dropReason == DROP_REASON_POLICY
+ setInjectionResult(entry, *dropReason == DROP_REASON_POLICY
? INPUT_EVENT_INJECTION_SUCCEEDED : INPUT_EVENT_INJECTION_FAILED);
return true;
}
@@ -926,7 +927,7 @@
return false;
}
- setInjectionResultLocked(entry, injectionResult);
+ setInjectionResult(entry, injectionResult);
if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) {
if (injectionResult != INPUT_EVENT_INJECTION_PERMISSION_DENIED) {
CancelationOptions::Mode mode(isPointerEvent ?
@@ -1972,17 +1973,17 @@
bool wasEmpty = connection->outboundQueue.isEmpty();
// Enqueue dispatch entries for the requested modes.
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_OUTSIDE);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_IS);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT);
- enqueueDispatchEntryLocked(connection, eventEntry, inputTarget,
+ enqueueDispatchEntry(connection, eventEntry, inputTarget,
InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER);
// If the outbound queue was previously empty, start the dispatch cycle going.
@@ -1991,7 +1992,7 @@
}
}
-void InputDispatcher::enqueueDispatchEntryLocked(
+void InputDispatcher::enqueueDispatchEntry(
const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget,
int32_t dispatchMode) {
int32_t inputTargetFlags = inputTarget->flags;
@@ -2226,9 +2227,9 @@
#endif
// Clear the dispatch queues.
- drainDispatchQueueLocked(&connection->outboundQueue);
+ drainDispatchQueue(&connection->outboundQueue);
traceOutboundQueueLength(connection);
- drainDispatchQueueLocked(&connection->waitQueue);
+ drainDispatchQueue(&connection->waitQueue);
traceWaitQueueLength(connection);
// The connection appears to be unrecoverably broken.
@@ -2243,16 +2244,16 @@
}
}
-void InputDispatcher::drainDispatchQueueLocked(Queue<DispatchEntry>* queue) {
+void InputDispatcher::drainDispatchQueue(Queue<DispatchEntry>* queue) {
while (!queue->isEmpty()) {
DispatchEntry* dispatchEntry = queue->dequeueAtHead();
- releaseDispatchEntryLocked(dispatchEntry);
+ releaseDispatchEntry(dispatchEntry);
}
}
-void InputDispatcher::releaseDispatchEntryLocked(DispatchEntry* dispatchEntry) {
+void InputDispatcher::releaseDispatchEntry(DispatchEntry* dispatchEntry) {
if (dispatchEntry->hasForegroundTarget()) {
- decrementPendingForegroundDispatchesLocked(dispatchEntry->eventEntry);
+ decrementPendingForegroundDispatches(dispatchEntry->eventEntry);
}
delete dispatchEntry;
}
@@ -2261,7 +2262,7 @@
InputDispatcher* d = static_cast<InputDispatcher*>(data);
{ // acquire lock
- AutoMutex _l(d->mLock);
+ std::scoped_lock _l(d->mLock);
ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
if (connectionIndex < 0) {
@@ -2399,7 +2400,7 @@
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+ enqueueDispatchEntry(connection, cancelationEventEntry, // increments ref
&target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
@@ -2509,7 +2510,7 @@
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
ConfigurationChangedEntry* newEntry =
new ConfigurationChangedEntry(args->sequenceNum, args->eventTime);
@@ -2537,7 +2538,7 @@
newKeyCode = AKEYCODE_HOME;
}
if (newKeyCode != AKEYCODE_UNKNOWN) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
mReplacedKeys.add(replacement, newKeyCode);
keyCode = newKeyCode;
@@ -2547,7 +2548,7 @@
// In order to maintain a consistent stream of up and down events, check to see if the key
// going up is one we've replaced in a down event and haven't yet replaced in an up event,
// even if the modifier was released between the down and the up events.
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
struct KeyReplacement replacement = {keyCode, deviceId};
ssize_t index = mReplacedKeys.indexOfKey(replacement);
if (index >= 0) {
@@ -2742,7 +2743,7 @@
bool needWake;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
DeviceResetEntry* newEntry =
new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId);
@@ -2896,7 +2897,7 @@
int32_t injectionResult;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::unique_lock _l(mLock);
if (syncMode == INPUT_EVENT_INJECTION_SYNC_NONE) {
injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED;
@@ -2917,7 +2918,7 @@
break;
}
- mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout);
+ mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED
@@ -2937,7 +2938,7 @@
break;
}
- mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout);
+ mInjectionSyncFinished.wait_for(_l, std::chrono::nanoseconds(remainingTimeout));
}
}
}
@@ -2959,7 +2960,7 @@
|| mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid);
}
-void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t injectionResult) {
+void InputDispatcher::setInjectionResult(EventEntry* entry, int32_t injectionResult) {
InjectionState* injectionState = entry->injectionState;
if (injectionState) {
#if DEBUG_INJECTION
@@ -2988,7 +2989,7 @@
}
injectionState->injectionResult = injectionResult;
- mInjectionResultAvailableCondition.broadcast();
+ mInjectionResultAvailable.notify_all();
}
}
@@ -2999,13 +3000,13 @@
}
}
-void InputDispatcher::decrementPendingForegroundDispatchesLocked(EventEntry* entry) {
+void InputDispatcher::decrementPendingForegroundDispatches(EventEntry* entry) {
InjectionState* injectionState = entry->injectionState;
if (injectionState) {
injectionState->pendingForegroundDispatches -= 1;
if (injectionState->pendingForegroundDispatches == 0) {
- mInjectionSyncFinishedCondition.broadcast();
+ mInjectionSyncFinished.notify_all();
}
}
}
@@ -3077,7 +3078,7 @@
ALOGD("setInputWindows displayId=%" PRId32, displayId);
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
// Copy old handles for release if they are no longer present.
const Vector<sp<InputWindowHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
@@ -3234,7 +3235,7 @@
ALOGD("setFocusedApplication displayId=%" PRId32, displayId);
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
sp<InputApplicationHandle> oldFocusedApplicationHandle =
getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);
@@ -3276,7 +3277,7 @@
ALOGD("setFocusedDisplay displayId=%" PRId32, displayId);
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (mFocusedDisplayId != displayId) {
sp<InputWindowHandle> oldFocusedWindowHandle =
@@ -3328,7 +3329,7 @@
bool changed;
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) {
if (mDispatchFrozen && !frozen) {
@@ -3363,7 +3364,7 @@
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
if (mInputFilterEnabled == enabled) {
return;
@@ -3386,7 +3387,7 @@
}
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken);
sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken);
@@ -3730,7 +3731,7 @@
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
// If InputWindowHandle is null and displayId is not ADISPLAY_ID_NONE,
// treat inputChannel as monitor channel for displayId.
@@ -3772,7 +3773,7 @@
#endif
{ // acquire lock
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/);
if (status) {
@@ -4033,7 +4034,7 @@
connection->outboundQueue.enqueueAtHead(dispatchEntry);
traceOutboundQueueLength(connection);
} else {
- releaseDispatchEntryLocked(dispatchEntry);
+ releaseDispatchEntry(dispatchEntry);
}
}
@@ -4271,7 +4272,7 @@
}
void InputDispatcher::dump(std::string& dump) {
- AutoMutex _l(mLock);
+ std::scoped_lock _l(mLock);
dump += "Input Dispatcher State:\n";
dumpDispatchStateLocked(dump);
@@ -4284,10 +4285,9 @@
void InputDispatcher::monitor() {
// Acquire and release the lock to ensure that the dispatcher has not deadlocked.
- mLock.lock();
+ std::unique_lock _l(mLock);
mLooper->wake();
- mDispatcherIsAliveCondition.wait(mLock);
- mLock.unlock();
+ mDispatcherIsAlive.wait(_l);
}
diff --git a/services/inputflinger/InputDispatcher.h b/services/inputflinger/InputDispatcher.h
index 9d8919b..24ae32f 100644
--- a/services/inputflinger/InputDispatcher.h
+++ b/services/inputflinger/InputDispatcher.h
@@ -17,6 +17,7 @@
#ifndef _UI_INPUT_DISPATCHER_H
#define _UI_INPUT_DISPATCHER_H
+#include <condition_variable>
#include <input/Input.h>
#include <input/InputApplication.h>
#include <input/InputTransport.h>
@@ -884,9 +885,9 @@
sp<InputDispatcherPolicyInterface> mPolicy;
InputDispatcherConfiguration mConfig;
- Mutex mLock;
+ std::mutex mLock;
- Condition mDispatcherIsAliveCondition;
+ std::condition_variable mDispatcherIsAlive;
sp<Looper> mLooper;
@@ -944,13 +945,13 @@
GUARDED_BY(mLock);
// Event injection and synchronization.
- Condition mInjectionResultAvailableCondition;
+ std::condition_variable mInjectionResultAvailable;
bool hasInjectionPermission(int32_t injectorPid, int32_t injectorUid);
- void setInjectionResultLocked(EventEntry* entry, int32_t injectionResult);
+ void setInjectionResult(EventEntry* entry, int32_t injectionResult);
- Condition mInjectionSyncFinishedCondition;
+ std::condition_variable mInjectionSyncFinished;
void incrementPendingForegroundDispatches(EventEntry* entry);
- void decrementPendingForegroundDispatchesLocked(EventEntry* entry);
+ void decrementPendingForegroundDispatches(EventEntry* entry);
// Key repeat tracking.
struct KeyRepeatState {
@@ -1136,7 +1137,7 @@
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock);
- void enqueueDispatchEntryLocked(const sp<Connection>& connection,
+ void enqueueDispatchEntry(const sp<Connection>& connection,
EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
@@ -1144,8 +1145,8 @@
uint32_t seq, bool handled) REQUIRES(mLock);
void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
bool notify) REQUIRES(mLock);
- void drainDispatchQueueLocked(Queue<DispatchEntry>* queue);
- void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry);
+ void drainDispatchQueue(Queue<DispatchEntry>* queue);
+ void releaseDispatchEntry(DispatchEntry* dispatchEntry);
static int handleReceiveCallback(int fd, int events, void* data);
void synthesizeCancelationEventsForAllConnectionsLocked(
@@ -1197,7 +1198,7 @@
bool afterKeyEventLockedInterruptible(const sp<Connection>& connection,
DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock);
bool afterMotionEventLockedInterruptible(const sp<Connection>& connection,
- DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled);
+ DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock);
void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock);
void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry);
diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp
index 20699c9..1651057 100644
--- a/services/inputflinger/tests/InputClassifier_test.cpp
+++ b/services/inputflinger/tests/InputClassifier_test.cpp
@@ -29,20 +29,20 @@
static NotifyMotionArgs generateBasicMotionArgs() {
// Create a basic motion event for testing
- constexpr size_t pointerCount = 1;
- PointerProperties properties[pointerCount];
- properties[0].id = 0;
- properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
+ PointerProperties properties;
+ properties.id = 0;
+ properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER;
- PointerCoords coords[pointerCount];
- coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 1);
- coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, 1);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, 1);
static constexpr nsecs_t downTime = 2;
NotifyMotionArgs motionArgs(1/*sequenceNum*/, downTime/*eventTime*/, 3/*deviceId*/,
AINPUT_SOURCE_ANY, ADISPLAY_ID_DEFAULT, 4/*policyFlags*/, AMOTION_EVENT_ACTION_DOWN,
0/*actionButton*/, 0/*flags*/, AMETA_NONE, 0/*buttonState*/, MotionClassification::NONE,
AMOTION_EVENT_EDGE_FLAG_NONE, 5/*deviceTimestamp*/,
- 0/*pointerCount*/, properties, coords, 0/*xPrecision*/, 0/*yPrecision*/,
+ 1/*pointerCount*/, &properties, &coords, 0/*xPrecision*/, 0/*yPrecision*/,
downTime, {}/*videoFrames*/);
return motionArgs;
}
diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp
index 538d728..cd0ea5d 100644
--- a/services/sensorservice/SensorDirectConnection.cpp
+++ b/services/sensorservice/SensorDirectConnection.cpp
@@ -100,7 +100,7 @@
return NO_ERROR;
}
- if (mService->isOperationRestricted(mOpPackageName)) {
+ if (!mService->isOperationPermitted(mOpPackageName)) {
return PERMISSION_DENIED;
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index 9a37ff1..3fbd61e 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -1691,13 +1691,13 @@
return (packageName.contains(mWhiteListedPackage.string()));
}
-bool SensorService::isOperationRestricted(const String16& opPackageName) {
+bool SensorService::isOperationPermitted(const String16& opPackageName) {
Mutex::Autolock _l(mLock);
- if (mCurrentOperatingMode != RESTRICTED) {
+ if (mCurrentOperatingMode == RESTRICTED) {
String8 package(opPackageName);
- return !isWhiteListedPackage(package);
+ return isWhiteListedPackage(package);
}
- return false;
+ return true;
}
void SensorService::UidPolicy::registerSelf() {
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 136ee27..fbfe05d 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -272,7 +272,7 @@
// allowed to register for or call flush on sensors. Typically only cts test packages are
// allowed.
bool isWhiteListedPackage(const String8& packageName);
- bool isOperationRestricted(const String16& opPackageName);
+ bool isOperationPermitted(const String16& opPackageName);
// Reset the state of SensorService to NORMAL mode.
status_t resetToNormalMode();
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index 7884362..4751e5f 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -105,6 +105,10 @@
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 @@
}
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 8149cba..dc103cb 100644
--- a/services/surfaceflinger/BufferLayer.h
+++ b/services/surfaceflinger/BufferLayer.h
@@ -78,6 +78,8 @@
// 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 @@
// 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/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 7d2dcba..cc78ece 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -496,7 +496,7 @@
BufferLayerConsumer::Image::~Image() {
if (mGraphicBuffer != nullptr) {
- ALOGE("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
+ ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId());
mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId());
}
}
diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp
index 7cd9e49..e4179ee 100644
--- a/services/surfaceflinger/BufferQueueLayer.cpp
+++ b/services/surfaceflinger/BufferQueueLayer.cpp
@@ -392,10 +392,9 @@
void BufferQueueLayer::onFrameAvailable(const BufferItem& item) {
// Add this buffer from our internal queue tracker
{ // Autolock scope
- if (mFlinger->mUse90Hz && mFlinger->mUseSmart90ForVideo) {
- // Report the requested present time to the Scheduler, if the feature is turned on.
- mFlinger->mScheduler->addFramePresentTimeForLayer(item.mTimestamp,
- item.mIsAutoTimestamp, mName.c_str());
+ if (mFlinger->mUseSmart90ForVideo) {
+ // Report mApi ID for each layer.
+ mFlinger->mScheduler->addNativeWindowApi(item.mApi);
}
Mutex::Autolock lock(mQueueItemLock);
diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp
index b2383ad..1ca0b02 100644
--- a/services/surfaceflinger/BufferStateLayer.cpp
+++ b/services/surfaceflinger/BufferStateLayer.cpp
@@ -156,9 +156,22 @@
}
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 @@
}
}
- // 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 @@
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 @@
}
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 @@
}
}
- // 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 97662e8..668830a 100644
--- a/services/surfaceflinger/BufferStateLayer.h
+++ b/services/surfaceflinger/BufferStateLayer.h
@@ -89,6 +89,9 @@
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 @@
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 a2692bc..3364399 100644
--- a/services/surfaceflinger/ColorLayer.cpp
+++ b/services/surfaceflinger/ColorLayer.cpp
@@ -79,6 +79,18 @@
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 @@
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 9a72b40..53d5b5b 100644
--- a/services/surfaceflinger/ColorLayer.h
+++ b/services/surfaceflinger/ColorLayer.h
@@ -35,18 +35,21 @@
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 6cc87ba..9f635b9 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h
@@ -24,12 +24,21 @@
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 785a6d7..e6ee078 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h
@@ -41,6 +41,22 @@
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 e7a17c4..cd63b57 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h
@@ -64,6 +64,15 @@
// 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 d8f0cdd..6a4818f 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 @@
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 a0c2a63..aab18db 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 @@
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 54c7407..29cd08a 100644
--- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
+++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h
@@ -38,6 +38,9 @@
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 c497013..9598430 100644
--- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp
@@ -24,7 +24,7 @@
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 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 109e9f8..96e9731 100644
--- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp
@@ -52,7 +52,9 @@
}
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 517b641..40c4da9 100644
--- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp
@@ -31,6 +31,25 @@
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 @@
} // 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 ad4c7bf..d22bdaf 100644
--- a/services/surfaceflinger/CompositionEngine/src/Output.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp
@@ -147,7 +147,7 @@
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 78807ff..9549054 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 @@
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 10da49d..379ad87 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 @@
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 @@
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 10f27b8..861ea57 100644
--- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
+++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp
@@ -46,7 +46,7 @@
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 0000000..6741cc9
--- /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 885cdd4..94349de 100644
--- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
+++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h
@@ -65,6 +65,8 @@
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 f7dcf6f..2504fa6 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 @@
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 @@
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 0000000..d4c76bc
--- /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 8343e5a..9cb43bc 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp
@@ -214,7 +214,6 @@
[&](const auto& tmpCapabilities) {
capabilities = tmpCapabilities;
});
-
return capabilities;
}
@@ -1159,6 +1158,30 @@
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 542e840..e24db15 100644
--- a/services/surfaceflinger/DisplayHardware/ComposerHal.h
+++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h
@@ -203,6 +203,8 @@
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 @@
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 910a527..62073b6 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp
@@ -277,6 +277,11 @@
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 @@
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 f96614f..4209e45 100644
--- a/services/surfaceflinger/DisplayHardware/HWC2.h
+++ b/services/surfaceflinger/DisplayHardware/HWC2.h
@@ -264,6 +264,7 @@
[[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 @@
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 3b9e0e6..1099041 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
@@ -784,6 +784,19 @@
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 ca59a26..de863b8 100644
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ b/services/surfaceflinger/DisplayHardware/HWComposer.h
@@ -140,6 +140,9 @@
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 @@
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 af27ca3..16e69b9 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 @@
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 @@
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 @@
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 @@
}
}
-
-
Rect Layer::getCroppedBufferSize(const State& s) const {
Rect size = getBufferSize(s);
Rect crop = getCrop(s);
@@ -342,36 +351,6 @@
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 @@
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,60 +390,33 @@
}
}
- 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;
+ compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode);
+ compositionState.alpha = alpha;
+ compositionState.type = type;
+ compositionState.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;
- }
-
- /*
- * 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;
+void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState,
+ bool includeGeometry) const {
+ if (includeGeometry) {
+ latchGeometry(compositionState);
}
+}
- // 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);
- }
+const char* Layer::getDebugName() const {
+ return mName.string();
}
void Layer::forceClientComposition(const sp<DisplayDevice>& display) {
@@ -1250,8 +1037,8 @@
// 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 af50f59..aba3ff2 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -70,6 +70,7 @@
namespace compositionengine {
class Layer;
class OutputLayer;
+struct LayerFECompositionState;
}
namespace impl {
@@ -80,8 +81,9 @@
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 @@
uint32_t w;
uint32_t h;
uint32_t flags;
+ LayerMetadata metadata;
};
class Layer : public virtual compositionengine::LayerFE {
@@ -183,6 +186,9 @@
InputWindowInfo inputInfo;
wp<Layer> touchableRegionCrop;
+ // dataspace is only used by BufferStateLayer and ColorLayer
+ ui::Dataspace dataspace;
+
// The fields below this point are only used by BufferStateLayer
Geometry active;
@@ -194,7 +200,6 @@
sp<GraphicBuffer> buffer;
sp<Fence> acquireFence;
- ui::Dataspace dataspace;
HdrMetadata hdrMetadata;
Region surfaceDamageRegion;
int32_t api;
@@ -410,6 +415,11 @@
*/
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
@@ -433,6 +443,9 @@
}
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,
@@ -443,13 +456,19 @@
/*
* 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,
@@ -692,16 +711,10 @@
// 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 2d6d33c..c25c418 100644
--- a/services/surfaceflinger/LayerProtoHelper.cpp
+++ b/services/surfaceflinger/LayerProtoHelper.cpp
@@ -92,12 +92,13 @@
void LayerProtoHelper::writeToProto(const ui::Transform& transform,
TransformProto* transformProto) {
- const uint32_t type = transform.getType();
+ const uint32_t type = transform.getType() | (transform.getOrientation() << 8);
transformProto->set_type(type);
- if (type &
- (ui::Transform::SCALE | ui::Transform::ROTATE | ui::Transform::TRANSLATE |
- ui::Transform::UNKNOWN)) {
+ // 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]);
transformProto->set_dsdy(transform[1][0]);
diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp
index bd8548c..718e996 100644
--- a/services/surfaceflinger/RegionSamplingThread.cpp
+++ b/services/surfaceflinger/RegionSamplingThread.cpp
@@ -21,27 +21,170 @@
#include "RegionSamplingThread.h"
+#include <cutils/properties.h>
#include <gui/IRegionSamplingListener.h>
#include <utils/Trace.h>
+#include <string>
#include "DisplayDevice.h"
#include "Layer.h"
#include "SurfaceFlinger.h"
namespace android {
+using namespace std::chrono_literals;
template <typename T>
struct SpHash {
size_t operator()(const sp<T>& p) const { return std::hash<T*>()(p.get()); }
};
-RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger) : mFlinger(flinger) {
- std::lock_guard threadLock(mThreadMutex);
- mThread = std::thread([this]() { threadMain(); });
- pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+constexpr auto lumaSamplingStepTag = "LumaSamplingStep";
+enum class samplingStep {
+ noWorkNeeded,
+ idleTimerWaiting,
+ waitForZeroPhase,
+ waitForSamplePhase,
+ sample
+};
+
+constexpr auto defaultRegionSamplingOffset = -3ms;
+constexpr auto defaultRegionSamplingPeriod = 100ms;
+constexpr auto defaultRegionSamplingTimerTimeout = 100ms;
+// TODO: (b/127403193) duration to string conversion could probably be constexpr
+template <typename Rep, typename Per>
+inline std::string toNsString(std::chrono::duration<Rep, Per> t) {
+ return std::to_string(std::chrono::duration_cast<std::chrono::nanoseconds>(t).count());
}
+RegionSamplingThread::EnvironmentTimingTunables::EnvironmentTimingTunables() {
+ char value[PROPERTY_VALUE_MAX] = {};
+
+ property_get("debug.sf.region_sampling_offset_ns", value,
+ toNsString(defaultRegionSamplingOffset).c_str());
+ int const samplingOffsetNsRaw = atoi(value);
+
+ property_get("debug.sf.region_sampling_period_ns", value,
+ toNsString(defaultRegionSamplingPeriod).c_str());
+ int const samplingPeriodNsRaw = atoi(value);
+
+ property_get("debug.sf.region_sampling_timer_timeout_ns", value,
+ toNsString(defaultRegionSamplingTimerTimeout).c_str());
+ int const samplingTimerTimeoutNsRaw = atoi(value);
+
+ if ((samplingPeriodNsRaw < 0) || (samplingTimerTimeoutNsRaw < 0)) {
+ ALOGW("User-specified sampling tuning options nonsensical. Using defaults");
+ mSamplingOffset = defaultRegionSamplingOffset;
+ mSamplingPeriod = defaultRegionSamplingPeriod;
+ mSamplingTimerTimeout = defaultRegionSamplingTimerTimeout;
+ } else {
+ mSamplingOffset = std::chrono::nanoseconds(samplingOffsetNsRaw);
+ mSamplingPeriod = std::chrono::nanoseconds(samplingPeriodNsRaw);
+ mSamplingTimerTimeout = std::chrono::nanoseconds(samplingTimerTimeoutNsRaw);
+ }
+}
+
+struct SamplingOffsetCallback : DispSync::Callback {
+ SamplingOffsetCallback(RegionSamplingThread& samplingThread, Scheduler& scheduler,
+ std::chrono::nanoseconds targetSamplingOffset)
+ : mRegionSamplingThread(samplingThread),
+ mScheduler(scheduler),
+ mTargetSamplingOffset(targetSamplingOffset) {}
+
+ ~SamplingOffsetCallback() { stopVsyncListener(); }
+
+ SamplingOffsetCallback(const SamplingOffsetCallback&) = delete;
+ SamplingOffsetCallback& operator=(const SamplingOffsetCallback&) = delete;
+
+ void startVsyncListener() {
+ std::lock_guard lock(mMutex);
+ if (mVsyncListening) return;
+
+ mPhaseIntervalSetting = Phase::ZERO;
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.addEventListener("SamplingThreadDispSyncListener", 0, this, mLastCallbackTime);
+ });
+ mVsyncListening = true;
+ }
+
+ void stopVsyncListener() {
+ std::lock_guard lock(mMutex);
+ stopVsyncListenerLocked();
+ }
+
+private:
+ void stopVsyncListenerLocked() /*REQUIRES(mMutex)*/ {
+ if (!mVsyncListening) return;
+
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.removeEventListener(this, &mLastCallbackTime);
+ });
+ mVsyncListening = false;
+ }
+
+ void onDispSyncEvent(nsecs_t /* when */) final {
+ std::unique_lock<decltype(mMutex)> lock(mMutex);
+
+ if (mPhaseIntervalSetting == Phase::ZERO) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForSamplePhase));
+ mPhaseIntervalSetting = Phase::SAMPLING;
+ mScheduler.withPrimaryDispSync([this](android::DispSync& sync) {
+ sync.changePhaseOffset(this, mTargetSamplingOffset.count());
+ });
+ return;
+ }
+
+ if (mPhaseIntervalSetting == Phase::SAMPLING) {
+ mPhaseIntervalSetting = Phase::ZERO;
+ mScheduler.withPrimaryDispSync(
+ [this](android::DispSync& sync) { sync.changePhaseOffset(this, 0); });
+ stopVsyncListenerLocked();
+ lock.unlock();
+ mRegionSamplingThread.notifySamplingOffset();
+ return;
+ }
+ }
+
+ RegionSamplingThread& mRegionSamplingThread;
+ Scheduler& mScheduler;
+ const std::chrono::nanoseconds mTargetSamplingOffset;
+ mutable std::mutex mMutex;
+ nsecs_t mLastCallbackTime = 0;
+ enum class Phase {
+ ZERO,
+ SAMPLING
+ } mPhaseIntervalSetting /*GUARDED_BY(mMutex) macro doesnt work with unique_lock?*/
+ = Phase::ZERO;
+ bool mVsyncListening /*GUARDED_BY(mMutex)*/ = false;
+};
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+ const TimingTunables& tunables)
+ : mFlinger(flinger),
+ mScheduler(scheduler),
+ mTunables(tunables),
+ mIdleTimer(std::chrono::duration_cast<std::chrono::milliseconds>(
+ mTunables.mSamplingTimerTimeout),
+ [] {}, [this] { checkForStaleLuma(); }),
+ mPhaseCallback(std::make_unique<SamplingOffsetCallback>(*this, mScheduler,
+ tunables.mSamplingOffset)),
+ lastSampleTime(0ns) {
+ {
+ std::lock_guard threadLock(mThreadMutex);
+ mThread = std::thread([this]() { threadMain(); });
+ pthread_setname_np(mThread.native_handle(), "RegionSamplingThread");
+ }
+ mIdleTimer.start();
+}
+
+RegionSamplingThread::RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler)
+ : RegionSamplingThread(flinger, scheduler,
+ TimingTunables{defaultRegionSamplingOffset,
+ defaultRegionSamplingPeriod,
+ defaultRegionSamplingTimerTimeout}) {}
+
RegionSamplingThread::~RegionSamplingThread() {
+ mIdleTimer.stop();
+
{
std::lock_guard lock(mMutex);
mRunning = false;
@@ -71,8 +214,41 @@
mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener)));
}
-void RegionSamplingThread::sampleNow() {
+void RegionSamplingThread::checkForStaleLuma() {
std::lock_guard lock(mMutex);
+
+ if (mDiscardedFrames) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::waitForZeroPhase));
+ mDiscardedFrames = false;
+ mPhaseCallback->startVsyncListener();
+ }
+}
+
+void RegionSamplingThread::notifyNewContent() {
+ doSample();
+}
+
+void RegionSamplingThread::notifySamplingOffset() {
+ doSample();
+}
+
+void RegionSamplingThread::doSample() {
+ std::lock_guard lock(mMutex);
+ auto now = std::chrono::nanoseconds(systemTime(SYSTEM_TIME_MONOTONIC));
+ if (lastSampleTime + mTunables.mSamplingPeriod > now) {
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::idleTimerWaiting));
+ mDiscardedFrames = true;
+ return;
+ }
+
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::sample));
+
+ mDiscardedFrames = false;
+ lastSampleTime = now;
+
+ mIdleTimer.reset();
+ mPhaseCallback->stopVsyncListener();
+
mSampleRequested = true;
mCondition.notify_one();
}
@@ -238,6 +414,7 @@
for (size_t d = 0; d < activeDescriptors.size(); ++d) {
activeDescriptors[d].listener->onSampleCollected(lumas[d]);
}
+ ATRACE_INT(lumaSamplingStepTag, static_cast<int>(samplingStep::noWorkNeeded));
}
void RegionSamplingThread::threadMain() {
diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h
index ab06513..d4e57bf 100644
--- a/services/surfaceflinger/RegionSamplingThread.h
+++ b/services/surfaceflinger/RegionSamplingThread.h
@@ -16,6 +16,7 @@
#pragma once
+#include <chrono>
#include <condition_variable>
#include <mutex>
#include <thread>
@@ -25,17 +26,42 @@
#include <binder/IBinder.h>
#include <ui/Rect.h>
#include <utils/StrongPointer.h>
+#include "Scheduler/IdleTimer.h"
namespace android {
class GraphicBuffer;
class IRegionSamplingListener;
class Layer;
+class Scheduler;
class SurfaceFlinger;
+struct SamplingOffsetCallback;
class RegionSamplingThread : public IBinder::DeathRecipient {
public:
- explicit RegionSamplingThread(SurfaceFlinger& flinger);
+ struct TimingTunables {
+ // debug.sf.sampling_offset_ns
+ // When asynchronously collecting sample, the offset, from zero phase in the vsync timeline
+ // at which the sampling should start.
+ std::chrono::nanoseconds mSamplingOffset;
+ // debug.sf.sampling_period_ns
+ // This is the maximum amount of time the luma recieving client
+ // should have to wait for a new luma value after a frame is updated. The inverse of this is
+ // roughly the sampling rate. Sampling system rounds up sub-vsync sampling period to vsync
+ // period.
+ std::chrono::nanoseconds mSamplingPeriod;
+ // debug.sf.sampling_timer_timeout_ns
+ // This is the interval at which the luma sampling system will check that the luma clients
+ // have up to date information. It defaults to the mSamplingPeriod.
+ std::chrono::nanoseconds mSamplingTimerTimeout;
+ };
+ struct EnvironmentTimingTunables : TimingTunables {
+ EnvironmentTimingTunables();
+ };
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler,
+ const TimingTunables& tunables);
+ explicit RegionSamplingThread(SurfaceFlinger& flinger, Scheduler& scheduler);
+
~RegionSamplingThread();
// Add a listener to receive luma notifications. The luma reported via listener will
@@ -44,8 +70,13 @@
const sp<IRegionSamplingListener>& listener);
// Remove the listener to stop receiving median luma notifications.
void removeListener(const sp<IRegionSamplingListener>& listener);
- // Instruct the thread to perform a median luma sampling on the layers.
- void sampleNow();
+
+ // Notifies sampling engine that new content is available. This will trigger a sampling
+ // pass at some point in the future.
+ void notifyNewContent();
+
+ // Notifies the sampling engine that it has a good timing window in which to sample.
+ void notifySamplingOffset();
private:
struct Descriptor {
@@ -63,12 +94,19 @@
const sp<GraphicBuffer>& buffer, const Point& leftTop,
const std::vector<RegionSamplingThread::Descriptor>& descriptors);
+ void doSample();
void binderDied(const wp<IBinder>& who) override;
+ void checkForStaleLuma();
void captureSample() REQUIRES(mMutex);
void threadMain();
SurfaceFlinger& mFlinger;
+ Scheduler& mScheduler;
+ const TimingTunables mTunables;
+ scheduler::IdleTimer mIdleTimer;
+
+ std::unique_ptr<SamplingOffsetCallback> const mPhaseCallback;
std::mutex mThreadMutex;
std::thread mThread GUARDED_BY(mThreadMutex);
@@ -79,6 +117,8 @@
bool mSampleRequested GUARDED_BY(mMutex) = false;
std::unordered_map<wp<IBinder>, Descriptor, WpHash> mDescriptors GUARDED_BY(mMutex);
+ std::chrono::nanoseconds lastSampleTime GUARDED_BY(mMutex);
+ bool mDiscardedFrames GUARDED_BY(mMutex) = false;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp
index 665179e..5296da9 100644
--- a/services/surfaceflinger/Scheduler/DispSync.cpp
+++ b/services/surfaceflinger/Scheduler/DispSync.cpp
@@ -43,6 +43,7 @@
namespace android {
DispSync::~DispSync() = default;
+DispSync::Callback::~Callback() = default;
namespace impl {
@@ -75,9 +76,18 @@
void updateModel(nsecs_t period, nsecs_t phase, nsecs_t referenceTime) {
if (mTraceDetailedInfo) ATRACE_CALL();
Mutex::Autolock lock(mMutex);
- mPeriod = period;
+
mPhase = phase;
mReferenceTime = referenceTime;
+ 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);
+ const nsecs_t baseTime = now - mReferenceTime;
+ const nsecs_t numOldPeriods = baseTime / mPeriod;
+ mReferenceTime = mReferenceTime + (numOldPeriods)*mPeriod;
+ }
+ mPeriod = period;
if (mTraceDetailedInfo) {
ATRACE_INT64("DispSync:Period", mPeriod);
ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2);
@@ -175,7 +185,8 @@
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);
@@ -191,8 +202,34 @@
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);
@@ -201,13 +238,14 @@
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;
@@ -249,6 +287,7 @@
const char* mName;
nsecs_t mPhase;
nsecs_t mLastEventTime;
+ nsecs_t mLastCallbackTime;
DispSync::Callback* mCallback;
};
@@ -273,6 +312,13 @@
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));
@@ -284,12 +330,21 @@
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;
}
}
@@ -336,7 +391,7 @@
// 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));
}
@@ -398,7 +453,10 @@
mThread = new DispSyncThread(name, mTraceDetailedInfo);
}
-DispSync::~DispSync() {}
+DispSync::~DispSync() {
+ mThread->stop();
+ mThread->requestExitAndWait();
+}
void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) {
mIgnorePresentFences = !hasSyncFramework;
@@ -417,7 +475,7 @@
if (mTraceDetailedInfo && kEnableZeroPhaseTracer) {
mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>();
- addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get());
+ addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0);
}
}
@@ -428,8 +486,16 @@
void DispSync::resetLocked() {
mPhase = 0;
- mReferenceTime = 0;
+ const size_t lastSampleIdx = (mFirstResyncSample + mNumResyncSamples - 1) % MAX_RESYNC_SAMPLES;
+ // Keep the most recent sample, when we resync to hardware we'll overwrite this
+ // with a more accurate signal
+ if (mResyncSamples[lastSampleIdx] != 0) {
+ mReferenceTime = mResyncSamples[lastSampleIdx];
+ }
mModelUpdated = false;
+ for (size_t i = 0; i < MAX_RESYNC_SAMPLES; i++) {
+ mResyncSamples[i] = 0;
+ }
mNumResyncSamples = 0;
mFirstResyncSample = 0;
mNumResyncSamplesSincePresent = 0;
@@ -503,9 +569,10 @@
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) {
@@ -515,9 +582,9 @@
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) {
@@ -529,7 +596,6 @@
Mutex::Autolock lock(mMutex);
mPeriod = period;
mPhase = 0;
- mReferenceTime = 0;
mThread->updateModel(mPeriod, mPhase, mReferenceTime);
}
diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h
index 4a90f10..de2b874 100644
--- a/services/surfaceflinger/Scheduler/DispSync.h
+++ b/services/surfaceflinger/Scheduler/DispSync.h
@@ -14,8 +14,7 @@
* limitations under the License.
*/
-#ifndef ANDROID_DISPSYNC_H
-#define ANDROID_DISPSYNC_H
+#pragma once
#include <stddef.h>
@@ -35,10 +34,16 @@
public:
class Callback {
public:
- virtual ~Callback() = default;
+ Callback() = default;
+ virtual ~Callback();
virtual void onDispSyncEvent(nsecs_t when) = 0;
+
+ protected:
+ Callback(Callback const&) = delete;
+ Callback& operator=(Callback const&) = delete;
};
+ DispSync() = default;
virtual ~DispSync();
virtual void reset() = 0;
@@ -49,14 +54,19 @@
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;
virtual nsecs_t expectedPresentTime() = 0;
virtual void dump(std::string& result) const = 0;
+
+protected:
+ DispSync(DispSync const&) = delete;
+ DispSync& operator=(DispSync const&) = delete;
};
namespace impl {
@@ -130,12 +140,21 @@
// 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,
@@ -204,7 +223,7 @@
// These member variables are the state used during the resynchronization
// process to store information about the hardware vsync event times used
// to compute the model.
- nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES];
+ nsecs_t mResyncSamples[MAX_RESYNC_SAMPLES] = {0};
size_t mFirstResyncSample;
size_t mNumResyncSamples;
int mNumResyncSamplesSincePresent;
@@ -239,5 +258,3 @@
} // namespace impl
} // namespace android
-
-#endif // ANDROID_DISPSYNC_H
diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
index d848c97..6e89648 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp
@@ -40,13 +40,15 @@
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 5e3d181..2858678 100644
--- a/services/surfaceflinger/Scheduler/DispSyncSource.h
+++ b/services/surfaceflinger/Scheduler/DispSyncSource.h
@@ -45,6 +45,7 @@
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/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp
index e05c985..0063c8a 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.cpp
+++ b/services/surfaceflinger/Scheduler/Scheduler.cpp
@@ -28,6 +28,7 @@
#include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h>
#include <configstore/Utils.h>
#include <cutils/properties.h>
+#include <system/window.h>
#include <ui/DisplayStatInfo.h>
#include <utils/Timers.h>
#include <utils/Trace.h>
@@ -67,9 +68,14 @@
mPrimaryDispSync = std::move(primaryDispSync);
mEventControlThread = std::make_unique<impl::EventControlThread>(function);
+ mSetIdleTimerMs = set_idle_timer_ms(0);
+
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.set_idle_timer_ms", value, "0");
- mSetIdleTimerMs = atoi(value);
+ int int_value = atoi(value);
+ if (int_value) {
+ mSetIdleTimerMs = atoi(value);
+ }
if (mSetIdleTimerMs > 0) {
mIdleTimer =
@@ -291,21 +297,33 @@
mPrimaryDispSync->dump(result);
}
-void Scheduler::addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
- const std::string layerName) {
- // This is V1 logic. It calculates the average FPS based on the timestamp frequency
- // regardless of which layer the timestamp came from.
- // For now, the averages and FPS are recorded in the systrace.
- determineTimestampAverage(isAutoTimestamp, framePresentTime);
-
- // This is V2 logic. It calculates the average and median timestamp difference based on the
- // individual layer history. The results are recorded in the systrace.
- determineLayerTimestampStats(layerName, framePresentTime);
+void Scheduler::addNativeWindowApi(int apiId) {
+ std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+ mWindowApiHistory[mApiHistoryCounter] = apiId;
+ mApiHistoryCounter++;
+ mApiHistoryCounter = mApiHistoryCounter % scheduler::ARRAY_SIZE;
}
-void Scheduler::incrementFrameCounter() {
- std::lock_guard<std::mutex> lock(mLayerHistoryLock);
- mLayerHistory.incrementCounter();
+void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) {
+ fn(*mPrimaryDispSync);
+}
+
+void Scheduler::updateFpsBasedOnNativeWindowApi() {
+ int mode;
+ {
+ std::lock_guard<std::mutex> lock(mWindowApiHistoryLock);
+ mode = scheduler::calculate_mode(mWindowApiHistory);
+ }
+ ATRACE_INT("NativeWindowApiMode", mode);
+
+ if (mode == NATIVE_WINDOW_API_MEDIA) {
+ // TODO(b/127365162): These callback names are not accurate anymore. Update.
+ mediaChangeRefreshRate(MediaFeatureState::MEDIA_PLAYING);
+ ATRACE_INT("DetectedVideo", 1);
+ } else {
+ mediaChangeRefreshRate(MediaFeatureState::MEDIA_OFF);
+ ATRACE_INT("DetectedVideo", 0);
+ }
}
void Scheduler::setChangeRefreshRateCallback(
@@ -323,85 +341,6 @@
}
}
-void Scheduler::determineLayerTimestampStats(const std::string layerName,
- const nsecs_t framePresentTime) {
- std::vector<int64_t> differencesMs;
- std::string differencesText = "";
- {
- std::lock_guard<std::mutex> lock(mLayerHistoryLock);
- mLayerHistory.insert(layerName, framePresentTime);
-
- // Traverse through the layer history, and determine the differences in present times.
- nsecs_t newestPresentTime = framePresentTime;
- for (int i = 1; i < mLayerHistory.getSize(); i++) {
- std::unordered_map<std::string, nsecs_t> layers = mLayerHistory.get(i);
- for (auto layer : layers) {
- if (layer.first != layerName) {
- continue;
- }
- int64_t differenceMs = (newestPresentTime - layer.second) / 1000000;
- // Dismiss noise.
- if (differenceMs > 10 && differenceMs < 60) {
- differencesMs.push_back(differenceMs);
- }
- IF_ALOGV() { differencesText += (std::to_string(differenceMs) + " "); }
- newestPresentTime = layer.second;
- }
- }
- }
- ALOGV("Layer %s timestamp intervals: %s", layerName.c_str(), differencesText.c_str());
-
- if (!differencesMs.empty()) {
- // Mean/Average is a good indicator for when 24fps videos are playing, because the frames
- // come in 33, and 49 ms intervals with occasional 41ms.
- const int64_t meanMs = scheduler::calculate_mean(differencesMs);
- const auto tagMean = "TimestampMean_" + layerName;
- ATRACE_INT(tagMean.c_str(), meanMs);
-
- // Mode and median are good indicators for 30 and 60 fps videos, because the majority of
- // frames come in 16, or 33 ms intervals.
- const auto tagMedian = "TimestampMedian_" + layerName;
- ATRACE_INT(tagMedian.c_str(), scheduler::calculate_median(&differencesMs));
-
- const auto tagMode = "TimestampMode_" + layerName;
- ATRACE_INT(tagMode.c_str(), scheduler::calculate_mode(differencesMs));
- }
-}
-
-void Scheduler::determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime) {
- ATRACE_INT("AutoTimestamp", isAutoTimestamp);
-
- // Video does not have timestamp automatically set, so we discard timestamps that are
- // coming in from other sources for now.
- if (isAutoTimestamp) {
- return;
- }
- int64_t differenceMs = (framePresentTime - mPreviousFrameTimestamp) / 1000000;
- mPreviousFrameTimestamp = framePresentTime;
-
- if (differenceMs < 10 || differenceMs > 100) {
- // Dismiss noise.
- return;
- }
- ATRACE_INT("TimestampDiff", differenceMs);
-
- mTimeDifferences[mCounter % scheduler::ARRAY_SIZE] = differenceMs;
- mCounter++;
- int64_t mean = scheduler::calculate_mean(mTimeDifferences);
- ATRACE_INT("AutoTimestampMean", mean);
-
- // TODO(b/113612090): This are current numbers from trial and error while running videos
- // from YouTube at 24, 30, and 60 fps.
- if (mean > 14 && mean < 18) {
- ATRACE_INT("MediaFPS", 60);
- } else if (mean > 31 && mean < 34) {
- ATRACE_INT("MediaFPS", 30);
- return;
- } else if (mean > 39 && mean < 42) {
- ATRACE_INT("MediaFPS", 24);
- }
-}
-
void Scheduler::resetIdleTimer() {
if (mIdleTimer) {
mIdleTimer->reset();
@@ -409,21 +348,15 @@
}
void Scheduler::resetTimerCallback() {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mChangeRefreshRateCallback) {
- // We do not notify the applications about config changes when idle timer is reset.
- mChangeRefreshRateCallback(RefreshRateType::PERFORMANCE, ConfigEvent::None);
- ATRACE_INT("ExpiredIdleTimer", 0);
- }
+ // We do not notify the applications about config changes when idle timer is reset.
+ timerChangeRefreshRate(IdleTimerState::RESET);
+ ATRACE_INT("ExpiredIdleTimer", 0);
}
void Scheduler::expiredTimerCallback() {
- std::lock_guard<std::mutex> lock(mCallbackLock);
- if (mChangeRefreshRateCallback) {
- // We do not notify the applications about config changes when idle timer expires.
- mChangeRefreshRateCallback(RefreshRateType::DEFAULT, ConfigEvent::None);
- ATRACE_INT("ExpiredIdleTimer", 1);
- }
+ // We do not notify the applications about config changes when idle timer expires.
+ timerChangeRefreshRate(IdleTimerState::EXPIRED);
+ ATRACE_INT("ExpiredIdleTimer", 1);
}
std::string Scheduler::doDump() {
@@ -432,4 +365,46 @@
return stream.str();
}
+void Scheduler::mediaChangeRefreshRate(MediaFeatureState mediaFeatureState) {
+ // Default playback for media is DEFAULT when media is on.
+ RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ ConfigEvent configEvent = ConfigEvent::None;
+
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mCurrentMediaFeatureState = mediaFeatureState;
+ // Only switch to PERFORMANCE if idle timer was reset, when turning
+ // media off. If the timer is IDLE, stay at DEFAULT.
+ if (mediaFeatureState == MediaFeatureState::MEDIA_OFF &&
+ mCurrentIdleTimerState == IdleTimerState::RESET) {
+ refreshRateType = RefreshRateType::PERFORMANCE;
+ }
+ }
+ changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::timerChangeRefreshRate(IdleTimerState idleTimerState) {
+ RefreshRateType refreshRateType = RefreshRateType::DEFAULT;
+ ConfigEvent configEvent = ConfigEvent::None;
+
+ {
+ std::lock_guard<std::mutex> lock(mFeatureStateLock);
+ mCurrentIdleTimerState = idleTimerState;
+ // Only switch to PERFOMANCE if the idle timer was reset, and media is
+ // not playing. Otherwise, stay at DEFAULT.
+ if (idleTimerState == IdleTimerState::RESET &&
+ mCurrentMediaFeatureState == MediaFeatureState::MEDIA_OFF) {
+ refreshRateType = RefreshRateType::PERFORMANCE;
+ }
+ }
+ changeRefreshRate(refreshRateType, configEvent);
+}
+
+void Scheduler::changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent) {
+ std::lock_guard<std::mutex> lock(mCallbackLock);
+ if (mChangeRefreshRateCallback) {
+ mChangeRefreshRateCallback(refreshRateType, configEvent);
+ }
+}
+
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/Scheduler.h b/services/surfaceflinger/Scheduler/Scheduler.h
index f8667d0..73896d5 100644
--- a/services/surfaceflinger/Scheduler/Scheduler.h
+++ b/services/surfaceflinger/Scheduler/Scheduler.h
@@ -17,6 +17,7 @@
#pragma once
#include <cstdint>
+#include <functional>
#include <memory>
#include <ui/DisplayStatInfo.h>
@@ -104,6 +105,9 @@
// Getter methods.
EventThread* getEventThread(const sp<ConnectionHandle>& handle);
+ // Provides access to the DispSync object for the primary display.
+ void withPrimaryDispSync(std::function<void(DispSync&)> const& fn);
+
sp<EventThreadConnection> getEventConnection(const sp<ConnectionHandle>& handle);
// Should be called when receiving a hotplug event.
@@ -141,13 +145,16 @@
void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime);
void setIgnorePresentFences(bool ignore);
nsecs_t expectedPresentTime();
- // Adds the present time for given layer to the history of present times.
- void addFramePresentTimeForLayer(const nsecs_t framePresentTime, bool isAutoTimestamp,
- const std::string layerName);
- // Increments counter in the layer history to indicate that SF has started a new frame.
- void incrementFrameCounter();
+ // apiId indicates the API (NATIVE_WINDOW_API_xxx) that queues the buffer.
+ // TODO(b/123956502): Remove this call with V1 go/content-fps-detection-in-scheduler.
+ void addNativeWindowApi(int apiId);
+ // Updates FPS based on the most occured request for Native Window API.
+ void updateFpsBasedOnNativeWindowApi();
// Callback that gets invoked when Scheduler wants to change the refresh rate.
void setChangeRefreshRateCallback(const ChangeRefreshRateCallback& changeRefreshRateCallback);
+
+ // Returns whether idle timer is enabled or not
+ bool isIdleTimerEnabled() { return mSetIdleTimerMs > 0; }
// Returns relevant information about Scheduler for dumpsys purposes.
std::string doDump();
@@ -162,17 +169,16 @@
private:
friend class TestableScheduler;
+ // In order to make sure that the features don't override themselves, we need a state machine
+ // to keep track which feature requested the config change.
+ enum class MediaFeatureState { MEDIA_PLAYING, MEDIA_OFF };
+ enum class IdleTimerState { EXPIRED, RESET };
+
// Creates a connection on the given EventThread and forwards the given callbacks.
sp<EventThreadConnection> createConnectionInternal(EventThread*, ResyncCallback&&);
nsecs_t calculateAverage() const;
void updateFrameSkipping(const int64_t skipCount);
- // Collects the statistical mean (average) and median between timestamp
- // intervals for each frame for each layer.
- void determineLayerTimestampStats(const std::string layerName, const nsecs_t framePresentTime);
- // Collects the average difference between timestamps for each frame regardless
- // of which layer the timestamp came from.
- void determineTimestampAverage(bool isAutoTimestamp, const nsecs_t framePresentTime);
// Function that resets the idle timer.
void resetIdleTimer();
// Function that is called when the timer resets.
@@ -181,6 +187,12 @@
void expiredTimerCallback();
// Sets vsync period.
void setVsyncPeriod(const nsecs_t period);
+ // Media feature's function to change the refresh rate.
+ void mediaChangeRefreshRate(MediaFeatureState mediaFeatureState);
+ // Idle timer feature's function to change the refresh rate.
+ void timerChangeRefreshRate(IdleTimerState idleTimerState);
+ // Acquires a lock and calls the ChangeRefreshRateCallback() with given parameters.
+ void changeRefreshRate(RefreshRateType refreshRateType, ConfigEvent configEvent);
// If fences from sync Framework are supported.
const bool mHasSyncFramework;
@@ -214,10 +226,13 @@
std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{};
size_t mCounter = 0;
- // DetermineLayerTimestampStats is called from BufferQueueLayer::onFrameAvailable which
- // can run on any thread, and cause failure.
- std::mutex mLayerHistoryLock;
- LayerHistory mLayerHistory GUARDED_BY(mLayerHistoryLock);
+ // The following few fields follow native window api bits that come with buffers. If there are
+ // more buffers with NATIVE_WINDOW_API_MEDIA we render at 60Hz, otherwise we render at 90Hz.
+ // There is not dependency on timestamp for V0.
+ // TODO(b/123956502): Remove this when more robust logic for content fps detection is developed.
+ std::mutex mWindowApiHistoryLock;
+ std::array<int, scheduler::ARRAY_SIZE> mWindowApiHistory GUARDED_BY(mWindowApiHistoryLock);
+ int64_t mApiHistoryCounter = 0;
// Timer that records time between requests for next vsync. If the time is higher than a given
// interval, a callback is fired. Set this variable to >0 to use this feature.
@@ -226,6 +241,13 @@
std::mutex mCallbackLock;
ChangeRefreshRateCallback mChangeRefreshRateCallback GUARDED_BY(mCallbackLock);
+
+ // In order to make sure that the features don't override themselves, we need a state machine
+ // to keep track which feature requested the config change.
+ std::mutex mFeatureStateLock;
+ MediaFeatureState mCurrentMediaFeatureState GUARDED_BY(mFeatureStateLock) =
+ MediaFeatureState::MEDIA_OFF;
+ IdleTimerState mCurrentIdleTimerState GUARDED_BY(mFeatureStateLock) = IdleTimerState::RESET;
};
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
index 191022d..fb5414f 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp
@@ -34,23 +34,5 @@
return v->at(n);
}
-int64_t calculate_mode(const std::vector<int64_t>& v) {
- if (v.empty()) {
- return 0;
- }
-
- // Create a map with all the counts for the indivicual values in the vector.
- std::unordered_map<int64_t, int64_t> counts;
- for (int64_t value : v) {
- counts[value]++;
- }
-
- // Sort the map, and return the number with the highest count. If two numbers have
- // the same count, first one is returned.
- using ValueType = const decltype(counts)::value_type&;
- const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
- return std::max_element(counts.begin(), counts.end(), compareCounts)->first;
-}
-
} // namespace scheduler
} // namespace android
diff --git a/services/surfaceflinger/Scheduler/SchedulerUtils.h b/services/surfaceflinger/Scheduler/SchedulerUtils.h
index edd23de..9e6e8c7 100644
--- a/services/surfaceflinger/Scheduler/SchedulerUtils.h
+++ b/services/surfaceflinger/Scheduler/SchedulerUtils.h
@@ -18,6 +18,7 @@
#include <cinttypes>
#include <numeric>
+#include <unordered_map>
#include <vector>
namespace android {
@@ -45,7 +46,24 @@
int64_t calculate_median(std::vector<int64_t>* v);
// Calculates the statistical mode in the vector. Return 0 if the vector is empty.
-int64_t calculate_mode(const std::vector<int64_t>& v);
+template <typename T>
+auto calculate_mode(const T& v) {
+ if (v.empty()) {
+ return 0;
+ }
+
+ // Create a map with all the counts for the indivicual values in the vector.
+ std::unordered_map<int64_t, int> counts;
+ for (int64_t value : v) {
+ counts[value]++;
+ }
+
+ // Sort the map, and return the number with the highest count. If two numbers have
+ // the same count, first one is returned.
+ using ValueType = const decltype(counts)::value_type&;
+ const auto compareCounts = [](ValueType l, ValueType r) { return l.second <= r.second; };
+ return static_cast<int>(std::max_element(counts.begin(), counts.end(), compareCounts)->first);
+}
} // namespace scheduler
} // namespace android
\ No newline at end of file
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index e445eb6..83e53fa 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -274,6 +274,7 @@
mDebugInTransaction(0),
mLastTransactionTime(0),
mForceFullDamage(false),
+ mTracing(*this),
mTimeStats(factory.createTimeStats()),
mRefreshStartTime(0),
mHasPoweredOff(false),
@@ -370,11 +371,13 @@
auto listSize = property_get_int32("debug.sf.max_igbp_list_size", int32_t(defaultListSize));
mMaxGraphicBufferProducerListSize = (listSize > 0) ? size_t(listSize) : defaultListSize;
- property_get("debug.sf.use_90Hz", value, "0");
- mUse90Hz = atoi(value);
-
+ mUseSmart90ForVideo = use_smart_90_for_video(false);
property_get("debug.sf.use_smart_90_for_video", value, "0");
- mUseSmart90ForVideo = atoi(value);
+
+ int int_value = atoi(value);
+ if (int_value) {
+ mUseSmart90ForVideo = true;
+ }
property_get("debug.sf.luma_sampling", value, "1");
mLumaSampling = atoi(value);
@@ -563,18 +566,16 @@
readPersistentProperties();
mBootStage = BootStage::FINISHED;
- // TODO(b/122905403): Once the display policy is completely integrated, this flag should go
- // away and it should be controlled by flipping the switch in setting. The switch in
- // settings should only be available to P19 devices, if they are opted into 90Hz fishfood.
- // The boot must be complete before we can set the active config.
+ // set the refresh rate according to the policy
+ const auto displayId = getInternalDisplayIdLocked();
+ LOG_ALWAYS_FATAL_IF(!displayId);
- if (mUse90Hz) {
- mPhaseOffsets->setRefreshRateType(
- scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE);
+ const auto performanceRefreshRate =
+ mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
+
+ if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None);
} else {
- mPhaseOffsets->setRefreshRateType(
- scheduler::RefreshRateConfigs::RefreshRateType::DEFAULT);
setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None);
}
}));
@@ -633,6 +634,10 @@
mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(),
mSfConnectionHandle.get());
+ mRegionSamplingThread =
+ new RegionSamplingThread(*this, *mScheduler,
+ RegionSamplingThread::EnvironmentTimingTunables());
+
// Get a RenderEngine for the given display / config (can't fail)
int32_t renderEngineFeature = 0;
renderEngineFeature |= (useColorManagement ?
@@ -699,7 +704,7 @@
ALOGE("Run StartPropertySetThread failed!");
}
- if (mUse90Hz) {
+ if (mScheduler->isIdleTimerEnabled()) {
mScheduler->setChangeRefreshRateCallback(
[this](RefreshRateType type, Scheduler::ConfigEvent event) {
Mutex::Autolock lock(mStateLock);
@@ -971,6 +976,8 @@
display->setActiveConfig(mUpcomingActiveConfig.configId);
mScheduler->resyncToHardwareVsync(true, getVsyncPeriod());
+ const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
+ mVsyncModulator.setPhaseOffsets(early, gl, late);
ATRACE_INT("ActiveConfigMode", mUpcomingActiveConfig.configId);
if (mUpcomingActiveConfig.event != Scheduler::ConfigEvent::None) {
mScheduler->onConfigChanged(mAppConnectionHandle, display->getId()->value,
@@ -980,15 +987,6 @@
bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS {
ATRACE_CALL();
- // we may be in the process of changing the active state
- if (mWaitForNextInvalidate) {
- mWaitForNextInvalidate = false;
-
- repaintEverythingForHWC();
- // We do not want to give another frame to HWC while we are transitioning.
- return true;
- }
-
if (mCheckPendingFence) {
if (mPreviousPresentFence != Fence::NO_FENCE &&
(mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled)) {
@@ -1040,7 +1038,6 @@
getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId);
// we need to submit an empty frame to HWC to start the process
- mWaitForNextInvalidate = true;
mCheckPendingFence = true;
return false;
@@ -1311,6 +1308,33 @@
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(
@@ -1414,15 +1438,10 @@
}
void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) {
- ATRACE_CALL();
- mPhaseOffsets->setRefreshRateType(refreshRate);
-
- const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets();
- mVsyncModulator.setPhaseOffsets(early, gl, late);
-
if (mBootStage != BootStage::FINISHED) {
return;
}
+ ATRACE_CALL();
// Don't do any updating if the current fps is the same as the new one.
const auto displayId = getInternalDisplayIdLocked();
@@ -1440,6 +1459,7 @@
return;
}
+ mPhaseOffsets->setRefreshRateType(refreshRate);
setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event);
}
@@ -1584,23 +1604,36 @@
ATRACE_CALL();
switch (what) {
case MessageQueue::INVALIDATE: {
+ bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE &&
+ (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
+ bool hwcFrameMissed = mHadDeviceComposition && frameMissed;
+ bool gpuFrameMissed = mHadClientComposition && frameMissed;
+ ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
+ ATRACE_INT("HwcFrameMissed", static_cast<int>(hwcFrameMissed));
+ ATRACE_INT("GpuFrameMissed", static_cast<int>(gpuFrameMissed));
+ if (frameMissed) {
+ mFrameMissedCount++;
+ 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.
+ mScheduler->updateFpsBasedOnNativeWindowApi();
+ }
+
if (performSetActiveConfig()) {
break;
}
- if (mUse90Hz && mUseSmart90ForVideo) {
- // This call is made each time SF wakes up and creates a new frame. It is part
- // of video detection feature.
- mScheduler->incrementFrameCounter();
- }
- bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE &&
- (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled);
- bool hwcFrameMissed = !mHadClientComposition && frameMissed;
- if (frameMissed) {
- ATRACE_INT("FrameMissed", static_cast<int>(frameMissed));
- mFrameMissedCount++;
- mTimeStats->incrementMissedFrames();
- }
// For now, only propagate backpressure when missing a hwc frame.
if (hwcFrameMissed) {
if (mPropagateBackpressure) {
@@ -1674,18 +1707,20 @@
doComposition(display, repaintEverything);
}
- doTracing("handleRefresh");
logLayerStats();
postFrame();
postComposition();
mHadClientComposition = false;
+ mHadDeviceComposition = false;
for (const auto& [token, displayDevice] : mDisplays) {
auto display = displayDevice->getCompositionDisplay();
const auto displayId = display->getId();
mHadClientComposition =
mHadClientComposition || getHwComposer().hasClientComposition(displayId);
+ mHadDeviceComposition =
+ mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId);
}
mVsyncModulator.onRefreshed(mHadClientComposition);
@@ -1700,6 +1735,9 @@
if (mVisibleRegionsDirty) {
computeLayerBounds();
+ if (mTracingEnabled) {
+ mTracing.notify("visibleRegionsDirty");
+ }
}
for (auto& layer : mLayersPendingRefresh) {
@@ -1720,24 +1758,30 @@
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);
}
}
}
@@ -1838,14 +1882,6 @@
prepareFrame(displayDevice);
}
-void SurfaceFlinger::doTracing(const char* where) {
- ATRACE_CALL();
- ATRACE_NAME(where);
- if (CC_UNLIKELY(mTracing.isEnabled())) {
- mTracing.traceLayers(where, dumpProtoInfo(LayerVector::StateSet::Drawing));
- }
-}
-
void SurfaceFlinger::logLayerStats() {
ATRACE_CALL();
if (CC_UNLIKELY(mLayerStats.isEnabled())) {
@@ -2058,8 +2094,8 @@
mTransactionCompletedThread.addPresentFence(mPreviousPresentFence);
mTransactionCompletedThread.sendCallbacks();
- if (mLumaSampling) {
- mRegionSamplingThread->sampleNow();
+ if (mLumaSampling && mRegionSamplingThread) {
+ mRegionSamplingThread->notifyNewContent();
}
}
@@ -2920,18 +2956,39 @@
// we composite should be considered an animation as well.
mAnimCompositionPending = mAnimTransactionPending;
- mDrawingState = mCurrentState;
- // clear the "changed" flags in current state
- mCurrentState.colorMatrixChanged = false;
+ withTracingLock([&]() {
+ mDrawingState = mCurrentState;
+ // clear the "changed" flags in current state
+ mCurrentState.colorMatrixChanged = false;
- mDrawingState.traverseInZOrder([](Layer* layer) {
- layer->commitChildList();
+ mDrawingState.traverseInZOrder([](Layer* layer) { layer->commitChildList(); });
});
+
mTransactionPending = false;
mAnimTransactionPending = false;
mTransactionCV.broadcast();
}
+void SurfaceFlinger::withTracingLock(std::function<void()> lockedOperation) {
+ if (mTracingEnabledChanged) {
+ mTracingEnabled = mTracing.isEnabled();
+ mTracingEnabledChanged = false;
+ }
+
+ // Synchronize with Tracing thread
+ std::unique_lock<std::mutex> lock;
+ if (mTracingEnabled) {
+ lock = std::unique_lock<std::mutex>(mDrawingStateLock);
+ }
+
+ lockedOperation();
+
+ // Synchronize with Tracing thread
+ if (mTracingEnabled) {
+ lock.unlock();
+ }
+}
+
void SurfaceFlinger::computeVisibleRegions(const sp<const DisplayDevice>& displayDevice,
Region& outDirtyRegion, Region& outOpaqueRegion) {
ATRACE_CALL();
@@ -3437,12 +3494,13 @@
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();
}
@@ -3502,6 +3560,8 @@
int64_t desiredPresentTime) {
ATRACE_CALL();
+ const int64_t postTime = systemTime();
+
bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess();
Mutex::Autolock _l(mStateLock);
@@ -3514,17 +3574,19 @@
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;
@@ -3550,7 +3612,7 @@
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)) {
@@ -3663,7 +3725,8 @@
}
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()));
@@ -3873,7 +3936,7 @@
if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded;
}
if (what & layer_state_t::eInputInfoChanged) {
- if (callingThreadHasUnscopedSurfaceFlingerAccess()) {
+ if (privileged) {
layer->setInputInfo(s.inputInfo);
flags |= eTraversalNeeded;
} else {
@@ -3905,7 +3968,11 @@
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
@@ -3943,14 +4010,27 @@
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.
@@ -3960,9 +4040,8 @@
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.
@@ -3971,9 +4050,8 @@
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;
@@ -3984,18 +4062,10 @@
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);
@@ -4038,7 +4108,8 @@
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
@@ -4052,8 +4123,8 @@
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();
@@ -4067,30 +4138,31 @@
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;
}
@@ -4326,6 +4398,14 @@
return NO_ERROR;
}
+status_t SurfaceFlinger::dumpCritical(int fd, const DumpArgs&, bool asProto) {
+ if (asProto && mTracing.isEnabled()) {
+ mTracing.writeToFileAsync();
+ }
+
+ return doDump(fd, DumpArgs(), asProto);
+}
+
void SurfaceFlinger::listLayersLocked(std::string& result) const {
mCurrentState.traverseInZOrder(
[&](Layer* layer) { StringAppendF(&result, "%s\n", layer->getName().string()); });
@@ -4391,7 +4471,7 @@
" present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n",
dispSyncPresentTimeOffset, getVsyncPeriod());
- StringAppendF(&result, "Scheduler enabled. 90Hz feature: %s\n", mUse90Hz ? "on" : "off");
+ StringAppendF(&result, "Scheduler enabled.");
StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n",
mUseSmart90ForVideo ? "on" : "off");
mScheduler->dump(mAppConnectionHandle, result);
@@ -4626,7 +4706,9 @@
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);
@@ -4646,6 +4728,14 @@
result.append("\n");
}
+ {
+ StringAppendF(&result, "Composition layers\n");
+ mDrawingState.traverseInZOrder([&](Layer* layer) {
+ auto compositionLayer = layer->getCompositionLayer();
+ if (compositionLayer) compositionLayer->dump(result);
+ });
+ }
+
/*
* Dump Display state
*/
@@ -4745,6 +4835,7 @@
*/
result.append("\nScheduler state:\n");
result.append(mScheduler->doDump() + "\n");
+ StringAppendF(&result, "+ Smart video mode: %s\n\n", mUseSmart90ForVideo ? "on" : "off");
result.append(mRefreshRateStats->doDump() + "\n");
}
@@ -4796,7 +4887,6 @@
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:
@@ -4832,6 +4922,7 @@
// 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:
@@ -4847,7 +4938,9 @@
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:
@@ -5079,13 +5172,24 @@
n = data.readInt32();
if (n) {
ALOGD("LayerTracing enabled");
+ Mutex::Autolock lock(mStateLock);
+ mTracingEnabledChanged = true;
mTracing.enable();
- doTracing("tracing.enable");
reply->writeInt32(NO_ERROR);
} else {
ALOGD("LayerTracing disabled");
- status_t err = mTracing.disable();
- reply->writeInt32(err);
+ bool writeFile = false;
+ {
+ Mutex::Autolock lock(mStateLock);
+ mTracingEnabledChanged = true;
+ writeFile = mTracing.disable();
+ }
+
+ if (writeFile) {
+ reply->writeInt32(mTracing.writeToFile());
+ } else {
+ reply->writeInt32(NO_ERROR);
+ }
}
return NO_ERROR;
}
@@ -5124,6 +5228,20 @@
reply->writeBool(getHwComposer().isUsingVrComposer());
return NO_ERROR;
}
+ // Set buffer size for SF tracing (value in KB)
+ case 1029: {
+ n = data.readInt32();
+ if (n <= 0 || n > MAX_TRACING_MEMORY) {
+ ALOGW("Invalid buffer size: %d KB", n);
+ reply->writeInt32(BAD_VALUE);
+ return BAD_VALUE;
+ }
+
+ ALOGD("Updating trace buffer to %d KB", n);
+ mTracing.setBufferSize(n * 1024);
+ reply->writeInt32(NO_ERROR);
+ return NO_ERROR;
+ }
// Is device color managed?
case 1030: {
reply->writeBool(useColorManagement);
@@ -5297,7 +5415,8 @@
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();
@@ -5658,6 +5777,16 @@
}
}
}
+
+ // If idle timer and fps detection are disabled and we are in RefreshRateType::DEFAULT,
+ // there is no trigger to move to RefreshRateType::PERFORMANCE, even if it is an allowed.
+ if (!mScheduler->isIdleTimerEnabled() && !mUseSmart90ForVideo) {
+ const auto performanceRefreshRate =
+ mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE);
+ if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) {
+ setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::Changed);
+ }
+ }
}
status_t SurfaceFlinger::setAllowedDisplayConfigs(const android::sp<android::IBinder>& displayToken,
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index deffe9d..0d39cb5 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -367,6 +367,7 @@
friend class BufferStateLayer;
friend class MonitoredProducer;
friend class RegionSamplingThread;
+ friend class SurfaceTracing;
// For unit tests
friend class TestableSurfaceFlinger;
@@ -376,6 +377,7 @@
enum { LOG_FRAME_STATS_PERIOD = 30*60*60 };
static const size_t MAX_LAYERS = 4096;
+ static const int MAX_TRACING_MEMORY = 100 * 1024 * 1024; // 100MB
// We're reference counted, never destroy SurfaceFlinger directly
virtual ~SurfaceFlinger();
@@ -492,6 +494,9 @@
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
@@ -572,6 +577,7 @@
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);
@@ -580,12 +586,13 @@
uint32_t setTransactionFlags(uint32_t flags);
uint32_t setTransactionFlags(uint32_t flags, Scheduler::TransactionStart transactionStart);
void latchAndReleaseBuffer(const sp<Layer>& layer);
- void commitTransaction();
+ void commitTransaction() REQUIRES(mStateLock);
bool containsAnyInvalidClientState(const Vector<ComposerState>& states);
bool transactionIsReadyToBeApplied(int64_t desiredPresentTime,
const Vector<ComposerState>& states);
- uint32_t setClientStateLocked(const ComposerState& composerState, bool privileged);
- uint32_t setDisplayStateLocked(const DisplayState& s);
+ 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);
@@ -597,21 +604,21 @@
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);
@@ -773,7 +780,6 @@
void prepareFrame(const sp<DisplayDevice>& display);
void doComposition(const sp<DisplayDevice>& display, bool repainEverything);
void doDebugFlashRegions(const sp<DisplayDevice>& display, bool repaintEverything);
- void doTracing(const char* where);
void logLayerStats();
void doDisplayComposition(const sp<DisplayDevice>& display, const Region& dirtyRegion);
@@ -890,6 +896,7 @@
void dumpDisplayIdentificationData(std::string& result) const;
void dumpWideColorInfo(std::string& result) const;
LayersProto dumpProtoInfo(LayerVector::StateSet stateSet) const;
+ void withTracingLock(std::function<void()> operation) REQUIRES(mStateLock);
LayersProto dumpVisibleLayersProtoInfo(const sp<DisplayDevice>& display) const;
bool isLayerTripleBufferingDisabled() const {
@@ -898,9 +905,7 @@
status_t doDump(int fd, const DumpArgs& args, bool asProto);
- status_t dumpCritical(int fd, const DumpArgs&, bool asProto) override {
- return doDump(fd, DumpArgs(), asProto);
- }
+ status_t dumpCritical(int fd, const DumpArgs&, bool asProto);
status_t dumpAll(int fd, const DumpArgs& args, bool asProto) override {
return doDump(fd, args, asProto);
@@ -931,6 +936,9 @@
bool mAnimTransactionPending;
SortedVector< sp<Layer> > mLayersPendingRemoval;
+ // guards access to the mDrawing state if tracing is enabled.
+ mutable std::mutex mDrawingStateLock;
+
// global color transform states
Daltonizer mDaltonizer;
float mGlobalSaturationFactor = 1.0f;
@@ -970,7 +978,12 @@
// Tracks layers that need to update a display's dirty region.
std::vector<sp<Layer>> mLayersPendingRefresh;
sp<Fence> mPreviousPresentFence = Fence::NO_FENCE;
+ // True if in the previous frame at least one layer was composed via the GPU.
bool mHadClientComposition = false;
+ // True if in the previous frame at least one layer was composed via HW Composer.
+ // Note that it is possible for a frame to be composed via both client and device
+ // composition, for example in the case of overlays.
+ bool mHadDeviceComposition = false;
enum class BootStage {
BOOTLOADER,
@@ -1004,16 +1017,17 @@
bool mPropagateBackpressure = true;
std::unique_ptr<SurfaceInterceptor> mInterceptor{mFactory.createSurfaceInterceptor(this)};
SurfaceTracing mTracing;
+ bool mTracingEnabled = false;
+ bool mTracingEnabledChanged GUARDED_BY(mStateLock) = false;
LayerStats mLayerStats;
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;
- bool mLumaSampling = true;
- sp<RegionSamplingThread> mRegionSamplingThread = new RegionSamplingThread(*this);
-
// Restrict layers to use two buffers in their bufferqueues.
bool mLayerTripleBufferingDisabled = false;
@@ -1044,18 +1058,19 @@
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;
@@ -1100,7 +1115,6 @@
/* ------------------------------------------------------------------------
* Scheduler
*/
- bool mUse90Hz = false;
bool mUseSmart90ForVideo = false;
std::unique_ptr<Scheduler> mScheduler;
sp<Scheduler::ConnectionHandle> mAppConnectionHandle;
@@ -1136,10 +1150,12 @@
// below flags are set by main thread only
bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false;
- bool mWaitForNextInvalidate = false;
bool mCheckPendingFence = false;
/* ------------------------------------------------------------------------ */
+ bool mLumaSampling = true;
+ sp<RegionSamplingThread> mRegionSamplingThread;
+
sp<IInputFlinger> mInputFlinger;
InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock);
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp
index cb9a6c0..e130511 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.cpp
+++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp
@@ -209,6 +209,22 @@
return static_cast<int32_t>(defaultValue);
}
+int32_t set_idle_timer_ms(int32_t defaultValue) {
+ auto temp = SurfaceFlingerProperties::set_idle_timer_ms();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
+bool use_smart_90_for_video(bool defaultValue) {
+ auto temp = SurfaceFlingerProperties::use_smart_90_for_video();
+ if (temp.has_value()) {
+ return *temp;
+ }
+ return defaultValue;
+}
+
#define DISPLAY_PRIMARY_SIZE 3
constexpr float kSrgbRedX = 0.4123f;
diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h
index 9917683..6f90117 100644
--- a/services/surfaceflinger/SurfaceFlingerProperties.h
+++ b/services/surfaceflinger/SurfaceFlingerProperties.h
@@ -55,6 +55,10 @@
int32_t wcg_composition_pixel_format(
android::hardware::graphics::common::V1_2::PixelFormat defaultValue);
+int32_t set_idle_timer_ms(int32_t defaultValue);
+
+bool use_smart_90_for_video(bool defaultValue);
+
android::ui::DisplayPrimaries getDisplayNativePrimaries();
} // namespace sysprop
} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index b7e9a91..f1c4347 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -18,6 +18,7 @@
#define ATRACE_TAG ATRACE_TAG_GRAPHICS
#include "SurfaceTracing.h"
+#include <SurfaceFlinger.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -27,6 +28,48 @@
namespace android {
+void SurfaceTracing::mainLoop() {
+ bool enabled = true;
+ // Upon activation, logs the first frame
+ traceLayers("tracing.enable");
+ do {
+ std::unique_lock<std::mutex> sfLock(mFlinger.mDrawingStateLock);
+ mConditionalVariable.wait(sfLock);
+ LayersTraceProto entry = traceLayersLocked(mWhere);
+ sfLock.unlock();
+ {
+ std::scoped_lock bufferLock(mTraceLock);
+ mBuffer.emplace(std::move(entry));
+ if (mWriteToFile) {
+ writeProtoFileLocked();
+ mWriteToFile = false;
+ }
+
+ enabled = mEnabled;
+ }
+ } while (enabled);
+}
+
+void SurfaceTracing::traceLayers(const char* where) {
+ std::unique_lock<std::mutex> sfLock(mFlinger.mDrawingStateLock);
+ LayersTraceProto entry = traceLayersLocked(where);
+ sfLock.unlock();
+ std::scoped_lock bufferLock(mTraceLock);
+ mBuffer.emplace(std::move(entry));
+}
+
+void SurfaceTracing::notify(const char* where) {
+ std::lock_guard<std::mutex> sfLock(mFlinger.mDrawingStateLock);
+ mWhere = strdup(where);
+ mConditionalVariable.notify_one();
+}
+
+void SurfaceTracing::writeToFileAsync() {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
+ mWriteToFile = true;
+ mConditionalVariable.notify_one();
+}
+
void SurfaceTracing::LayersTraceBuffer::reset(size_t newSize) {
// use the swap trick to make sure memory is released
std::queue<LayersTraceProto>().swap(mStorage);
@@ -58,50 +101,60 @@
}
}
-void SurfaceTracing::enable(size_t bufferSizeInByte) {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+void SurfaceTracing::enable() {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
if (mEnabled) {
return;
}
+
+ mBuffer.reset(mBufferSize);
mEnabled = true;
- mBuffer.reset(bufferSizeInByte);
+ mThread = std::thread(&SurfaceTracing::mainLoop, this);
}
-status_t SurfaceTracing::disable() {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+status_t SurfaceTracing::writeToFile() {
+ mThread.join();
+ return mLastErr;
+}
+
+bool SurfaceTracing::disable() {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
if (!mEnabled) {
- return NO_ERROR;
+ return false;
}
+
mEnabled = false;
- status_t err(writeProtoFileLocked());
- ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
- ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
- mBuffer.reset(0);
- return err;
+ mWriteToFile = true;
+ mConditionalVariable.notify_all();
+ return true;
}
bool SurfaceTracing::isEnabled() const {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
return mEnabled;
}
-void SurfaceTracing::traceLayers(const char* where, LayersProto layers) {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- if (!mEnabled) {
- return;
- }
+void SurfaceTracing::setBufferSize(size_t bufferSizeInByte) {
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
+ mBufferSize = bufferSizeInByte;
+ mBuffer.setSize(bufferSizeInByte);
+}
+
+LayersTraceProto SurfaceTracing::traceLayersLocked(const char* where) {
+ ATRACE_CALL();
LayersTraceProto entry;
entry.set_elapsed_realtime_nanos(elapsedRealtimeNano());
entry.set_where(where);
+ LayersProto layers(mFlinger.dumpProtoInfo(LayerVector::StateSet::Drawing));
entry.mutable_layers()->Swap(&layers);
- mBuffer.emplace(std::move(entry));
+ return entry;
}
-status_t SurfaceTracing::writeProtoFileLocked() {
+void SurfaceTracing::writeProtoFileLocked() {
ATRACE_CALL();
LayersTraceFileProto fileProto;
@@ -110,19 +163,23 @@
fileProto.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
mBuffer.flush(&fileProto);
+ mBuffer.reset(mBufferSize);
if (!fileProto.SerializeToString(&output)) {
- return PERMISSION_DENIED;
+ ALOGE("Could not save the proto file! Permission denied");
+ mLastErr = PERMISSION_DENIED;
}
- if (!android::base::WriteStringToFile(output, kDefaultFileName, true)) {
- return PERMISSION_DENIED;
+ if (!android::base::WriteStringToFile(output, kDefaultFileName, S_IRWXU | S_IRGRP, getuid(),
+ getgid(), true)) {
+ ALOGE("Could not save the proto file! There are missing fields");
+ mLastErr = PERMISSION_DENIED;
}
- return NO_ERROR;
+ mLastErr = NO_ERROR;
}
void SurfaceTracing::dump(std::string& result) const {
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+ std::lock_guard<std::mutex> bufferLock(mTraceLock);
base::StringAppendF(&result, "Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
base::StringAppendF(&result, " number of entries: %zu (%.2fMB / %.2fMB)\n",
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index fd919af..a75ac61 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -18,30 +18,38 @@
#include <layerproto/LayerProtoHeader.h>
#include <utils/Errors.h>
+#include <utils/StrongPointer.h>
+#include <android-base/thread_annotations.h>
+#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
+#include <thread>
using namespace android::surfaceflinger;
namespace android {
+class SurfaceFlinger;
+
constexpr auto operator""_MB(unsigned long long const num) {
return num * 1024 * 1024;
}
-
/*
* SurfaceTracing records layer states during surface flinging.
*/
class SurfaceTracing {
public:
- void enable() { enable(kDefaultBufferCapInByte); }
- void enable(size_t bufferSizeInByte);
- status_t disable();
- void traceLayers(const char* where, LayersProto);
-
+ SurfaceTracing(SurfaceFlinger& flinger) : mFlinger(flinger) {}
+ void enable();
+ bool disable();
+ status_t writeToFile();
bool isEnabled() const;
+ void notify(const char* where);
+
+ void setBufferSize(size_t bufferSizeInByte);
+ void writeToFileAsync();
void dump(std::string& result) const;
private:
@@ -54,6 +62,7 @@
size_t used() const { return mUsedInBytes; }
size_t frameCount() const { return mStorage.size(); }
+ void setSize(size_t newSize) { mSizeInBytes = newSize; }
void reset(size_t newSize);
void emplace(LayersTraceProto&& proto);
void flush(LayersTraceFileProto* fileProto);
@@ -64,11 +73,23 @@
std::queue<LayersTraceProto> mStorage;
};
- status_t writeProtoFileLocked();
+ void mainLoop();
+ void traceLayers(const char* where);
+ LayersTraceProto traceLayersLocked(const char* where);
+ void writeProtoFileLocked() REQUIRES(mTraceLock);
- bool mEnabled = false;
- mutable std::mutex mTraceMutex;
- LayersTraceBuffer mBuffer;
+ const SurfaceFlinger& mFlinger;
+
+ char* mWhere;
+ status_t mLastErr = NO_ERROR;
+ std::thread mThread;
+ std::condition_variable mConditionalVariable;
+ mutable std::mutex mTraceLock;
+
+ LayersTraceBuffer mBuffer GUARDED_BY(mTraceLock);
+ size_t mBufferSize GUARDED_BY(mTraceLock) = kDefaultBufferCapInByte;
+ bool mEnabled GUARDED_BY(mTraceLock) = false;
+ bool mWriteToFile GUARDED_BY(mTraceLock) = false;
};
} // namespace android
diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
index 429636b..fe6dc93 100644
--- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
+++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop
@@ -286,3 +286,24 @@
access: Readonly
prop_name: "ro.surface_flinger.display_primary_white"
}
+
+# setIdleTimerMs indicates what is considered a timeout in milliseconds for Scheduler. This value is
+# used by the Scheduler to trigger inactivity callbacks that will switch the display to a lower
+# refresh rate. Setting this property to 0 means there is no timer.
+prop {
+ api_name: "set_idle_timer_ms"
+ type: Integer
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.set_idle_timer_ms"
+}
+
+# useSmart90ForVideo indicates whether Scheduler should detect content FPS, and try to adjust the
+# screen refresh rate based on that.
+prop {
+ api_name: "use_smart_90_for_video"
+ type: Boolean
+ scope: Internal
+ access: Readonly
+ prop_name: "ro.surface_flinger.use_smart_90_for_video"
+}
diff --git a/services/surfaceflinger/tests/Credentials_test.cpp b/services/surfaceflinger/tests/Credentials_test.cpp
index 61d09da..b667a74 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 @@
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/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
index 91999ae..be862c9 100644
--- a/services/surfaceflinger/tests/SurfaceFlinger_test.filter
+++ b/services/surfaceflinger/tests/SurfaceFlinger_test.filter
@@ -1,5 +1,5 @@
{
"presubmit": {
- "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*"
+ "filter": "CredentialsTest.*:SurfaceFlingerStress.*:SurfaceInterceptorTest.*:LayerTransactionTest.*:LayerTypeTransactionTest.*:LayerUpdateTest.*:GeometryLatchingTest.*:CropLatchingTest.*:ChildLayerTest.*:ScreenCaptureTest.*:ScreenCaptureChildOnlyTest.*:DereferenceSurfaceControlTest.*:BoundlessLayerTest.*:MultiDisplayLayerBoundsTest.*"
}
}
diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp
index 34cdff7..bc5642e 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>
@@ -198,12 +199,15 @@
class ScreenCapture : public RefBase {
public:
static void captureScreen(std::unique_ptr<ScreenCapture>* sc) {
+ captureScreen(sc, SurfaceComposerClient::getInternalDisplayToken());
+ }
+
+ static void captureScreen(std::unique_ptr<ScreenCapture>* sc, sp<IBinder> displayToken) {
const auto sf = ComposerService::getComposerService();
- const auto token = sf->getInternalDisplayToken();
SurfaceComposerClient::Transaction().apply(true);
sp<GraphicBuffer> outBuffer;
- ASSERT_EQ(NO_ERROR, sf->captureScreen(token, &outBuffer, Rect(), 0, 0, false));
+ ASSERT_EQ(NO_ERROR, sf->captureScreen(displayToken, &outBuffer, Rect(), 0, 0, false));
*sc = std::make_unique<ScreenCapture>(outBuffer);
}
@@ -482,6 +486,12 @@
return screenshot;
}
+ void asTransaction(const std::function<void(Transaction&)>& exec) {
+ Transaction t;
+ exec(t);
+ t.apply(true);
+ }
+
static status_t getBuffer(sp<GraphicBuffer>* outBuffer, sp<Fence>* outFence) {
static BufferGenerator bufferGenerator;
return bufferGenerator.get(outBuffer, outFence);
@@ -2013,8 +2023,8 @@
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) {
@@ -2044,13 +2054,13 @@
{
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);
}
}
@@ -2067,37 +2077,36 @@
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
@@ -2105,9 +2114,9 @@
{
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);
}
}
@@ -2284,8 +2293,8 @@
// 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) {
@@ -2368,8 +2377,8 @@
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) {
@@ -2382,8 +2391,8 @@
{
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));
@@ -2391,8 +2400,8 @@
{
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));
@@ -2400,8 +2409,8 @@
{
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);
}
}
@@ -2483,8 +2492,8 @@
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++;
}
@@ -2519,8 +2528,8 @@
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++;
}
@@ -2555,8 +2564,8 @@
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();
@@ -2654,8 +2663,8 @@
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) {
@@ -2678,8 +2687,8 @@
.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) {
@@ -2700,8 +2709,8 @@
.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) {
@@ -2724,8 +2733,8 @@
.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) {
@@ -2748,8 +2757,8 @@
.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) {
@@ -2770,8 +2779,8 @@
.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) {
@@ -4086,11 +4095,6 @@
fillSurfaceRGBA8(mSyncSurfaceControl, 31, 31, 31);
}
- void asTransaction(const std::function<void(Transaction&)>& exec) {
- Transaction t;
- exec(t);
- t.apply(true);
- }
sp<SurfaceControl> mBGSurfaceControl;
sp<SurfaceControl> mFGSurfaceControl;
@@ -5421,4 +5425,162 @@
}
}
+class MultiDisplayLayerBoundsTest : public LayerTransactionTest {
+protected:
+ virtual void SetUp() {
+ LayerTransactionTest::SetUp();
+ ASSERT_EQ(NO_ERROR, mClient->initCheck());
+
+ mMainDisplay = SurfaceComposerClient::getInternalDisplayToken();
+ SurfaceComposerClient::getDisplayInfo(mMainDisplay, &mMainDisplayInfo);
+
+ sp<IGraphicBufferConsumer> consumer;
+ BufferQueue::createBufferQueue(&mProducer, &consumer);
+ consumer->setConsumerName(String8("Virtual disp consumer"));
+ consumer->setDefaultBufferSize(mMainDisplayInfo.w, mMainDisplayInfo.h);
+ }
+
+ virtual void TearDown() {
+ SurfaceComposerClient::destroyDisplay(mVirtualDisplay);
+ LayerTransactionTest::TearDown();
+ mColorLayer = 0;
+ }
+
+ void createDisplay(const Rect& layerStackRect, uint32_t layerStack) {
+ mVirtualDisplay =
+ SurfaceComposerClient::createDisplay(String8("VirtualDisplay"), false /*secure*/);
+ asTransaction([&](Transaction& t) {
+ t.setDisplaySurface(mVirtualDisplay, mProducer);
+ t.setDisplayLayerStack(mVirtualDisplay, layerStack);
+ t.setDisplayProjection(mVirtualDisplay, mMainDisplayInfo.orientation, layerStackRect,
+ Rect(mMainDisplayInfo.w, mMainDisplayInfo.h));
+ });
+ }
+
+ void createColorLayer(uint32_t layerStack) {
+ mColorLayer =
+ createSurface(mClient, "ColorLayer", 0 /* buffer width */, 0 /* buffer height */,
+ PIXEL_FORMAT_RGBA_8888, ISurfaceComposerClient::eFXSurfaceColor);
+ ASSERT_TRUE(mColorLayer != nullptr);
+ ASSERT_TRUE(mColorLayer->isValid());
+ asTransaction([&](Transaction& t) {
+ t.setLayerStack(mColorLayer, layerStack);
+ t.setCrop_legacy(mColorLayer, Rect(0, 0, 30, 40));
+ t.setLayer(mColorLayer, INT32_MAX - 2);
+ t.setColor(mColorLayer,
+ half3{mExpectedColor.r / 255.0f, mExpectedColor.g / 255.0f,
+ mExpectedColor.b / 255.0f});
+ t.show(mColorLayer);
+ });
+ }
+
+ DisplayInfo mMainDisplayInfo;
+ sp<IBinder> mMainDisplay;
+ sp<IBinder> mVirtualDisplay;
+ sp<IGraphicBufferProducer> mProducer;
+ sp<SurfaceControl> mColorLayer;
+ Color mExpectedColor = {63, 63, 195, 255};
+};
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInVirtualDisplay) {
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 1 /* layerStack */);
+ createColorLayer(1 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer does not render on main display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), {0, 0, 0, 255});
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ // Verify color layer renders correctly on virtual display.
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(1, 1, 9, 9), {0, 0, 0, 0});
+}
+
+TEST_F(MultiDisplayLayerBoundsTest, RenderLayerInMirroredVirtualDisplay) {
+ // Create a display and set its layer stack to the main display's layer stack so
+ // the contents of the main display are mirrored on to the virtual display.
+
+ // Assumption here is that the new mirrored display has the same viewport as the
+ // primary display that it is mirroring.
+ createDisplay({mMainDisplayInfo.viewportW, mMainDisplayInfo.viewportH}, 0 /* layerStack */);
+ createColorLayer(0 /* layerStack */);
+
+ asTransaction([&](Transaction& t) { t.setPosition(mColorLayer, 10, 10); });
+
+ // Verify color layer renders correctly on main display and it is mirrored on the
+ // virtual display.
+ std::unique_ptr<ScreenCapture> sc;
+ ScreenCapture::captureScreen(&sc, mMainDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ sc->expectColor(Rect(0, 0, 9, 9), {0, 0, 0, 255});
+
+ ScreenCapture::captureScreen(&sc, mVirtualDisplay);
+ sc->expectColor(Rect(10, 10, 40, 50), mExpectedColor);
+ 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 0000000..4274254
--- /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 b1d45f39..25ce4ac 100644
--- a/services/surfaceflinger/tests/unittests/Android.bp
+++ b/services/surfaceflinger/tests/unittests/Android.bp
@@ -35,7 +35,9 @@
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 30ae764..ea2818d 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 @@
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 @@
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 0000000..92bdebd
--- /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/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 79b5ca0..46fd964 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -257,7 +257,10 @@
return mFlinger->onHotplugReceived(sequenceId, display, connection);
}
- auto setDisplayStateLocked(const DisplayState& s) { return mFlinger->setDisplayStateLocked(s); }
+ auto setDisplayStateLocked(const DisplayState& s) {
+ Mutex::Autolock _l(mFlinger->mStateLock);
+ return mFlinger->setDisplayStateLocked(s);
+ }
// Allow reading display state without locking, as if called on the SF main thread.
auto onInitializeDisplays() NO_THREAD_SAFETY_ANALYSIS {
@@ -341,6 +344,8 @@
// still be referenced by something despite our best efforts to destroy
// it after each test is done.
mutableDisplays().clear();
+ mutableCurrentState().displays.clear();
+ mutableDrawingState().displays.clear();
mutableEventQueue().reset();
mutableInterceptor().reset();
mutableScheduler().reset();
diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
index e6f1a06..bb92020 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h
@@ -121,6 +121,8 @@
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 d7e20c4..6dc28bc 100644
--- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
+++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h
@@ -79,8 +79,9 @@
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 2f7e5ea..f6c4f62 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 @@
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 9213ae5..afcda5b 100644
--- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
+++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h
@@ -36,14 +36,27 @@
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