diff options
289 files changed, 12215 insertions, 4198 deletions
diff --git a/cmds/atrace/TEST_MAPPING b/cmds/atrace/TEST_MAPPING new file mode 100644 index 0000000000..f43db2291e --- /dev/null +++ b/cmds/atrace/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "CtsAtraceHostTestCases" + } + ] +} diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index bd3d2d89d9..5836f11745 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -193,10 +193,12 @@ static const TracingCategory k_categories[] = { { REQ, "events/cpufreq_interactive/enable" }, } }, { "sync", "Synchronization", 0, { - // before linux kernel 4.9 + // linux kernel < 4.9 { OPT, "events/sync/enable" }, - // starting in linux kernel 4.9 + // linux kernel == 4.9.x { OPT, "events/fence/enable" }, + // linux kernel > 4.9 + { OPT, "events/dma_fence/enable" }, } }, { "workq", "Kernel Workqueues", 0, { { REQ, "events/workqueue/enable" }, diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index a2824244c6..66c9cc6148 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -91,6 +91,8 @@ on late-init chmod 0666 /sys/kernel/tracing/events/sync/enable chmod 0666 /sys/kernel/debug/tracing/events/fence/enable chmod 0666 /sys/kernel/tracing/events/fence/enable + chmod 0666 /sys/kernel/debug/tracing/events/dma_fence/enable + chmod 0666 /sys/kernel/tracing/events/dma_fence/enable chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/enable chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/enable chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_heap_grow/enable diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp index bb089e69b1..ddae9ea8f6 100644 --- a/cmds/dumpstate/DumpstateService.cpp +++ b/cmds/dumpstate/DumpstateService.cpp @@ -44,17 +44,18 @@ static binder::Status exception(uint32_t code, const std::string& msg) { return binder::Status::fromExceptionCode(code, String8(msg.c_str())); } -static binder::Status error(uint32_t code, const std::string& msg) { - MYLOGE("%s (%d) ", msg.c_str(), code); - return binder::Status::fromServiceSpecificError(code, String8(msg.c_str())); -} - -// Takes ownership of data. -static void* callAndNotify(void* data) { +// Creates a bugreport and exits, thus preserving the oneshot nature of the service. +// Note: takes ownership of data. +[[noreturn]] static void* dumpstate_thread_main(void* data) { std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data)); ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package); - MYLOGD("Finished Run()\n"); - return nullptr; + MYLOGD("Finished taking a bugreport. Exiting.\n"); + exit(0); +} + +[[noreturn]] static void signalErrorAndExit(sp<IDumpstateListener> listener, int error_code) { + listener->onError(error_code); + exit(0); } class DumpstateToken : public BnDumpstateToken {}; @@ -120,6 +121,25 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, const sp<IDumpstateListener>& listener) { MYLOGI("startBugreport() with mode: %d\n", bugreport_mode); + // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a + // time. + std::lock_guard<std::mutex> lock(lock_); + if (ds_ != nullptr) { + MYLOGE("Error! There is already a bugreport in progress. Returning."); + if (listener != nullptr) { + listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS); + } + return exception(binder::Status::EX_SERVICE_SPECIFIC, + "There is already a bugreport in progress"); + } + + // From here on, all conditions that indicate we are done with this incoming request should + // result in exiting the service to free it up for next invocation. + if (listener == nullptr) { + MYLOGE("Invalid input: no listener"); + exit(0); + } + if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_REMOTE && @@ -127,30 +147,23 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI && bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) { - return exception(binder::Status::EX_ILLEGAL_ARGUMENT, - StringPrintf("Invalid bugreport mode: %d", bugreport_mode)); + MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode); + signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); } if (bugreport_fd.get() == -1 || screenshot_fd.get() == -1) { - return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Invalid file descriptor"); + // TODO(b/111441001): screenshot fd should be optional + MYLOGE("Invalid filedescriptor"); + signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); } std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd, screenshot_fd); - // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a - // time. - std::lock_guard<std::mutex> lock(lock_); - if (ds_ != nullptr) { - return exception(binder::Status::EX_SERVICE_SPECIFIC, - "There is already a bugreport in progress"); - } ds_ = &(Dumpstate::GetInstance()); ds_->SetOptions(std::move(options)); - if (listener != nullptr) { - ds_->listener_ = listener; - } + ds_->listener_ = listener; DumpstateInfo* ds_info = new DumpstateInfo(); ds_info->ds = ds_; @@ -158,11 +171,12 @@ binder::Status DumpstateService::startBugreport(int32_t calling_uid, ds_info->calling_package = calling_package; pthread_t thread; - status_t err = pthread_create(&thread, nullptr, callAndNotify, ds_info); + status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info); if (err != 0) { delete ds_info; ds_info = nullptr; - return error(err, "Could not create a background thread."); + MYLOGE("Could not create a thread"); + signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR); } return binder::Status::ok(); } diff --git a/cmds/dumpstate/README.md b/cmds/dumpstate/README.md index 1bf55e4dec..c818c05117 100644 --- a/cmds/dumpstate/README.md +++ b/cmds/dumpstate/README.md @@ -14,7 +14,8 @@ Then incremental ones: mmm -j frameworks/native/cmds/dumpstate ``` -If you're working on device-specific code, you might need to build them as well. Example: +If you're working on device-specific code, you might need to build them as well. +Example: ``` mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ hardware/interfaces/dumpstate @@ -26,17 +27,20 @@ mmm -j frameworks/native/cmds/dumpstate device/acme/secret_device/dumpstate/ har mmm -j frameworks/native/cmds/dumpstate && adb push ${OUT}/system/bin/dumpstate system/bin && adb push ${OUT}/system/lib64/*dumpstate*.so /system/lib64/ && adb shell am bug-report ``` -Make sure that the device is remounted before running the above command. -* If you're working with `userdebug` variant, you may need to run the following to remount your device: +Make sure that the device is remounted before running the above command. * If +you're working with `userdebug` variant, you may need to run the following to +remount your device: - ``` +``` adb root && adb remount -R && adb wait-for-device && adb root && adb remount - ``` -* If you're working with `eng` variant, you may need to run the following to remount your device: +``` - ``` - adb root && adb remount - ``` +* If you're working with `eng` variant, you may need to run the following to + remount your device: + + ``` + adb root && adb remount + ``` ## To build, deploy, and run unit tests @@ -82,7 +86,6 @@ Example: adb shell setprop dumpstate.version split-dumpsys && adb shell dumpstate -v ``` - Then to restore the default version: ``` @@ -91,8 +94,9 @@ adb shell setprop dumpstate.version default ## Code style and formatting -Use the style defined at the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) -and make sure to run the following command prior to `repo upload`: +Use the style defined at the +[Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) and +make sure to run the following command prior to `repo upload`: ``` git clang-format --style=file HEAD~ diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl index b1005d3885..37ff4428a1 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl @@ -36,6 +36,9 @@ interface IDumpstate { IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener, boolean getSectionDetails); + // NOTE: If you add to or change these modes, please also change the corresponding enums + // in system server, in BugreportParams.java. + // These modes encapsulate a set of run time options for generating bugreports. // Takes a bugreport without user interference. const int BUGREPORT_MODE_FULL = 0; diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl index 907a67c907..ea1e467dca 100644 --- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl +++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl @@ -19,6 +19,11 @@ package android.os; /** * Listener for dumpstate events. * + * <p>When bugreport creation is complete one of {@code onError} or {@code onFinished} is called. + * + * <p>These methods are synchronous by design in order to make dumpstate's lifecycle simpler + * to handle. + * * {@hide} */ interface IDumpstateListener { @@ -27,7 +32,10 @@ interface IDumpstateListener { * * @param progress the progress in [0, 100] */ - oneway void onProgress(int progress); + void onProgress(int progress); + + // NOTE: If you add to or change these error codes, please also change the corresponding enums + // in system server, in BugreportManager.java. /* Options specified are invalid or incompatible */ const int BUGREPORT_ERROR_INVALID_INPUT = 1; @@ -41,15 +49,18 @@ interface IDumpstateListener { /* The request to get user consent timed out */ const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4; + /* There is currently a bugreport running. The caller should try again later. */ + const int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5; + /** * Called on an error condition with one of the error codes listed above. */ - oneway void onError(int errorCode); + void onError(int errorCode); /** * Called when taking bugreport finishes successfully. */ - oneway void onFinished(); + void onFinished(); // TODO(b/111441001): Remove old methods when not used anymore. void onProgressUpdated(int progress); diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 61745b5f1b..738c20fefc 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> @@ -113,6 +115,7 @@ void add_mountinfo(); #define LOGPERSIST_DATA_DIR "/data/misc/logd" #define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur" #define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref" +#define XFRM_STAT_PROC_FILE "/proc/net/xfrm_stat" #define WLUTIL "/vendor/xbin/wlutil" #define WMTRACE_DATA_DIR "/data/misc/wmtrace" @@ -128,6 +131,19 @@ static const std::string ANR_FILE_PREFIX = "anr_"; // TODO: temporary variables and functions used during C++ refactoring static Dumpstate& ds = Dumpstate::GetInstance(); +#define RETURN_IF_USER_DENIED_CONSENT() \ + if (ds.IsUserConsentDenied()) { \ + MYLOGE("Returning early as user denied consent to share bugreport with calling app."); \ + return Dumpstate::RunStatus::USER_CONSENT_DENIED; \ + } + +// Runs func_ptr, but checks user consent before and after running it. Returns USER_CONSENT_DENIED +// if consent is found to be denied. +#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(func_ptr, ...) \ + RETURN_IF_USER_DENIED_CONSENT(); \ + func_ptr(__VA_ARGS__); \ + RETURN_IF_USER_DENIED_CONSENT(); + namespace android { namespace os { namespace { @@ -157,7 +173,7 @@ bool CopyFile(int in_fd, int out_fd) { } static bool CopyFileToFd(const std::string& input_file, int out_fd) { - MYLOGD("Going to copy bugreport file (%s) to %d\n", ds.path_.c_str(), out_fd); + MYLOGD("Going to copy file (%s) to %d\n", input_file.c_str(), out_fd); // Obtain a handle to the source file. android::base::unique_fd in_fd(OpenForRead(input_file)); @@ -165,14 +181,14 @@ static bool CopyFileToFd(const std::string& input_file, int out_fd) { if (CopyFile(in_fd.get(), out_fd)) { return true; } - MYLOGE("Failed to copy zip file: %s\n", strerror(errno)); + MYLOGE("Failed to copy file: %s\n", strerror(errno)); } return false; } static bool UnlinkAndLogOnError(const std::string& file) { - if (unlink(file.c_str()) != -1) { - MYLOGE("Failed to remove file (%s): %s\n", file.c_str(), strerror(errno)); + if (unlink(file.c_str())) { + MYLOGE("Failed to unlink file (%s): %s\n", file.c_str(), strerror(errno)); return false; } return true; @@ -460,9 +476,7 @@ static bool dump_anrd_trace() { if (!ds.AddZipEntry("anrd_trace.txt", path)) { MYLOGE("Unable to add anrd_trace file %s to zip file\n", path); } else { - if (remove(path)) { - MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno)); - } + android::os::UnlinkAndLogOnError(path); return true; } } else { @@ -767,6 +781,17 @@ status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, ZipWriter::ErrorCodeString(err)); return UNKNOWN_ERROR; } + bool finished_entry = false; + auto finish_entry = [this, &finished_entry] { + if (!finished_entry) { + // This should only be called when we're going to return an earlier error, + // which would've been logged. This may imply the file is already corrupt + // and any further logging from FinishEntry is more likely to mislead than + // not. + this->zip_writer_->FinishEntry(); + } + }; + auto scope_guard = android::base::make_scope_guard(finish_entry); auto start = std::chrono::steady_clock::now(); auto end = start + timeout; struct pollfd pfd = {fd, POLLIN}; @@ -783,11 +808,11 @@ status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms())); if (rc < 0) { - MYLOGE("Error in poll while adding from fd to zip entry %s:%s", entry_name.c_str(), - strerror(errno)); + MYLOGE("Error in poll while adding from fd to zip entry %s:%s\n", + entry_name.c_str(), strerror(errno)); return -errno; } else if (rc == 0) { - MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms", + MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms\n", entry_name.c_str(), strerror(errno), timeout.count()); return TIMED_OUT; } @@ -808,6 +833,7 @@ status_t Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd, } err = zip_writer_->FinishEntry(); + finished_entry = true; if (err != 0) { MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err)); return UNKNOWN_ERROR; @@ -1041,9 +1067,9 @@ static void DumpIpAddrAndRules() { RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"}); } -static void RunDumpsysTextByPriority(const std::string& title, int priority, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { auto start = std::chrono::steady_clock::now(); sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); @@ -1051,6 +1077,7 @@ static void RunDumpsysTextByPriority(const std::string& title, int priority, Dumpsys::setServiceArgs(args, /* asProto = */ false, priority); Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ false); for (const String16& service : services) { + RETURN_IF_USER_DENIED_CONSENT(); std::string path(title); path.append(" - ").append(String8(service).c_str()); DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_); @@ -1076,6 +1103,7 @@ static void RunDumpsysTextByPriority(const std::string& title, int priority, break; } } + return Dumpstate::RunStatus::OK; } static void RunDumpsysText(const std::string& title, int priority, @@ -1088,24 +1116,27 @@ static void RunDumpsysText(const std::string& title, int priority, } /* Dump all services registered with Normal or Default priority. */ -static void RunDumpsysTextNormalPriority(const std::string& title, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysTextNormalPriority(const std::string& title, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { DurationReporter duration_reporter(title); dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str()); fsync(STDOUT_FILENO); RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, timeout, service_timeout); - RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, - service_timeout); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout, + service_timeout); } -static void RunDumpsysProto(const std::string& title, int priority, - std::chrono::milliseconds timeout, - std::chrono::milliseconds service_timeout) { +static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority, + std::chrono::milliseconds timeout, + std::chrono::milliseconds service_timeout) { if (!ds.IsZipping()) { MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str()); - return; + return Dumpstate::RunStatus::OK; } sp<android::IServiceManager> sm = defaultServiceManager(); Dumpsys dumpsys(sm.get()); @@ -1116,6 +1147,7 @@ static void RunDumpsysProto(const std::string& title, int priority, auto start = std::chrono::steady_clock::now(); Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ true); for (const String16& service : services) { + RETURN_IF_USER_DENIED_CONSENT(); std::string path(kProtoPath); path.append(String8(service).c_str()); if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) { @@ -1144,35 +1176,54 @@ static void RunDumpsysProto(const std::string& title, int priority, break; } } + return Dumpstate::RunStatus::OK; } // Runs dumpsys on services that must dump first and will take less than 100ms to dump. -static void RunDumpsysCritical() { +static Dumpstate::RunStatus RunDumpsysCritical() { RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, /* timeout= */ 5s, /* service_timeout= */ 500ms); - RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, - /* timeout= */ 5s, /* service_timeout= */ 500ms); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL, + /* timeout= */ 5s, /* service_timeout= */ 500ms); } // Runs dumpsys on services that must dump first but can take up to 250ms to dump. -static void RunDumpsysHigh() { +static Dumpstate::RunStatus RunDumpsysHigh() { // TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both // high priority. Reduce timeout once they are able to dump in a shorter time or // moved to a parallel task. RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, /* timeout= */ 90s, /* service_timeout= */ 30s); - RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, - /* timeout= */ 5s, /* service_timeout= */ 1s); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH, + /* timeout= */ 5s, /* service_timeout= */ 1s); } // Runs dumpsys on services that must dump but can take up to 10s to dump. -static void RunDumpsysNormal() { +static Dumpstate::RunStatus RunDumpsysNormal() { RunDumpsysTextNormalPriority("DUMPSYS", /* timeout= */ 90s, /* service_timeout= */ 10s); - RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, - /* timeout= */ 90s, /* service_timeout= */ 10s); + + RETURN_IF_USER_DENIED_CONSENT(); + + return RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, + /* timeout= */ 90s, /* service_timeout= */ 10s); } static void DumpHals() { + if (!ds.IsZipping()) { + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, + CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); + return; + } + DurationReporter duration_reporter("DUMP HALS"); + RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, + CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); + using android::hidl::manager::V1_0::IServiceManager; using android::hardware::defaultServiceManager; @@ -1222,9 +1273,16 @@ static void DumpHals() { } } -static void dumpstate() { +// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent +// via the consent they are shown. Ignores other errors that occur while running various +// commands. The consent checking is currently done around long running tasks, which happen to +// be distributed fairly evenly throughout the function. +static Dumpstate::RunStatus dumpstate() { DurationReporter duration_reporter("DUMPSTATE"); + // Dump various things. Note that anything that takes "long" (i.e. several seconds) should + // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped + // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK). dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version"); RunCommand("UPTIME", {"uptime"}); DumpBlockStatFiles(); @@ -1232,7 +1290,9 @@ static void dumpstate() { DumpFile("MEMORY INFO", "/proc/meminfo"); RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o", "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"}); - RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20); + DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat"); DumpFile("VMALLOC INFO", "/proc/vmallocinfo"); DumpFile("SLAB INFO", "/proc/slabinfo"); @@ -1247,16 +1307,11 @@ static void dumpstate() { RunCommand("PROCESSES AND THREADS", {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"}); - RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT); - if (ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, - CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); - DumpHals(); - } else { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); - } + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"}, + CommandOptions::AS_ROOT); + + DumpHals(); RunCommand("PRINTENV", {"printenv"}); RunCommand("NETSTAT", {"netstat", "-nW"}); @@ -1275,7 +1330,9 @@ static void dumpstate() { } RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT); - for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES"); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES"); + for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS"); for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)"); @@ -1313,7 +1370,7 @@ static void dumpstate() { RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"}); RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"}); - RunDumpsysHigh(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh); RunCommand("SYSTEM PROPERTIES", {"getprop"}); @@ -1330,12 +1387,13 @@ static void dumpstate() { DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats"); DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state"); + RunDumpsys("WINSCOPE TRACE", {"window", "trace"}); /* Add window and surface trace files. */ if (!PropertiesHelper::IsUserBuild()) { 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); @@ -1355,14 +1413,16 @@ static void dumpstate() { printf("== Android Framework Services\n"); printf("========================================================\n"); - RunDumpsysNormal(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal); printf("========================================================\n"); printf("== Checkins\n"); printf("========================================================\n"); RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"}); - RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"}); + + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"}); + RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"}); RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"}); RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"}); @@ -1426,19 +1486,27 @@ static void dumpstate() { printf("========================================================\n"); // This differs from the usual dumpsys stats, which is the stats report data. RunDumpsys("STATSDSTATS", {"stats", "--metadata"}); + return Dumpstate::RunStatus::OK; } -/* Dumps state for the default case. Returns true if everything went fine. */ -static bool DumpstateDefault() { +/* + * Dumps state for the default case; drops root after it's no longer necessary. + * + * Returns RunStatus::OK if everything went fine. + * Returns RunStatus::ERROR if there was an error. + * Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport + * with the caller. + */ +static Dumpstate::RunStatus DumpstateDefault() { // Try to dump anrd trace if the daemon is running. dump_anrd_trace(); - // Invoking the following dumpsys calls before dump_traces() to try and + // Invoking the following dumpsys calls before DumpTraces() to try and // keep the system stats as close to its initial state as possible. - RunDumpsysCritical(); + RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical); /* collect stack traces from Dalvik and native processes (needs root) */ - dump_traces_path = 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()); @@ -1455,9 +1523,12 @@ static bool DumpstateDefault() { add_mountinfo(); DumpIpTablesAsRoot(); - // Capture any IPSec policies in play. No keys are exposed here. + // Capture any IPSec policies in play. No keys are exposed here. RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"}, CommandOptions::WithTimeout(10).Build()); + // Dump IPsec stats. No keys are exposed here. + DumpFile("XFRM STATS", XFRM_STAT_PROC_FILE); + // Run ss as root so we can see socket marks. RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build()); @@ -1471,11 +1542,11 @@ static bool DumpstateDefault() { } if (!DropRootUser()) { - return false; + return Dumpstate::RunStatus::ERROR; } - dumpstate(); - return true; + RETURN_IF_USER_DENIED_CONSENT(); + return dumpstate(); } // This method collects common dumpsys for telephony and wifi @@ -1558,20 +1629,123 @@ static void DumpstateWifiOnly() { RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10)); - if (ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, - CommandOptions::WithTimeout(2).AsRootIfAvailable().Build()); - DumpHals(); - } else { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, - CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); - } + DumpHals(); printf("========================================================\n"); printf("== dumpstate: done (id %d)\n", ds.id_); printf("========================================================\n"); } +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"); @@ -1588,13 +1762,8 @@ void Dumpstate::DumpstateBoard() { for (int i = 0; i < NUM_OF_DUMPS; i++) { paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(), kDumpstateBoardFiles[i].c_str())); - remover.emplace_back(android::base::make_scope_guard(std::bind( - [](std::string path) { - if (remove(path.c_str()) != 0 && errno != ENOENT) { - MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno)); - } - }, - paths[i]))); + remover.emplace_back(android::base::make_scope_guard( + std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i]))); } sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService()); @@ -1615,6 +1784,7 @@ void Dumpstate::DumpstateBoard() { return; } + // TODO(128270426): Check for consent in between? for (size_t i = 0; i < paths.size(); i++) { MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str()); @@ -1742,7 +1912,9 @@ bool Dumpstate::FinishZipFile() { } // TODO: Should truncate the existing file. // ... and re-open it for further logging. - redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str())); + if (!redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()))) { + return false; + } fprintf(stderr, "\n"); int32_t err = zip_writer_->Finish(); @@ -1755,9 +1927,7 @@ bool Dumpstate::FinishZipFile() { ds.zip_file.reset(nullptr); MYLOGD("Removing temporary file %s\n", tmp_path_.c_str()) - if (remove(tmp_path_.c_str()) != 0) { - MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno)); - } + android::os::UnlinkAndLogOnError(tmp_path_); return true; } @@ -2341,6 +2511,7 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, register_sig_handler(); + // TODO(b/111441001): maybe skip if already started? if (options_->do_start_service) { MYLOGI("Starting 'dumpstate' service\n"); android::status_t ret; @@ -2363,12 +2534,17 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // If we are going to use a socket, do it as early as possible // to avoid timeouts from bugreport. if (options_->use_socket) { - redirect_to_socket(stdout, "dumpstate"); + if (!redirect_to_socket(stdout, "dumpstate")) { + return ERROR; + } } if (options_->use_control_socket) { MYLOGD("Opening control socket\n"); control_socket_fd_ = open_socket("dumpstate"); + if (control_socket_fd_ == -1) { + return ERROR; + } options_->do_progress_updates = 1; } @@ -2427,7 +2603,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, if (is_redirecting) { // Redirect stderr to log_path_ for debugging. TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr))); - redirect_to_file(stderr, const_cast<char*>(log_path_.c_str())); + if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) { + return ERROR; + } if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(), strerror(errno)); @@ -2440,7 +2618,9 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, /* TODO: rather than generating a text file now and zipping it later, it would be more efficient to redirect stdout to the zip entry directly, but the libziparchive doesn't support that option yet. */ - redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str())); + if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) { + return ERROR; + } if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) { MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n", tmp_path_.c_str(), strerror(errno)); @@ -2462,9 +2642,12 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, DumpstateWifiOnly(); } else { // Dump state for the default case. This also drops root. - if (!DumpstateDefault()) { - // Something went wrong. - return RunStatus::ERROR; + RunStatus s = DumpstateDefault(); + if (s != RunStatus::OK) { + if (s == RunStatus::USER_CONSENT_TIMED_OUT) { + HandleUserConsentDenied(); + } + return s; } } @@ -2487,15 +2670,22 @@ Dumpstate::RunStatus Dumpstate::RunInternal(int32_t calling_uid, // Do an early return if there were errors. We make an exception for consent // timing out because it's possible the user got distracted. In this case the // bugreport is not shared but made available for manual retrieval. + MYLOGI("User denied consent. Returning\n"); return status; } - if (options_->screenshot_fd.get() != -1) { + if (options_->do_fb && options_->screenshot_fd.get() != -1) { bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_, options_->screenshot_fd.get()); if (copy_succeeded) { android::os::UnlinkAndLogOnError(screenshot_path_); } } + if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) { + MYLOGI( + "Did not receive user consent yet." + " Will not copy the bugreport artifacts to caller.\n"); + // TODO(b/111441001): cancel outstanding requests + } } /* vibrate a few but shortly times to let user know it's finished */ @@ -2549,6 +2739,11 @@ void Dumpstate::CheckUserConsent(int32_t calling_uid, const android::String16& c } } +bool Dumpstate::IsUserConsentDenied() const { + return ds.consent_callback_ != nullptr && + ds.consent_callback_->getResult() == UserConsentResult::DENIED; +} + void Dumpstate::CleanupFiles() { android::os::UnlinkAndLogOnError(tmp_path_); android::os::UnlinkAndLogOnError(screenshot_path_); @@ -2598,21 +2793,26 @@ Dumpstate::RunStatus Dumpstate::CopyBugreportIfUserConsented() { return Dumpstate::RunStatus::ERROR; } -/* Main entry point for dumpstate binary. */ -int run_main(int argc, char* argv[]) { +Dumpstate::RunStatus Dumpstate::ParseCommandlineAndRun(int argc, char* argv[]) { std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>(); Dumpstate::RunStatus status = options->Initialize(argc, argv); if (status == Dumpstate::RunStatus::OK) { - ds.SetOptions(std::move(options)); + SetOptions(std::move(options)); // When directly running dumpstate binary, the output is not expected to be written // to any external file descriptor. - assert(ds.options_->bugreport_fd.get() == -1); + assert(options_->bugreport_fd.get() == -1); // calling_uid and calling_package are for user consent to share the bugreport with // an app; they are irrelvant here because bugreport is only written to a local // directory, and not shared. - status = ds.Run(-1 /* calling_uid */, "" /* calling_package */); + status = Run(-1 /* calling_uid */, "" /* calling_package */); } + return status; +} + +/* Main entry point for dumpstate binary. */ +int run_main(int argc, char* argv[]) { + Dumpstate::RunStatus status = ds.ParseCommandlineAndRun(argc, argv); switch (status) { case Dumpstate::RunStatus::OK: diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 7fb2f3b2e9..d02ec759a7 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -73,13 +73,13 @@ extern "C" { */ 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 @@ class Dumpstate { // 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(); /* @@ -322,10 +328,19 @@ class Dumpstate { /* Main entry point for running a complete bugreport. */ RunStatus Run(int32_t calling_uid, const std::string& calling_package); + RunStatus ParseCommandlineAndRun(int argc, char* argv[]); + /* Sets runtime options. */ 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 { @@ -517,21 +532,30 @@ int dump_files(const std::string& title, const char* dir, bool (*skip)(const cha /** opens a socket and returns its file descriptor */ int open_socket(const char *service); -/* redirect output to a service control socket */ -void redirect_to_socket(FILE *redirect, const char *service); +/* + * Redirects 'redirect' to a service control socket. + * + * Returns true if redirect succeeds. + */ +bool redirect_to_socket(FILE* redirect, const char* service); -/* redirect output to a new file */ -void redirect_to_file(FILE *redirect, char *path); +/* + * Redirects 'redirect' to a file indicated by 'path', truncating it. + * + * Returns true if redirect succeeds. + */ +bool redirect_to_file(FILE* redirect, char* path); -/* redirect output to an existing file */ -void redirect_to_existing_file(FILE *redirect, char *path); +/* + * Redirects 'redirect' to an existing file indicated by 'path', appending it. + * + * Returns true if redirect succeeds. + */ +bool redirect_to_existing_file(FILE* redirect, char* path); /* 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/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp index 570c6c9b61..fc3642c912 100644 --- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp +++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp @@ -21,6 +21,10 @@ #include <libgen.h> #include <android-base/file.h> +#include <android/os/BnDumpstate.h> +#include <android/os/BnDumpstateListener.h> +#include <binder/IServiceManager.h> +#include <binder/ProcessState.h> #include <cutils/properties.h> #include <ziparchive/zip_archive.h> @@ -34,6 +38,24 @@ namespace dumpstate { using ::testing::Test; using ::std::literals::chrono_literals::operator""s; +using android::base::unique_fd; + +class DumpstateListener; + +namespace { + +sp<IDumpstate> GetDumpstateService() { + return android::interface_cast<IDumpstate>( + android::defaultServiceManager()->getService(String16("dumpstate"))); +} + +int OpenForWrite(const std::string& filename) { + return TEMP_FAILURE_RETRY(open(filename.c_str(), + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); +} + +} // namespace struct SectionInfo { std::string name; @@ -46,41 +68,71 @@ struct SectionInfo { * Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the * section details generated by dumpstate are added to a vector to be used by Tests later. */ -class DumpstateListener : public IDumpstateListener { +class DumpstateListener : public BnDumpstateListener { public: - int outFd_, max_progress_; - std::shared_ptr<std::vector<SectionInfo>> sections_; DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections) - : outFd_(fd), max_progress_(5000), sections_(sections) { + : out_fd_(fd), sections_(sections) { + } + + DumpstateListener(int fd) : out_fd_(fd) { } + binder::Status onProgress(int32_t progress) override { - dprintf(outFd_, "\rIn progress %d", progress); + dprintf(out_fd_, "\rIn progress %d", progress); return binder::Status::ok(); } + binder::Status onError(int32_t error_code) override { - dprintf(outFd_, "\rError %d", error_code); + std::lock_guard<std::mutex> lock(lock_); + error_code_ = error_code; + dprintf(out_fd_, "\rError code %d", error_code); return binder::Status::ok(); } + binder::Status onFinished() override { - dprintf(outFd_, "\rFinished"); + std::lock_guard<std::mutex> lock(lock_); + is_finished_ = true; + dprintf(out_fd_, "\rFinished"); return binder::Status::ok(); } + binder::Status onProgressUpdated(int32_t progress) override { - dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_); + dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_); return binder::Status::ok(); } + binder::Status onMaxProgressUpdated(int32_t max_progress) override { + std::lock_guard<std::mutex> lock(lock_); max_progress_ = max_progress; return binder::Status::ok(); } + binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes, int32_t duration_ms) override { - sections_->push_back({name, status, size_bytes, duration_ms}); + std::lock_guard<std::mutex> lock(lock_); + if (sections_.get() != nullptr) { + sections_->push_back({name, status, size_bytes, duration_ms}); + } return binder::Status::ok(); } - IBinder* onAsBinder() override { - return nullptr; + + bool getIsFinished() { + std::lock_guard<std::mutex> lock(lock_); + return is_finished_; } + + int getErrorCode() { + std::lock_guard<std::mutex> lock(lock_); + return error_code_; + } + + private: + int out_fd_; + int max_progress_ = 5000; + int error_code_ = -1; + bool is_finished_ = false; + std::shared_ptr<std::vector<SectionInfo>> sections_; + std::mutex lock_; }; /** @@ -109,7 +161,7 @@ class ZippedBugreportGenerationTest : public Test { ds.listener_name_ = "Smokey"; ds.report_section_ = true; auto start = std::chrono::steady_clock::now(); - run_main(ARRAY_SIZE(argv), argv); + ds.ParseCommandlineAndRun(ARRAY_SIZE(argv), argv); auto end = std::chrono::steady_clock::now(); duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); } @@ -293,6 +345,148 @@ TEST_F(BugreportSectionTest, WifiSectionGenerated) { SectionExists("DUMPSYS - wifi", /* bytes= */ 100000); } +class DumpstateBinderTest : public Test { + protected: + void SetUp() override { + // In case there is a stray service, stop it first. + property_set("ctl.stop", "bugreportd"); + // dry_run results in a faster bugreport. + property_set("dumpstate.dry_run", "true"); + // We need to receive some async calls later. Ensure we have binder threads. + ProcessState::self()->startThreadPool(); + } + + void TearDown() override { + property_set("ctl.stop", "bugreportd"); + property_set("dumpstate.dry_run", ""); + + unlink("/data/local/tmp/tmp.zip"); + unlink("/data/local/tmp/tmp.png"); + } + + // Waits until listener gets the callbacks. + void WaitTillExecutionComplete(DumpstateListener* listener) { + // Wait till one of finished, error or timeout. + static const int kBugreportTimeoutSeconds = 120; + int i = 0; + while (!listener->getIsFinished() && listener->getErrorCode() == -1 && + i < kBugreportTimeoutSeconds) { + sleep(1); + i++; + } + } +}; + +TEST_F(DumpstateBinderTest, Baseline) { + // In the beginning dumpstate binder service is not running. + sp<android::os::IDumpstate> ds_binder(GetDumpstateService()); + EXPECT_EQ(ds_binder, nullptr); + + // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service + // and makes it wait. + property_set("dumpstate.dry_run", "true"); + property_set("ctl.start", "bugreportd"); + + // Now we are able to retrieve dumpstate binder service. + ds_binder = GetDumpstateService(); + EXPECT_NE(ds_binder, nullptr); + + // Prepare arguments + unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip")); + unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png")); + + EXPECT_NE(bugreport_fd.get(), -1); + EXPECT_NE(screenshot_fd.get(), -1); + + sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)))); + android::binder::Status status = + ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener); + // startBugreport is an async call. Verify binder call succeeded first, then wait till listener + // gets expected callbacks. + EXPECT_TRUE(status.isOk()); + WaitTillExecutionComplete(listener.get()); + + // Bugreport generation requires user consent, which we cannot get in a test set up, + // so instead of getting is_finished_, we are more likely to get a consent error. + EXPECT_TRUE( + listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT || + listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); + + // The service should have died on its own, freeing itself up for a new invocation. + sleep(2); + ds_binder = GetDumpstateService(); + EXPECT_EQ(ds_binder, nullptr); +} + +TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) { + // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service + // and makes it wait. + property_set("ctl.start", "bugreportd"); + sp<android::os::IDumpstate> ds_binder(GetDumpstateService()); + EXPECT_NE(ds_binder, nullptr); + + // Prepare arguments + unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); + unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); + + EXPECT_NE(bugreport_fd.get(), -1); + EXPECT_NE(screenshot_fd.get(), -1); + + // Call startBugreport with bad arguments. + sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout)))); + android::binder::Status status = + ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + 2000, // invalid bugreport mode + listener); + EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT); + + // The service should have died, freeing itself up for a new invocation. + sleep(2); + ds_binder = GetDumpstateService(); + EXPECT_EQ(ds_binder, nullptr); +} + +TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) { + // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service + // and makes it wait. + property_set("dumpstate.dry_run", "true"); + property_set("ctl.start", "bugreportd"); + sp<android::os::IDumpstate> ds_binder(GetDumpstateService()); + EXPECT_NE(ds_binder, nullptr); + + // Prepare arguments + unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip")); + unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png")); + + EXPECT_NE(bugreport_fd.get(), -1); + EXPECT_NE(screenshot_fd.get(), -1); + + sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout)))); + android::binder::Status status = + ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1); + EXPECT_TRUE(status.isOk()); + + // try to make another call to startBugreport. This should fail. + sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout)))); + status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd, + Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2); + EXPECT_FALSE(status.isOk()); + WaitTillExecutionComplete(listener2.get()); + EXPECT_EQ(listener2->getErrorCode(), + IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS); + + // Meanwhile the first call works as expected. Service should not die in this case. + WaitTillExecutionComplete(listener1.get()); + + // Bugreport generation requires user consent, which we cannot get in a test set up, + // so instead of getting is_finished_, we are more likely to get a consent error. + EXPECT_TRUE( + listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT || + listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT); +} + } // namespace dumpstate } // namespace os } // namespace android diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp index 528e43d44d..0bb80dcfba 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 @@ Dumpstate& Dumpstate::GetInstance() { 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,14 +102,16 @@ DurationReporter::DurationReporter(const std::string& title, bool log_only) 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 { - // 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()); } } @@ -280,6 +280,12 @@ static void __for_each_pid(void (*helper)(int, const char *, void *), const char if (header) printf("\n------ %s ------\n", header); while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } int pid; int fd; char cmdpath[255]; @@ -352,6 +358,12 @@ static void for_each_tid_helper(int pid, const char *cmdline, void *arg) { func(pid, pid, cmdline); while ((de = readdir(d))) { + if (ds.IsUserConsentDenied()) { + MYLOGE( + "Returning early because user denied consent to share bugreport with calling app."); + closedir(d); + return; + } int tid; int fd; char commpath[255]; @@ -710,12 +722,12 @@ int open_socket(const char *service) { int s = android_get_control_socket(service); if (s < 0) { MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno)); - exit(1); + return -1; } fcntl(s, F_SETFD, FD_CLOEXEC); if (listen(s, 4) < 0) { MYLOGE("listen(control socket): %s\n", strerror(errno)); - exit(1); + return -1; } struct sockaddr addr; @@ -723,18 +735,23 @@ int open_socket(const char *service) { int fd = accept(s, &addr, &alen); if (fd < 0) { MYLOGE("accept(control socket): %s\n", strerror(errno)); - exit(1); + return -1; } return fd; } /* redirect output to a service control socket */ -void redirect_to_socket(FILE *redirect, const char *service) { +bool redirect_to_socket(FILE* redirect, const char* service) { int fd = open_socket(service); + if (fd == -1) { + return false; + } fflush(redirect); - dup2(fd, fileno(redirect)); + // TODO: handle dup2 failure + TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); + return true; } // TODO: should call is_valid_output_file and/or be merged into it. @@ -764,7 +781,7 @@ void create_parent_dirs(const char *path) { } } -void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) { +bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) { create_parent_dirs(path); int fd = TEMP_FAILURE_RETRY(open(path, @@ -772,128 +789,20 @@ void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) { S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); if (fd < 0) { MYLOGE("%s: %s\n", path, strerror(errno)); - exit(1); + return false; } TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect))); close(fd); + return true; } -void redirect_to_file(FILE *redirect, char *path) { - _redirect_to_file(redirect, path, O_TRUNC); +bool redirect_to_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_TRUNC); } -void redirect_to_existing_file(FILE *redirect, char *path) { - _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(); +bool redirect_to_existing_file(FILE* redirect, char* path) { + return _redirect_to_file(redirect, path, O_APPEND); } void dump_route_tables() { diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index e33b2a82ab..b60bbc0536 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -35,6 +35,7 @@ cc_defaults { "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], product_variables: { @@ -227,6 +228,7 @@ cc_binary { "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], } diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index 49383e5112..2efcf11ef9 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -39,6 +39,7 @@ #include <sys/xattr.h> #include <unistd.h> +#include <android-base/file.h> #include <android-base/logging.h> #include <android-base/properties.h> #include <android-base/scopeguard.h> @@ -80,6 +81,8 @@ namespace installd { // An uuid used in unit tests. static constexpr const char* kTestUuid = "TEST"; +static constexpr const mode_t kRollbackFolderMode = 0700; + static constexpr const char* kCpPath = "/system/bin/cp"; static constexpr const char* kXattrDefault = "user.default"; @@ -822,8 +825,8 @@ static int32_t copy_directory_recursive(const char* from, const char* to) { binder::Status InstalldNativeService::snapshotAppData( const std::unique_ptr<std::string>& volumeUuid, - const std::string& packageName, int32_t user, int32_t storageFlags, - int64_t* _aidl_return) { + const std::string& packageName, int32_t user, int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -840,16 +843,19 @@ binder::Status InstalldNativeService::snapshotAppData( bool clear_ce_on_exit = false; bool clear_de_on_exit = false; - auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name] { + auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name, + &snapshotId] { if (clear_de_on_exit) { - auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name); + auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } } if (clear_ce_on_exit) { - auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); + auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId, + package_name); if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) { LOG(WARNING) << "Failed to delete app data snapshot: " << to; } @@ -885,15 +891,21 @@ binder::Status InstalldNativeService::snapshotAppData( if (storageFlags & FLAG_STORAGE_DE) { auto from = create_data_user_de_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_de_rollback_path(volume_uuid, user); + auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId); + auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user, + snapshotId, package_name); - int rd = delete_dir_contents(to, true /* ignore_if_missing */); - if (rd != 0) { - res = error(rd, "Failed clearing existing snapshot " + to); - return res; + int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); + if (rc != 0) { + return error(rc, "Failed to create folder " + to); + } + + rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */); + if (rc != 0) { + return error(rc, "Failed clearing existing snapshot " + rollback_package_path); } - int rc = copy_directory_recursive(from.c_str(), to.c_str()); + rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_de_on_exit = true; @@ -903,15 +915,21 @@ binder::Status InstalldNativeService::snapshotAppData( if (storageFlags & FLAG_STORAGE_CE) { auto from = create_data_user_ce_package_path(volume_uuid, user, package_name); - auto to = create_data_misc_ce_rollback_path(volume_uuid, user); + auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId); + auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, + snapshotId, package_name); - int rd = delete_dir_contents(to, true /* ignore_if_missing */); - if (rd != 0) { - res = error(rd, "Failed clearing existing snapshot " + to); - return res; + int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode); + if (rc != 0) { + return error(rc, "Failed to create folder " + to); } - int rc = copy_directory_recursive(from.c_str(), to.c_str()); + rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */); + if (rc != 0) { + return error(rc, "Failed clearing existing snapshot " + rollback_package_path); + } + + rc = copy_directory_recursive(from.c_str(), to.c_str()); if (rc != 0) { res = error(rc, "Failed copying " + from + " to " + to); clear_ce_on_exit = true; @@ -919,7 +937,7 @@ binder::Status InstalldNativeService::snapshotAppData( } if (_aidl_return != nullptr) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user, - package_name); + snapshotId, package_name); rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return)); if (rc != 0) { res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path); @@ -934,8 +952,8 @@ binder::Status InstalldNativeService::snapshotAppData( binder::Status InstalldNativeService::restoreAppDataSnapshot( const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, - const int32_t appId, const int64_t ceDataInode, const std::string& seInfo, - const int32_t user, int32_t storageFlags) { + const int32_t appId, const std::string& seInfo, const int32_t user, + const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -945,9 +963,9 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( const char* package_name = packageName.c_str(); auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid, - user, package_name); + user, snapshotId, package_name); auto from_de = create_data_misc_de_rollback_package_path(volume_uuid, - user, package_name); + user, snapshotId, package_name); const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) && (access(from_ce.c_str(), F_OK) == 0); @@ -964,7 +982,11 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( // app with no data in those cases is arguably better than leaving the app // with mismatched / stale data. LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot."; - binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, ceDataInode); + // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot + // can only be called when user unlocks the phone, meaning that CE user data + // is decrypted. + binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, + 0 /* ceDataInode */); if (!res.isOk()) { return res; } @@ -1000,7 +1022,8 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( binder::Status InstalldNativeService::destroyAppDataSnapshot( const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, - const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags) { + const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, + int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); CHECK_ARGUMENT_PACKAGE_NAME(packageName); @@ -1011,7 +1034,7 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( if (storageFlags & FLAG_STORAGE_DE) { auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid, - user, package_name); + user, snapshotId, package_name); int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */); if (res != 0) { @@ -1021,7 +1044,7 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( if (storageFlags & FLAG_STORAGE_CE) { auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, - user, package_name, ceSnapshotInode); + user, snapshotId, package_name, ceSnapshotInode); int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + ce_snapshot_path); diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 578132da5b..0e91cb27ba 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -61,14 +61,14 @@ public: binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags); binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid, - const std::string& packageName, const int32_t user, int32_t storageFlags, - int64_t* _aidl_return); + const std::string& packageName, const int32_t user, const int32_t snapshotId, + int32_t storageFlags, int64_t* _aidl_return); binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid, - const std::string& packageName, const int32_t appId, const int64_t ceDataInode, - const std::string& seInfo, const int32_t user, int32_t storageFlags); + const std::string& packageName, const int32_t appId, const std::string& seInfo, + const int32_t user, const int32_t snapshotId, int32_t storageFlags); binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, - int32_t storageFlags); + const int32_t snapshotId, int32_t storageFlags); binder::Status getAppSize(const std::unique_ptr<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index 5d4f176a24..56739181bb 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -3,6 +3,8 @@ set noparent agampe@google.com calin@google.com jsharkey@android.com +maco@google.com mathieuc@google.com +narayan@google.com ngeoffray@google.com toddke@google.com diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index 3de5c79ac1..287f2d9f41 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -14,6 +14,15 @@ }, { "name": "installd_utils_test" + }, + { + "name": "CtsUsesLibraryHostTestCases" + }, + { + "name": "CtsClassloaderSplitsHostTestCases" + }, + { + "name": "CtsCompilationTestCases" } ] } diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index b3452100c8..63c9765a39 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -106,11 +106,11 @@ interface IInstalld { @nullable @utf8InCpp String dexMetadata); long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, - int userId, int storageFlags); + int userId, int snapshotId, int storageFlags); void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, - int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags); + int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags); void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, - int userId, long ceSnapshotInode, int storageFlags); + int userId, long ceSnapshotInode, int snapshotId, int storageFlags); const int FLAG_STORAGE_DE = 0x1; const int FLAG_STORAGE_CE = 0x2; diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index 852aa799fb..72571cf622 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -45,6 +45,7 @@ #include <private/android_filesystem_config.h> #include <processgroup/sched_policy.h> #include <selinux/android.h> +#include <server_configurable_flags/get_flags.h> #include <system/thread_defs.h> #include "dexopt.h" @@ -260,6 +261,47 @@ static std::string MapPropertyToArg(const std::string& property, return ""; } +// Determines which binary we should use for execution (the debug or non-debug version). +// e.g. dex2oatd vs dex2oat +static const char* select_execution_binary(const char* binary, const char* debug_binary, + bool background_job_compile) { + return select_execution_binary( + binary, + debug_binary, + background_job_compile, + is_debug_runtime(), + (android::base::GetProperty("ro.build.version.codename", "") == "REL"), + is_debuggable_build()); +} + +// Determines which binary we should use for execution (the debug or non-debug version). +// e.g. dex2oatd vs dex2oat +// This is convenient method which is much easier to test because it doesn't read +// system properties. +const char* select_execution_binary( + const char* binary, + const char* debug_binary, + bool background_job_compile, + bool is_debug_runtime, + bool is_release, + bool is_debuggable_build) { + // Do not use debug binaries for release candidates (to give more soak time). + bool is_debug_bg_job = background_job_compile && is_debuggable_build && !is_release; + + // If the runtime was requested to use libartd.so, we'll run the debug version - assuming + // the file is present (it may not be on images with very little space available). + bool useDebug = (is_debug_runtime || is_debug_bg_job) && (access(debug_binary, X_OK) == 0); + + return useDebug ? debug_binary : binary; +} + +// Namespace for Android Runtime flags applied during boot time. +static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot"; +// Feature flag name for running the JIT in Zygote experiment, b/119800099. +static const char* ENABLE_APEX_IMAGE = "enable_apex_image"; +// Location of the apex image. +static const char* kApexImage = "/system/framework/apex.art"; + class RunDex2Oat : public ExecVHelper { public: RunDex2Oat(int zip_fd, @@ -293,6 +335,14 @@ class RunDex2Oat : public ExecVHelper { : "dalvik.vm.boot-dex2oat-threads"; std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s"); + std::string bootclasspath; + char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); + if (dex2oat_bootclasspath != nullptr) { + bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath); + } + // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query + // BOOTCLASSPATH. + const std::string dex2oat_isa_features_key = StringPrintf("dalvik.vm.isa.%s.features", instruction_set); std::string instruction_set_features_arg = @@ -338,20 +388,24 @@ class RunDex2Oat : public ExecVHelper { std::string dex2oat_large_app_threshold_arg = MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s"); - // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat. - const char* dex2oat_bin = kDex2oatPath; - // Do not use dex2oatd for release candidates (give dex2oat more soak time). - bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL"; - if (is_debug_runtime() || - (background_job_compile && is_debuggable_build() && !is_release)) { - if (access(kDex2oatDebugPath, X_OK) == 0) { - dex2oat_bin = kDex2oatDebugPath; - } - } + + const char* dex2oat_bin = select_execution_binary( + kDex2oatPath, kDex2oatDebugPath, background_job_compile); bool generate_minidebug_info = kEnableMinidebugInfo && GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); + std::string boot_image; + std::string use_apex_image = + server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, + ENABLE_APEX_IMAGE, + /*default_value=*/ ""); + if (use_apex_image == "true") { + boot_image = StringPrintf("-Ximage:%s", kApexImage); + } else { + boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s"); + } + // clang FORTIFY doesn't let us use strlen in constant array bounds, so we // use arraysize instead. std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd); @@ -366,7 +420,7 @@ class RunDex2Oat : public ExecVHelper { std::string dex2oat_image_fd; std::string target_sdk_version_arg; if (target_sdk_version != 0) { - StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); + target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); } std::string class_loader_context_arg; if (class_loader_context != nullptr) { @@ -437,6 +491,8 @@ class RunDex2Oat : public ExecVHelper { AddArg(instruction_set_variant_arg); AddArg(instruction_set_features_arg); + AddRuntimeArg(boot_image); + AddRuntimeArg(bootclasspath); AddRuntimeArg(dex2oat_Xms_arg); AddRuntimeArg(dex2oat_Xmx_arg); @@ -468,7 +524,7 @@ class RunDex2Oat : public ExecVHelper { if (disable_cdex) { AddArg(kDisableCompactDexFlag); } - AddArg(target_sdk_version_arg); + AddRuntimeArg(target_sdk_version_arg); if (enable_hidden_api_checks) { AddRuntimeArg("-Xhidden-api-checks"); } @@ -552,7 +608,7 @@ static unique_fd create_profile(uid_t uid, const std::string& profile, int32_t f // the app uid. If we cannot do that, there's no point in returning the fd // since dex2oat/profman will fail with SElinux denials. if (fchown(fd.get(), uid, uid) < 0) { - PLOG(ERROR) << "Could not chwon profile " << profile; + PLOG(ERROR) << "Could not chown profile " << profile; return invalid_unique_fd(); } return fd; @@ -647,7 +703,12 @@ class RunProfman : public ExecVHelper { const std::vector<std::string>& dex_locations, bool copy_and_update, bool store_aggregation_counters) { - const char* profman_bin = is_debug_runtime() ? kProfmanDebugPath: kProfmanPath; + + // TODO(calin): Assume for now we run in the bg compile job (which is in + // most of the invocation). With the current data flow, is not very easy or + // clean to discover this in RunProfman (it will require quite a messy refactoring). + const char* profman_bin = select_execution_binary( + kProfmanPath, kProfmanDebugPath, /*background_job_compile=*/ true); if (copy_and_update) { CHECK_EQ(1u, profile_fds.size()); @@ -1462,8 +1523,10 @@ class RunDexoptAnalyzer : public ExecVHelper { bool downgrade, const char* class_loader_context) { CHECK_GE(zip_fd, 0); - const char* dexoptanalyzer_bin = - is_debug_runtime() ? kDexoptanalyzerDebugPath : kDexoptanalyzerPath; + + // We always run the analyzer in the background job. + const char* dexoptanalyzer_bin = select_execution_binary( + kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true); std::string dex_file_arg = "--dex-file=" + dex_file; std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd); @@ -1958,11 +2021,6 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins /* child -- drop privileges before continuing */ drop_capabilities(uid); - // Clear BOOTCLASSPATH. - // Let dex2oat use the BCP from boot image, excluding updatable BCP - // modules for AOT to avoid app recompilation after their upgrades. - unsetenv("BOOTCLASSPATH"); - SetDex2OatScheduling(boot_complete); if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) { PLOG(ERROR) << "flock(" << out_oat_path << ") failed"; diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index 5902659e8b..a8c48c564e 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -36,7 +36,7 @@ static constexpr int DEX2OAT_FOR_FILTER = 3; // Location of binaries in the Android Runtime APEX. static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat"; static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd"; -static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profmand"; +static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profman"; static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand"; static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer"; static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd"; @@ -128,6 +128,14 @@ bool create_cache_path_default(char path[PKG_PATH_MAX], const char *src, bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path); +const char* select_execution_binary( + const char* binary, + const char* debug_binary, + bool background_job_compile, + bool is_debug_runtime, + bool is_release, + bool is_debuggable_build); + } // namespace installd } // namespace android diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp index b3a6dafa9a..1394701f55 100644 --- a/cmds/installd/globals.cpp +++ b/cmds/installd/globals.cpp @@ -44,6 +44,8 @@ static constexpr const char* PROFILES_SUBDIR = "misc/profiles"; // sub-directory static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // 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; std::string android_app_lib_dir; @@ -54,6 +56,7 @@ std::string android_media_dir; std::string android_mnt_expand_dir; std::string android_profiles_dir; std::string android_root_dir; +std::string android_staging_dir; std::vector<std::string> android_system_dirs; @@ -110,6 +113,9 @@ bool init_globals_from_data_and_root(const char* data, const char* root) { // Get the android profiles directory. android_profiles_dir = android_data_dir + PROFILES_SUBDIR; + // Get the android session staging directory. + android_staging_dir = android_data_dir + STAGING_SUBDIR; + // Take note of the system and vendor directories. android_system_dirs.clear(); android_system_dirs.push_back(android_root_dir + APP_SUBDIR); diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h index 633e33bb7e..a88a86eab9 100644 --- a/cmds/installd/globals.h +++ b/cmds/installd/globals.h @@ -38,6 +38,7 @@ extern std::string android_media_dir; extern std::string android_mnt_expand_dir; extern std::string android_profiles_dir; extern std::string android_root_dir; +extern std::string android_staging_dir; extern std::vector<std::string> android_system_dirs; diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp index 670abea616..0fdc9d6cbb 100644 --- a/cmds/installd/otapreopt_chroot.cpp +++ b/cmds/installd/otapreopt_chroot.cpp @@ -41,23 +41,6 @@ using android::base::StringPrintf; namespace android { namespace installd { -// Configuration for bind-mounted Bionic artifacts. - -static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker"; -static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker"; - -static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/"; -static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/"; - -static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64"; -static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64"; - -static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/"; -static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/"; - -static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"}; - - static void CloseDescriptor(int fd) { if (fd >= 0) { int result = close(fd); @@ -94,43 +77,6 @@ static void DeactivateApexPackages(const std::vector<apex::ApexFile>& active_pac } } -// Copied from system/core/init/mount_namespace.cpp. -static bool BindMount(const std::string& source, const std::string& mount_point, - bool recursive = false) { - unsigned long mountflags = MS_BIND; - if (recursive) { - mountflags |= MS_REC; - } - if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) { - PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point; - return false; - } - return true; -} - -// Copied from system/core/init/mount_namespace.cpp and and adjusted (bind -// mounts are not made private, as the /postinstall is already private (see -// `android::installd::otapreopt_chroot`). -static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source, - const std::string& linker_mount_point, - const std::string& lib_mount_dir) { - if (access(linker_source.c_str(), F_OK) != 0) { - PLOG(INFO) << linker_source << " does not exist. Skipping mounting Bionic there."; - return true; - } - if (!BindMount(linker_source, linker_mount_point)) { - return false; - } - for (const auto& libname : kBionicLibFileNames) { - std::string mount_point = lib_mount_dir + libname; - std::string source = lib_dir_source + libname; - if (!BindMount(source, mount_point)) { - return false; - } - } - return true; -} - // Entry for otapreopt_chroot. Expected parameters are: // [cmd] [status-fd] [target-slot] "dexopt" [dexopt-params] // The file descriptor denoted by status-fd will be closed. The rest of the parameters will @@ -274,23 +220,6 @@ static int otapreopt_chroot(const int argc, char **arg) { // the Android Runtime APEX, as it is required by otapreopt to run dex2oat. std::vector<apex::ApexFile> active_packages = ActivateApexPackages(); - // Bind-mount Bionic artifacts from the Runtime APEX. - // This logic is copied and adapted from system/core/init/mount_namespace.cpp. - if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint, - kBionicLibsMountPointDir)) { - LOG(ERROR) << "Failed to mount 32-bit Bionic artifacts from the Runtime APEX."; - // Clean up and exit. - DeactivateApexPackages(active_packages); - exit(215); - } - if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64, - kBionicLibsMountPointDir64)) { - LOG(ERROR) << "Failed to mount 64-bit Bionic artifacts from the Runtime APEX."; - // Clean up and exit. - DeactivateApexPackages(active_packages); - exit(216); - } - // Now go on and run otapreopt. // Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 9c9db0f21d..1ed49a0cfd 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -31,6 +31,7 @@ cc_test { "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], static_libs: [ "libdiskusage", @@ -54,6 +55,7 @@ cc_test { "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], static_libs: [ "libdiskusage", @@ -77,6 +79,7 @@ cc_test { "libprocessgroup", "libselinux", "libutils", + "server_configurable_flags", ], static_libs: [ "libdiskusage", @@ -96,6 +99,7 @@ cc_test { "libbase", "libcutils", "libutils", + "server_configurable_flags", ], static_libs: [ "liblog", diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 78edce08fa..71b710b0d3 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -1175,5 +1175,64 @@ TEST_F(BootProfileTest, CollectProfiles) { ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end()); } +TEST_F(DexoptTest, select_execution_binary) { + LOG(INFO) << "DexoptTestselect_execution_binary"; + + std::string release_str = app_private_dir_ce_ + "/release"; + std::string debug_str = app_private_dir_ce_ + "/debug"; + + // Setup the binaries. Note that we only need executable files to actually + // test the execution binary selection + run_cmd("touch " + release_str); + run_cmd("touch " + debug_str); + run_cmd("chmod 777 " + release_str); + run_cmd("chmod 777 " + debug_str); + + const char* release = release_str.c_str(); + const char* debug = debug_str.c_str(); + + ASSERT_STREQ(release, select_execution_binary( + release, + debug, + /*background_job_compile=*/ false, + /*is_debug_runtime=*/ false, + /*is_release=*/ false, + /*is_debuggable_build=*/ false)); + + ASSERT_STREQ(release, select_execution_binary( + release, + debug, + /*background_job_compile=*/ true, + /*is_debug_runtime=*/ false, + /*is_release=*/ true, + /*is_debuggable_build=*/ true)); + + ASSERT_STREQ(debug, select_execution_binary( + release, + debug, + /*background_job_compile=*/ false, + /*is_debug_runtime=*/ true, + /*is_release=*/ false, + /*is_debuggable_build=*/ false)); + + ASSERT_STREQ(debug, select_execution_binary( + release, + debug, + /*background_job_compile=*/ true, + /*is_debug_runtime=*/ false, + /*is_release=*/ false, + /*is_debuggable_build=*/ true)); + + + // Select the release when the debug file is not there. + ASSERT_STREQ(release, select_execution_binary( + release, + "does_not_exist", + /*background_job_compile=*/ false, + /*is_debug_runtime=*/ true, + /*is_release=*/ false, + /*is_debuggable_build=*/ false)); +} + } // namespace installd } // namespace android diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 48b07c417c..a31d510565 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -259,32 +259,59 @@ static bool mkdirs(const std::string& path, mode_t mode) { return false; } - return (::mkdir(path.c_str(), mode) != -1); + if (::mkdir(path.c_str(), mode) != 0) { + PLOG(DEBUG) << "Failed to create folder " << path; + return false; + } + return true; } -TEST_F(ServiceTest, CreateAppDataSnapshot) { - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); +class AppDataSnapshotTest : public testing::Test { +private: + std::string rollback_ce_base_dir; + std::string rollback_de_base_dir; - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); +protected: + InstalldNativeService* service; - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); + std::string fake_package_ce_path; + std::string fake_package_de_path; - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); + virtual void SetUp() { + setenv("ANDROID_LOG_TAGS", "*:v", 1); + android::base::InitLogging(nullptr); - auto deleter = [&rollback_ce_dir, &rollback_de_dir, - &fake_package_ce_path, &fake_package_de_path]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); + service = new InstalldNativeService(); + ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700)); + + init_globals_from_data_and_root(); + + rollback_ce_base_dir = create_data_misc_ce_rollback_base_path("TEST", 0); + rollback_de_base_dir = create_data_misc_de_rollback_base_path("TEST", 0); + + fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); + fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); + + ASSERT_TRUE(mkdirs(rollback_ce_base_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_base_dir, 0700)); + ASSERT_TRUE(mkdirs(fake_package_ce_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_de_path, 0700)); + } + + virtual void TearDown() { + ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_ce_base_dir, true)); + ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_de_base_dir, true)); + ASSERT_EQ(0, delete_dir_contents(fake_package_ce_path, true)); + ASSERT_EQ(0, delete_dir_contents(fake_package_de_path, true)); + + delete service; + ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user/0", true)); + } +}; + +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 37); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 37); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_path + "/file1", @@ -296,7 +323,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { // Request a snapshot of the CE content but not the DE content. int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode)); + "com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode)); struct stat buf; memset(&buf, 0, sizeof(buf)); ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf)); @@ -318,7 +345,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { // Request a snapshot of the DE content but not the CE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode)); + "com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode)); // Only DE content snapshot was requested. ASSERT_EQ(ce_snapshot_inode, 0); @@ -339,7 +366,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { // Request a snapshot of both the CE as well as the DE content. ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + "com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); ASSERT_TRUE(android::base::ReadFileToString( rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */)); @@ -349,27 +376,73 @@ TEST_F(ServiceTest, CreateAppDataSnapshot) { ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content); } -TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) { - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_TwoSnapshotsWithTheSameId) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 67); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 67); - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); + auto another_fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.bar"); + auto another_fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.bar"); - auto deleter = [&rollback_ce_dir, &rollback_de_dir]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); + // Since this test sets up data for another package, some bookkeeping is required. + auto deleter = [&]() { + ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_ce_path, true)); + ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_de_path, true)); }; - auto scope_guard = android::base::make_scope_guard(deleter); + ASSERT_TRUE(mkdirs(another_fake_package_ce_path, 0700)); + ASSERT_TRUE(mkdirs(another_fake_package_de_path, 0700)); + + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_CE", fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "TEST_CONTENT_DE", fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "ANOTHER_TEST_CONTENT_CE", another_fake_package_ce_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::WriteStringToFile( + "ANOTHER_TEST_CONTENT_DE", another_fake_package_de_path + "/file1", + 0700, 10000, 20000, false /* follow_symlinks */)); + + // Request snapshot for the package com.foo. + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + // Now request snapshot with the same id for the package com.bar + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + "com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + + // Check that both snapshots have correct data in them. + std::string com_foo_ce_content, com_foo_de_content; + std::string com_bar_ce_content, com_bar_de_content; + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.foo/file1", &com_foo_ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.foo/file1", &com_foo_de_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_ce_dir + "/com.bar/file1", &com_bar_ce_content, false /* follow_symlinks */)); + ASSERT_TRUE(android::base::ReadFileToString( + rollback_de_dir + "/com.bar/file1", &com_bar_de_content, false /* follow_symlinks */)); + ASSERT_EQ("TEST_CONTENT_CE", com_foo_ce_content); + ASSERT_EQ("TEST_CONTENT_DE", com_foo_de_content); + ASSERT_EQ("ANOTHER_TEST_CONTENT_CE", com_bar_ce_content); + ASSERT_EQ("ANOTHER_TEST_CONTENT_DE", com_bar_de_content); +} + +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_AppDataAbsent) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 73); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 73); + + // Similuating app data absence. + ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_ce_path, true)); + ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true)); + int64_t ce_snapshot_inode; ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode)); + "com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_DE, nullptr)); + "com.foo", 0, 73, FLAG_STORAGE_DE, nullptr)); // No CE content snapshot was performed. ASSERT_EQ(ce_snapshot_inode, 0); @@ -380,29 +453,12 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) { ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); } -TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); - - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_package_path("TEST", 0, 13, "com.foo"); + auto rollback_de_dir = create_data_misc_de_rollback_package_path("TEST", 0, 13, "com.foo"); - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); - - auto deleter = [&rollback_ce_dir, &rollback_de_dir, - &fake_package_ce_path, &fake_package_de_path]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); // Simulate presence of an existing snapshot ASSERT_TRUE(android::base::WriteStringToFile( @@ -421,62 +477,40 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); + "com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Previous snapshot (with data for file1) must be cleared. struct stat sb; - ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo/file1").c_str(), &sb)); - ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo/file1").c_str(), &sb)); + ASSERT_EQ(-1, stat((rollback_ce_dir + "/file1").c_str(), &sb)); + ASSERT_EQ(-1, stat((rollback_de_dir + "/file1").c_str(), &sb)); + // New snapshot (with data for file2) must be present. + ASSERT_NE(-1, stat((rollback_ce_dir + "/file2").c_str(), &sb)); + ASSERT_NE(-1, stat((rollback_de_dir + "/file2").c_str(), &sb)); } -TEST_F(ServiceTest, SnapshotAppData_WrongVolumeUuid) { - // Setup app data to make sure that fails due to wrong volumeUuid being +TEST_F(AppDataSnapshotTest, SnapshotAppData_WrongVolumeUuid) { + // Setup rollback folders to make sure that fails due to wrong volumeUuid being // passed, not because of some other reason. - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 17); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 17); - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto deleter = [&rollback_ce_dir, &rollback_de_dir]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"), - "com.foo", 0, FLAG_STORAGE_DE, nullptr)); + "com.foo", 0, 17, FLAG_STORAGE_DE, nullptr)); } -TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) { - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); +TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsCache) { auto fake_package_ce_cache_path = fake_package_ce_path + "/cache"; auto fake_package_ce_code_cache_path = fake_package_ce_path + "/code_cache"; auto fake_package_de_cache_path = fake_package_de_path + "/cache"; auto fake_package_de_code_cache_path = fake_package_de_path + "/code_cache"; - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); - - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 700)); - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto deleter = [&fake_package_ce_path, &fake_package_de_path, - &rollback_ce_dir, &rollback_de_dir]() { - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - delete_dir_contents_and_dir(rollback_ce_dir, true); - delete_dir_contents_and_dir(rollback_de_dir, true); - }; - auto scope_guard = android::base::make_scope_guard(deleter); + + ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 0700)); + ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 0700)); ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_CE", fake_package_ce_cache_path + "/file1", @@ -491,7 +525,7 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) { "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr)); + "com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr)); // The snapshot call must clear cache. struct stat sb; ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb)); @@ -500,34 +534,17 @@ TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) { ASSERT_EQ(-1, stat((fake_package_de_code_cache_path + "/file1").c_str(), &sb)); } -TEST_F(ServiceTest, RestoreAppDataSnapshot) { - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); - - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); - - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); +TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 239); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 239); - auto deleter = [&rollback_ce_dir, &rollback_de_dir, - &fake_package_ce_path, &fake_package_de_path]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); // Write contents to the rollback location. We'll write the same files to the // app data location and make sure the restore has overwritten them. - ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700)); + ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700)); ASSERT_TRUE(android::base::WriteStringToFile( "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); @@ -542,7 +559,7 @@ TEST_F(ServiceTest, RestoreAppDataSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"), - "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); + "com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); std::string ce_content, de_content; ASSERT_TRUE(android::base::ReadFileToString( @@ -553,29 +570,9 @@ TEST_F(ServiceTest, RestoreAppDataSnapshot) { ASSERT_EQ("DE_RESTORE_CONTENT", de_content); } -TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) { - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); - - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); - - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); - - auto deleter = [&rollback_ce_dir, &rollback_de_dir, - &fake_package_ce_path, &fake_package_de_path]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); +TEST_F(AppDataSnapshotTest, CreateSnapshotThenDestroyIt) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 57); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 57); // Prepare data for snapshot. ASSERT_TRUE(android::base::WriteStringToFile( @@ -588,7 +585,7 @@ TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) { int64_t ce_snapshot_inode; // Request a snapshot of both the CE as well as the DE content. ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), - "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk()); + "com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk()); // Because CE data snapshot was requested, ce_snapshot_inode can't be null. ASSERT_NE(0, ce_snapshot_inode); // Check snapshot is there. @@ -598,39 +595,19 @@ TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) { ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), - "com.foo", 0, ce_snapshot_inode, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); + "com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); } -TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); - - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); - - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); - - auto deleter = [&rollback_ce_dir, &rollback_de_dir, - &fake_package_ce_path, &fake_package_de_path]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); +TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 1543); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 1543); // Create a snapshot - ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700)); + ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700)); ASSERT_TRUE(android::base::WriteStringToFile( "CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); @@ -639,7 +616,7 @@ TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), - "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); + "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. struct stat sb; @@ -648,67 +625,33 @@ TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { // Check that deleting already deleted snapshot is no-op. ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), - "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); + "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); } -TEST_F(ServiceTest, DestroyAppDataSnapshot_WrongVolumeUuid) { +TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that test fails due to wrong volumeUuid // being passed, not because of some other reason. - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); - - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); - - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); - - auto deleter = [&rollback_ce_dir, &rollback_de_dir, - &fake_package_ce_path, &fake_package_de_path]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 43); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 43); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"), - "com.foo", 0, 0, FLAG_STORAGE_DE).isOk()); + "com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk()); } -TEST_F(ServiceTest, RestoreAppDataSnapshot_WrongVolumeUuid) { +TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { // Setup rollback data to make sure that fails due to wrong volumeUuid being // passed, not because of some other reason. - auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0); - auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0); - - ASSERT_TRUE(mkdirs(rollback_ce_dir, 700)); - ASSERT_TRUE(mkdirs(rollback_de_dir, 700)); - - auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo"); - auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo"); - - ASSERT_TRUE(mkdirs(fake_package_ce_path, 700)); - ASSERT_TRUE(mkdirs(fake_package_de_path, 700)); - - auto deleter = [&rollback_ce_dir, &rollback_de_dir, - &fake_package_ce_path, &fake_package_de_path]() { - delete_dir_contents(rollback_ce_dir, true); - delete_dir_contents(rollback_de_dir, true); - delete_dir_contents(fake_package_ce_path, true); - delete_dir_contents(fake_package_de_path, true); - rmdir(rollback_ce_dir.c_str()); - rmdir(rollback_de_dir.c_str()); - }; - auto scope_guard = android::base::make_scope_guard(deleter); + auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 41); + auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 41); + + ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); + ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"), - "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE)); + "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE)); } } // namespace installd diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp index 1782aa2816..e61eb6e52f 100644 --- a/cmds/installd/tests/installd_utils_test.cpp +++ b/cmds/installd/tests/installd_utils_test.cpp @@ -546,56 +546,86 @@ TEST_F(UtilsTest, MatchExtension_Invalid) { } TEST_F(UtilsTest, TestRollbackPaths) { - EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", - create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo")); - EXPECT_EQ("/data/misc_ce/10/rollback/com.foo", - create_data_misc_ce_rollback_package_path(nullptr, 10, "com.foo")); - - EXPECT_EQ("/data/misc_de/0/rollback/com.foo", - create_data_misc_de_rollback_package_path(nullptr, 0, "com.foo")); - EXPECT_EQ("/data/misc_de/10/rollback/com.foo", - create_data_misc_de_rollback_package_path(nullptr, 10, "com.foo")); - - EXPECT_EQ("/data/misc_ce/0/rollback", - create_data_misc_ce_rollback_path(nullptr, 0)); - EXPECT_EQ("/data/misc_ce/10/rollback", - create_data_misc_ce_rollback_path(nullptr, 10)); - - EXPECT_EQ("/data/misc_de/0/rollback", - create_data_misc_de_rollback_path(nullptr, 0)); - EXPECT_EQ("/data/misc_de/10/rollback", - create_data_misc_de_rollback_path(nullptr, 10)); - - EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", - create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 0)); - EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", - create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 239)); - - auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo"); - auto deleter = [&rollback_ce_package_path]() { - delete_dir_contents_and_dir(rollback_ce_package_path, true /* ignore_if_missing */); + EXPECT_EQ("/data/misc_ce/0/rollback/239/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 239, "com.foo")); + EXPECT_EQ("/data/misc_ce/10/rollback/37/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 10, 37, "com.foo")); + + EXPECT_EQ("/data/misc_de/0/rollback/73/com.foo", + create_data_misc_de_rollback_package_path(nullptr, 0, 73, "com.foo")); + EXPECT_EQ("/data/misc_de/10/rollback/13/com.foo", + create_data_misc_de_rollback_package_path(nullptr, 10, 13, "com.foo")); + + EXPECT_EQ("/data/misc_ce/0/rollback/57", + create_data_misc_ce_rollback_path(nullptr, 0, 57)); + EXPECT_EQ("/data/misc_ce/10/rollback/1543", + create_data_misc_ce_rollback_path(nullptr, 10, 1543)); + + EXPECT_EQ("/data/misc_de/0/rollback/43", + create_data_misc_de_rollback_path(nullptr, 0, 43)); + EXPECT_EQ("/data/misc_de/10/rollback/41", + create_data_misc_de_rollback_path(nullptr, 10, 41)); + + EXPECT_EQ("/data/misc_ce/0/rollback/17/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 17, "com.foo", 0)); + EXPECT_EQ("/data/misc_ce/0/rollback/19/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 19, "com.foo", 239)); + + auto rollback_ce_path = create_data_misc_ce_rollback_path(nullptr, 0, 53); + auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, 53, + "com.foo"); + auto deleter = [&rollback_ce_path]() { + delete_dir_contents_and_dir(rollback_ce_path, true /* ignore_if_missing */); }; auto scope_guard = android::base::make_scope_guard(deleter); - ASSERT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700)); + EXPECT_NE(-1, mkdir(rollback_ce_path.c_str(), 700)); + EXPECT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700)); ino_t ce_data_inode; - ASSERT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode)); + EXPECT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode)); - EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", - create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", ce_data_inode)); + EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.foo", ce_data_inode)); // Check that path defined by inode is picked even if it's not the same as // the fallback one. - EXPECT_EQ("/data/misc_ce/0/rollback/com.foo", - create_data_misc_ce_rollback_package_path(nullptr, 0, "com.bar", ce_data_inode)); + EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo", + create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.bar", ce_data_inode)); // These last couple of cases are never exercised in production because we // only snapshot apps in the primary data partition. Exercise them here for // the sake of completeness. - EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/com.example", - create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); - EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/com.example", - create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example")); + EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/7/com.example", + create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 7, + "com.example")); + EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/11/com.example", + create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 11, + "com.example")); +} + +TEST_F(UtilsTest, TestCreateDirIfNeeded) { + system("mkdir -p /data/local/tmp/user/0"); + + auto deleter = [&]() { + delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */); + }; + auto scope_guard = android::base::make_scope_guard(deleter); + + // Create folder and check it's permissions. + ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700)); + struct stat st; + ASSERT_EQ(0, stat("/data/local/tmp/user/0/foo", &st)); + ASSERT_EQ(0700, st.st_mode & ALLPERMS); + + // Check that create_dir_if_needed is no-op if folder already exists with + // correct permissions. + ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700)); + + // Check -1 is returned if folder exists but with different permissions. + ASSERT_EQ(-1, create_dir_if_needed("/data/local/tmp/user/0/foo", 0750)); + + // Check that call fails if parent doesn't exist. + ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700)); } } // namespace installd diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp index 5b487bb515..da097db06b 100644 --- a/cmds/installd/utils.cpp +++ b/cmds/installd/utils.cpp @@ -197,32 +197,44 @@ std::string create_data_user_de_path(const char* volume_uuid, userid_t userid) { return StringPrintf("%s/user_de/%u", data.c_str(), userid); } - -std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user) { +std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) { return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user); } -std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user) { +std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user) { return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user); } +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id) { + return StringPrintf("%s/%d", create_data_misc_ce_rollback_base_path(volume_uuid, user).c_str(), + snapshot_id); +} + +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id) { + return StringPrintf("%s/%d", create_data_misc_de_rollback_base_path(volume_uuid, user).c_str(), + snapshot_id); +} + std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, - userid_t user, const char* package_name) { + userid_t user, int32_t snapshot_id, const char* package_name) { return StringPrintf("%s/%s", - create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name); + create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name); } std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, - userid_t user, const char* package_name, ino_t ce_rollback_inode) { - auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name); - auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user); + userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode) { + auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshot_id, + package_name); + auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id); return resolve_ce_path_by_inode_or_fallback(user_path, ce_rollback_inode, fallback); } std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, - userid_t user, const char* package_name) { + userid_t user, int32_t snapshot_id, const char* package_name) { return StringPrintf("%s/%s", - create_data_misc_de_rollback_path(volume_uuid, user).c_str(), package_name); + create_data_misc_de_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name); } /** @@ -528,6 +540,30 @@ static int _delete_dir_contents(DIR *d, return result; } +int create_dir_if_needed(const std::string& pathname, mode_t perms) { + struct stat st; + + int rc; + if ((rc = stat(pathname.c_str(), &st)) != 0) { + if (errno == ENOENT) { + return mkdir(pathname.c_str(), perms); + } else { + return rc; + } + } else if (!S_ISDIR(st.st_mode)) { + LOG(DEBUG) << pathname << " is not a folder"; + return -1; + } + + mode_t actual_perms = st.st_mode & ALLPERMS; + if (actual_perms != perms) { + LOG(WARNING) << pathname << " permissions " << actual_perms << " expected " << perms; + return -1; + } + + return 0; +} + int delete_dir_contents(const std::string& pathname, bool ignore_if_missing) { return delete_dir_contents(pathname.c_str(), 0, nullptr, ignore_if_missing); } @@ -892,6 +928,8 @@ bool validate_secondary_dex_path(const std::string& pkgname, const std::string& static int validate_apk_path_internal(const std::string& path, int maxSubdirs) { if (validate_path(android_app_dir, path, maxSubdirs) == 0) { return 0; + } else if (validate_path(android_staging_dir, path, maxSubdirs) == 0) { + return 0; } else if (validate_path(android_app_private_dir, path, maxSubdirs) == 0) { return 0; } else if (validate_path(android_app_ephemeral_dir, path, maxSubdirs) == 0) { diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h index 0711b34b9c..955d524069 100644 --- a/cmds/installd/utils.h +++ b/cmds/installd/utils.h @@ -61,14 +61,18 @@ std::string create_data_user_de_package_path(const char* volume_uuid, std::string create_data_user_ce_package_path_as_user_link( const char* volume_uuid, userid_t userid, const char* package_name); -std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user); -std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user); +std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id); +std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user, + int32_t snapshot_id); std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, - userid_t user, const char* package_name); + userid_t user, int32_t snapshot_id, const char* package_name); std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid, - userid_t user, const char* package_name, ino_t ce_rollback_inode); + userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode); std::string create_data_misc_de_rollback_package_path(const char* volume_uuid, - userid_t user, const char* package_name); + userid_t user, int32_t snapshot_id, const char* package_name); std::string create_data_media_path(const char* volume_uuid, userid_t userid); std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name); @@ -109,6 +113,8 @@ int create_user_config_path(char path[PKG_PATH_MAX], userid_t userid); bool is_valid_filename(const std::string& name); bool is_valid_package_name(const std::string& packageName); +int create_dir_if_needed(const std::string& pathname, mode_t mode); + int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false); int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false); diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c index 3b8955b166..71f08375f7 100644 --- a/cmds/ip-up-vpn/ip-up-vpn.c +++ b/cmds/ip-up-vpn/ip-up-vpn.c @@ -95,6 +95,7 @@ int main(int argc, char **argv) strncpy(ifr.ifr_name, interface, IFNAMSIZ); if (ioctl(s, SIOCSIFFLAGS, &ifr)) { ALOGE("Cannot bring up %s: %s", interface, strerror(errno)); + fclose(state); return 1; } @@ -102,6 +103,7 @@ int main(int argc, char **argv) if (!set_address(&ifr.ifr_addr, address) || ioctl(s, SIOCSIFADDR, &ifr)) { ALOGE("Cannot set address: %s", strerror(errno)); + fclose(state); return 1; } @@ -109,6 +111,7 @@ int main(int argc, char **argv) if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) { if (ioctl(s, SIOCSIFNETMASK, &ifr)) { ALOGE("Cannot set netmask: %s", strerror(errno)); + fclose(state); return 1; } } @@ -123,6 +126,7 @@ int main(int argc, char **argv) fprintf(state, "%s\n", env("REMOTE_ADDR")); } else { ALOGE("Cannot parse parameters"); + fclose(state); return 1; } diff --git a/cmds/service/service.cpp b/cmds/service/service.cpp index 34a3fdc420..d5dc6b741d 100644 --- a/cmds/service/service.cpp +++ b/cmds/service/service.cpp @@ -232,7 +232,6 @@ int main(int argc, char* const argv[]) else if (strcmp(key, "categories") == 0) { char* context2 = nullptr; - int categoryCount = 0; categories[categoryCount] = strtok_r(value, ",", &context2); while (categories[categoryCount] != nullptr) diff --git a/data/etc/car_core_hardware.xml b/data/etc/car_core_hardware.xml index 6cbe4ae0fe..5c1cab66e3 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 d75de1ee1a..44883cc498 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 struct AChoreographer AChoreographer; */ 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 @@ typedef void (*AChoreographer_frameCallback)(long frameTimeNanos, void* data); 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 0e79239edd..ef2ad9998c 100644 --- a/include/android/surface_control.h +++ b/include/android/surface_control.h @@ -40,9 +40,9 @@ __BEGIN_DECLS 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 @@ typedef struct ASurfaceTransactionStats ASurfaceTransactionStats; * 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/input/IInputFlinger.h b/include/input/IInputFlinger.h index ff443c6928..cd12fcd255 100644 --- a/include/input/IInputFlinger.h +++ b/include/input/IInputFlinger.h @@ -24,6 +24,7 @@ #include <utils/Vector.h> #include <input/InputWindow.h> +#include <input/ISetInputWindowsListener.h> namespace android { @@ -35,7 +36,8 @@ class IInputFlinger : public IInterface { public: DECLARE_META_INTERFACE(InputFlinger) - virtual void setInputWindows(const Vector<InputWindowInfo>& inputHandles) = 0; + virtual void setInputWindows(const Vector<InputWindowInfo>& inputHandles, + const sp<ISetInputWindowsListener>& setInputWindowsListener) = 0; virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0; virtual void registerInputChannel(const sp<InputChannel>& channel) = 0; virtual void unregisterInputChannel(const sp<InputChannel>& channel) = 0; diff --git a/include/input/ISetInputWindowsListener.h b/include/input/ISetInputWindowsListener.h new file mode 100644 index 0000000000..15d31b25c1 --- /dev/null +++ b/include/input/ISetInputWindowsListener.h @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <binder/IInterface.h> +#include <binder/Parcel.h> + +namespace android { + +class ISetInputWindowsListener : public IInterface { +public: + DECLARE_META_INTERFACE(SetInputWindowsListener) + virtual void onSetInputWindowsFinished() = 0; +}; + +class BnSetInputWindowsListener: public BnInterface<ISetInputWindowsListener> { +public: + enum SetInputWindowsTag : uint32_t { + ON_SET_INPUT_WINDOWS_FINISHED + }; + + virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags = 0) override; +}; + +}; // namespace android diff --git a/include/input/InputDevice.h b/include/input/InputDevice.h index ce8db91980..48ac88d50e 100644 --- a/include/input/InputDevice.h +++ b/include/input/InputDevice.h @@ -51,6 +51,15 @@ struct InputDeviceIdentifier { // is intended to be a minimum way to distinguish from other active devices and may // reuse values that are not associated with an input anymore. uint16_t nonce; + + /** + * Return InputDeviceIdentifier.name that has been adjusted as follows: + * - all characters besides alphanumerics, dash, + * and underscore have been replaced with underscores. + * This helps in situations where a file that matches the device name is needed, + * while conforming to the filename limitations. + */ + std::string getCanonicalName() const; }; /* diff --git a/include/input/InputWindow.h b/include/input/InputWindow.h index a065a4ce28..916af699e0 100644 --- a/include/input/InputWindow.h +++ b/include/input/InputWindow.h @@ -116,7 +116,7 @@ struct InputWindowInfo { INPUT_FEATURE_NO_INPUT_CHANNEL = 0x00000002, INPUT_FEATURE_DISABLE_USER_ACTIVITY = 0x00000004, }; - + /* These values are filled in by the WM and passed through SurfaceFlinger * unless specified otherwise. */ @@ -165,6 +165,8 @@ struct InputWindowInfo { int32_t displayId; int32_t portalToDisplayId = ADISPLAY_ID_NONE; InputApplicationInfo applicationInfo; + bool replaceTouchableRegionWithCrop; + wp<IBinder> touchableRegionCropHandle; void addTouchableRegion(const Rect& region); diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 8b66f693cc..92da10c056 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -25,15 +25,6 @@ namespace android { -enum { - /* Device id of the built in keyboard. */ - DEVICE_ID_BUILT_IN_KEYBOARD = 0, - - /* Device id of a generic virtual keyboard with a full layout that can be used - * to synthesize key events. */ - DEVICE_ID_VIRTUAL_KEYBOARD = -1, -}; - class KeyLayoutMap; class KeyCharacterMap; diff --git a/include/input/TouchVideoFrame.h b/include/input/TouchVideoFrame.h index 640bf1f693..b49c623204 100644 --- a/include/input/TouchVideoFrame.h +++ b/include/input/TouchVideoFrame.h @@ -19,6 +19,7 @@ #include <stdint.h> #include <sys/time.h> +#include <ui/DisplayInfo.h> #include <vector> namespace android { @@ -30,44 +31,48 @@ namespace android { */ class TouchVideoFrame { public: - TouchVideoFrame(uint32_t width, uint32_t height, std::vector<int16_t> data, - const struct timeval& timestamp) : - mWidth(width), mHeight(height), mData(std::move(data)), mTimestamp(timestamp) { - } + TouchVideoFrame(uint32_t height, uint32_t width, std::vector<int16_t> data, + const struct timeval& timestamp); - bool operator==(const TouchVideoFrame& rhs) const { - return mWidth == rhs.mWidth - && mHeight == rhs.mHeight - && mData == rhs.mData - && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec - && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec; - } + bool operator==(const TouchVideoFrame& rhs) const; /** - * Width of the frame + * Height of the frame */ - uint32_t getWidth() const { return mWidth; } + uint32_t getHeight() const; /** - * Height of the frame + * Width of the frame */ - uint32_t getHeight() const { return mHeight; } + uint32_t getWidth() const; /** * The touch strength data. * The array is a 2-D row-major matrix, with dimensions (height, width). * Total size of the array should equal getHeight() * getWidth(). * Data is allowed to be negative. */ - const std::vector<int16_t>& getData() const { return mData; } + const std::vector<int16_t>& getData() const; /** * Time at which the heatmap was taken. */ - const struct timeval& getTimestamp() const { return mTimestamp; } + const struct timeval& getTimestamp() const; + + /** + * Rotate the video frame. + * The rotation value is an enum from ui/DisplayInfo.h + */ + void rotate(int32_t orientation); private: - uint32_t mWidth; uint32_t mHeight; + uint32_t mWidth; std::vector<int16_t> mData; struct timeval mTimestamp; + + /** + * Common method for 90 degree and 270 degree rotation + */ + void rotateQuarterTurn(bool clockwise); + void rotate180(); }; } // namespace android diff --git a/include/input/VirtualKeyMap.h b/include/input/VirtualKeyMap.h index 24e0e0ed9e..4f7cfb6b75 100644 --- a/include/input/VirtualKeyMap.h +++ b/include/input/VirtualKeyMap.h @@ -49,7 +49,7 @@ class VirtualKeyMap { public: ~VirtualKeyMap(); - static status_t load(const std::string& filename, VirtualKeyMap** outMap); + static std::unique_ptr<VirtualKeyMap> load(const std::string& filename); inline const Vector<VirtualKeyDefinition>& getVirtualKeys() const { return mVirtualKeys; diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h index 3c81f0f286..853f0c9fde 100644 --- a/include/powermanager/IPowerManager.h +++ b/include/powermanager/IPowerManager.h @@ -45,7 +45,7 @@ public: IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11, IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13, - SET_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 14, + SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14, REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17, REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18, SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19, diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index a3f87557ab..8b33a56484 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp @@ -63,6 +63,26 @@ Status Status::fromStatusT(status_t status) { return ret; } +std::string Status::exceptionToString(int32_t exceptionCode) { + switch (exceptionCode) { + #define EXCEPTION_TO_CASE(EXCEPTION) case EXCEPTION: return #EXCEPTION; + EXCEPTION_TO_CASE(EX_NONE) + EXCEPTION_TO_CASE(EX_SECURITY) + EXCEPTION_TO_CASE(EX_BAD_PARCELABLE) + EXCEPTION_TO_CASE(EX_ILLEGAL_ARGUMENT) + EXCEPTION_TO_CASE(EX_NULL_POINTER) + EXCEPTION_TO_CASE(EX_ILLEGAL_STATE) + EXCEPTION_TO_CASE(EX_NETWORK_MAIN_THREAD) + EXCEPTION_TO_CASE(EX_UNSUPPORTED_OPERATION) + EXCEPTION_TO_CASE(EX_SERVICE_SPECIFIC) + EXCEPTION_TO_CASE(EX_PARCELABLE) + EXCEPTION_TO_CASE(EX_HAS_REPLY_HEADER) + EXCEPTION_TO_CASE(EX_TRANSACTION_FAILED) + #undef EXCEPTION_TO_CASE + default: return std::to_string(exceptionCode); + } +} + Status::Status(int32_t exceptionCode, int32_t errorCode) : mException(exceptionCode), mErrorCode(errorCode) {} @@ -184,7 +204,7 @@ String8 Status::toString8() const { if (mException == EX_NONE) { ret.append("No error"); } else { - ret.appendFormat("Status(%d): '", mException); + ret.appendFormat("Status(%d, %s): '", mException, exceptionToString(mException).c_str()); if (mException == EX_SERVICE_SPECIFIC || mException == EX_TRANSACTION_FAILED) { ret.appendFormat("%d: ", mErrorCode); diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl index 5b66b923e7..ccde12a664 100644 --- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl +++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl @@ -54,4 +54,9 @@ interface IPackageManagerNative { long getVersionCodeForPackage(in String packageName); + /** + * Return if each app, identified by its package name allows its audio to be recorded. + * Unknown packages are mapped to false. + */ + boolean[] isAudioPlaybackCaptureAllowed(in @utf8InCpp String[] packageNames); } diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 1674516ca2..aa44285b6e 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -143,6 +143,9 @@ public: * dies. The @a cookie is optional. If non-NULL, you can * supply a NULL @a recipient, and the recipient previously * added with that cookie will be unlinked. + * + * If the binder is dead, this will return DEAD_OBJECT. Deleting + * the object will also unlink all death recipients. */ // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient, diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h index c3738f8b29..7d889b6b14 100644 --- a/libs/binder/include/binder/Status.h +++ b/libs/binder/include/binder/Status.h @@ -22,6 +22,7 @@ #include <binder/Parcel.h> #include <utils/String8.h> +#include <string> namespace android { namespace binder { @@ -97,6 +98,8 @@ public: static Status fromStatusT(status_t status); + static std::string exceptionToString(status_t exceptionCode); + Status() = default; ~Status() = default; diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index f9c8c8a0ff..bd6886d1ee 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -31,6 +31,7 @@ using ::android::Parcel; using ::android::sp; using ::android::status_t; using ::android::String16; +using ::android::String8; using ::android::wp; namespace ABBinderTag { @@ -67,8 +68,6 @@ AIBinder::AIBinder(const AIBinder_Class* clazz) : mClazz(clazz) {} AIBinder::~AIBinder() {} bool AIBinder::associateClass(const AIBinder_Class* clazz) { - using ::android::String8; - if (clazz == nullptr) return false; if (mClazz == clazz) return true; @@ -119,6 +118,33 @@ const String16& ABBinder::getInterfaceDescriptor() const { return getClass()->getInterfaceDescriptor(); } +status_t ABBinder::dump(int fd, const ::android::Vector<String16>& args) { + AIBinder_onDump onDump = getClass()->onDump; + + if (onDump == nullptr) { + return STATUS_OK; + } + + // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be + // null in Java + if (args.size() > INT32_MAX) { + LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size(); + return STATUS_BAD_VALUE; + } + + std::vector<String8> utf8Args; // owns memory of utf8s + utf8Args.reserve(args.size()); + std::vector<const char*> utf8Pointers; // what can be passed over NDK API + utf8Pointers.reserve(args.size()); + + for (size_t i = 0; i < args.size(); i++) { + utf8Args.push_back(String8(args[i])); + utf8Pointers.push_back(utf8Args[i].c_str()); + } + + return onDump(this, fd, utf8Pointers.data(), utf8Pointers.size()); +} + status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply, binder_flags_t flags) { if (isUserCommand(code)) { @@ -232,10 +258,29 @@ AIBinder_Class* AIBinder_Class_define(const char* interfaceDescriptor, return new AIBinder_Class(interfaceDescriptor, onCreate, onDestroy, onTransact); } +void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) { + CHECK(clazz != nullptr) << "setOnDump requires non-null clazz"; + + // this is required to be called before instances are instantiated + clazz->onDump = onDump; +} + void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) { CHECK(who == mWho); mOnDied(mCookie); + + sp<AIBinder_DeathRecipient> recipient = mParentRecipient.promote(); + sp<IBinder> strongWho = who.promote(); + + // otherwise this will be cleaned up later with pruneDeadTransferEntriesLocked + if (recipient != nullptr && strongWho != nullptr) { + status_t result = recipient->unlinkToDeath(strongWho, mCookie); + if (result != ::android::DEAD_OBJECT) { + LOG(WARNING) << "Unlinking to dead binder resulted in: " << result; + } + } + mWho = nullptr; } @@ -244,24 +289,34 @@ AIBinder_DeathRecipient::AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinde CHECK(onDied != nullptr); } -binder_status_t AIBinder_DeathRecipient::linkToDeath(AIBinder* binder, void* cookie) { +void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() { + mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(), + [](const sp<TransferDeathRecipient>& tdr) { + return tdr->getWho() == nullptr; + }), + mDeathRecipients.end()); +} + +binder_status_t AIBinder_DeathRecipient::linkToDeath(sp<IBinder> binder, void* cookie) { CHECK(binder != nullptr); std::lock_guard<std::mutex> l(mDeathRecipientsMutex); sp<TransferDeathRecipient> recipient = - new TransferDeathRecipient(binder->getBinder(), cookie, mOnDied); + new TransferDeathRecipient(binder, cookie, this, mOnDied); - status_t status = binder->getBinder()->linkToDeath(recipient, cookie, 0 /*flags*/); + status_t status = binder->linkToDeath(recipient, cookie, 0 /*flags*/); if (status != STATUS_OK) { return PruneStatusT(status); } mDeathRecipients.push_back(recipient); + + pruneDeadTransferEntriesLocked(); return STATUS_OK; } -binder_status_t AIBinder_DeathRecipient::unlinkToDeath(AIBinder* binder, void* cookie) { +binder_status_t AIBinder_DeathRecipient::unlinkToDeath(sp<IBinder> binder, void* cookie) { CHECK(binder != nullptr); std::lock_guard<std::mutex> l(mDeathRecipientsMutex); @@ -269,10 +324,10 @@ binder_status_t AIBinder_DeathRecipient::unlinkToDeath(AIBinder* binder, void* c for (auto it = mDeathRecipients.rbegin(); it != mDeathRecipients.rend(); ++it) { sp<TransferDeathRecipient> recipient = *it; - if (recipient->getCookie() == cookie && recipient->getWho() == binder->getBinder()) { + if (recipient->getCookie() == cookie && recipient->getWho() == binder) { mDeathRecipients.erase(it.base() - 1); - status_t status = binder->getBinder()->unlinkToDeath(recipient, cookie, 0 /*flags*/); + status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/); if (status != ::android::OK) { LOG(ERROR) << __func__ << ": removed reference to death recipient but unlink failed."; @@ -325,6 +380,30 @@ binder_status_t AIBinder_ping(AIBinder* binder) { return PruneStatusT(binder->getBinder()->pingBinder()); } +binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) { + if (binder == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + + ABBinder* bBinder = binder->asABBinder(); + if (bBinder != nullptr) { + AIBinder_onDump onDump = binder->getClass()->onDump; + if (onDump == nullptr) { + return STATUS_OK; + } + return PruneStatusT(onDump(bBinder, fd, args, numArgs)); + } + + ::android::Vector<String16> utf16Args; + utf16Args.setCapacity(numArgs); + for (uint32_t i = 0; i < numArgs; i++) { + utf16Args.push(String16(String8(args[i]))); + } + + status_t status = binder->getBinder()->dump(fd, utf16Args); + return PruneStatusT(status); +} + binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) { if (binder == nullptr || recipient == nullptr) { @@ -333,7 +412,7 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* } // returns binder_status_t - return recipient->linkToDeath(binder, cookie); + return recipient->linkToDeath(binder->getBinder(), cookie); } binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient, @@ -344,7 +423,7 @@ binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient } // returns binder_status_t - return recipient->unlinkToDeath(binder, cookie); + return recipient->unlinkToDeath(binder->getBinder(), cookie); } uid_t AIBinder_getCallingUid() { @@ -498,9 +577,15 @@ AIBinder_DeathRecipient* AIBinder_DeathRecipient_new( LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter."; return nullptr; } - return new AIBinder_DeathRecipient(onBinderDied); + auto ret = new AIBinder_DeathRecipient(onBinderDied); + ret->incStrong(nullptr); + return ret; } void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) { - delete recipient; + if (recipient == nullptr) { + return; + } + + recipient->decStrong(nullptr); } diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 7852298ef9..5cb68c291b 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -25,6 +25,7 @@ #include <binder/Binder.h> #include <binder/IBinder.h> +#include <utils/Vector.h> inline bool isUserCommand(transaction_code_t code) { return code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION; @@ -66,6 +67,7 @@ struct ABBinder : public AIBinder, public ::android::BBinder { ABBinder* asABBinder() override { return this; } const ::android::String16& getInterfaceDescriptor() const override; + ::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override; ::android::status_t onTransact(uint32_t code, const ::android::Parcel& data, ::android::Parcel* reply, binder_flags_t flags) override; @@ -106,10 +108,14 @@ struct AIBinder_Class { const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; } + // required to be non-null, implemented for every class const AIBinder_Class_onCreate onCreate; const AIBinder_Class_onDestroy onDestroy; const AIBinder_Class_onTransact onTransact; + // optional methods for a class + AIBinder_onDump onDump; + private: // This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to // one. @@ -122,13 +128,14 @@ struct AIBinder_Class { // // When the AIBinder_DeathRecipient is dropped, so are the actual underlying death recipients. When // the IBinder dies, only a wp to it is kept. -struct AIBinder_DeathRecipient { +struct AIBinder_DeathRecipient : ::android::RefBase { // One of these is created for every linkToDeath. This is to be able to recover data when a // 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) - : mWho(who), mCookie(cookie), mOnDied(onDied) {} + const ::android::wp<AIBinder_DeathRecipient>& parentRecipient, + const AIBinder_DeathRecipient_onBinderDied onDied) + : mWho(who), mCookie(cookie), mParentRecipient(parentRecipient), mOnDied(onDied) {} void binderDied(const ::android::wp<::android::IBinder>& who) override; @@ -138,14 +145,24 @@ struct AIBinder_DeathRecipient { private: ::android::wp<::android::IBinder> mWho; void* mCookie; - const AIBinder_DeathRecipient_onBinderDied& mOnDied; + + ::android::wp<AIBinder_DeathRecipient> mParentRecipient; + + // This is kept separately from AIBinder_DeathRecipient in case the death recipient is + // deleted while the death notification is fired + const AIBinder_DeathRecipient_onBinderDied mOnDied; }; explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied); - binder_status_t linkToDeath(AIBinder* binder, void* cookie); - binder_status_t unlinkToDeath(AIBinder* binder, void* cookie); + binder_status_t linkToDeath(::android::sp<::android::IBinder>, void* cookie); + binder_status_t unlinkToDeath(::android::sp<::android::IBinder> binder, void* cookie); private: + // When the user of this API deletes a Bp object but not the death recipient, the + // TransferDeathRecipient object can't be cleaned up. This is called whenever a new + // TransferDeathRecipient is linked, and it ensures that mDeathRecipients can't grow unbounded. + void pruneDeadTransferEntriesLocked(); + std::mutex mDeathRecipientsMutex; std::vector<::android::sp<TransferDeathRecipient>> mDeathRecipients; AIBinder_DeathRecipient_onBinderDied mOnDied; diff --git a/libs/binder/ndk/include_apex/android/binder_manager.h b/libs/binder/ndk/include_apex/android/binder_manager.h index 80b6c07025..055c79bca1 100644 --- a/libs/binder/ndk/include_apex/android/binder_manager.h +++ b/libs/binder/ndk/include_apex/android/binder_manager.h @@ -33,6 +33,15 @@ __BEGIN_DECLS binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance); /** + * Gets a binder object with this specific instance name. Will return nullptr immediately if the + * service is not available This also implicitly calls AIBinder_incStrong (so the caller of this + * function is responsible for calling AIBinder_decStrong). + * + * \param instance identifier of the service used to lookup the service. + */ +__attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const char* instance); + +/** * Gets a binder object with this specific instance name. Blocks for a couple of seconds waiting on * it. This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible * for calling AIBinder_decStrong). diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 9c6c55e736..80d12541be 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -179,6 +179,31 @@ __attribute__((warn_unused_result)) AIBinder_Class* AIBinder_Class_define( __INTRODUCED_IN(29); /** + * Dump information about an AIBinder (usually for debugging). + * + * When no arguments are provided, a brief overview of the interview should be given. + * + * \param binder interface being dumped + * \param fd file descriptor to be dumped to, should be flushed, ownership is not passed. + * \param args array of null-terminated strings for dump (may be null if numArgs is 0) + * \param numArgs number of args to be sent + * + * \return binder_status_t result of transaction (if remote, for instance) + */ +typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char** args, + uint32_t numArgs); + +/** + * This sets the implementation of the dump method for a class. + * + * If this isn't set, nothing will be dumped when dump is called (for instance with + * android.os.Binder#dump). Must be called before any instance of the class is created. + * + * \param dump function to call when an instance of this binder class is being dumped. + */ +void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29); + +/** * Creates a new binder object of the appropriate class. * * Ownership of args is passed to this object. The lifecycle is implemented with AIBinder_incStrong @@ -237,6 +262,21 @@ bool AIBinder_isAlive(const AIBinder* binder) __INTRODUCED_IN(29); binder_status_t AIBinder_ping(AIBinder* binder) __INTRODUCED_IN(29); /** + * Built-in transaction for all binder objects. This dumps information about a given binder. + * + * See also AIBinder_Class_setOnDump, AIBinder_onDump + * + * \param binder the binder to dump information about + * \param fd where information should be dumped to + * \param args null-terminated arguments to pass (may be null if numArgs is 0) + * \param numArgs number of args to send + * + * \return STATUS_OK if dump succeeds (or if there is nothing to dump) + */ +binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) + __INTRODUCED_IN(29); + +/** * Registers for notifications that the associated binder is dead. The same death recipient may be * associated with multiple different binders. If the binder is local, then no death recipient will * be given (since if the local process dies, then no recipient will exist to recieve a @@ -261,6 +301,11 @@ binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* * may return a binder transaction failure and in case the death recipient cannot be found, it * returns STATUS_NAME_NOT_FOUND. * + * This only ever needs to be called when the AIBinder_DeathRecipient remains for use with other + * AIBinder objects. If the death recipient is deleted, all binders will automatically be unlinked. + * If the binder dies, it will automatically unlink. If the binder is deleted, it will be + * automatically unlinked. + * * \param binder the binder object to remove a previously linked death recipient from. * \param recipient the callback to remove. * \param cookie the cookie used to link to death. 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 a42c60b5b2..83a10488e0 100644 --- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h @@ -104,6 +104,39 @@ class ICInterface : public SharedRefBase { * this will be checked using AIBinder_isRemote. */ virtual bool isRemote() = 0; + + /** + * Dumps information about the interface. By default, dumps nothing. + */ + 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 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); + }; }; /** @@ -117,7 +150,7 @@ class BnCInterface : public INTERFACE { SpAIBinder asBinder() override; - bool isRemote() override { return true; } + bool isRemote() override { return false; } protected: /** @@ -144,10 +177,63 @@ class BpCInterface : public INTERFACE { bool isRemote() override { return AIBinder_isRemote(mBinder.get()); } + binder_status_t dump(int fd, const char** args, uint32_t numArgs) override { + return AIBinder_dump(asBinder().get(), fd, args, numArgs); + } + private: 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/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index f0d25f79e3..7e6581736f 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -2,10 +2,12 @@ LIBBINDER_NDK { # introduced=29 global: AIBinder_associateClass; AIBinder_Class_define; + AIBinder_Class_setOnDump; AIBinder_DeathRecipient_delete; AIBinder_DeathRecipient_new; AIBinder_debugGetRefCount; AIBinder_decStrong; + AIBinder_dump; AIBinder_fromJavaBinder; AIBinder_getCallingPid; AIBinder_getCallingUid; @@ -91,6 +93,7 @@ LIBBINDER_NDK { # introduced=29 ABinderProcess_setThreadPoolMaxThreadCount; # apex ABinderProcess_startThreadPool; # apex AServiceManager_addService; # apex + AServiceManager_checkService; # apex AServiceManager_getService; # apex local: *; diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index 9ddc5559cf..d0b166d318 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -37,6 +37,18 @@ binder_status_t AServiceManager_addService(AIBinder* binder, const char* instanc status_t status = sm->addService(String16(instance), binder->getBinder()); return PruneStatusT(status); } +AIBinder* AServiceManager_checkService(const char* instance) { + if (instance == nullptr) { + return nullptr; + } + + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->checkService(String16(instance)); + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ret.get()); + return ret.get(); +} AIBinder* AServiceManager_getService(const char* instance) { if (instance == nullptr) { return nullptr; diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp index c159d71b59..8467734c75 100644 --- a/libs/binder/ndk/test/main_client.cpp +++ b/libs/binder/ndk/test/main_client.cpp @@ -35,6 +35,19 @@ constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; // EXPECT_EQ(nullptr, foo.get()); // } +TEST(NdkBinder, CheckServiceThatDoesntExist) { + AIBinder* binder = AServiceManager_checkService("asdfghkl;"); + ASSERT_EQ(nullptr, binder); +} + +TEST(NdkBinder, CheckServiceThatDoesExist) { + AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService); + EXPECT_NE(nullptr, binder); + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder)); + + AIBinder_decStrong(binder); +} + TEST(NdkBinder, DoubleNumber) { sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); @@ -73,12 +86,15 @@ TEST(NdkBinder, DeathRecipient) { // the binder driver should return this if the service dies during the transaction EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die()); + foo = nullptr; + AIBinder_decStrong(binder); + binder = nullptr; + std::unique_lock<std::mutex> lock(deathMutex); EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; })); EXPECT_TRUE(deathRecieved); AIBinder_DeathRecipient_delete(recipient); - AIBinder_decStrong(binder); } TEST(NdkBinder, RetrieveNonNdkService) { diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 4cddf94720..5fd4a95d7b 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -45,14 +45,14 @@ using android::base::unique_fd; namespace android { namespace bpf { -typedef struct { +struct time_key_t { uint32_t uid; uint32_t freq; -} time_key_t; +}; -typedef struct { +struct val_t { uint64_t ar[100]; -} val_t; +}; static std::mutex gInitializedMutex; static bool gInitialized = false; @@ -65,19 +65,13 @@ static unique_fd gMapFd; static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) { std::string data; - if (!android::base::ReadFileToString(path, &data)) { - ALOGD("Failed to read file %s", path.c_str()); - return false; - } + if (!android::base::ReadFileToString(path, &data)) return false; auto strings = android::base::Split(data, " \n"); for (const auto &s : strings) { if (s.empty()) continue; uint32_t n; - if (!android::base::ParseUint(s, &n)) { - ALOGD("Failed to parse file %s", path.c_str()); - return false; - } + if (!android::base::ParseUint(s, &n)) return false; out->emplace_back(n); } return true; @@ -141,16 +135,8 @@ static bool attachTracepointProgram(const std::string &eventType, const std::str std::string path = StringPrintf(BPF_FS_PATH "prog_time_in_state_tracepoint_%s_%s", eventType.c_str(), eventName.c_str()); int prog_fd = bpf_obj_get(path.c_str()); - if (prog_fd < 0) { - ALOGD("bpf_obj_get() failed for program %s", path.c_str()); - return false; - } - if (bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) < 0) { - ALOGD("Failed to attach bpf program to tracepoint %s/%s", eventType.c_str(), - eventName.c_str()); - return false; - } - return true; + if (prog_fd < 0) return false; + return bpf_attach_tracepoint(prog_fd, eventType.c_str(), eventName.c_str()) >= 0; } // Start tracking and aggregating data to be reported by getUidCpuFreqTimes and getUidsCpuFreqTimes. @@ -224,20 +210,16 @@ bool getUidsCpuFreqTimes( auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val, const BpfMap<time_key_t, val_t> &) { if (freqTimeMap->find(key.uid) == freqTimeMap->end()) { - std::vector<std::vector<uint64_t>> v; + (*freqTimeMap)[key.uid].resize(gNPolicies); for (uint32_t i = 0; i < gNPolicies; ++i) { - std::vector<uint64_t> v2(gPolicyFreqs[i].size(), 0); - v.emplace_back(v2); + (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0); } - (*freqTimeMap)[key.uid] = v; } for (size_t policy = 0; policy < gNPolicies; ++policy) { for (const auto &cpu : gPolicyCpus[policy]) { - uint32_t cpuTime = val.ar[cpu]; - if (cpuTime == 0) continue; auto freqIdx = policyFreqIdxs[policy][key.freq]; - (*freqTimeMap)[key.uid][policy][freqIdx] += cpuTime; + (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu]; } } return android::netdutils::status::ok; diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h index 0205452766..9f6103ed9b 100644 --- a/libs/cputimeinstate/cputimeinstate.h +++ b/libs/cputimeinstate/cputimeinstate.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + #include <unordered_map> #include <vector> diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp index d940752396..0571dccfdc 100644 --- a/libs/graphicsenv/Android.bp +++ b/libs/graphicsenv/Android.bp @@ -16,6 +16,7 @@ cc_library_shared { name: "libgraphicsenv", srcs: [ + "GpuStatsInfo.cpp", "GraphicsEnv.cpp", "IGpuService.cpp" ], diff --git a/libs/graphicsenv/GpuStatsInfo.cpp b/libs/graphicsenv/GpuStatsInfo.cpp new file mode 100644 index 0000000000..0fa0d9e32c --- /dev/null +++ b/libs/graphicsenv/GpuStatsInfo.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <inttypes.h> + +#include <android-base/stringprintf.h> +#include <binder/Parcel.h> +#include <graphicsenv/GpuStatsInfo.h> + +namespace android { + +using base::StringAppendF; + +status_t GpuStatsGlobalInfo::writeToParcel(Parcel* parcel) const { + status_t status; + if ((status = parcel->writeUtf8AsUtf16(driverPackageName)) != OK) return status; + if ((status = parcel->writeUtf8AsUtf16(driverVersionName)) != OK) return status; + if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status; + if ((status = parcel->writeInt64(driverBuildTime)) != OK) return status; + if ((status = parcel->writeInt32(glLoadingCount)) != OK) return status; + if ((status = parcel->writeInt32(glLoadingFailureCount)) != OK) return status; + if ((status = parcel->writeInt32(vkLoadingCount)) != OK) return status; + if ((status = parcel->writeInt32(vkLoadingFailureCount)) != OK) return status; + return OK; +} + +status_t GpuStatsGlobalInfo::readFromParcel(const Parcel* parcel) { + status_t status; + if ((status = parcel->readUtf8FromUtf16(&driverPackageName)) != OK) return status; + if ((status = parcel->readUtf8FromUtf16(&driverVersionName)) != OK) return status; + if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status; + if ((status = parcel->readInt64(&driverBuildTime)) != OK) return status; + if ((status = parcel->readInt32(&glLoadingCount)) != OK) return status; + if ((status = parcel->readInt32(&glLoadingFailureCount)) != OK) return status; + if ((status = parcel->readInt32(&vkLoadingCount)) != OK) return status; + if ((status = parcel->readInt32(&vkLoadingFailureCount)) != OK) return status; + return OK; +} + +std::string GpuStatsGlobalInfo::toString() const { + std::string result; + StringAppendF(&result, "driverPackageName = %s\n", driverPackageName.c_str()); + StringAppendF(&result, "driverVersionName = %s\n", driverVersionName.c_str()); + StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode); + StringAppendF(&result, "driverBuildTime = %" PRId64 "\n", driverBuildTime); + StringAppendF(&result, "glLoadingCount = %d\n", glLoadingCount); + StringAppendF(&result, "glLoadingFailureCount = %d\n", glLoadingFailureCount); + StringAppendF(&result, "vkLoadingCount = %d\n", vkLoadingCount); + StringAppendF(&result, "vkLoadingFailureCount = %d\n", vkLoadingFailureCount); + return result; +} + +status_t GpuStatsAppInfo::writeToParcel(Parcel* parcel) const { + status_t status; + if ((status = parcel->writeUtf8AsUtf16(appPackageName)) != OK) return status; + if ((status = parcel->writeUint64(driverVersionCode)) != OK) return status; + if ((status = parcel->writeInt64Vector(glDriverLoadingTime)) != OK) return status; + if ((status = parcel->writeInt64Vector(vkDriverLoadingTime)) != OK) return status; + return OK; +} + +status_t GpuStatsAppInfo::readFromParcel(const Parcel* parcel) { + status_t status; + if ((status = parcel->readUtf8FromUtf16(&appPackageName)) != OK) return status; + if ((status = parcel->readUint64(&driverVersionCode)) != OK) return status; + if ((status = parcel->readInt64Vector(&glDriverLoadingTime)) != OK) return status; + if ((status = parcel->readInt64Vector(&vkDriverLoadingTime)) != OK) return status; + return OK; +} + +std::string GpuStatsAppInfo::toString() const { + std::string result; + StringAppendF(&result, "appPackageName = %s\n", appPackageName.c_str()); + StringAppendF(&result, "driverVersionCode = %" PRIu64 "\n", driverVersionCode); + result.append("glDriverLoadingTime:"); + for (int32_t loadingTime : glDriverLoadingTime) { + StringAppendF(&result, " %d", loadingTime); + } + result.append("\n"); + result.append("vkDriverLoadingTime:"); + for (int32_t loadingTime : vkDriverLoadingTime) { + StringAppendF(&result, " %d", loadingTime); + } + result.append("\n"); + return result; +} + +} // namespace android diff --git a/libs/graphicsenv/GraphicsEnv.cpp b/libs/graphicsenv/GraphicsEnv.cpp index 75fe2d31df..13c0d876c0 100644 --- a/libs/graphicsenv/GraphicsEnv.cpp +++ b/libs/graphicsenv/GraphicsEnv.cpp @@ -146,38 +146,119 @@ int GraphicsEnv::getCanLoadSystemLibraries() { return 0; } -void GraphicsEnv::setDriverPath(const std::string path) { - if (!mDriverPath.empty()) { - ALOGV("ignoring attempt to change driver path from '%s' to '%s'", mDriverPath.c_str(), - path.c_str()); +void GraphicsEnv::setDriverPathAndSphalLibraries(const std::string path, + const std::string sphalLibraries) { + if (!mDriverPath.empty() || !mSphalLibraries.empty()) { + ALOGV("ignoring attempt to change driver path from '%s' to '%s' or change sphal libraries " + "from '%s' to '%s'", + mDriverPath.c_str(), path.c_str(), mSphalLibraries.c_str(), sphalLibraries.c_str()); return; } - ALOGV("setting driver path to '%s'", path.c_str()); + ALOGV("setting driver path to '%s' and sphal libraries to '%s'", path.c_str(), + sphalLibraries.c_str()); mDriverPath = path; + mSphalLibraries = sphalLibraries; } void GraphicsEnv::setGpuStats(const std::string& driverPackageName, - const std::string& driverVersionName, - const uint64_t driverVersionCode, const std::string& appPackageName) { + const std::string& driverVersionName, uint64_t driverVersionCode, + int64_t driverBuildTime, const std::string& appPackageName) { ATRACE_CALL(); + std::lock_guard<std::mutex> lock(mStatsLock); ALOGV("setGpuStats:\n" "\tdriverPackageName[%s]\n" "\tdriverVersionName[%s]\n" - "\tdriverVersionCode[%llu]\n" + "\tdriverVersionCode[%" PRIu64 "]\n" + "\tdriverBuildTime[%" PRId64 "]\n" "\tappPackageName[%s]\n", - driverPackageName.c_str(), driverVersionName.c_str(), - (unsigned long long)driverVersionCode, appPackageName.c_str()); - - mGpuStats = { - .driverPackageName = driverPackageName, - .driverVersionName = driverVersionName, - .driverVersionCode = driverVersionCode, - .appPackageName = appPackageName, - }; + driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime, + appPackageName.c_str()); + + mGpuStats.driverPackageName = driverPackageName; + mGpuStats.driverVersionName = driverVersionName; + mGpuStats.driverVersionCode = driverVersionCode; + mGpuStats.driverBuildTime = driverBuildTime; + mGpuStats.appPackageName = appPackageName; +} + +void GraphicsEnv::setDriverToLoad(GraphicsEnv::Driver driver) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mStatsLock); + switch (driver) { + case GraphicsEnv::Driver::GL: + case GraphicsEnv::Driver::GL_UPDATED: + case GraphicsEnv::Driver::ANGLE: { + if (mGpuStats.glDriverToLoad == GraphicsEnv::Driver::NONE) { + mGpuStats.glDriverToLoad = driver; + break; + } + + if (mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) { + mGpuStats.glDriverFallback = driver; + } + break; + } + case Driver::VULKAN: + case Driver::VULKAN_UPDATED: { + if (mGpuStats.vkDriverToLoad == GraphicsEnv::Driver::NONE) { + mGpuStats.vkDriverToLoad = driver; + break; + } + + if (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE) { + mGpuStats.vkDriverFallback = driver; + } + break; + } + default: + break; + } } -void GraphicsEnv::sendGpuStats() { +void GraphicsEnv::setDriverLoaded(GraphicsEnv::Api api, bool isLoaded, int64_t driverLoadingTime) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mStatsLock); + GraphicsEnv::Driver driver = GraphicsEnv::Driver::NONE; + bool isIntendedDriverLoaded = false; + if (api == GraphicsEnv::Api::API_GL) { + driver = mGpuStats.glDriverToLoad; + isIntendedDriverLoaded = isLoaded && + ((mGpuStats.glDriverFallback == GraphicsEnv::Driver::NONE) || + (mGpuStats.glDriverToLoad == mGpuStats.glDriverFallback)); + } else { + driver = mGpuStats.vkDriverToLoad; + isIntendedDriverLoaded = + isLoaded && (mGpuStats.vkDriverFallback == GraphicsEnv::Driver::NONE); + } + + sendGpuStatsLocked(driver, isIntendedDriverLoaded, driverLoadingTime); +} + +void GraphicsEnv::clearDriverLoadingInfo(GraphicsEnv::Api api) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mStatsLock); + if (api == GraphicsEnv::Api::API_GL) { + mGpuStats.glDriverToLoad = GraphicsEnv::Driver::NONE; + mGpuStats.glDriverFallback = GraphicsEnv::Driver::NONE; + } +} + +static sp<IGpuService> getGpuService() { + const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu")); + if (!binder) { + ALOGE("Failed to get gpu service"); + return nullptr; + } + + return interface_cast<IGpuService>(binder); +} + +void GraphicsEnv::sendGpuStatsLocked(GraphicsEnv::Driver driver, bool isDriverLoaded, + int64_t driverLoadingTime) { ATRACE_CALL(); // Do not sendGpuStats for those skipping the GraphicsEnvironment setup @@ -186,21 +267,23 @@ void GraphicsEnv::sendGpuStats() { ALOGV("sendGpuStats:\n" "\tdriverPackageName[%s]\n" "\tdriverVersionName[%s]\n" - "\tdriverVersionCode[%llu]\n" - "\tappPackageName[%s]\n", + "\tdriverVersionCode[%" PRIu64 "]\n" + "\tdriverBuildTime[%" PRId64 "]\n" + "\tappPackageName[%s]\n" + "\tdriver[%d]\n" + "\tisDriverLoaded[%d]\n" + "\tdriverLoadingTime[%" PRId64 "]", mGpuStats.driverPackageName.c_str(), mGpuStats.driverVersionName.c_str(), - (unsigned long long)mGpuStats.driverVersionCode, mGpuStats.appPackageName.c_str()); - - const sp<IBinder> binder = defaultServiceManager()->checkService(String16("gpu")); - if (!binder) { - ALOGE("Failed to get gpu service for [%s]", mGpuStats.appPackageName.c_str()); - return; + mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, mGpuStats.appPackageName.c_str(), + static_cast<int32_t>(driver), isDriverLoaded, driverLoadingTime); + + const sp<IGpuService> gpuService = getGpuService(); + if (gpuService) { + gpuService->setGpuStats(mGpuStats.driverPackageName, mGpuStats.driverVersionName, + mGpuStats.driverVersionCode, mGpuStats.driverBuildTime, + mGpuStats.appPackageName, driver, isDriverLoaded, + driverLoadingTime); } - - interface_cast<IGpuService>(binder)->setGpuStats(mGpuStats.driverPackageName, - mGpuStats.driverVersionName, - mGpuStats.driverVersionCode, - mGpuStats.appPackageName); } void* GraphicsEnv::loadLibrary(std::string name) { @@ -457,6 +540,23 @@ android_namespace_t* GraphicsEnv::getDriverNamespace() { mDriverNamespace = nullptr; return; } + + if (mSphalLibraries.empty()) return; + + // Make additional libraries in sphal to be accessible + auto sphalNamespace = android_get_exported_namespace("sphal"); + if (!sphalNamespace) { + ALOGE("Depend on these libraries[%s] in sphal, but failed to get sphal namespace", + mSphalLibraries.c_str()); + mDriverNamespace = nullptr; + return; + } + + if (!android_link_namespaces(mDriverNamespace, sphalNamespace, mSphalLibraries.c_str())) { + ALOGE("Failed to link sphal namespace[%s]", dlerror()); + mDriverNamespace = nullptr; + return; + } }); return mDriverNamespace; diff --git a/libs/graphicsenv/IGpuService.cpp b/libs/graphicsenv/IGpuService.cpp index 762a27b799..1dc1c0e4e9 100644 --- a/libs/graphicsenv/IGpuService.cpp +++ b/libs/graphicsenv/IGpuService.cpp @@ -28,17 +28,67 @@ public: explicit BpGpuService(const sp<IBinder>& impl) : BpInterface<IGpuService>(impl) {} virtual void setGpuStats(const std::string& driverPackageName, - const std::string& driverVersionName, const uint64_t driverVersionCode, - const std::string& appPackageName) { + const std::string& driverVersionName, uint64_t driverVersionCode, + int64_t driverBuildTime, const std::string& appPackageName, + GraphicsEnv::Driver driver, bool isDriverLoaded, + int64_t driverLoadingTime) { Parcel data, reply; data.writeInterfaceToken(IGpuService::getInterfaceDescriptor()); data.writeUtf8AsUtf16(driverPackageName); data.writeUtf8AsUtf16(driverVersionName); data.writeUint64(driverVersionCode); + data.writeInt64(driverBuildTime); data.writeUtf8AsUtf16(appPackageName); + data.writeInt32(static_cast<int32_t>(driver)); + data.writeBool(isDriverLoaded); + data.writeInt64(driverLoadingTime); - remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply); + remote()->transact(BnGpuService::SET_GPU_STATS, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual status_t getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* 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_GLOBAL_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); + } + + 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); } }; @@ -62,10 +112,50 @@ status_t BnGpuService::onTransact(uint32_t code, const Parcel& data, Parcel* rep uint64_t driverVersionCode; if ((status = data.readUint64(&driverVersionCode)) != OK) return status; + int64_t driverBuildTime; + if ((status = data.readInt64(&driverBuildTime)) != OK) return status; + std::string appPackageName; if ((status = data.readUtf8FromUtf16(&appPackageName)) != OK) return status; - setGpuStats(driverPackageName, driverVersionName, driverVersionCode, appPackageName); + int32_t driver; + if ((status = data.readInt32(&driver)) != OK) return status; + + bool isDriverLoaded; + if ((status = data.readBool(&isDriverLoaded)) != OK) return status; + + int64_t driverLoadingTime; + if ((status = data.readInt64(&driverLoadingTime)) != OK) return status; + + setGpuStats(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime, + appPackageName, static_cast<GraphicsEnv::Driver>(driver), isDriverLoaded, + driverLoadingTime); + + return OK; + } + case GET_GPU_STATS_GLOBAL_INFO: { + CHECK_INTERFACE(IGpuService, data, reply); + + std::vector<GpuStatsGlobalInfo> stats; + const status_t result = getGpuStatsGlobalInfo(&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 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; } diff --git a/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h new file mode 100644 index 0000000000..a92ca70493 --- /dev/null +++ b/libs/graphicsenv/include/graphicsenv/GpuStatsInfo.h @@ -0,0 +1,68 @@ +/* + * 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 <vector> + +#include <binder/Parcelable.h> + +namespace android { + +/* + * class for transporting gpu global stats from GpuService to authorized + * recipents. This class is intended to be a data container. + */ +class GpuStatsGlobalInfo : public Parcelable { +public: + GpuStatsGlobalInfo() = default; + GpuStatsGlobalInfo(const GpuStatsGlobalInfo&) = default; + virtual ~GpuStatsGlobalInfo() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + std::string toString() const; + + std::string driverPackageName = ""; + std::string driverVersionName = ""; + uint64_t driverVersionCode = 0; + int64_t driverBuildTime = 0; + int32_t glLoadingCount = 0; + int32_t glLoadingFailureCount = 0; + int32_t vkLoadingCount = 0; + int32_t vkLoadingFailureCount = 0; +}; + +/* + * class for transporting gpu app stats from GpuService to authorized recipents. + * This class is intended to be a data container. + */ +class GpuStatsAppInfo : public Parcelable { +public: + GpuStatsAppInfo() = default; + GpuStatsAppInfo(const GpuStatsAppInfo&) = default; + virtual ~GpuStatsAppInfo() = default; + virtual status_t writeToParcel(Parcel* parcel) const; + virtual status_t readFromParcel(const Parcel* parcel); + std::string toString() const; + + std::string appPackageName = ""; + uint64_t driverVersionCode = 0; + std::vector<int64_t> glDriverLoadingTime = {}; + std::vector<int64_t> vkDriverLoadingTime = {}; +}; + +} // namespace android diff --git a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h index c4482b7b63..cb4239f775 100644 --- a/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h +++ b/libs/graphicsenv/include/graphicsenv/GraphicsEnv.h @@ -28,11 +28,43 @@ namespace android { struct NativeLoaderNamespace; class GraphicsEnv { +public: + enum Api { + API_GL = 0, + API_VK = 1, + }; + + enum Driver { + NONE = 0, + GL = 1, + GL_UPDATED = 2, + VULKAN = 3, + VULKAN_UPDATED = 4, + ANGLE = 5, + }; + +private: struct GpuStats { std::string driverPackageName; std::string driverVersionName; uint64_t driverVersionCode; + int64_t driverBuildTime; std::string appPackageName; + Driver glDriverToLoad; + Driver glDriverFallback; + Driver vkDriverToLoad; + Driver vkDriverFallback; + + GpuStats() + : driverPackageName(""), + driverVersionName(""), + driverVersionCode(0), + driverBuildTime(0), + appPackageName(""), + glDriverToLoad(Driver::NONE), + glDriverFallback(Driver::NONE), + vkDriverToLoad(Driver::NONE), + vkDriverFallback(Driver::NONE) {} }; public: @@ -45,11 +77,18 @@ public: // (drivers must be stored uncompressed and page aligned); such elements // in the search path must have a '!' after the zip filename, e.g. // /data/app/com.example.driver/base.apk!/lib/arm64-v8a - void setDriverPath(const std::string path); + // Also set additional required sphal libraries to the linker for loading + // graphics drivers. The string is a list of libraries separated by ':', + // which is required by android_link_namespaces. + void setDriverPathAndSphalLibraries(const std::string path, const std::string sphalLibraries); android_namespace_t* getDriverNamespace(); void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, - const uint64_t versionCode, const std::string& appPackageName); - void sendGpuStats(); + uint64_t versionCode, int64_t driverBuildTime, + const std::string& appPackageName); + void setDriverToLoad(Driver driver); + void setDriverLoaded(Api api, bool isDriverLoaded, int64_t driverLoadingTime); + void clearDriverLoadingInfo(Api api); + void sendGpuStatsLocked(Driver driver, bool isDriverLoaded, int64_t driverLoadingTime); bool shouldUseAngle(std::string appName); bool shouldUseAngle(); @@ -82,6 +121,8 @@ private: GraphicsEnv() = default; std::string mDriverPath; + std::string mSphalLibraries; + std::mutex mStatsLock; GpuStats mGpuStats; std::string mAnglePath; std::string mAngleAppName; diff --git a/libs/graphicsenv/include/graphicsenv/IGpuService.h b/libs/graphicsenv/include/graphicsenv/IGpuService.h index 1e74d607d1..ac022b5d65 100644 --- a/libs/graphicsenv/include/graphicsenv/IGpuService.h +++ b/libs/graphicsenv/include/graphicsenv/IGpuService.h @@ -16,10 +16,12 @@ #pragma once +#include <vector> + #include <binder/IInterface.h> #include <cutils/compiler.h> - -#include <vector> +#include <graphicsenv/GpuStatsInfo.h> +#include <graphicsenv/GraphicsEnv.h> namespace android { @@ -29,18 +31,28 @@ namespace android { */ class IGpuService : public IInterface { public: - DECLARE_META_INTERFACE(GpuService); + DECLARE_META_INTERFACE(GpuService) // set GPU stats from GraphicsEnvironment. virtual void setGpuStats(const std::string& driverPackageName, - const std::string& driverVersionName, const uint64_t driverVersionCode, - const std::string& appPackageName) = 0; + const std::string& driverVersionName, uint64_t driverVersionCode, + int64_t driverBuildTime, const std::string& appPackageName, + GraphicsEnv::Driver driver, bool isDriverLoaded, + int64_t driverLoadingTime) = 0; + + // 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> { public: enum IGpuServiceTag { SET_GPU_STATS = IBinder::FIRST_CALL_TRANSACTION, + GET_GPU_STATS_GLOBAL_INFO, + GET_GPU_STATS_APP_INFO, // Always append new enum to the end. }; diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp index 0510492803..4c2e6532ff 100644 --- a/libs/gui/Android.bp +++ b/libs/gui/Android.bp @@ -31,44 +31,7 @@ cc_library_shared { "-Werror", ], cppflags: [ - "-Weverything", - - // The static constructors and destructors in this library have not been noted to - // introduce significant overheads - "-Wno-exit-time-destructors", - "-Wno-global-constructors", - - // We only care about compiling as C++14 - "-Wno-c++98-compat-pedantic", - - // We don't need to enumerate every case in a switch as long as a default case - // is present - "-Wno-switch-enum", - - // Allow calling variadic macros without a __VA_ARGS__ list - "-Wno-gnu-zero-variadic-macro-arguments", - - // Don't warn about struct padding - "-Wno-padded", - - // We are aware of the risks inherent in comparing floats for equality - "-Wno-float-equal", - - // Pure abstract classes trigger this warning - "-Wno-weak-vtables", - - // Allow four-character integer literals - "-Wno-four-char-constants", - - // Allow documentation warnings - "-Wno-documentation", - - // Allow implicit instantiation for templated class function - "-Wno-undefined-func-template", - - // Allow explicitly marking struct as packed even when unnecessary - "-Wno-packed", - + "-Wextra", "-DDEBUG_ONLY_CODE=0", ], @@ -91,6 +54,7 @@ cc_library_shared { "BufferQueueConsumer.cpp", "BufferQueueCore.cpp", "BufferQueueProducer.cpp", + "BufferQueueThreadState.cpp", "BufferSlot.cpp", "ConsumerBase.cpp", "CpuConsumer.cpp", @@ -120,35 +84,48 @@ cc_library_shared { "view/Surface.cpp", "bufferqueue/1.0/B2HProducerListener.cpp", "bufferqueue/1.0/H2BGraphicBufferProducer.cpp", + "bufferqueue/1.0/H2BProducerListener.cpp", + "bufferqueue/2.0/B2HGraphicBufferProducer.cpp", + "bufferqueue/2.0/B2HProducerListener.cpp", + "bufferqueue/2.0/H2BGraphicBufferProducer.cpp", + "bufferqueue/2.0/H2BProducerListener.cpp", + "bufferqueue/2.0/types.cpp", ], shared_libs: [ "android.frameworks.bufferhub@1.0", + "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hidl.token@1.0-utils", "libbase", - "libsync", "libbinder", "libbufferhub", "libbufferhubqueue", // TODO(b/70046255): Remove this once BufferHub is integrated into libgui. - "libpdx_default_transport", "libcutils", "libEGL", "libGLESv2", - "libui", - "libutils", - "libnativewindow", - "liblog", - "libinput", "libhidlbase", "libhidltransport", - "android.hidl.token@1.0-utils", - "android.hardware.graphics.bufferqueue@1.0", + "libhwbinder", + "libinput", + "liblog", + "libnativewindow", + "libpdx_default_transport", + "libsync", + "libui", + "libutils", + "libvndksupport", ], // bufferhub is not used when building libgui for vendors target: { vendor: { - cflags: ["-DNO_BUFFERHUB", "-DNO_INPUT"], + cflags: [ + "-DNO_BUFFERHUB", + "-DNO_INPUT", + ], exclude_srcs: [ "BufferHubConsumer.cpp", "BufferHubProducer.cpp", @@ -157,16 +134,16 @@ cc_library_shared { "android.frameworks.bufferhub@1.0", "libbufferhub", "libbufferhubqueue", + "libinput", "libpdx_default_transport", - "libinput" ], }, }, header_libs: [ "libdvr_headers", - "libnativebase_headers", "libgui_headers", + "libnativebase_headers", "libpdx_headers", ], @@ -175,9 +152,11 @@ cc_library_shared { "libEGL", "libnativewindow", "libui", - "android.hidl.token@1.0-utils", "android.hardware.graphics.bufferqueue@1.0", + "android.hardware.graphics.bufferqueue@2.0", "android.hardware.graphics.common@1.1", + "android.hardware.graphics.common@1.2", + "android.hidl.token@1.0-utils", ], export_header_lib_headers: [ diff --git a/libs/gui/BufferQueueConsumer.cpp b/libs/gui/BufferQueueConsumer.cpp index 3837c3e11a..f2d5c8edd8 100644 --- a/libs/gui/BufferQueueConsumer.cpp +++ b/libs/gui/BufferQueueConsumer.cpp @@ -34,9 +34,10 @@ #include <gui/IConsumerListener.h> #include <gui/IProducerListener.h> -#include <binder/IPCThreadState.h> +#include <private/gui/BufferQueueThreadState.h> #ifndef __ANDROID_VNDK__ #include <binder/PermissionCache.h> +#include <vndksupport/linker.h> #endif #include <system/window.h> @@ -758,19 +759,29 @@ status_t BufferQueueConsumer::dumpState(const String8& prefix, String8* outResul return savedErrno ? -savedErrno : UNKNOWN_ERROR; } - const IPCThreadState* ipc = IPCThreadState::self(); - const uid_t uid = ipc->getCallingUid(); + bool denied = false; + const uid_t uid = BufferQueueThreadState::getCallingUid(); #ifndef __ANDROID_VNDK__ // permission check can't be done for vendors as vendors have no access to - // the PermissionController - const pid_t pid = ipc->getCallingPid(); - if ((uid != shellUid) && - !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { - outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " - "from pid=%d, uid=%d\n", pid, uid); + // the PermissionController. We need to do a runtime check as well, since + // the system variant of libgui can be loaded in a vendor process. For eg: + // if a HAL uses an llndk library that depends on libgui (libmediandk etc). + if (!android_is_in_vendor_process()) { + const pid_t pid = BufferQueueThreadState::getCallingPid(); + if ((uid != shellUid) && + !PermissionCache::checkPermission(String16("android.permission.DUMP"), pid, uid)) { + outResult->appendFormat("Permission Denial: can't dump BufferQueueConsumer " + "from pid=%d, uid=%d\n", + pid, uid); + denied = true; + } + } #else if (uid != shellUid) { + denied = true; + } #endif + if (denied) { android_errorWriteWithInfoLog(0x534e4554, "27046057", static_cast<int32_t>(uid), nullptr, 0); return PERMISSION_DENIED; diff --git a/libs/gui/BufferQueueProducer.cpp b/libs/gui/BufferQueueProducer.cpp index 5e250a4185..a462b0362f 100644 --- a/libs/gui/BufferQueueProducer.cpp +++ b/libs/gui/BufferQueueProducer.cpp @@ -35,6 +35,7 @@ #include <gui/GLConsumer.h> #include <gui/IConsumerListener.h> #include <gui/IProducerListener.h> +#include <private/gui/BufferQueueThreadState.h> #include <utils/Log.h> #include <utils/Trace.h> @@ -1210,7 +1211,7 @@ status_t BufferQueueProducer::connect(const sp<IProducerListener>& listener, status = BAD_VALUE; break; } - mCore->mConnectedPid = IPCThreadState::self()->getCallingPid(); + mCore->mConnectedPid = BufferQueueThreadState::getCallingPid(); mCore->mBufferHasBeenQueued = false; mCore->mDequeueBufferCannotBlock = false; if (mDequeueTimeout < 0) { @@ -1233,7 +1234,7 @@ status_t BufferQueueProducer::disconnect(int api, DisconnectMode mode) { Mutex::Autolock lock(mCore->mMutex); if (mode == DisconnectMode::AllLocal) { - if (IPCThreadState::self()->getCallingPid() != mCore->mConnectedPid) { + if (BufferQueueThreadState::getCallingPid() != mCore->mConnectedPid) { return NO_ERROR; } api = BufferQueueCore::CURRENTLY_CONNECTED_API; diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp new file mode 100644 index 0000000000..3b531ec752 --- /dev/null +++ b/libs/gui/BufferQueueThreadState.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <binder/IPCThreadState.h> +#include <hwbinder/IPCThreadState.h> +#include <private/gui/BufferQueueThreadState.h> +#include <unistd.h> + +namespace android { + +uid_t BufferQueueThreadState::getCallingUid() { + if (hardware::IPCThreadState::self()->isServingCall()) { + return hardware::IPCThreadState::self()->getCallingUid(); + } + return IPCThreadState::self()->getCallingUid(); +} + +pid_t BufferQueueThreadState::getCallingPid() { + if (hardware::IPCThreadState::self()->isServingCall()) { + return hardware::IPCThreadState::self()->getCallingPid(); + } + return IPCThreadState::self()->getCallingPid(); +} + +} // namespace android diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp index d997674972..9dde15dd0e 100644 --- a/libs/gui/IGraphicBufferProducer.cpp +++ b/libs/gui/IGraphicBufferProducer.cpp @@ -30,16 +30,21 @@ #ifndef NO_BUFFERHUB #include <gui/BufferHubProducer.h> #endif + +#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> #include <gui/BufferQueueDefs.h> #include <gui/IGraphicBufferProducer.h> #include <gui/IProducerListener.h> -#include <gui/bufferqueue/1.0/H2BGraphicBufferProducer.h> - namespace android { // ---------------------------------------------------------------------------- -using ::android::hardware::graphics::bufferqueue::V1_0::utils:: +using H2BGraphicBufferProducerV1_0 = + ::android::hardware::graphics::bufferqueue::V1_0::utils:: + H2BGraphicBufferProducer; +using H2BGraphicBufferProducerV2_0 = + ::android::hardware::graphics::bufferqueue::V2_0::utils:: H2BGraphicBufferProducer; enum { @@ -534,7 +539,9 @@ public: BpGraphicBufferProducer::~BpGraphicBufferProducer() {} class HpGraphicBufferProducer : public HpInterface< - BpGraphicBufferProducer, H2BGraphicBufferProducer> { + BpGraphicBufferProducer, + H2BGraphicBufferProducerV1_0, + H2BGraphicBufferProducerV2_0> { public: explicit HpGraphicBufferProducer(const sp<IBinder>& base) : PBase(base) {} @@ -651,7 +658,7 @@ public: } }; -IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer, +IMPLEMENT_HYBRID_META_INTERFACE(GraphicBufferProducer, "android.gui.IGraphicBufferProducer"); // ---------------------------------------------------------------------- diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 62abfa81c4..936063a5bd 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -15,7 +15,8 @@ */ #include <binder/Parcel.h> - +#include <gui/bufferqueue/1.0/H2BProducerListener.h> +#include <gui/bufferqueue/2.0/H2BProducerListener.h> #include <gui/IProducerListener.h> namespace android { @@ -61,7 +62,24 @@ public: // translation unit (see clang warning -Wweak-vtables) BpProducerListener::~BpProducerListener() {} -IMPLEMENT_META_INTERFACE(ProducerListener, "android.gui.IProducerListener") +class HpProducerListener : public HpInterface< + BpProducerListener, + hardware::graphics::bufferqueue::V1_0::utils::H2BProducerListener, + hardware::graphics::bufferqueue::V2_0::utils::H2BProducerListener> { +public: + explicit HpProducerListener(const sp<IBinder>& base) : PBase{base} {} + + virtual void onBufferReleased() override { + mBase->onBufferReleased(); + } + + virtual bool needsReleaseNotify() override { + return mBase->needsReleaseNotify(); + } +}; + +IMPLEMENT_HYBRID_META_INTERFACE(ProducerListener, + "android.gui.IProducerListener") status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp index f77eeb246c..247dc8d9d2 100644 --- a/libs/gui/ISurfaceComposer.cpp +++ b/libs/gui/ISurfaceComposer.cpp @@ -100,7 +100,7 @@ public: const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ISurfaceComposer::Rotation rotation) { + ISurfaceComposer::Rotation rotation, bool captureSecureLayers) { Parcel data, reply; data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); data.writeStrongBinder(display); @@ -111,6 +111,7 @@ public: data.writeUint32(reqHeight); data.writeInt32(static_cast<int32_t>(useIdentityTransform)); data.writeInt32(static_cast<int32_t>(rotation)); + data.writeInt32(static_cast<int32_t>(captureSecureLayers)); status_t result = remote()->transact(BnSurfaceComposer::CAPTURE_SCREEN, data, &reply); if (result != NO_ERROR) { ALOGE("captureScreen failed to transact: %d", result); @@ -791,21 +792,127 @@ public: Parcel data, reply; status_t error = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to write interface token"); + ALOGE("removeRegionSamplingListener: Failed to write interface token"); return error; } error = data.writeStrongBinder(IInterface::asBinder(listener)); if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to write listener"); + ALOGE("removeRegionSamplingListener: Failed to write listener"); return error; } error = remote()->transact(BnSurfaceComposer::REMOVE_REGION_SAMPLING_LISTENER, data, &reply); if (error != NO_ERROR) { - ALOGE("addRegionSamplingListener: Failed to transact"); + ALOGE("removeRegionSamplingListener: Failed to transact"); } return error; } + + virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, + const std::vector<int32_t>& allowedConfigs) { + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(displayToken); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to writeStrongBinder: %d", result); + return result; + } + result = data.writeInt32Vector(allowedConfigs); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to writeInt32Vector: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::SET_ALLOWED_DISPLAY_CONFIGS, data, &reply); + if (result != NO_ERROR) { + ALOGE("setAllowedDisplayConfigs failed to transact: %d", result); + return result; + } + return reply.readInt32(); + } + + virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) { + if (!outAllowedConfigs) return BAD_VALUE; + Parcel data, reply; + status_t result = data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor()); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to writeInterfaceToken: %d", result); + return result; + } + result = data.writeStrongBinder(displayToken); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to writeStrongBinder: %d", result); + return result; + } + result = remote()->transact(BnSurfaceComposer::GET_ALLOWED_DISPLAY_CONFIGS, data, &reply); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to transact: %d", result); + return result; + } + result = reply.readInt32Vector(outAllowedConfigs); + if (result != NO_ERROR) { + ALOGE("getAllowedDisplayConfigs failed to readInt32Vector: %d", result); + return result; + } + return reply.readInt32(); + } + + virtual status_t 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 @@ -884,10 +991,11 @@ status_t BnSurfaceComposer::onTransact( uint32_t reqHeight = data.readUint32(); bool useIdentityTransform = static_cast<bool>(data.readInt32()); int32_t rotation = data.readInt32(); + bool captureSecureLayers = static_cast<bool>(data.readInt32()); status_t res = captureScreen(display, &outBuffer, reqDataspace, reqPixelFormat, sourceCrop, reqWidth, reqHeight, useIdentityTransform, - static_cast<ISurfaceComposer::Rotation>(rotation)); + static_cast<ISurfaceComposer::Rotation>(rotation), captureSecureLayers); reply->writeInt32(res); if (res == NO_ERROR) { reply->write(*outBuffer); @@ -1317,6 +1425,53 @@ status_t BnSurfaceComposer::onTransact( } return removeRegionSamplingListener(listener); } + case SET_ALLOWED_DISPLAY_CONFIGS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken = data.readStrongBinder(); + std::vector<int32_t> allowedConfigs; + data.readInt32Vector(&allowedConfigs); + status_t result = setAllowedDisplayConfigs(displayToken, allowedConfigs); + reply->writeInt32(result); + return result; + } + case GET_ALLOWED_DISPLAY_CONFIGS: { + CHECK_INTERFACE(ISurfaceComposer, data, reply); + sp<IBinder> displayToken = data.readStrongBinder(); + std::vector<int32_t> allowedConfigs; + status_t result = getAllowedDisplayConfigs(displayToken, &allowedConfigs); + reply->writeInt32Vector(allowedConfigs); + reply->writeInt32(result); + return result; + } + case 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/LayerMetadata.cpp b/libs/gui/LayerMetadata.cpp index 745433a605..04d2871c77 100644 --- a/libs/gui/LayerMetadata.cpp +++ b/libs/gui/LayerMetadata.cpp @@ -31,10 +31,23 @@ LayerMetadata::LayerMetadata(const LayerMetadata& other) = default; LayerMetadata::LayerMetadata(LayerMetadata&& other) = default; -void LayerMetadata::merge(const LayerMetadata& other) { +bool LayerMetadata::merge(const LayerMetadata& other, bool eraseEmpty) { + bool changed = false; for (const auto& entry : other.mMap) { - mMap[entry.first] = entry.second; + auto it = mMap.find(entry.first); + if (it != mMap.cend() && it->second != entry.second) { + if (eraseEmpty && entry.second.empty()) { + mMap.erase(it); + } else { + it->second = entry.second; + } + changed = true; + } else if (it == mMap.cend() && !entry.second.empty()) { + mMap[entry.first] = entry.second; + changed = true; + } } + return changed; } status_t LayerMetadata::writeToParcel(Parcel* parcel) const { diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 206bc30359..84ba64494f 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -100,6 +100,7 @@ status_t layer_state_t::write(Parcel& output) const output.writeFloat(bgColorAlpha); output.writeUint32(static_cast<uint32_t>(bgColorDataspace)); + output.writeBool(colorSpaceAgnostic); return NO_ERROR; } @@ -177,6 +178,7 @@ status_t layer_state_t::read(const Parcel& input) bgColorAlpha = input.readFloat(); bgColorDataspace = static_cast<ui::Dataspace>(input.readUint32()); + colorSpaceAgnostic = input.readBool(); return NO_ERROR; } @@ -414,10 +416,13 @@ void InputWindowCommands::merge(const InputWindowCommands& other) { .insert(transferTouchFocusCommands.end(), std::make_move_iterator(other.transferTouchFocusCommands.begin()), std::make_move_iterator(other.transferTouchFocusCommands.end())); + + syncInputWindows |= other.syncInputWindows; } void InputWindowCommands::clear() { transferTouchFocusCommands.clear(); + syncInputWindows = false; } void InputWindowCommands::write(Parcel& output) const { @@ -426,6 +431,8 @@ void InputWindowCommands::write(Parcel& output) const { output.writeStrongBinder(transferTouchFocusCommand.fromToken); output.writeStrongBinder(transferTouchFocusCommand.toToken); } + + output.writeBool(syncInputWindows); } void InputWindowCommands::read(const Parcel& input) { @@ -437,6 +444,8 @@ void InputWindowCommands::read(const Parcel& input) { transferTouchFocusCommand.toToken = input.readStrongBinder(); transferTouchFocusCommands.emplace_back(transferTouchFocusCommand); } + + syncInputWindows = input.readBool(); } }; // namespace android diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 3affa23482..93b41914bf 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 @@ int Surface::setSwapInterval(int interval) { 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 @@ int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) { // 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 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { // The consumer doesn't send it back to prevent us from having two // file descriptors of the same fence. mFrameEventHistory->updateAcquireFence(mNextFrameNumber, - std::make_shared<FenceTime>(std::move(fence))); + std::make_shared<FenceTime>(fence)); // Cache timestamps of signaled fences so we can close their file // descriptors. @@ -761,6 +847,11 @@ int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) { 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 bf2a03d980..b0e827536d 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -189,7 +189,7 @@ void TransactionCompletedListener::addSurfaceControlToCallbacks( } void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) { - std::lock_guard lock(mMutex); + std::lock_guard<std::mutex> lock(mMutex); /* This listener knows all the sp<IBinder> to sp<SurfaceControl> for all its registered * callbackIds, except for when Transactions are merged together. This probably cannot be @@ -242,7 +242,7 @@ public: } int32_t getId(const sp<GraphicBuffer>& buffer) { - std::lock_guard lock(mMutex); + std::lock_guard<std::mutex> lock(mMutex); auto itr = mBuffers.find(buffer); if (itr == mBuffers.end()) { @@ -253,7 +253,7 @@ public: } int32_t cache(const sp<GraphicBuffer>& buffer) { - std::lock_guard lock(mMutex); + std::lock_guard<std::mutex> lock(mMutex); int32_t bufferId = getNextAvailableId(); @@ -989,6 +989,20 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setDesir return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorSpaceAgnostic( + const sp<SurfaceControl>& sc, const bool agnostic) { + layer_state_t* s = getLayerState(sc); + if (!s) { + mStatus = BAD_INDEX; + return *this; + } + s->what |= layer_state_t::eColorSpaceAgnosticChanged; + s->colorSpaceAgnostic = agnostic; + + registerSurfaceControlForCallback(sc); + return *this; +} + SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::addTransactionCompletedCallback( TransactionCompletedCallbackTakesContext callback, void* callbackContext) { @@ -1083,6 +1097,11 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::transfer return *this; } +SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::syncInputWindows() { + mInputWindowCommands.syncInputWindows = true; + return *this; +} + #endif SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setColorTransform( @@ -1377,6 +1396,18 @@ status_t SurfaceComposerClient::setActiveConfig(const sp<IBinder>& display, int return ComposerService::getComposerService()->setActiveConfig(display, id); } +status_t SurfaceComposerClient::setAllowedDisplayConfigs( + const sp<IBinder>& displayToken, const std::vector<int32_t>& allowedConfigs) { + return ComposerService::getComposerService()->setAllowedDisplayConfigs(displayToken, + allowedConfigs); +} + +status_t SurfaceComposerClient::getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) { + return ComposerService::getComposerService()->getAllowedDisplayConfigs(displayToken, + outAllowedConfigs); +} + status_t SurfaceComposerClient::getDisplayColorModes(const sp<IBinder>& display, Vector<ColorMode>* outColorModes) { return ComposerService::getComposerService()->getDisplayColorModes(display, outColorModes); @@ -1459,23 +1490,56 @@ status_t SurfaceComposerClient::isWideColorDisplay(const sp<IBinder>& display, 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, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - uint32_t rotation, sp<GraphicBuffer>* outBuffer) { + uint32_t rotation, bool captureSecureLayers, sp<GraphicBuffer>* outBuffer) { sp<ISurfaceComposer> s(ComposerService::getComposerService()); if (s == nullptr) return NO_INIT; status_t ret = s->captureScreen(display, outBuffer, reqDataSpace, reqPixelFormat, sourceCrop, - reqWidth, reqHeight, useIdentityTransform, - static_cast<ISurfaceComposer::Rotation>(rotation)); + reqWidth, reqHeight, useIdentityTransform, + static_cast<ISurfaceComposer::Rotation>(rotation), + captureSecureLayers); if (ret != NO_ERROR) { return ret; } return ret; } +status_t ScreenshotClient::capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, sp<GraphicBuffer>* outBuffer) { + return capture(display, reqDataSpace, reqPixelFormat, sourceCrop, reqWidth, + reqHeight, useIdentityTransform, rotation, false, outBuffer); +} + status_t ScreenshotClient::captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, diff --git a/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp new file mode 100644 index 0000000000..2712f42c89 --- /dev/null +++ b/libs/gui/bufferqueue/1.0/H2BProducerListener.cpp @@ -0,0 +1,59 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "H2BProducerListener@1.0" + +#include <android-base/logging.h> + +#include <gui/bufferqueue/1.0/H2BProducerListener.h> +#include <hidl/Status.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V1_0 { +namespace utils { + +using ::android::hardware::Return; + +H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base) + : CBase{base} { +} + +void H2BProducerListener::onBufferReleased() { + if (!mBase->onBufferReleased().isOk()) { + LOG(ERROR) << "onBufferReleased: transaction failed."; + } +} + +bool H2BProducerListener::needsReleaseNotify() { + Return<bool> transResult = mBase->needsReleaseNotify(); + if (!transResult.isOk()) { + LOG(ERROR) << "needsReleaseNotify: transaction failed."; + return false; + } + return static_cast<bool>(transResult); +} + +} // namespace utils +} // namespace V1_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + diff --git a/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp new file mode 100644 index 0000000000..e0395939e9 --- /dev/null +++ b/libs/gui/bufferqueue/2.0/B2HGraphicBufferProducer.cpp @@ -0,0 +1,331 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "H2BGraphicBufferProducer@2.0" + +#include <android-base/logging.h> + +#include <android/hardware/graphics/bufferqueue/2.0/types.h> +#include <android/hardware/graphics/common/1.2/types.h> +#include <gui/bufferqueue/2.0/H2BProducerListener.h> +#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <ui/GraphicBuffer.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <vndk/hardware_buffer.h> + +namespace android { + +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// B2HGraphicBufferProducer +// ======================== + +B2HGraphicBufferProducer::B2HGraphicBufferProducer( + sp<BGraphicBufferProducer> const& base) + : mBase{base} { +} + +Return<HStatus> B2HGraphicBufferProducer::setMaxDequeuedBufferCount( + int32_t maxDequeuedBuffers) { + HStatus hStatus{}; + bool converted = b2h( + mBase->setMaxDequeuedBufferCount( + static_cast<int>(maxDequeuedBuffers)), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::requestBuffer( + int32_t slot, + requestBuffer_cb _hidl_cb) { + sp<GraphicBuffer> bBuffer; + HStatus hStatus{}; + HardwareBuffer hBuffer{}; + uint32_t hGenerationNumber{}; + bool converted = + b2h(mBase->requestBuffer( + static_cast<int>(slot), &bBuffer), + &hStatus) && + b2h(bBuffer, &hBuffer, &hGenerationNumber); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + hBuffer, hGenerationNumber); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::setAsyncMode(bool async) { + HStatus hStatus{}; + bool converted = b2h(mBase->setAsyncMode(async), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::dequeueBuffer( + DequeueBufferInput const& input, + dequeueBuffer_cb _hidl_cb) { + int bSlot{}; + sp<BFence> bFence; + HStatus hStatus{}; + DequeueBufferOutput hOutput{}; + HFenceWrapper hFenceWrapper; + bool converted = + b2h(mBase->dequeueBuffer( + &bSlot, + &bFence, + input.width, + input.height, + static_cast<PixelFormat>(input.format), + input.usage, + &hOutput.bufferAge, + nullptr /* outTimestamps */), + &hStatus, + &hOutput.bufferNeedsReallocation, + &hOutput.releaseAllBuffers) && + b2h(bFence, &hFenceWrapper); + hOutput.fence = hFenceWrapper.getHandle(); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + static_cast<int32_t>(bSlot), + hOutput); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::detachBuffer(int32_t slot) { + HStatus hStatus{}; + bool converted = b2h( + mBase->detachBuffer(static_cast<int>(slot)), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::detachNextBuffer( + detachNextBuffer_cb _hidl_cb) { + sp<GraphicBuffer> bBuffer; + sp<BFence> bFence; + HStatus hStatus{}; + HardwareBuffer hBuffer{}; + HFenceWrapper hFenceWrapper; + bool converted = + b2h(mBase->detachNextBuffer(&bBuffer, &bFence), &hStatus) && + b2h(bBuffer, &hBuffer) && + b2h(bFence, &hFenceWrapper); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + hBuffer, + hFenceWrapper.getHandle()); + return {}; +} + +Return<void> B2HGraphicBufferProducer::attachBuffer( + HardwareBuffer const& hBuffer, + uint32_t generationNumber, + attachBuffer_cb _hidl_cb) { + sp<GraphicBuffer> bBuffer; + if (!h2b(hBuffer, &bBuffer) || !bBuffer) { + _hidl_cb(HStatus::UNKNOWN_ERROR, + static_cast<int32_t>(SlotIndex::INVALID), + false); + return {}; + } + bBuffer->setGenerationNumber(generationNumber); + + int bSlot{}; + HStatus hStatus{}; + bool releaseAllBuffers{}; + bool converted = b2h( + mBase->attachBuffer(&bSlot, bBuffer), &hStatus, + nullptr /* bufferNeedsReallocation */, + &releaseAllBuffers); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + static_cast<int32_t>(bSlot), + releaseAllBuffers); + return {}; +} + +Return<void> B2HGraphicBufferProducer::queueBuffer( + int32_t slot, + QueueBufferInput const& hInput, + queueBuffer_cb _hidl_cb) { + using HOutput = QueueBufferOutput; + using BInput = BGraphicBufferProducer::QueueBufferInput; + using BOutput = BGraphicBufferProducer::QueueBufferOutput; + + BInput bInput{ + hInput.timestamp, + hInput.isAutoTimestamp, + static_cast<android_dataspace>(hInput.dataSpace), + {}, /* crop */ + 0 /* scalingMode = NATIVE_WINDOW_SCALING_MODE_FREEZE */, + static_cast<uint32_t>(hInput.transform), + {}, /* fence */ + static_cast<uint32_t>(hInput.stickyTransform), + false /* getFrameTimestamps */}; + + // Convert crop. + if (!h2b(hInput.crop, &bInput.crop)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{}); + return {}; + } + + // Convert surfaceDamage. + if (!h2b(hInput.surfaceDamage, &bInput.surfaceDamage)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{}); + return {}; + } + + // Convert fence. + if (!h2b(hInput.fence, &bInput.fence)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{}); + return {}; + } + + BOutput bOutput{}; + HStatus hStatus{}; + bool converted = b2h( + mBase->queueBuffer(static_cast<int>(slot), bInput, &bOutput), + &hStatus); + + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + HOutput{bOutput.width, + bOutput.height, + static_cast<int32_t>(bOutput.transformHint), + bOutput.numPendingBuffers, + bOutput.nextFrameNumber, + bOutput.bufferReplaced}); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::cancelBuffer( + int32_t slot, + hidl_handle const& fence) { + sp<BFence> bFence; + if (!h2b(fence.getNativeHandle(), &bFence)) { + return {HStatus::UNKNOWN_ERROR}; + } + HStatus hStatus{}; + bool converted = b2h( + mBase->cancelBuffer(static_cast<int>(slot), bFence), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<void> B2HGraphicBufferProducer::query(int32_t what, query_cb _hidl_cb) { + int value{}; + int result = mBase->query(static_cast<int>(what), &value); + _hidl_cb(static_cast<int32_t>(result), static_cast<int32_t>(value)); + return {}; +} + +Return<void> B2HGraphicBufferProducer::connect( + sp<HProducerListener> const& hListener, + HConnectionType hConnectionType, + bool producerControlledByApp, + connect_cb _hidl_cb) { + using BOutput = BGraphicBufferProducer::QueueBufferOutput; + using HOutput = HGraphicBufferProducer::QueueBufferOutput; + sp<BProducerListener> bListener = new H2BProducerListener(hListener); + if (!bListener) { + _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{}); + return {}; + } + int bConnectionType; + if (!h2b(hConnectionType, &bConnectionType)) { + _hidl_cb(HStatus::UNKNOWN_ERROR, HOutput{}); + return {}; + } + BOutput bOutput{}; + HStatus hStatus{}; + bool converted = b2h( + mBase->connect(bListener, + bConnectionType, + producerControlledByApp, + &bOutput), + &hStatus); + _hidl_cb(converted ? hStatus : HStatus::UNKNOWN_ERROR, + HOutput{bOutput.width, + bOutput.height, + static_cast<int32_t>(bOutput.transformHint), + bOutput.numPendingBuffers, + bOutput.nextFrameNumber, + bOutput.bufferReplaced}); + return {}; +} + +Return<HStatus> B2HGraphicBufferProducer::disconnect( + HConnectionType hConnectionType) { + int bConnectionType; + if (!h2b(hConnectionType, &bConnectionType)) { + return {HStatus::UNKNOWN_ERROR}; + } + HStatus hStatus{}; + bool converted = b2h(mBase->disconnect(bConnectionType), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<HStatus> B2HGraphicBufferProducer::allocateBuffers( + uint32_t width, uint32_t height, + uint32_t format, uint64_t usage) { + mBase->allocateBuffers( + width, height, static_cast<PixelFormat>(format), usage); + return {HStatus::OK}; +} + +Return<HStatus> B2HGraphicBufferProducer::allowAllocation(bool allow) { + HStatus hStatus{}; + bool converted = b2h(mBase->allowAllocation(allow), &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<HStatus> B2HGraphicBufferProducer::setGenerationNumber( + uint32_t generationNumber) { + HStatus hStatus{}; + bool converted = b2h( + mBase->setGenerationNumber(generationNumber), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<HStatus> B2HGraphicBufferProducer::setDequeueTimeout( + int64_t timeoutNs) { + HStatus hStatus{}; + bool converted = b2h( + mBase->setDequeueTimeout(static_cast<nsecs_t>(timeoutNs)), + &hStatus); + return {converted ? hStatus : HStatus::UNKNOWN_ERROR}; +} + +Return<uint64_t> B2HGraphicBufferProducer::getUniqueId() { + uint64_t outId{}; + HStatus hStatus{}; + bool converted = b2h(mBase->getUniqueId(&outId), &hStatus); + return {converted ? outId : 0}; +} + +Return<void> B2HGraphicBufferProducer::getConsumerName( + getConsumerName_cb _hidl_cb) { + _hidl_cb(hidl_string{mBase->getConsumerName().c_str()}); + return {}; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + diff --git a/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp b/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp new file mode 100644 index 0000000000..c4c96eba50 --- /dev/null +++ b/libs/gui/bufferqueue/2.0/B2HProducerListener.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gui/bufferqueue/2.0/B2HProducerListener.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// B2HProducerListener +B2HProducerListener::B2HProducerListener(sp<BProducerListener> const& base) + : mBase{base}, + mNeedsReleaseNotify{base ? base->needsReleaseNotify() : false} { +} + +Return<void> B2HProducerListener::onBuffersReleased(uint32_t count) { + if (mNeedsReleaseNotify) { + for (; count > 0; --count) { + mBase->onBufferReleased(); + } + } + return {}; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + diff --git a/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp new file mode 100644 index 0000000000..1023ef108a --- /dev/null +++ b/libs/gui/bufferqueue/2.0/H2BGraphicBufferProducer.cpp @@ -0,0 +1,514 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "H2BGraphicBufferProducer@2.0" + +#include <android-base/logging.h> + +#include <android/hardware/graphics/common/1.2/types.h> +#include <gui/bufferqueue/2.0/B2HProducerListener.h> +#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <ui/GraphicBuffer.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <vndk/hardware_buffer.h> + +namespace android { + +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// H2BGraphicBufferProducer +// ======================== + +status_t H2BGraphicBufferProducer::requestBuffer(int slot, + sp<GraphicBuffer>* bBuffer) { + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->requestBuffer(slot, + [&converted, &bStatus, bBuffer]( + HStatus hStatus, + HardwareBuffer const& hBuffer, + uint32_t generationNumber) { + converted = + h2b(hStatus, &bStatus) && + h2b(hBuffer, bBuffer); + if (*bBuffer) { + (*bBuffer)->setGenerationNumber(generationNumber); + } + }); + if (!transResult.isOk()) { + LOG(ERROR) << "requestBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "requestBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setMaxDequeuedBufferCount( + int maxDequeuedBuffers) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setMaxDequeuedBufferCount( + static_cast<int32_t>(maxDequeuedBuffers)); + if (!transResult.isOk()) { + LOG(ERROR) << "setMaxDequeuedBufferCount: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setMaxDequeuedBufferCount: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setAsyncMode(bool async) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setAsyncMode(async); + if (!transResult.isOk()) { + LOG(ERROR) << "setAsyncMode: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setAsyncMode: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::dequeueBuffer( + int* slot, sp<BFence>* fence, + uint32_t w, uint32_t h, + PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, FrameEventHistoryDelta* /* outTimestamps */) { + + using HInput = HGraphicBufferProducer::DequeueBufferInput; + HInput input{w, h, static_cast<uint32_t>(format), usage}; + + using HOutput = HGraphicBufferProducer::DequeueBufferOutput; + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->dequeueBuffer(input, + [&converted, &bStatus, slot, fence, outBufferAge] ( + HStatus hStatus, int32_t hSlot, HOutput const& hOutput) { + converted = h2b(hStatus, &bStatus); + if (!converted || bStatus != OK) { + return; + } + *slot = hSlot; + *outBufferAge = hOutput.bufferAge; + bStatus = + (hOutput.bufferNeedsReallocation ? + BUFFER_NEEDS_REALLOCATION : 0) | + (hOutput.releaseAllBuffers ? + RELEASE_ALL_BUFFERS : 0); + converted = h2b(hOutput.fence, fence); + }); + if (!transResult.isOk()) { + LOG(ERROR) << "dequeueBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "dequeueBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::detachBuffer(int slot) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->detachBuffer( + static_cast<int32_t>(slot)); + if (!transResult.isOk()) { + LOG(ERROR) << "detachBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "detachBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::detachNextBuffer( + sp<GraphicBuffer>* outBuffer, sp<BFence>* outFence) { + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->detachNextBuffer( + [&converted, &bStatus, outBuffer, outFence] ( + HStatus hStatus, + HardwareBuffer const& hBuffer, + hidl_handle const& hFence) { + converted = h2b(hStatus, &bStatus) && + h2b(hBuffer, outBuffer) && + h2b(hFence, outFence); + }); + if (!transResult.isOk()) { + LOG(ERROR) << "detachNextBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "detachNextBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::attachBuffer( + int* outSlot, sp<GraphicBuffer> const& buffer) { + HardwareBuffer hBuffer{}; + uint32_t hGenerationNumber{}; + if (!b2h(buffer, &hBuffer, &hGenerationNumber)) { + LOG(ERROR) << "attachBuffer: invalid input buffer."; + return BAD_VALUE; + } + + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->attachBuffer(hBuffer, hGenerationNumber, + [&converted, &bStatus, outSlot]( + HStatus hStatus, int32_t hSlot, bool releaseAllBuffers) { + converted = h2b(hStatus, &bStatus); + *outSlot = static_cast<int>(hSlot); + if (converted && releaseAllBuffers && bStatus == OK) { + bStatus = IGraphicBufferProducer::RELEASE_ALL_BUFFERS; + } + }); + if (!transResult.isOk()) { + LOG(ERROR) << "attachBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "attachBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::queueBuffer( + int slot, + QueueBufferInput const& input, + QueueBufferOutput* output) { + HRect hCrop{}; + (void)b2h(input.crop, &hCrop); + + using HInput = HGraphicBufferProducer::QueueBufferInput; + HInput hInput{ + input.timestamp, + static_cast<bool>(input.isAutoTimestamp), + static_cast<int32_t>(input.dataSpace), + {}, // crop + static_cast<int32_t>(input.transform), + static_cast<int32_t>(input.stickyTransform), + {}, // fence + {} // surfaceDamage + }; + + // Convert crop. + if (!b2h(input.crop, &hInput.crop)) { + LOG(ERROR) << "queueBuffer: corrupted input crop rectangle."; + return UNKNOWN_ERROR; + } + + // Convert surfaceDamage. + size_t numRects; + Rect const* rectArray = input.surfaceDamage.getArray(&numRects); + hInput.surfaceDamage.resize(numRects); + for (size_t i = 0; i < numRects; ++i) { + if (!b2h(rectArray[i], &hInput.surfaceDamage[i])) { + LOG(ERROR) << "queueBuffer: corrupted input surface damage."; + return UNKNOWN_ERROR; + } + } + + // Convert fence. + HFenceWrapper hFenceWrapper; + if (!b2h(input.fence, &hFenceWrapper)) { + LOG(ERROR) << "queueBuffer: corrupted input fence."; + return UNKNOWN_ERROR; + } + hInput.fence = hFenceWrapper.getHandle(); + + using HOutput = HGraphicBufferProducer::QueueBufferOutput; + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->queueBuffer( + static_cast<int32_t>(slot), + hInput, + [&converted, &bStatus, output]( + HStatus hStatus, + HOutput const& hOutput) { + converted = h2b(hStatus, &bStatus); + output->width = hOutput.width; + output->height = hOutput.height; + output->transformHint = + static_cast<uint32_t>(hOutput.transformHint); + output->numPendingBuffers = hOutput.numPendingBuffers; + output->nextFrameNumber = hOutput.nextFrameNumber; + output->bufferReplaced = hOutput.bufferReplaced; + }); + + if (!transResult.isOk()) { + LOG(ERROR) << "queueBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "queueBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::cancelBuffer(int slot, sp<BFence> const& fence) { + HFenceWrapper hFenceWrapper; + if (!b2h(fence, &hFenceWrapper)) { + LOG(ERROR) << "cancelBuffer: corrupted input fence."; + return UNKNOWN_ERROR; + } + status_t bStatus{}; + Return<HStatus> transResult = mBase->cancelBuffer( + static_cast<int32_t>(slot), + hFenceWrapper.getHandle()); + if (!transResult.isOk()) { + LOG(ERROR) << "cancelBuffer: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "cancelBuffer: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +int H2BGraphicBufferProducer::query(int what, int* value) { + int result{}; + Return<void> transResult = mBase->query( + static_cast<int32_t>(what), + [&result, value](int32_t r, int32_t v) { + result = static_cast<int>(r); + *value = static_cast<int>(v); + }); + if (!transResult.isOk()) { + LOG(ERROR) << "query: transaction failed."; + return FAILED_TRANSACTION; + } + return result; +} + +status_t H2BGraphicBufferProducer::connect( + sp<IProducerListener> const& listener, int api, + bool producerControlledByApp, QueueBufferOutput* output) { + HConnectionType hConnectionType; + if (!b2h(api, &hConnectionType)) { + LOG(ERROR) << "connect: corrupted input connection type."; + return UNKNOWN_ERROR; + } + sp<HProducerListener> hListener = nullptr; + if (listener && listener->needsReleaseNotify()) { + hListener = new B2HProducerListener(listener); + if (!hListener) { + LOG(ERROR) << "connect: failed to wrap listener."; + return UNKNOWN_ERROR; + } + } + + using HOutput = HGraphicBufferProducer::QueueBufferOutput; + bool converted{}; + status_t bStatus{}; + Return<void> transResult = mBase->connect( + hListener, + hConnectionType, + producerControlledByApp, + [&converted, &bStatus, output]( + HStatus hStatus, + HOutput hOutput) { + converted = h2b(hStatus, &bStatus); + output->width = hOutput.width; + output->height = hOutput.height; + output->transformHint = + static_cast<uint32_t>(hOutput.transformHint); + output->numPendingBuffers = hOutput.numPendingBuffers; + output->nextFrameNumber = hOutput.nextFrameNumber; + output->bufferReplaced = hOutput.bufferReplaced; + }); + if (!transResult.isOk()) { + LOG(ERROR) << "connect: transaction failed."; + return FAILED_TRANSACTION; + } + if (!converted) { + LOG(ERROR) << "connect: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; + +} + +status_t H2BGraphicBufferProducer::disconnect(int api, DisconnectMode mode) { + HConnectionType hConnectionType; + if (mode == DisconnectMode::AllLocal) { + hConnectionType = HConnectionType::CURRENTLY_CONNECTED; + } else if (!b2h(api, &hConnectionType)) { + LOG(ERROR) << "connect: corrupted input connection type."; + return UNKNOWN_ERROR; + } + + status_t bStatus{}; + Return<HStatus> transResult = mBase->disconnect(hConnectionType); + if (!transResult.isOk()) { + LOG(ERROR) << "disconnect: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "disconnect: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setSidebandStream( + sp<NativeHandle> const& stream) { + if (stream) { + LOG(INFO) << "setSidebandStream: not supported."; + return INVALID_OPERATION; + } + return OK; +} + +void H2BGraphicBufferProducer::allocateBuffers( + uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->allocateBuffers( + width, height, static_cast<uint32_t>(format), usage); + if (!transResult.isOk()) { + LOG(ERROR) << "allocateBuffer: transaction failed."; + return; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "allocateBuffer: corrupted transaction."; + return; + } +} + +status_t H2BGraphicBufferProducer::allowAllocation(bool allow) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->allowAllocation(allow); + if (!transResult.isOk()) { + LOG(ERROR) << "allowAllocation: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "allowAllocation: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::setGenerationNumber( + uint32_t generationNumber) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setGenerationNumber(generationNumber); + if (!transResult.isOk()) { + LOG(ERROR) << "setGenerationNumber: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setGenerationNumber: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +String8 H2BGraphicBufferProducer::getConsumerName() const { + String8 bName; + Return<void> transResult = mBase->getConsumerName( + [&bName](hidl_string const& name) { + bName = name.c_str(); + }); + return bName; +} + +status_t H2BGraphicBufferProducer::setSharedBufferMode(bool sharedBufferMode) { + if (sharedBufferMode) { + LOG(INFO) << "setSharedBufferMode: not supported."; + return INVALID_OPERATION; + } + return OK; +} + +status_t H2BGraphicBufferProducer::setAutoRefresh(bool autoRefresh) { + if (autoRefresh) { + LOG(INFO) << "setAutoRefresh: not supported."; + return INVALID_OPERATION; + } + return OK; +} + +status_t H2BGraphicBufferProducer::setDequeueTimeout(nsecs_t timeout) { + status_t bStatus{}; + Return<HStatus> transResult = mBase->setDequeueTimeout( + static_cast<int64_t>(timeout)); + if (!transResult.isOk()) { + LOG(ERROR) << "setDequeueTimeout: transaction failed."; + return FAILED_TRANSACTION; + } + if (!h2b(static_cast<HStatus>(transResult), &bStatus)) { + LOG(ERROR) << "setDequeueTimeout: corrupted transaction."; + return FAILED_TRANSACTION; + } + return bStatus; +} + +status_t H2BGraphicBufferProducer::getLastQueuedBuffer( + sp<GraphicBuffer>*, + sp<BFence>*, + float[16]) { + LOG(INFO) << "getLastQueuedBuffer: not supported."; + return INVALID_OPERATION; +} + +void H2BGraphicBufferProducer::getFrameTimestamps(FrameEventHistoryDelta*) { + LOG(INFO) << "getFrameTimestamps: not supported."; +} + +status_t H2BGraphicBufferProducer::getUniqueId(uint64_t* outId) const { + Return<uint64_t> transResult = mBase->getUniqueId(); + if (!transResult.isOk()) { + LOG(ERROR) << "getUniqueId: transaction failed."; + return FAILED_TRANSACTION; + } + *outId = static_cast<uint64_t>(transResult); + return OK; +} + +status_t H2BGraphicBufferProducer::getConsumerUsage(uint64_t*) const { + LOG(INFO) << "getConsumerUsage: not supported."; + return INVALID_OPERATION; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android diff --git a/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp new file mode 100644 index 0000000000..b81a357d63 --- /dev/null +++ b/libs/gui/bufferqueue/2.0/H2BProducerListener.cpp @@ -0,0 +1,57 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "H2BProducerListener@2.0" + +#include <android-base/logging.h> + +#include <gui/bufferqueue/2.0/H2BProducerListener.h> +#include <hidl/Status.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using ::android::hardware::Return; + +H2BProducerListener::H2BProducerListener(sp<HProducerListener> const& base) + : CBase{base} { +} + +void H2BProducerListener::onBufferReleased() { + if (mBase) { + Return<void> transResult = mBase->onBuffersReleased(1); + if (!transResult.isOk()) { + LOG(ERROR) << "onBuffersReleased: transaction failed."; + } + } +} + +bool H2BProducerListener::needsReleaseNotify() { + return static_cast<bool>(mBase); +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + diff --git a/libs/gui/bufferqueue/2.0/types.cpp b/libs/gui/bufferqueue/2.0/types.cpp new file mode 100644 index 0000000000..a11051739f --- /dev/null +++ b/libs/gui/bufferqueue/2.0/types.cpp @@ -0,0 +1,301 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cutils/native_handle.h> +#include <gui/BufferQueueCore.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <system/window.h> +#include <vndk/hardware_buffer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// Status +// ====== + +bool b2h(status_t from, HStatus* to, + bool* bufferNeedsReallocation, bool* releaseAllBuffers) { + switch (from) { + case OK: + *to = HStatus::OK; break; + case NO_MEMORY: + *to = HStatus::NO_MEMORY; break; + case NO_INIT: + *to = HStatus::NO_INIT; break; + case BAD_VALUE: + *to = HStatus::BAD_VALUE; break; + case DEAD_OBJECT: + *to = HStatus::DEAD_OBJECT; break; + case INVALID_OPERATION: + *to = HStatus::INVALID_OPERATION; break; + case TIMED_OUT: + *to = HStatus::TIMED_OUT; break; + case WOULD_BLOCK: + *to = HStatus::WOULD_BLOCK; break; + case UNKNOWN_ERROR: + *to = HStatus::UNKNOWN_ERROR; break; + default: + using BGBP = ::android::IGraphicBufferProducer; + status_t mask = + (bufferNeedsReallocation ? BGBP::BUFFER_NEEDS_REALLOCATION : 0) + | (releaseAllBuffers ? BGBP::RELEASE_ALL_BUFFERS : 0); + if (from & ~mask) { + *to = static_cast<HStatus>(from); + } else { + *to = HStatus::OK; + if (bufferNeedsReallocation) { + *bufferNeedsReallocation = from & BGBP::BUFFER_NEEDS_REALLOCATION; + } + if (releaseAllBuffers) { + *releaseAllBuffers = from & BGBP::RELEASE_ALL_BUFFERS; + } + } + } + return true; +} + +bool h2b(HStatus from, status_t* to) { + switch (from) { + case HStatus::OK: + *to = OK; break; + case HStatus::NO_MEMORY: + *to = NO_MEMORY; break; + case HStatus::NO_INIT: + *to = NO_INIT; break; + case HStatus::BAD_VALUE: + *to = BAD_VALUE; break; + case HStatus::DEAD_OBJECT: + *to = DEAD_OBJECT; break; + case HStatus::INVALID_OPERATION: + *to = INVALID_OPERATION; break; + case HStatus::TIMED_OUT: + *to = TIMED_OUT; break; + case HStatus::WOULD_BLOCK: + *to = WOULD_BLOCK; break; + case HStatus::UNKNOWN_ERROR: + *to = UNKNOWN_ERROR; break; + default: + *to = static_cast<status_t>(from); + } + return true; +} + +// Fence +// ===== + +HFenceWrapper::HFenceWrapper(native_handle_t* h) : mHandle{h} { +} + +HFenceWrapper::~HFenceWrapper() { + native_handle_delete(mHandle); +} + +HFenceWrapper& HFenceWrapper::set(native_handle_t* h) { + native_handle_delete(mHandle); + mHandle = h; + return *this; +} + +HFenceWrapper& HFenceWrapper::operator=(native_handle_t* h) { + return set(h); +} + +hidl_handle HFenceWrapper::getHandle() const { + return hidl_handle{mHandle}; +} + +HFenceWrapper::operator hidl_handle() const { + return getHandle(); +} + +bool b2h(sp<BFence> const& from, HFenceWrapper* to) { + if (!from) { + to->set(nullptr); + return true; + } + int fenceFd = from->get(); + if (fenceFd == -1) { + to->set(nullptr); + return true; + } + native_handle_t* nh = native_handle_create(1, 0); + if (!nh) { + return false; + } + nh->data[0] = fenceFd; + to->set(nh); + return true; +} + +bool h2b(native_handle_t const* from, sp<BFence>* to) { + if (!from || from->numFds == 0) { + *to = new ::android::Fence(); + return true; + } + if (from->numFds != 1 || from->numInts != 0) { + return false; + } + *to = new BFence(dup(from->data[0])); + return true; +} + +// ConnectionType +// ============== + +bool b2h(int from, HConnectionType* to) { + *to = static_cast<HConnectionType>(from); + switch (from) { + case BufferQueueCore::CURRENTLY_CONNECTED_API: + *to = HConnectionType::CURRENTLY_CONNECTED; break; + case NATIVE_WINDOW_API_EGL: + *to = HConnectionType::EGL; break; + case NATIVE_WINDOW_API_CPU: + *to = HConnectionType::CPU; break; + case NATIVE_WINDOW_API_MEDIA: + *to = HConnectionType::MEDIA; break; + case NATIVE_WINDOW_API_CAMERA: + *to = HConnectionType::CAMERA; break; + } + return true; +} + +bool h2b(HConnectionType from, int* to) { + *to = static_cast<int>(from); + switch (from) { + case HConnectionType::CURRENTLY_CONNECTED: + *to = BufferQueueCore::CURRENTLY_CONNECTED_API; break; + case HConnectionType::EGL: + *to = NATIVE_WINDOW_API_EGL; break; + case HConnectionType::CPU: + *to = NATIVE_WINDOW_API_CPU; break; + case HConnectionType::MEDIA: + *to = NATIVE_WINDOW_API_MEDIA; break; + case HConnectionType::CAMERA: + *to = NATIVE_WINDOW_API_CAMERA; break; + } + return true; +} + +// Rect +// ==== + +bool b2h(BRect const& from, HRect* to) { + BRect* dst = reinterpret_cast<BRect*>(to->data()); + dst->left = from.left; + dst->top = from.top; + dst->right = from.right; + dst->bottom = from.bottom; + return true; +} + +bool h2b(HRect const& from, BRect* to) { + BRect const* src = reinterpret_cast<BRect const*>(from.data()); + to->left = src->left; + to->top = src->top; + to->right = src->right; + to->bottom = src->bottom; + return true; +} + +// Region +// ====== + +bool b2h(BRegion const& from, HRegion* to) { + size_t numRects; + BRect const* rectArray = from.getArray(&numRects); + to->resize(numRects); + for (size_t i = 0; i < numRects; ++i) { + if (!b2h(rectArray[i], &(*to)[i])) { + return false; + } + } + return true; +} + +bool h2b(HRegion const& from, BRegion* to) { + if (from.size() > 0) { + BRect bRect; + if (!h2b(from[0], &bRect)) { + return false; + } + to->set(bRect); + for (size_t i = 1; i < from.size(); ++i) { + if (!h2b(from[i], &bRect)) { + return false; + } + to->addRectUnchecked( + static_cast<int>(bRect.left), + static_cast<int>(bRect.top), + static_cast<int>(bRect.right), + static_cast<int>(bRect.bottom)); + } + } else { + to->clear(); + } + return true; +} + +// GraphicBuffer +// ============= + +// The handle is not cloned. Its lifetime is tied to the original GraphicBuffer. +bool b2h(sp<GraphicBuffer> const& from, HardwareBuffer* to, + uint32_t* toGenerationNumber) { + if (!from) { + return false; + } + AHardwareBuffer* hwBuffer = from->toAHardwareBuffer(); + to->nativeHandle.setTo( + const_cast<native_handle_t*>( + AHardwareBuffer_getNativeHandle(hwBuffer)), + false); + AHardwareBuffer_describe( + hwBuffer, + reinterpret_cast<AHardwareBuffer_Desc*>(to->description.data())); + if (toGenerationNumber) { + *toGenerationNumber = from->getGenerationNumber(); + } + return true; +} + +// The handle is cloned. +bool h2b(HardwareBuffer const& from, sp<GraphicBuffer>* to) { + AHardwareBuffer_Desc const* desc = + reinterpret_cast<AHardwareBuffer_Desc const*>( + from.description.data()); + native_handle_t const* handle = from.nativeHandle; + AHardwareBuffer* hwBuffer; + if (AHardwareBuffer_createFromHandle( + desc, handle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, + &hwBuffer) != OK) { + return false; + } + *to = GraphicBuffer::fromAHardwareBuffer(hwBuffer); + return true; +} + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + diff --git a/libs/gui/include/gui/DisplayEventReceiver.h b/libs/gui/include/gui/DisplayEventReceiver.h index 8c3f46305c..22de751498 100644 --- a/libs/gui/include/gui/DisplayEventReceiver.h +++ b/libs/gui/include/gui/DisplayEventReceiver.h @@ -52,6 +52,7 @@ public: enum { DISPLAY_EVENT_VSYNC = fourcc('v', 's', 'y', 'n'), DISPLAY_EVENT_HOTPLUG = fourcc('p', 'l', 'u', 'g'), + DISPLAY_EVENT_CONFIG_CHANGED = fourcc('c', 'o', 'n', 'f'), }; struct Event { @@ -70,10 +71,15 @@ public: bool connected; }; + struct Config { + int32_t configId; + }; + Header header; union { VSync vsync; Hotplug hotplug; + Config config; }; }; diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index 8ff8d81cf6..2f538adebc 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -35,6 +35,7 @@ #include <hidl/HybridInterface.h> #include <android/hardware/graphics/bufferqueue/1.0/IGraphicBufferProducer.h> +#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> namespace android { // ---------------------------------------------------------------------------- @@ -42,8 +43,6 @@ namespace android { class IProducerListener; class NativeHandle; class Surface; -typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer - HGraphicBufferProducer; /* * This class defines the Binder IPC interface for the producer side of @@ -62,7 +61,16 @@ typedef ::android::hardware::graphics::bufferqueue::V1_0::IGraphicBufferProducer class IGraphicBufferProducer : public IInterface { public: - DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, HGraphicBufferProducer) + using HGraphicBufferProducerV1_0 = + ::android::hardware::graphics::bufferqueue::V1_0:: + IGraphicBufferProducer; + using HGraphicBufferProducerV2_0 = + ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer; + + DECLARE_HYBRID_META_INTERFACE(GraphicBufferProducer, + HGraphicBufferProducerV1_0, + HGraphicBufferProducerV2_0) enum { // A flag returned by dequeueBuffer when the client needs to call @@ -366,7 +374,6 @@ public: const HdrMetadata& getHdrMetadata() const { return hdrMetadata; } void setHdrMetadata(const HdrMetadata& metadata) { hdrMetadata = metadata; } - private: int64_t timestamp{0}; int isAutoTimestamp{0}; android_dataspace dataSpace{HAL_DATASPACE_UNKNOWN}; diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index e808bd3bc3..a13d8e4945 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -17,8 +17,10 @@ #ifndef ANDROID_GUI_IPRODUCERLISTENER_H #define ANDROID_GUI_IPRODUCERLISTENER_H +#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> +#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> #include <binder/IInterface.h> - +#include <hidl/HybridInterface.h> #include <utils/RefBase.h> namespace android { @@ -47,7 +49,14 @@ public: class IProducerListener : public ProducerListener, public IInterface { public: - DECLARE_META_INTERFACE(ProducerListener) + using HProducerListener1 = + ::android::hardware::graphics::bufferqueue::V1_0::IProducerListener; + using HProducerListener2 = + ::android::hardware::graphics::bufferqueue::V2_0::IProducerListener; + DECLARE_HYBRID_META_INTERFACE( + ProducerListener, + HProducerListener1, + HProducerListener2) }; class BnProducerListener : public BnInterface<IProducerListener> diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h index 1a0b6bbfdf..3dffa8f8bc 100644 --- a/libs/gui/include/gui/ISurfaceComposer.h +++ b/libs/gui/include/gui/ISurfaceComposer.h @@ -209,7 +209,8 @@ public: const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - Rotation rotation = eRotateNone) = 0; + Rotation rotation = eRotateNone, + bool captureSecureLayers = false) = 0; /** * Capture the specified screen. This requires READ_FRAME_BUFFER * permission. This function will fail if there is a secure window on @@ -359,6 +360,53 @@ public: * Removes a listener that was streaming median luma updates from SurfaceFlinger. */ virtual status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) = 0; + + /* + * Sets the allowed display configurations to be used. + * The allowedConfigs in a vector of indexes corresponding to the configurations + * returned from getDisplayConfigs(). + */ + virtual status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, + const std::vector<int32_t>& allowedConfigs) = 0; + + /* + * Returns the allowed display configurations currently set. + * The allowedConfigs in a vector of indexes corresponding to the configurations + * returned from getDisplayConfigs(). + */ + virtual status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) = 0; + /* + * 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; }; // ---------------------------------------------------------------------------- @@ -406,6 +454,10 @@ public: GET_PHYSICAL_DISPLAY_IDS, ADD_REGION_SAMPLING_LISTENER, 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/LayerMetadata.h b/libs/gui/include/gui/LayerMetadata.h index 3ae10e461d..47f0cede3a 100644 --- a/libs/gui/include/gui/LayerMetadata.h +++ b/libs/gui/include/gui/LayerMetadata.h @@ -34,7 +34,9 @@ struct LayerMetadata : public Parcelable { LayerMetadata& operator=(const LayerMetadata& other); LayerMetadata& operator=(LayerMetadata&& other); - void merge(const LayerMetadata& other); + // Merges other into this LayerMetadata. If eraseEmpty is true, any entries in + // in this whose keys are paired with empty values in other will be erased. + bool merge(const LayerMetadata& other, bool eraseEmpty = false); status_t writeToParcel(Parcel* parcel) const override; status_t readFromParcel(const Parcel* parcel) override; diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index c780c078a0..35e795c30b 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -88,6 +88,7 @@ struct layer_state_t { eCachedBufferChanged = 0x2'00000000, eBackgroundColorChanged = 0x4'00000000, eMetadataChanged = 0x8'00000000, + eColorSpaceAgnosticChanged = 0x10'00000000, }; layer_state_t() @@ -115,7 +116,8 @@ struct layer_state_t { api(-1), colorTransform(mat4()), bgColorAlpha(0), - bgColorDataspace(ui::Dataspace::UNKNOWN) { + bgColorDataspace(ui::Dataspace::UNKNOWN), + colorSpaceAgnostic(false) { matrix.dsdx = matrix.dtdy = 1.0f; matrix.dsdy = matrix.dtdx = 0.0f; hdrMetadata.validTypes = 0; @@ -192,6 +194,10 @@ struct layer_state_t { // the background color layer float bgColorAlpha; ui::Dataspace bgColorDataspace; + + // A color space agnostic layer means the color of this layer can be + // interpreted in any color space. + bool colorSpaceAgnostic; }; struct ComposerState { @@ -254,6 +260,7 @@ struct InputWindowCommands { }; std::vector<TransferTouchFocusCommand> transferTouchFocusCommands; + bool syncInputWindows{false}; void merge(const InputWindowCommands& other); void clear(); diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index aa99bd2727..39d6d13682 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -50,6 +50,7 @@ struct DisplayInfo; class HdrCapabilities; class ISurfaceComposerClient; class IGraphicBufferProducer; +class IRegionSamplingListener; class Region; // --------------------------------------------------------------------------- @@ -111,6 +112,18 @@ public: // returned by getDisplayInfo static status_t setActiveConfig(const sp<IBinder>& display, int id); + // Sets the allowed display configurations to be used. + // The allowedConfigs in a vector of indexes corresponding to the configurations + // returned from getDisplayConfigs(). + static status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, + const std::vector<int32_t>& allowedConfigs); + + // Returns the allowed display configurations currently set. + // The allowedConfigs in a vector of indexes corresponding to the configurations + // returned from getDisplayConfigs(). + static status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs); + // Gets the list of supported color modes for the given display static status_t getDisplayColorModes(const sp<IBinder>& display, Vector<ui::ColorMode>* outColorModes); @@ -155,6 +168,32 @@ public: // Queries whether a given display is wide color display. static status_t isWideColorDisplay(const sp<IBinder>& display, bool* outIsWideColorDisplay); + /* + * Returns whether brightness operations are supported on a display. + * + * displayToken + * The token of the display. + * + * Returns whether brightness operations are supported on a display or not. + */ + static bool getDisplayBrightnessSupport(const sp<IBinder>& displayToken); + + /* + * Sets the brightness of a display. + * + * displayToken + * The token of the display whose brightness is set. + * brightness + * A number between 0.0 (minimum brightness) and 1.0 (maximum brightness), or -1.0f to + * turn the backlight off. + * + * Returns NO_ERROR upon success. Otherwise, + * NAME_NOT_FOUND if the display handle is invalid, or + * BAD_VALUE if the brightness value is invalid, or + * INVALID_OPERATION if brightness operaetions are not supported. + */ + static status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness); + // ------------------------------------------------------------------------ // surface creation / destruction @@ -348,6 +387,7 @@ public: Transaction& setSidebandStream(const sp<SurfaceControl>& sc, const sp<NativeHandle>& sidebandStream); Transaction& setDesiredPresentTime(nsecs_t desiredPresentTime); + Transaction& setColorSpaceAgnostic(const sp<SurfaceControl>& sc, const bool agnostic); Transaction& addTransactionCompletedCallback( TransactionCompletedCallbackTakesContext callback, void* callbackContext); @@ -378,6 +418,7 @@ public: #ifndef NO_INPUT Transaction& setInputWindowInfo(const sp<SurfaceControl>& sc, const InputWindowInfo& info); Transaction& transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); + Transaction& syncInputWindows(); #endif // Set a color transform matrix on the given layer on the built-in display. @@ -435,6 +476,10 @@ public: 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(); @@ -453,6 +498,10 @@ public: static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, + uint32_t rotation, bool captureSecureLayers, sp<GraphicBuffer>* outBuffer); + static status_t capture(const sp<IBinder>& display, const ui::Dataspace reqDataSpace, + const ui::PixelFormat reqPixelFormat, Rect sourceCrop, + uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, uint32_t rotation, sp<GraphicBuffer>* outBuffer); static status_t captureLayers(const sp<IBinder>& layerHandle, const ui::Dataspace reqDataSpace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h index c92fa9d0e5..c4d02452c5 100644 --- a/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h +++ b/libs/gui/include/gui/bufferqueue/1.0/H2BGraphicBufferProducer.h @@ -57,7 +57,6 @@ using ::android::IProducerListener; struct H2BGraphicBufferProducer : public ::android::H2BConverter< HGraphicBufferProducer, - BGraphicBufferProducer, BnGraphicBufferProducer> { explicit H2BGraphicBufferProducer(sp<HGraphicBufferProducer> const& base) : CBase(base) {} diff --git a/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h new file mode 100644 index 0000000000..211fdd5351 --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/1.0/H2BProducerListener.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H + +#include <android/hardware/graphics/bufferqueue/1.0/IProducerListener.h> +#include <gui/IProducerListener.h> +#include <hidl/HybridInterface.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V1_0 { +namespace utils { + +using HProducerListener = ::android::hardware::graphics::bufferqueue::V1_0:: + IProducerListener; + +using BProducerListener = ::android::IProducerListener; + +class H2BProducerListener + : public H2BConverter<HProducerListener, BnProducerListener> { +public: + H2BProducerListener(sp<HProducerListener> const& base); + virtual void onBufferReleased() override; + virtual bool needsReleaseNotify() override; +}; + +} // namespace utils +} // namespace V1_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V1_0_H2BPRODUCERLISTENER_H + diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h new file mode 100644 index 0000000000..1c58167752 --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/B2HGraphicBufferProducer.h @@ -0,0 +1,121 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H + +#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> +#include <gui/IGraphicBufferProducer.h> +#include <gui/bufferqueue/2.0/types.h> +#include <hidl/HidlSupport.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using HGraphicBufferProducer = + ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer; +using BGraphicBufferProducer = + ::android:: + IGraphicBufferProducer; +using HProducerListener = + ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; + +using ::android::hardware::Return; +using ::android::hardware::hidl_handle; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; + +using ::android::hardware::graphics::common::V1_2::HardwareBuffer; + +class B2HGraphicBufferProducer : public HGraphicBufferProducer { +public: + B2HGraphicBufferProducer(sp<BGraphicBufferProducer> const& base); + + virtual Return<HStatus> setMaxDequeuedBufferCount( + int32_t maxDequeuedBuffers) override; + + virtual Return<void> requestBuffer( + int32_t slot, + requestBuffer_cb _hidl_cb) override; + + virtual Return<HStatus> setAsyncMode(bool async) override; + + virtual Return<void> dequeueBuffer( + DequeueBufferInput const& input, + dequeueBuffer_cb _hidl_cb) override; + + virtual Return<HStatus> detachBuffer(int32_t slot) override; + + virtual Return<void> detachNextBuffer( + detachNextBuffer_cb _hidl_cb) override; + + virtual Return<void> attachBuffer( + HardwareBuffer const& buffer, + uint32_t generationNumber, + attachBuffer_cb _hidl_cb) override; + + virtual Return<void> queueBuffer( + int32_t slot, + QueueBufferInput const& input, + queueBuffer_cb _hidl_cb) override; + + virtual Return<HStatus> cancelBuffer( + int32_t slot, + hidl_handle const& fence) override; + + virtual Return<void> query(int32_t what, query_cb _hidl_cb) override; + + virtual Return<void> connect( + sp<HProducerListener> const& listener, + HConnectionType api, + bool producerControlledByApp, + connect_cb _hidl_cb) override; + + virtual Return<HStatus> disconnect(HConnectionType api) override; + + virtual Return<HStatus> allocateBuffers( + uint32_t width, uint32_t height, + uint32_t format, uint64_t usage) override; + + virtual Return<HStatus> allowAllocation(bool allow) override; + + virtual Return<HStatus> setGenerationNumber(uint32_t generationNumber) override; + + virtual Return<HStatus> setDequeueTimeout(int64_t timeoutNs) override; + + virtual Return<uint64_t> getUniqueId() override; + + virtual Return<void> getConsumerName(getConsumerName_cb _hidl_cb) override; + +protected: + sp<BGraphicBufferProducer> mBase; +}; + + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HGRAPHICBUFFERPRODUCER_H diff --git a/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h new file mode 100644 index 0000000000..b48a4736ad --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/B2HProducerListener.h @@ -0,0 +1,57 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H + +#include <android/hidl/base/1.0/IBase.h> +#include <binder/IBinder.h> +#include <gui/IProducerListener.h> +#include <hidl/Status.h> + +#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using ::android::hardware::Return; + +using HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; + +using BProducerListener = ::android::IProducerListener; + +struct B2HProducerListener : public HProducerListener { + explicit B2HProducerListener(sp<BProducerListener> const& base); + Return<void> onBuffersReleased(uint32_t count) override; +protected: + sp<BProducerListener> mBase; + bool mNeedsReleaseNotify; +}; + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_B2HPRODUCERLISTENER_H + diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h new file mode 100644 index 0000000000..7dd16172cf --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/H2BGraphicBufferProducer.h @@ -0,0 +1,107 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H + +#include <gui/IGraphicBufferProducer.h> +#include <gui/IProducerListener.h> +#include <hidl/HybridInterface.h> +#include <ui/Fence.h> + +#include <android/hardware/graphics/bufferqueue/2.0/IGraphicBufferProducer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using ::android::BnGraphicBufferProducer; +using ::android::IProducerListener; +using Fence = ::android::Fence; + +using HGraphicBufferProducer = + ::android::hardware::graphics::bufferqueue::V2_0:: + IGraphicBufferProducer; +using HProducerListener = + ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; +using BGraphicBufferProducer = + ::android::IGraphicBufferProducer; + +struct H2BGraphicBufferProducer + : public ::android::H2BConverter<HGraphicBufferProducer, + BnGraphicBufferProducer> { + explicit H2BGraphicBufferProducer( + sp<HGraphicBufferProducer> const& base) : CBase(base) {} + + virtual status_t requestBuffer(int slot, sp<GraphicBuffer>* buf) override; + virtual status_t setMaxDequeuedBufferCount(int maxDequeuedBuffers) override; + virtual status_t setAsyncMode(bool async) override; + virtual status_t dequeueBuffer( + int* slot, sp<Fence>* fence, + uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage, + uint64_t* outBufferAge, + FrameEventHistoryDelta* outTimestamps) override; + virtual status_t detachBuffer(int slot) override; + virtual status_t detachNextBuffer( + sp<GraphicBuffer>* outBuffer, + sp<Fence>* outFence) override; + virtual status_t attachBuffer( + int* outSlot, + sp<GraphicBuffer> const& buffer) override; + virtual status_t queueBuffer( + int slot, + QueueBufferInput const& input, + QueueBufferOutput* output) override; + virtual status_t cancelBuffer(int slot, sp<Fence> const& fence) override; + virtual int query(int what, int* value) override; + virtual status_t connect( + sp<IProducerListener> const& listener, + int api, + bool producerControlledByApp, + QueueBufferOutput* output) override; + virtual status_t disconnect( + int api, + DisconnectMode mode = DisconnectMode::Api) override; + virtual status_t setSidebandStream(sp<NativeHandle> const& stream) override; + virtual void allocateBuffers( + uint32_t width, uint32_t height, + PixelFormat format, uint64_t usage) override; + virtual status_t allowAllocation(bool allow) override; + virtual status_t setGenerationNumber(uint32_t generationNumber) override; + virtual String8 getConsumerName() const override; + virtual status_t setSharedBufferMode(bool sharedBufferMode) override; + virtual status_t setAutoRefresh(bool autoRefresh) override; + virtual status_t setDequeueTimeout(nsecs_t timeout) override; + virtual status_t getLastQueuedBuffer(sp<GraphicBuffer>* outBuffer, + sp<Fence>* outFence, float outTransformMatrix[16]) override; + virtual void getFrameTimestamps(FrameEventHistoryDelta* outDelta) override; + virtual status_t getUniqueId(uint64_t* outId) const override; + virtual status_t getConsumerUsage(uint64_t* outUsage) const override; +}; + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BGRAPHICBUFFERPRODUCER_H diff --git a/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h new file mode 100644 index 0000000000..898920bf8a --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/H2BProducerListener.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H + +#include <android/hardware/graphics/bufferqueue/2.0/IProducerListener.h> +#include <gui/IProducerListener.h> +#include <hidl/HybridInterface.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +using HProducerListener = ::android::hardware::graphics::bufferqueue::V2_0:: + IProducerListener; + +using BProducerListener = ::android::IProducerListener; + +class H2BProducerListener + : public H2BConverter<HProducerListener, BnProducerListener> { +public: + H2BProducerListener(sp<HProducerListener> const& base); + virtual void onBufferReleased() override; + virtual bool needsReleaseNotify() override; +}; + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_H2BPRODUCERLISTENER_H + diff --git a/libs/gui/include/gui/bufferqueue/2.0/types.h b/libs/gui/include/gui/bufferqueue/2.0/types.h new file mode 100644 index 0000000000..62176ce2ef --- /dev/null +++ b/libs/gui/include/gui/bufferqueue/2.0/types.h @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#ifndef ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H +#define ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H + +#include <android/hardware/graphics/bufferqueue/2.0/types.h> +#include <android/hardware/graphics/common/1.2/types.h> +#include <hidl/HidlSupport.h> +#include <ui/Fence.h> +#include <ui/GraphicBuffer.h> + +namespace android { +namespace hardware { +namespace graphics { +namespace bufferqueue { +namespace V2_0 { +namespace utils { + +// Status +// ====== + +using HStatus = ::android::hardware::graphics::bufferqueue::V2_0:: + Status; + +// A status_t value may have flags encoded. These flags are decoded into boolean +// values if their corresponding output pointers are not null. +bool b2h(status_t from, HStatus* to, + bool* bufferNeedsReallocation = nullptr, + bool* releaseAllBuffers = nullptr); +// Simple 1-to-1 mapping. If BUFFER_NEEDS_REALLOCATION or RELEASE_ALL_BUFFERS +// needs to be added, it must be done manually afterwards. +bool h2b(HStatus from, status_t* to); + +// Fence +// ===== + +using BFence = ::android::Fence; +// This class manages the lifetime of a copied handle. Its destructor calls +// native_handle_delete() but not native_handle_close(). +struct HFenceWrapper { + HFenceWrapper() = default; + // Sets mHandle to a new value. + HFenceWrapper(native_handle_t* h); + // Deletes mHandle without closing. + ~HFenceWrapper(); + // Deletes mHandle without closing, then sets mHandle to a new value. + HFenceWrapper& set(native_handle_t* h); + HFenceWrapper& operator=(native_handle_t* h); + // Returns a non-owning hidl_handle pointing to mHandle. + hidl_handle getHandle() const; + operator hidl_handle() const; +protected: + native_handle_t* mHandle{nullptr}; +}; + +// Does not clone the fd---only copy the fd. The returned HFenceWrapper should +// not outlive the input Fence object. +bool b2h(sp<BFence> const& from, HFenceWrapper* to); +// Clones the fd and puts it in a new Fence object. +bool h2b(native_handle_t const* from, sp<BFence>* to); + +// ConnectionType +// ============== + +using HConnectionType = ::android::hardware::graphics::bufferqueue::V2_0:: + ConnectionType; + +bool b2h(int from, HConnectionType* to); +bool h2b(HConnectionType from, int* to); + +// Rect +// ==== + +using BRect = ::android::Rect; +using HRect = ::android::hardware::graphics::common::V1_2::Rect; + +bool b2h(BRect const& from, HRect* to); +bool h2b(HRect const& from, BRect* to); + +// Region +// ====== + +using BRegion = ::android::Region; +using HRegion = ::android::hardware::hidl_vec<HRect>; + +bool b2h(BRegion const& from, HRegion* to); +bool h2b(HRegion const& from, BRegion* to); + +// GraphicBuffer +// ============= + +using HardwareBuffer = ::android::hardware::graphics::common::V1_2:: + HardwareBuffer; +using HardwareBufferDescription = ::android::hardware::graphics::common::V1_2:: + HardwareBufferDescription; + +// Does not clone the handle. The returned HardwareBuffer should not outlive the +// input GraphicBuffer. Note that HardwareBuffer does not carry the generation +// number, so this function needs another output argument. +bool b2h(sp<GraphicBuffer> const& from, HardwareBuffer* to, + uint32_t* toGenerationNumber = nullptr); +// Clones the handle and creates a new GraphicBuffer from the cloned handle. +// Note that the generation number of the GraphicBuffer has to be set manually +// afterwards because HardwareBuffer does not have such information. +bool h2b(HardwareBuffer const& from, sp<GraphicBuffer>* to); + +} // namespace utils +} // namespace V2_0 +} // namespace bufferqueue +} // namespace graphics +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_GRAPHICS_BUFFERQUEUE_V2_0_TYPES_H + diff --git a/libs/gui/include/private/gui/BufferQueueThreadState.h b/libs/gui/include/private/gui/BufferQueueThreadState.h new file mode 100644 index 0000000000..67dcf621ce --- /dev/null +++ b/libs/gui/include/private/gui/BufferQueueThreadState.h @@ -0,0 +1,30 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdint.h> +#include <sys/types.h> +#include <unistd.h> + +namespace android { + +// TODO: Replace this with b/127962003 +class BufferQueueThreadState { +public: + static pid_t getCallingPid(); + static uid_t getCallingUid(); +}; + +} // namespace android diff --git a/libs/gui/tests/Android.bp b/libs/gui/tests/Android.bp index f020a4067d..ab6dcaa3f6 100644 --- a/libs/gui/tests/Android.bp +++ b/libs/gui/tests/Android.bp @@ -23,6 +23,7 @@ cc_test { "IGraphicBufferProducer_test.cpp", "Malicious.cpp", "MultiTextureConsumer_test.cpp", + "RegionSampling_test.cpp", "StreamSplitter_test.cpp", "SurfaceTextureClient_test.cpp", "SurfaceTextureFBO_test.cpp", @@ -87,3 +88,26 @@ cc_test { "libdvr_headers", ], } + +cc_test { + name: "SamplingDemo", + + clang: true, + cflags: [ + "-Wall", + "-Werror", + ], + + srcs: [ + "SamplingDemo.cpp", + ], + + shared_libs: [ + "libbinder", + "libcutils", + "libgui", + "liblog", + "libui", + "libutils", + ] +} diff --git a/libs/gui/tests/RegionSampling_test.cpp b/libs/gui/tests/RegionSampling_test.cpp new file mode 100644 index 0000000000..d33ecfbdb9 --- /dev/null +++ b/libs/gui/tests/RegionSampling_test.cpp @@ -0,0 +1,300 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> +#include <thread> + +#include <binder/ProcessState.h> +#include <gui/DisplayEventReceiver.h> +#include <gui/IRegionSamplingListener.h> +#include <gui/ISurfaceComposer.h> +#include <gui/Surface.h> +#include <gui/SurfaceComposerClient.h> +#include <private/gui/ComposerService.h> +#include <utils/Looper.h> + +using namespace std::chrono_literals; + +namespace android::test { + +struct ChoreographerSync { + ChoreographerSync(DisplayEventReceiver& receiver) : receiver_(receiver) {} + ~ChoreographerSync() = default; + + void notify() const { + std::unique_lock<decltype(mutex_)> lk(mutex_); + + auto check_event = [](auto const& ev) -> bool { + return ev.header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC; + }; + DisplayEventReceiver::Event ev_; + int evs = receiver_.getEvents(&ev_, 1); + auto vsync_event_found = check_event(ev_); + while (evs) { + evs = receiver_.getEvents(&ev_, 1); + vsync_event_found |= check_event(ev_); + } + + if (vsync_event_found) { + notification_arrived_ = true; + cv_.notify_all(); + } + } + + void wait_vsync_notify() const { + std::unique_lock<decltype(mutex_)> lk(mutex_); + cv_.wait(lk, [this] { return notification_arrived_; }); + notification_arrived_ = false; + } + +private: + ChoreographerSync(ChoreographerSync const&) = delete; + ChoreographerSync& operator=(ChoreographerSync const&) = delete; + + std::mutex mutable mutex_; + std::condition_variable mutable cv_; + bool mutable notification_arrived_ = false; + DisplayEventReceiver& receiver_; +}; + +struct ChoreographerSim { + static std::unique_ptr<ChoreographerSim> make() { + auto receiver = std::make_unique<DisplayEventReceiver>(); + if (!receiver || receiver->initCheck() == NO_INIT) { + ALOGE("No display reciever"); + return nullptr; + } + return std::unique_ptr<ChoreographerSim>(new ChoreographerSim(std::move(receiver))); + } + + ~ChoreographerSim() { + poll_ = false; + looper->wake(); + choreographer_thread_.join(); + } + + void request_render_wait(std::function<void()> const& render_fn) { + display_event_receiver_->requestNextVsync(); + choreographer_.wait_vsync_notify(); + render_fn(); + + // Purpose is to make sure that the content is latched by the time we sample. + // Waiting one vsync after queueing could still race with vsync, so wait for two, after + // which the content is pretty reliably on screen. + display_event_receiver_->requestNextVsync(); + choreographer_.wait_vsync_notify(); + display_event_receiver_->requestNextVsync(); + choreographer_.wait_vsync_notify(); + } + +private: + ChoreographerSim(std::unique_ptr<DisplayEventReceiver> receiver) + : display_event_receiver_{std::move(receiver)}, + choreographer_{*display_event_receiver_}, + looper{new Looper(false)} { + choreographer_thread_ = std::thread([this] { + auto vsync_notify_fd = display_event_receiver_->getFd(); + looper->addFd(vsync_notify_fd, 0, Looper::EVENT_INPUT, + [](int /*fd*/, int /*events*/, void* data) -> int { + if (!data) return 0; + reinterpret_cast<ChoreographerSync*>(data)->notify(); + return 1; + }, + const_cast<void*>(reinterpret_cast<void const*>(&choreographer_))); + + while (poll_) { + auto const poll_interval = + std::chrono::duration_cast<std::chrono::milliseconds>(1s).count(); + auto rc = looper->pollOnce(poll_interval); + if ((rc != Looper::POLL_CALLBACK) && (rc != Looper::POLL_WAKE)) + ALOGW("Vsync Looper returned: %i\n", rc); + } + }); + } + + ChoreographerSim(ChoreographerSim const&) = delete; + ChoreographerSim& operator=(ChoreographerSim const&) = delete; + + std::unique_ptr<DisplayEventReceiver> const display_event_receiver_; + ChoreographerSync const choreographer_; + sp<Looper> looper; + std::thread choreographer_thread_; + std::atomic<bool> poll_{true}; +}; + +struct Listener : BnRegionSamplingListener { + void onSampleCollected(float medianLuma) override { + std::unique_lock<decltype(mutex)> lk(mutex); + received = true; + mLuma = medianLuma; + cv.notify_all(); + }; + bool wait_event(std::chrono::milliseconds timeout) { + std::unique_lock<decltype(mutex)> lk(mutex); + return cv.wait_for(lk, timeout, [this] { return received; }); + } + + float luma() { + std::unique_lock<decltype(mutex)> lk(mutex); + return mLuma; + } + + void reset() { + std::unique_lock<decltype(mutex)> lk(mutex); + received = false; + } + +private: + std::condition_variable cv; + std::mutex mutex; + bool received = false; + float mLuma = -0.0f; +}; + +// Hoisted to TestSuite setup to avoid flake in test (b/124675919) +std::unique_ptr<ChoreographerSim> gChoreographerSim = nullptr; + +struct RegionSamplingTest : ::testing::Test { +protected: + RegionSamplingTest() { ProcessState::self()->startThreadPool(); } + + static void SetUpTestSuite() { + gChoreographerSim = ChoreographerSim::make(); + ASSERT_NE(gChoreographerSim, nullptr); + } + + void SetUp() override { + mSurfaceComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mSurfaceComposerClient->initCheck()); + + mBackgroundLayer = + mSurfaceComposerClient->createSurface(String8("Background RegionSamplingTest"), 0, + 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + uint32_t layerPositionBottom = 0x7E000000; + SurfaceComposerClient::Transaction{} + .setLayer(mBackgroundLayer, layerPositionBottom) + .setPosition(mBackgroundLayer, 100, 100) + .setColor(mBackgroundLayer, half3{0.5, 0.5, 0.5}) + .show(mBackgroundLayer) + .apply(); + + mContentLayer = mSurfaceComposerClient->createSurface(String8("Content RegionSamplingTest"), + 300, 300, PIXEL_FORMAT_RGBA_8888, 0); + + SurfaceComposerClient::Transaction{} + .setLayer(mContentLayer, layerPositionBottom + 1) + .setPosition(mContentLayer, 100, 100) + .setColor(mContentLayer, half3{0.5, 0.5, 0.5}) + .show(mContentLayer) + .apply(); + + mTopLayer = mSurfaceComposerClient->createSurface(String8("TopLayer RegionSamplingTest"), 0, + 0, PIXEL_FORMAT_RGBA_8888, 0); + SurfaceComposerClient::Transaction{} + .setLayer(mTopLayer, layerPositionBottom + 2) + .setPosition(mTopLayer, 0, 0) + .show(mBackgroundLayer) + .apply(); + } + + void fill_render(uint32_t rgba_value) { + auto surface = mContentLayer->getSurface(); + ANativeWindow_Buffer outBuffer; + status_t status = surface->lock(&outBuffer, NULL); + ASSERT_EQ(status, android::OK); + auto b = reinterpret_cast<uint32_t*>(outBuffer.bits); + for (auto i = 0; i < outBuffer.height; i++) { + for (auto j = 0; j < outBuffer.width; j++) { + b[j] = rgba_value; + } + b += outBuffer.stride; + } + + gChoreographerSim->request_render_wait([&surface] { surface->unlockAndPost(); }); + } + + sp<SurfaceComposerClient> mSurfaceComposerClient; + sp<SurfaceControl> mBackgroundLayer; + sp<SurfaceControl> mContentLayer; + sp<SurfaceControl> mTopLayer; + + uint32_t const rgba_green = 0xFF00FF00; + float const luma_green = 0.7152; + uint32_t const rgba_blue = 0xFFFF0000; + float const luma_blue = 0.0722; + float const error_margin = 0.01; + float const luma_gray = 0.50; +}; + +TEST_F(RegionSamplingTest, DISABLED_CollectsLuma) { + fill_render(rgba_green); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> listener = new Listener(); + const Rect sampleArea{100, 100, 200, 200}; + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_green, error_margin); + + composer->removeRegionSamplingListener(listener); +} + +TEST_F(RegionSamplingTest, DISABLED_CollectsChangingLuma) { + fill_render(rgba_green); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> listener = new Listener(); + const Rect sampleArea{100, 100, 200, 200}; + composer->addRegionSamplingListener(sampleArea, mTopLayer->getHandle(), listener); + + EXPECT_TRUE(listener->wait_event(300ms)) << "timed out waiting for luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_green, error_margin); + + listener->reset(); + + fill_render(rgba_blue); + EXPECT_TRUE(listener->wait_event(300ms)) + << "timed out waiting for 2nd luma event to be received"; + EXPECT_NEAR(listener->luma(), luma_blue, error_margin); + + composer->removeRegionSamplingListener(listener); +} + +TEST_F(RegionSamplingTest, DISABLED_CollectsLumaFromTwoRegions) { + fill_render(rgba_green); + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<Listener> greenListener = new Listener(); + const Rect greenSampleArea{100, 100, 200, 200}; + composer->addRegionSamplingListener(greenSampleArea, mTopLayer->getHandle(), greenListener); + + sp<Listener> grayListener = new Listener(); + const Rect graySampleArea{500, 100, 600, 200}; + composer->addRegionSamplingListener(graySampleArea, mTopLayer->getHandle(), grayListener); + + EXPECT_TRUE(grayListener->wait_event(300ms)) + << "timed out waiting for luma event to be received"; + EXPECT_NEAR(grayListener->luma(), luma_gray, error_margin); + EXPECT_TRUE(greenListener->wait_event(300ms)) + << "timed out waiting for luma event to be received"; + EXPECT_NEAR(greenListener->luma(), luma_green, error_margin); + + composer->removeRegionSamplingListener(greenListener); + composer->removeRegionSamplingListener(grayListener); +} + +} // namespace android::test diff --git a/libs/gui/tests/SamplingDemo.cpp b/libs/gui/tests/SamplingDemo.cpp new file mode 100644 index 0000000000..9891587fe2 --- /dev/null +++ b/libs/gui/tests/SamplingDemo.cpp @@ -0,0 +1,135 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#define LOG_TAG "SamplingTest" + +#include <chrono> +#include <thread> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <gui/IRegionSamplingListener.h> +#include <gui/ISurfaceComposer.h> +#include <gui/SurfaceComposerClient.h> +#include <gui/SurfaceControl.h> +#include <private/gui/ComposerService.h> +#include <utils/Trace.h> + +using namespace std::chrono_literals; + +namespace android { + +class Button : public BnRegionSamplingListener { +public: + Button(const char* name, const Rect& samplingArea) { + sp<SurfaceComposerClient> client = new SurfaceComposerClient; + + mButton = client->createSurface(String8(name), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + + const int32_t width = samplingArea.getWidth(); + const int32_t height = samplingArea.getHeight(); + + SurfaceComposerClient::Transaction{} + .setLayer(mButton, 0x7fffffff) + .setCrop_legacy(mButton, + {0, 0, width - 2 * BUTTON_PADDING, height - 2 * BUTTON_PADDING}) + .setPosition(mButton, samplingArea.left + BUTTON_PADDING, + samplingArea.top + BUTTON_PADDING) + .setColor(mButton, half3{1, 1, 1}) + .show(mButton) + .apply(); + + mButtonBlend = client->createSurface(String8(name) + "Blend", 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + + SurfaceComposerClient::Transaction{} + .setLayer(mButtonBlend, 0x7ffffffe) + .setCrop_legacy(mButtonBlend, + {0, 0, width - 2 * SAMPLE_AREA_PADDING, + height - 2 * SAMPLE_AREA_PADDING}) + .setPosition(mButtonBlend, samplingArea.left + SAMPLE_AREA_PADDING, + samplingArea.top + SAMPLE_AREA_PADDING) + .setColor(mButtonBlend, half3{1, 1, 1}) + .setAlpha(mButtonBlend, 0.2) + .show(mButtonBlend) + .apply(true); + + const bool HIGHLIGHT_SAMPLING_AREA = false; + if (HIGHLIGHT_SAMPLING_AREA) { + mSamplingArea = + client->createSurface(String8("SamplingArea"), 0, 0, PIXEL_FORMAT_RGBA_8888, + ISurfaceComposerClient::eFXSurfaceColor); + + SurfaceComposerClient::Transaction{} + .setLayer(mSamplingArea, 0x7ffffffd) + .setCrop_legacy(mSamplingArea, {0, 0, 100, 32}) + .setPosition(mSamplingArea, 490, 1606) + .setColor(mSamplingArea, half3{0, 1, 0}) + .setAlpha(mSamplingArea, 0.1) + .show(mSamplingArea) + .apply(); + } + } + + sp<IBinder> getStopLayerHandle() { return mButtonBlend->getHandle(); } + +private: + static const int32_t BLEND_WIDTH = 2; + static const int32_t SAMPLE_AREA_PADDING = 8; + static const int32_t BUTTON_PADDING = BLEND_WIDTH + SAMPLE_AREA_PADDING; + + void setColor(float color) { + const float complement = std::fmod(color + 0.5f, 1.0f); + SurfaceComposerClient::Transaction{} + .setColor(mButton, half3{complement, complement, complement}) + .setColor(mButtonBlend, half3{color, color, color}) + .apply(); + } + + void onSampleCollected(float medianLuma) override { + ATRACE_CALL(); + setColor(medianLuma); + } + + sp<SurfaceComposerClient> mClient; + sp<SurfaceControl> mButton; + sp<SurfaceControl> mButtonBlend; + sp<SurfaceControl> mSamplingArea; +}; + +} // namespace android + +using namespace android; + +int main(int, const char**) { + const Rect homeButtonArea{490, 1606, 590, 1654}; + sp<android::Button> homeButton = new android::Button("HomeButton", homeButtonArea); + const Rect backButtonArea{200, 1606, 248, 1654}; + sp<android::Button> backButton = new android::Button("BackButton", backButtonArea); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + composer->addRegionSamplingListener(homeButtonArea, homeButton->getStopLayerHandle(), + homeButton); + composer->addRegionSamplingListener(backButtonArea, backButton->getStopLayerHandle(), + backButton); + + ProcessState::self()->startThreadPool(); + IPCThreadState::self()->joinThreadPool(); + + return 0; +} diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index f1278537e4..06fe86c0cf 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -615,7 +615,8 @@ public: const ui::Dataspace /*reqDataspace*/, const ui::PixelFormat /*reqPixelFormat*/, Rect /*sourceCrop*/, uint32_t /*reqWidth*/, uint32_t /*reqHeight*/, - bool /*useIdentityTransform*/, Rotation /*rotation*/) override { + bool /*useIdentityTransform*/, Rotation /*rotation*/, + bool /*captureSecureLayers*/) override { return NO_ERROR; } virtual status_t captureLayers(const sp<IBinder>& /*parentHandle*/, @@ -668,6 +669,14 @@ public: status_t getProtectedContentSupport(bool* /*outSupported*/) const override { return NO_ERROR; } status_t isWideColorDisplay(const sp<IBinder>&, bool*) const override { return NO_ERROR; } + status_t getDisplayBrightnessSupport(const sp<IBinder>& /*displayToken*/, + bool* /*outSupport*/) const override { + return NO_ERROR; + } + status_t setDisplayBrightness(const sp<IBinder>& /*displayToken*/, + float /*brightness*/) const override { + return NO_ERROR; + } status_t addRegionSamplingListener(const Rect& /*samplingArea*/, const sp<IBinder>& /*stopLayerHandle*/, @@ -678,6 +687,14 @@ public: const sp<IRegionSamplingListener>& /*listener*/) override { return NO_ERROR; } + status_t setAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/, + const std::vector<int32_t>& /*allowedConfigs*/) override { + return NO_ERROR; + } + status_t getAllowedDisplayConfigs(const sp<IBinder>& /*displayToken*/, + std::vector<int32_t>* /*outAllowedConfigs*/) override { + return NO_ERROR; + } protected: IBinder* onAsBinder() override { return nullptr; } diff --git a/libs/input/Android.bp b/libs/input/Android.bp index fc676f14e9..2d788119cd 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -28,6 +28,7 @@ cc_library { "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", + "TouchVideoFrame.cpp", "VirtualKeyMap.cpp", ], @@ -42,12 +43,13 @@ cc_library { target: { android: { srcs: [ + "IInputFlinger.cpp", + "InputApplication.cpp", "InputTransport.cpp", + "InputWindow.cpp", + "ISetInputWindowsListener.cpp", "VelocityControl.cpp", "VelocityTracker.cpp", - "InputApplication.cpp", - "InputWindow.cpp", - "IInputFlinger.cpp" ], shared_libs: [ diff --git a/libs/input/IInputFlinger.cpp b/libs/input/IInputFlinger.cpp index acf40bcbde..4ce5a10e5c 100644 --- a/libs/input/IInputFlinger.cpp +++ b/libs/input/IInputFlinger.cpp @@ -30,7 +30,8 @@ public: explicit BpInputFlinger(const sp<IBinder>& impl) : BpInterface<IInputFlinger>(impl) { } - virtual void setInputWindows(const Vector<InputWindowInfo>& inputInfo) { + virtual void setInputWindows(const Vector<InputWindowInfo>& inputInfo, + const sp<ISetInputWindowsListener>& setInputWindowsListener) { Parcel data, reply; data.writeInterfaceToken(IInputFlinger::getInterfaceDescriptor()); @@ -38,6 +39,8 @@ public: for (const auto& info : inputInfo) { info.write(data); } + data.writeStrongBinder(IInterface::asBinder(setInputWindowsListener)); + remote()->transact(BnInputFlinger::SET_INPUT_WINDOWS_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY); } @@ -83,7 +86,9 @@ status_t BnInputFlinger::onTransact( for (size_t i = 0; i < count; i++) { handles.add(InputWindowInfo(data)); } - setInputWindows(handles); + const sp<ISetInputWindowsListener> setInputWindowsListener = + ISetInputWindowsListener::asInterface(data.readStrongBinder()); + setInputWindows(handles, setInputWindowsListener); break; } case REGISTER_INPUT_CHANNEL_TRANSACTION: { diff --git a/libs/input/ISetInputWindowsListener.cpp b/libs/input/ISetInputWindowsListener.cpp new file mode 100644 index 0000000000..a0330da89e --- /dev/null +++ b/libs/input/ISetInputWindowsListener.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/ISetInputWindowsListener.h> + +namespace android { + +class BpSetInputWindowsListener : public BpInterface<ISetInputWindowsListener> { +public: + explicit BpSetInputWindowsListener(const sp<IBinder>& impl) + : BpInterface<ISetInputWindowsListener>(impl) { + } + + virtual ~BpSetInputWindowsListener() = default; + + virtual void onSetInputWindowsFinished() { + Parcel data, reply; + data.writeInterfaceToken(ISetInputWindowsListener::getInterfaceDescriptor()); + remote()->transact(BnSetInputWindowsListener::ON_SET_INPUT_WINDOWS_FINISHED, data, &reply, + IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(SetInputWindowsListener, "android.input.ISetInputWindowsListener"); + +status_t BnSetInputWindowsListener::onTransact(uint32_t code, const Parcel& data, Parcel* reply, + uint32_t flags) { + switch(code) { + case ON_SET_INPUT_WINDOWS_FINISHED: { + CHECK_INTERFACE(ISetInputWindowsListener, data, reply); + onSetInputWindowsFinished(); + return NO_ERROR; + } + default: { + return BBinder::onTransact(code, data, reply, flags); + } + } +} + +} // namespace android diff --git a/libs/input/InputDevice.cpp b/libs/input/InputDevice.cpp index 778c4539fa..dab6eac2f4 100644 --- a/libs/input/InputDevice.cpp +++ b/libs/input/InputDevice.cpp @@ -46,15 +46,9 @@ static bool isValidNameChar(char ch) { static void appendInputDeviceConfigurationFileRelativePath(std::string& path, const std::string& name, InputDeviceConfigurationFileType type) { - path.append(CONFIGURATION_FILE_DIR[type]); - for (size_t i = 0; i < name.length(); i++) { - char ch = name[i]; - if (!isValidNameChar(ch)) { - ch = '_'; - } - path.append(&ch, 1); - } - path.append(CONFIGURATION_FILE_EXTENSION[type]); + path += CONFIGURATION_FILE_DIR[type]; + path += name; + path += CONFIGURATION_FILE_EXTENSION[type]; } std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( @@ -84,7 +78,7 @@ std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( } // Try device name. - return getInputDeviceConfigurationFilePathByName(deviceIdentifier.name, type); + return getInputDeviceConfigurationFilePathByName(deviceIdentifier.getCanonicalName(), type); } std::string getInputDeviceConfigurationFilePathByName( @@ -140,6 +134,18 @@ std::string getInputDeviceConfigurationFilePathByName( return ""; } +// --- InputDeviceIdentifier + +std::string InputDeviceIdentifier::getCanonicalName() const { + std::string replacedName = name; + for (char& ch : replacedName) { + if (!isValidNameChar(ch)) { + ch = '_'; + } + } + return replacedName; +} + // --- InputDeviceInfo --- diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp index 0f7a1f04e5..e13b40ef24 100644 --- a/libs/input/InputTransport.cpp +++ b/libs/input/InputTransport.cpp @@ -59,6 +59,18 @@ static const nsecs_t RESAMPLE_MAX_DELTA = 20 * NANOS_PER_MS; // far into the future. This time is further bounded by 50% of the last time delta. static const nsecs_t RESAMPLE_MAX_PREDICTION = 8 * NANOS_PER_MS; +/** + * System property for enabling / disabling touch resampling. + * Resampling extrapolates / interpolates the reported touch event coordinates to better + * align them to the VSYNC signal, thus resulting in smoother scrolling performance. + * Resampling is not needed (and should be disabled) on hardware that already + * has touch events triggered by VSYNC. + * Set to "1" to enable resampling (default). + * Set to "0" to disable resampling. + * Resampling is enabled by default. + */ +static const char* PROPERTY_RESAMPLING_ENABLED = "ro.input.resampling"; + template<typename T> inline static T min(const T& a, const T& b) { return a < b ? a : b; @@ -545,18 +557,7 @@ InputConsumer::~InputConsumer() { } bool InputConsumer::isTouchResamplingEnabled() { - char value[PROPERTY_VALUE_MAX]; - int length = property_get("ro.input.noresample", value, nullptr); - if (length > 0) { - if (!strcmp("1", value)) { - return false; - } - if (strcmp("0", value)) { - ALOGD("Unrecognized property value for 'ro.input.noresample'. " - "Use '1' or '0'."); - } - } - return true; + return property_get_bool(PROPERTY_RESAMPLING_ENABLED, true); } status_t InputConsumer::consume(InputEventFactoryInterface* factory, diff --git a/libs/input/InputWindow.cpp b/libs/input/InputWindow.cpp index 5c5613df82..5a60347ed3 100644 --- a/libs/input/InputWindow.cpp +++ b/libs/input/InputWindow.cpp @@ -98,7 +98,8 @@ status_t InputWindowInfo::write(Parcel& output) const { output.writeInt32(portalToDisplayId); applicationInfo.write(output); output.write(touchableRegion); - + output.writeBool(replaceTouchableRegionWithCrop); + output.writeWeakBinder(touchableRegionCropHandle); return OK; } @@ -140,6 +141,8 @@ InputWindowInfo InputWindowInfo::read(const Parcel& from) { ret.portalToDisplayId = from.readInt32(); ret.applicationInfo = InputApplicationInfo::read(from); from.read(ret.touchableRegion); + ret.replaceTouchableRegionWithCrop = from.readBool(); + ret.touchableRegionCropHandle = from.readWeakBinder(); return ret; } diff --git a/libs/input/TouchVideoFrame.cpp b/libs/input/TouchVideoFrame.cpp new file mode 100644 index 0000000000..8a4298a36f --- /dev/null +++ b/libs/input/TouchVideoFrame.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <input/TouchVideoFrame.h> + +namespace android { + +TouchVideoFrame::TouchVideoFrame(uint32_t height, uint32_t width, std::vector<int16_t> data, + const struct timeval& timestamp) : + mHeight(height), mWidth(width),mData(std::move(data)), mTimestamp(timestamp) { +} + +bool TouchVideoFrame::operator==(const TouchVideoFrame& rhs) const { + return mHeight == rhs.mHeight + && mWidth == rhs.mWidth + && mData == rhs.mData + && mTimestamp.tv_sec == rhs.mTimestamp.tv_sec + && mTimestamp.tv_usec == rhs.mTimestamp.tv_usec; +} + +uint32_t TouchVideoFrame::getHeight() const { return mHeight; } + +uint32_t TouchVideoFrame::getWidth() const { return mWidth; } + +const std::vector<int16_t>& TouchVideoFrame::getData() const { return mData; } + +const struct timeval& TouchVideoFrame::getTimestamp() const { return mTimestamp; } + +void TouchVideoFrame::rotate(int32_t orientation) { + switch (orientation) { + case DISPLAY_ORIENTATION_90: + rotateQuarterTurn(true /*clockwise*/); + break; + case DISPLAY_ORIENTATION_180: + rotate180(); + break; + case DISPLAY_ORIENTATION_270: + rotateQuarterTurn(false /*clockwise*/); + break; + } +} + +/** + * Rotate once clockwise by a quarter turn === rotate 90 degrees + * Rotate once counterclockwise by a quarter turn === rotate 270 degrees + * For a clockwise rotation: + * An element at position (i, j) is rotated to (j, height - i - 1) + * For a counterclockwise rotation: + * An element at position (i, j) is rotated to (width - j - 1, i) + */ +void TouchVideoFrame::rotateQuarterTurn(bool clockwise) { + std::vector<int16_t> rotated(mData.size()); + for (size_t i = 0; i < mHeight; i++) { + for (size_t j = 0; j < mWidth; j++) { + size_t iRotated, jRotated; + if (clockwise) { + iRotated = j; + jRotated = mHeight - i - 1; + } else { + iRotated = mWidth - j - 1; + jRotated = i; + } + size_t indexRotated = iRotated * mHeight + jRotated; + rotated[indexRotated] = mData[i * mWidth + j]; + } + } + mData = std::move(rotated); + std::swap(mHeight, mWidth); +} + +/** + * An element at position (i, j) is rotated to (height - i - 1, width - j - 1) + * This is equivalent to moving element [i] to position [height * width - i - 1] + * Since element at [height * width - i - 1] would move to position [i], + * we can just swap elements [i] and [height * width - i - 1]. + */ +void TouchVideoFrame::rotate180() { + if (mData.size() == 0) { + return; + } + // Just need to swap elements i and (height * width - 1 - i) + for (size_t i = 0; i < mData.size() / 2; i++) { + std::swap(mData[i], mData[mHeight * mWidth - 1 - i]); + } +} + +} // namespace android diff --git a/libs/input/VirtualKeyMap.cpp b/libs/input/VirtualKeyMap.cpp index 3ec53bf5a0..624f152996 100644 --- a/libs/input/VirtualKeyMap.cpp +++ b/libs/input/VirtualKeyMap.cpp @@ -28,10 +28,6 @@ // Enables debug output for the parser. #define DEBUG_PARSER 0 -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - - namespace android { static const char* WHITESPACE = " \t\r"; @@ -46,39 +42,28 @@ VirtualKeyMap::VirtualKeyMap() { VirtualKeyMap::~VirtualKeyMap() { } -status_t VirtualKeyMap::load(const std::string& filename, VirtualKeyMap** outMap) { - *outMap = nullptr; - - Tokenizer* tokenizer; - status_t status = Tokenizer::open(String8(filename.c_str()), &tokenizer); - if (status) { +std::unique_ptr<VirtualKeyMap> VirtualKeyMap::load(const std::string& filename) { + Tokenizer* t; + status_t status = Tokenizer::open(String8(filename.c_str()), &t); + if (status != OK) { ALOGE("Error %d opening virtual key map file %s.", status, filename.c_str()); - } else { - VirtualKeyMap* map = new VirtualKeyMap(); - if (!map) { - ALOGE("Error allocating virtual key map."); - status = NO_MEMORY; - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map, tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - ALOGD("Parsed key character map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().string(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (status) { - delete map; - } else { - *outMap = map; - } - } - delete tokenizer; + return nullptr; + } + std::unique_ptr<Tokenizer> tokenizer(t); + // Using 'new' to access a non-public constructor + std::unique_ptr<VirtualKeyMap> map(new VirtualKeyMap()); + if (!map) { + ALOGE("Error allocating virtual key map."); + return nullptr; } - return status; + + Parser parser(map.get(), tokenizer.get()); + status = parser.parse(); + if (status != OK) { + return nullptr; + } + + return map; } diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index fdd945e90e..ade931e01a 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -3,10 +3,12 @@ cc_test { name: "libinput_tests", srcs: [ "InputChannel_test.cpp", + "InputDevice_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", + "InputWindow_test.cpp", + "TouchVideoFrame_test.cpp", "VelocityTracker_test.cpp", - "InputWindow_test.cpp" ], cflags: [ "-Wall", @@ -34,6 +36,7 @@ cc_library_static { "-O0", "-Wall", "-Werror", + "-Wextra", ], shared_libs: [ "libinput", diff --git a/libs/input/tests/InputDevice_test.cpp b/libs/input/tests/InputDevice_test.cpp new file mode 100644 index 0000000000..c174ae94a6 --- /dev/null +++ b/libs/input/tests/InputDevice_test.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +#include <gtest/gtest.h> +#include <input/InputDevice.h> + +namespace android { + +// --- InputDeviceIdentifierTest --- + +TEST(InputDeviceIdentifierTest, getCanonicalName) { + InputDeviceIdentifier identifier; + identifier.name = "test device"; + ASSERT_EQ(std::string("test_device"), identifier.getCanonicalName()); + + identifier.name = "deviceName-123 version_C!"; + ASSERT_EQ(std::string("deviceName-123_version_C_"), identifier.getCanonicalName()); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/input/tests/InputWindow_test.cpp b/libs/input/tests/InputWindow_test.cpp index 09dd72b13b..6db18abacf 100644 --- a/libs/input/tests/InputWindow_test.cpp +++ b/libs/input/tests/InputWindow_test.cpp @@ -37,6 +37,7 @@ TEST(InputWindowInfo, ParcellingWithoutToken) { } TEST(InputWindowInfo, Parcelling) { + sp<IBinder> touchableRegionCropHandle = new BBinder(); InputWindowInfo i; i.token = new BBinder(); i.name = "Foobar"; @@ -62,6 +63,8 @@ TEST(InputWindowInfo, Parcelling) { i.inputFeatures = 29; i.displayId = 34; i.portalToDisplayId = 2; + i.replaceTouchableRegionWithCrop = true; + i.touchableRegionCropHandle = touchableRegionCropHandle; Parcel p; i.write(p); @@ -92,6 +95,8 @@ TEST(InputWindowInfo, Parcelling) { ASSERT_EQ(i.inputFeatures, i2.inputFeatures); ASSERT_EQ(i.displayId, i2.displayId); ASSERT_EQ(i.portalToDisplayId, i2.portalToDisplayId); + ASSERT_EQ(i.replaceTouchableRegionWithCrop, i2.replaceTouchableRegionWithCrop); + ASSERT_EQ(i.touchableRegionCropHandle, i2.touchableRegionCropHandle); } } // namespace test diff --git a/libs/input/tests/TouchVideoFrame_test.cpp b/libs/input/tests/TouchVideoFrame_test.cpp new file mode 100644 index 0000000000..815424ee31 --- /dev/null +++ b/libs/input/tests/TouchVideoFrame_test.cpp @@ -0,0 +1,196 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include <input/TouchVideoFrame.h> + +namespace android { +namespace test { + +static const struct timeval TIMESTAMP = {1, 2}; + +TEST(TouchVideoFrame, Constructor) { + const std::vector<int16_t> data = {1, 2, 3, 4, 5, 6}; + constexpr uint32_t height = 3; + constexpr uint32_t width = 2; + + TouchVideoFrame frame(height, width, data, TIMESTAMP); + + ASSERT_EQ(data, frame.getData()); + ASSERT_EQ(height, frame.getHeight()); + ASSERT_EQ(width, frame.getWidth()); + ASSERT_EQ(TIMESTAMP.tv_sec, frame.getTimestamp().tv_sec); + ASSERT_EQ(TIMESTAMP.tv_usec, frame.getTimestamp().tv_usec); +} + +TEST(TouchVideoFrame, Equality) { + const std::vector<int16_t> data = {1, 2, 3, 4, 5, 6}; + constexpr uint32_t height = 3; + constexpr uint32_t width = 2; + TouchVideoFrame frame(height, width, data, TIMESTAMP); + + TouchVideoFrame identicalFrame(height, width, data, TIMESTAMP); + ASSERT_EQ(frame, identicalFrame); + + // The two cases below create an invalid frame, but it is OK for comparison purposes. + // There aren't any checks currently enforced on the frame dimensions and data + // Change height + TouchVideoFrame changedHeightFrame(height + 1, width, data, TIMESTAMP); + ASSERT_FALSE(frame == changedHeightFrame); + + // Change width + TouchVideoFrame changedWidthFrame(height, width + 1, data, TIMESTAMP); + ASSERT_FALSE(frame == changedWidthFrame); + + // Change data + const std::vector<int16_t> differentData = {1, 2, 3, 3, 5, 6}; + TouchVideoFrame changedDataFrame(height, width, differentData, TIMESTAMP); + ASSERT_FALSE(frame == changedDataFrame); + + // Change timestamp + const struct timeval differentTimestamp = {TIMESTAMP.tv_sec + 1, TIMESTAMP.tv_usec + 1}; + TouchVideoFrame changedTimestampFrame(height, width, data, differentTimestamp); + ASSERT_FALSE(frame == changedTimestampFrame); +} + +// --- Rotate 90 degrees --- + +TEST(TouchVideoFrame, Rotate90_0x0) { + TouchVideoFrame frame(0, 0, {}, TIMESTAMP); + TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_1x1) { + TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); + TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_2x2) { + TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {3, 1, 4, 2}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_3x2) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 3, {5, 3, 1, 6, 4, 2}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate90_3x2_4times) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(DISPLAY_ORIENTATION_90); + frame.rotate(DISPLAY_ORIENTATION_90); + ASSERT_EQ(frame, frameOriginal); +} + +// --- Rotate 180 degrees --- + +TEST(TouchVideoFrame, Rotate180_0x0) { + TouchVideoFrame frame(0, 0, {}, TIMESTAMP); + TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_1x1) { + TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); + TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_2x2) { + TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {4, 3, 2, 1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_3x2) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameRotated(3, 2, {6, 5, 4, 3, 2, 1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate180_3x2_2times) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameOriginal); +} + +TEST(TouchVideoFrame, Rotate180_3x3) { + TouchVideoFrame frame(3, 3, {1, 2, 3, 4, 5, 6, 7, 8, 9}, TIMESTAMP); + TouchVideoFrame frameRotated(3, 3, {9, 8, 7, 6, 5, 4, 3, 2, 1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_180); + ASSERT_EQ(frame, frameRotated); +} + +// --- Rotate 270 degrees --- + +TEST(TouchVideoFrame, Rotate270_0x0) { + TouchVideoFrame frame(0, 0, {}, TIMESTAMP); + TouchVideoFrame frameRotated(0, 0, {}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_1x1) { + TouchVideoFrame frame(1, 1, {1}, TIMESTAMP); + TouchVideoFrame frameRotated(1, 1, {1}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_2x2) { + TouchVideoFrame frame(2, 2, {1, 2, 3, 4}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 2, {2, 4, 1, 3}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_3x2) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameRotated(2, 3, {2, 4, 6, 1, 3, 5}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameRotated); +} + +TEST(TouchVideoFrame, Rotate270_3x2_4times) { + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + TouchVideoFrame frameOriginal(3, 2, {1, 2, 3, 4, 5, 6}, TIMESTAMP); + frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(DISPLAY_ORIENTATION_270); + frame.rotate(DISPLAY_ORIENTATION_270); + ASSERT_EQ(frame, frameOriginal); +} + +} // namespace test +} // namespace android diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp index 3c6754289f..a10645135f 100644 --- a/libs/input/tests/VelocityTracker_test.cpp +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -66,60 +66,60 @@ static void checkCoefficient(float actual, float target) { 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; + std::unique_ptr<MotionEvent> event = std::make_unique<MotionEvent>(); + constexpr size_t pointerCount = 1; - PointerProperties properties[pointerCount]; + 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 @@ static void computeAndCheckVelocity(const Position* positions, size_t numSamples 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 @@ TEST_F(VelocityTrackerTest, DISABLED_ThreePointsPositiveVelocityTest) { // 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 @@ TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { // @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 @@ TEST_F(VelocityTrackerTest, DISABLED_SwordfishFlingDown) { { 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, DISABLED_SwordfishFlingDown) { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { { 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 @@ TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { * 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 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Constan // -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 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Linear) // -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 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol // -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 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol // -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 @@ TEST_F(VelocityTrackerTest, LeastSquaresVelocityTrackerStrategyEstimator_Parabol // -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/libs/nativewindow/AHardwareBuffer.cpp b/libs/nativewindow/AHardwareBuffer.cpp index 994e953e76..52fc9d5bad 100644 --- a/libs/nativewindow/AHardwareBuffer.cpp +++ b/libs/nativewindow/AHardwareBuffer.cpp @@ -636,11 +636,11 @@ uint64_t AHardwareBuffer_convertFromGrallocUsageBits(uint64_t usage) { } const GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(const AHardwareBuffer* buffer) { - return reinterpret_cast<const GraphicBuffer*>(buffer); + return GraphicBuffer::fromAHardwareBuffer(buffer); } GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(AHardwareBuffer* buffer) { - return reinterpret_cast<GraphicBuffer*>(buffer); + return GraphicBuffer::fromAHardwareBuffer(buffer); } const ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(const AHardwareBuffer* buffer) { @@ -652,7 +652,7 @@ ANativeWindowBuffer* AHardwareBuffer_to_ANativeWindowBuffer(AHardwareBuffer* buf } AHardwareBuffer* AHardwareBuffer_from_GraphicBuffer(GraphicBuffer* buffer) { - return reinterpret_cast<AHardwareBuffer*>(buffer); + return buffer->toAHardwareBuffer(); } } // namespace android diff --git a/libs/nativewindow/Android.bp b/libs/nativewindow/Android.bp index d8478848cb..27ab482676 100644 --- a/libs/nativewindow/Android.bp +++ b/libs/nativewindow/Android.bp @@ -24,7 +24,7 @@ ndk_headers { cc_library_headers { name: "libnativewindow_headers", export_include_dirs: ["include"], - vendor_available: false, + vendor_available: true, } ndk_library { diff --git a/libs/nativewindow/include/android/data_space.h b/libs/nativewindow/include/android/data_space.h index 3ac1c58ea9..2899bcf1f7 100644 --- a/libs/nativewindow/include/android/data_space.h +++ b/libs/nativewindow/include/android/data_space.h @@ -75,7 +75,7 @@ enum ADataSpace { * scRGB: * * The red, green, and blue components are stored in extended sRGB space, - * but are linear, not gamma-encoded. + * and gamma-encoded using the SRGB transfer function. * The RGB primaries and the white point are the same as BT.709. * * The values are floating point. diff --git a/libs/nativewindow/include/android/hdr_metadata.h b/libs/nativewindow/include/android/hdr_metadata.h index 7e1313b993..88772a92fc 100644 --- a/libs/nativewindow/include/android/hdr_metadata.h +++ b/libs/nativewindow/include/android/hdr_metadata.h @@ -33,6 +33,15 @@ __BEGIN_DECLS */ /** + * HDR metadata standards that are supported by Android. + */ +enum AHdrMetadataType : uint32_t { + HDR10_SMPTE2086 = 1, + HDR10_CTA861_3 = 2, + HDR10PLUS_SEI = 3, +}; + +/** * Color is defined in CIE XYZ coordinates. */ struct AColor_xy { diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS new file mode 100644 index 0000000000..c00fbbaad2 --- /dev/null +++ b/libs/renderengine/OWNERS @@ -0,0 +1,2 @@ +lpy@google.com +stoza@google.com diff --git a/libs/renderengine/RenderEngine.cpp b/libs/renderengine/RenderEngine.cpp index 6dd7283a15..166c267bc8 100644 --- a/libs/renderengine/RenderEngine.cpp +++ b/libs/renderengine/RenderEngine.cpp @@ -24,15 +24,16 @@ namespace android { namespace renderengine { -std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags) { +std::unique_ptr<impl::RenderEngine> RenderEngine::create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize) { char prop[PROPERTY_VALUE_MAX]; property_get(PROPERTY_DEBUG_RENDERENGINE_BACKEND, prop, "gles"); if (strcmp(prop, "gles") == 0) { ALOGD("RenderEngine GLES Backend"); - return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags); + return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize); } ALOGE("UNKNOWN BackendType: %s, create GLES RenderEngine.", prop); - return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags); + return renderengine::gl::GLESRenderEngine::create(hwcFormat, featureFlags, imageCacheSize); } RenderEngine::~RenderEngine() = default; diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 8f9071e6b8..f65130906d 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -37,6 +37,7 @@ #include <sync/sync.h> #include <ui/ColorSpace.h> #include <ui/DebugUtils.h> +#include <ui/GraphicBuffer.h> #include <ui/Rect.h> #include <ui/Region.h> #include <utils/KeyedVector.h> @@ -225,7 +226,8 @@ static status_t selectEGLConfig(EGLDisplay display, EGLint format, EGLint render return err; } -std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags) { +std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize) { // initialize EGL for the default display EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (!eglInitialize(display, nullptr, nullptr)) { @@ -295,7 +297,8 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(int hwcFormat, uint32 case GLES_VERSION_2_0: case GLES_VERSION_3_0: engine = std::make_unique<GLESRenderEngine>(featureFlags, display, config, ctxt, dummy, - protectedContext, protectedDummy); + protectedContext, protectedDummy, + imageCacheSize); break; } @@ -351,7 +354,7 @@ EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext, - EGLSurface protectedDummy) + EGLSurface protectedDummy, uint32_t imageCacheSize) : renderengine::impl::RenderEngine(featureFlags), mEGLDisplay(display), mEGLConfig(config), @@ -361,6 +364,7 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG mProtectedDummySurface(protectedDummy), mVpWidth(0), mVpHeight(0), + mFramebufferImageCacheSize(imageCacheSize), mUseColorManagement(featureFlags & USE_COLOR_MANAGEMENT) { glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); @@ -428,6 +432,15 @@ GLESRenderEngine::GLESRenderEngine(uint32_t featureFlags, EGLDisplay display, EG } GLESRenderEngine::~GLESRenderEngine() { + std::lock_guard<std::mutex> lock(mRenderingMutex); + unbindFrameBuffer(mDrawingBuffer.get()); + mDrawingBuffer = nullptr; + while (!mFramebufferImageCache.empty()) { + EGLImageKHR expired = mFramebufferImageCache.front().second; + mFramebufferImageCache.pop_front(); + eglDestroyImageKHR(mEGLDisplay, expired); + } + mImageCache.clear(); eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglTerminate(mEGLDisplay); } @@ -618,22 +631,23 @@ void GLESRenderEngine::bindExternalTextureImage(uint32_t texName, const Image& i } status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, - sp<Fence> bufferFence, bool readCache) { - return bindExternalTextureBuffer(texName, buffer, bufferFence, readCache, - /*persistCache=*/false); + sp<Fence> bufferFence) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + return bindExternalTextureBufferLocked(texName, buffer, bufferFence); } -status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, - sp<Fence> bufferFence, bool readCache, - bool persistCache) { +status_t GLESRenderEngine::bindExternalTextureBufferLocked(uint32_t texName, + sp<GraphicBuffer> buffer, + sp<Fence> bufferFence) { + if (buffer == nullptr) { + return BAD_VALUE; + } ATRACE_CALL(); - if (readCache) { - auto cachedImage = mImageCache.find(buffer->getId()); + auto cachedImage = mImageCache.find(buffer->getId()); - if (cachedImage != mImageCache.end()) { - bindExternalTextureImage(texName, *cachedImage->second); - return NO_ERROR; - } + if (cachedImage != mImageCache.end()) { + bindExternalTextureImage(texName, *cachedImage->second); + return NO_ERROR; } std::unique_ptr<Image> newImage = createImage(); @@ -670,35 +684,20 @@ status_t GLESRenderEngine::bindExternalTextureBuffer(uint32_t texName, sp<Graphi } } } - - // We don't always want to persist to the cache, e.g. on older devices we - // might bind for synchronization purposes, but that might leak if we never - // call drawLayers again, so it's just better to recreate the image again - // if needed when we draw. - if (persistCache) { - mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); - } + mImageCache.insert(std::make_pair(buffer->getId(), std::move(newImage))); return NO_ERROR; } -void GLESRenderEngine::evictImages(const std::vector<LayerSettings>& layers) { - ATRACE_CALL(); - // destroy old image references that we're not going to draw with. - std::unordered_set<uint64_t> bufIds; - for (auto layer : layers) { - if (layer.source.buffer.buffer != nullptr) { - bufIds.emplace(layer.source.buffer.buffer->getId()); - } - } - - for (auto it = mImageCache.begin(); it != mImageCache.end();) { - if (bufIds.count(it->first) == 0) { - it = mImageCache.erase(it); - } else { - it++; - } +void GLESRenderEngine::unbindExternalTextureBuffer(uint64_t bufferId) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + const auto& cachedImage = mImageCache.find(bufferId); + if (cachedImage != mImageCache.end()) { + ALOGV("Destroying image for buffer: %" PRIu64, bufferId); + mImageCache.erase(bufferId); + return; } + ALOGV("Failed to find image for buffer: %" PRIu64, bufferId); } FloatRect GLESRenderEngine::setupLayerCropping(const LayerSettings& layer, Mesh& mesh) { @@ -785,129 +784,167 @@ bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { } return success; } +EGLImageKHR GLESRenderEngine::createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, + bool isProtected) { + sp<GraphicBuffer> graphicBuffer = GraphicBuffer::from(nativeBuffer); + uint64_t bufferId = graphicBuffer->getId(); + for (const auto& image : mFramebufferImageCache) { + if (image.first == bufferId) { + return image.second; + } + } + EGLint attributes[] = { + isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, + isProtected ? EGL_TRUE : EGL_NONE, + EGL_NONE, + }; + EGLImageKHR image = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, + nativeBuffer, attributes); + if (image != EGL_NO_IMAGE_KHR) { + if (mFramebufferImageCache.size() >= mFramebufferImageCacheSize) { + EGLImageKHR expired = mFramebufferImageCache.front().second; + mFramebufferImageCache.pop_front(); + eglDestroyImageKHR(mEGLDisplay, expired); + } + mFramebufferImageCache.push_back({bufferId, image}); + } + return image; +} status_t GLESRenderEngine::drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, ANativeWindowBuffer* const buffer, - base::unique_fd* drawFence) { + base::unique_fd&& bufferFence, base::unique_fd* drawFence) { ATRACE_CALL(); if (layers.empty()) { ALOGV("Drawing empty layer stack"); return NO_ERROR; } - BindNativeBufferAsFramebuffer fbo(*this, buffer); + if (bufferFence.get() >= 0 && !waitFence(std::move(bufferFence))) { + ATRACE_NAME("Waiting before draw"); + sync_wait(bufferFence.get(), -1); + } - if (fbo.getStatus() != NO_ERROR) { - ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", - buffer->handle); - checkErrors(); - return fbo.getStatus(); + if (buffer == nullptr) { + ALOGE("No output buffer provided. Aborting GPU composition."); + return BAD_VALUE; } - evictImages(layers); + { + std::lock_guard<std::mutex> lock(mRenderingMutex); - // clear the entire buffer, sometimes when we reuse buffers we'd persist - // ghost images otherwise. - // we also require a full transparent framebuffer for overlays. This is - // probably not quite efficient on all GPUs, since we could filter out - // opaque layers. - clearWithColor(0.0, 0.0, 0.0, 0.0); + BindNativeBufferAsFramebuffer fbo(*this, buffer); - setViewportAndProjection(display.physicalDisplay, display.clip); + if (fbo.getStatus() != NO_ERROR) { + ALOGE("Failed to bind framebuffer! Aborting GPU composition for buffer (%p).", + buffer->handle); + checkErrors(); + return fbo.getStatus(); + } - setOutputDataSpace(display.outputDataspace); - setDisplayMaxLuminance(display.maxLuminance); + // clear the entire buffer, sometimes when we reuse buffers we'd persist + // ghost images otherwise. + // we also require a full transparent framebuffer for overlays. This is + // probably not quite efficient on all GPUs, since we could filter out + // opaque layers. + clearWithColor(0.0, 0.0, 0.0, 0.0); - mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; - mState.projectionMatrix = projectionMatrix; - if (!display.clearRegion.isEmpty()) { - glDisable(GL_BLEND); - fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); - } + setViewportAndProjection(display.physicalDisplay, display.clip); - Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); - for (auto layer : layers) { - mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; + setOutputDataSpace(display.outputDataspace); + setDisplayMaxLuminance(display.maxLuminance); - const FloatRect bounds = layer.geometry.boundaries; - Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); - position[0] = vec2(bounds.left, bounds.top); - position[1] = vec2(bounds.left, bounds.bottom); - position[2] = vec2(bounds.right, bounds.bottom); - position[3] = vec2(bounds.right, bounds.top); + mat4 projectionMatrix = mState.projectionMatrix * display.globalTransform; + mState.projectionMatrix = projectionMatrix; + if (!display.clearRegion.isEmpty()) { + glDisable(GL_BLEND); + fillRegionWithColor(display.clearRegion, 0.0, 0.0, 0.0, 1.0); + } - setupLayerCropping(layer, mesh); - setColorTransform(display.colorTransform * layer.colorTransform); + Mesh mesh(Mesh::TRIANGLE_FAN, 4, 2, 2); + for (auto layer : layers) { + mState.projectionMatrix = projectionMatrix * layer.geometry.positionTransform; - bool usePremultipliedAlpha = true; - bool disableTexture = true; - bool isOpaque = false; + const FloatRect bounds = layer.geometry.boundaries; + Mesh::VertexArray<vec2> position(mesh.getPositionArray<vec2>()); + position[0] = vec2(bounds.left, bounds.top); + position[1] = vec2(bounds.left, bounds.bottom); + position[2] = vec2(bounds.right, bounds.bottom); + position[3] = vec2(bounds.right, bounds.top); - if (layer.source.buffer.buffer != nullptr) { - disableTexture = false; - isOpaque = layer.source.buffer.isOpaque; + setupLayerCropping(layer, mesh); + setColorTransform(display.colorTransform * layer.colorTransform); - sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; + bool usePremultipliedAlpha = true; + bool disableTexture = true; + bool isOpaque = false; - bool readCache = layer.source.buffer.cacheHint == Buffer::CachingHint::USE_CACHE; - bindExternalTextureBuffer(layer.source.buffer.textureName, gBuf, - layer.source.buffer.fence, readCache, /*persistCache=*/true); + if (layer.source.buffer.buffer != nullptr) { + disableTexture = false; + isOpaque = layer.source.buffer.isOpaque; - usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; - Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); - mat4 texMatrix = layer.source.buffer.textureTransform; + sp<GraphicBuffer> gBuf = layer.source.buffer.buffer; + bindExternalTextureBufferLocked(layer.source.buffer.textureName, gBuf, + layer.source.buffer.fence); - texture.setMatrix(texMatrix.asArray()); - texture.setFiltering(layer.source.buffer.useTextureFiltering); + usePremultipliedAlpha = layer.source.buffer.usePremultipliedAlpha; + Texture texture(Texture::TEXTURE_EXTERNAL, layer.source.buffer.textureName); + mat4 texMatrix = layer.source.buffer.textureTransform; - texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); - setSourceY410BT2020(layer.source.buffer.isY410BT2020); + texture.setMatrix(texMatrix.asArray()); + texture.setFiltering(layer.source.buffer.useTextureFiltering); - renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); - texCoords[0] = vec2(0.0, 0.0); - texCoords[1] = vec2(0.0, 1.0); - texCoords[2] = vec2(1.0, 1.0); - texCoords[3] = vec2(1.0, 0.0); - setupLayerTexturing(texture); - } + texture.setDimensions(gBuf->getWidth(), gBuf->getHeight()); + setSourceY410BT2020(layer.source.buffer.isY410BT2020); - const half3 solidColor = layer.source.solidColor; - const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); - // Buffer sources will have a black solid color ignored in the shader, - // so in that scenario the solid color passed here is arbitrary. - setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, - layer.geometry.roundedCornersRadius); - if (layer.disableBlending) { - glDisable(GL_BLEND); - } - setSourceDataSpace(layer.sourceDataspace); + renderengine::Mesh::VertexArray<vec2> texCoords(mesh.getTexCoordArray<vec2>()); + texCoords[0] = vec2(0.0, 0.0); + texCoords[1] = vec2(0.0, 1.0); + texCoords[2] = vec2(1.0, 1.0); + texCoords[3] = vec2(1.0, 0.0); + setupLayerTexturing(texture); + } - drawMesh(mesh); + const half3 solidColor = layer.source.solidColor; + const half4 color = half4(solidColor.r, solidColor.g, solidColor.b, layer.alpha); + // Buffer sources will have a black solid color ignored in the shader, + // so in that scenario the solid color passed here is arbitrary. + setupLayerBlending(usePremultipliedAlpha, isOpaque, disableTexture, color, + layer.geometry.roundedCornersRadius); + if (layer.disableBlending) { + glDisable(GL_BLEND); + } + setSourceDataSpace(layer.sourceDataspace); + + drawMesh(mesh); - // Cleanup if there's a buffer source - if (layer.source.buffer.buffer != nullptr) { - disableBlending(); - setSourceY410BT2020(false); - disableTexturing(); + // Cleanup if there's a buffer source + if (layer.source.buffer.buffer != nullptr) { + disableBlending(); + setSourceY410BT2020(false); + disableTexturing(); + } } - } - *drawFence = flush(); - // If flush failed or we don't support native fences, we need to force the - // gl command stream to be executed. - if (drawFence->get() < 0) { - bool success = finish(); - if (!success) { - ALOGE("Failed to flush RenderEngine commands"); - checkErrors(); - // Chances are, something illegal happened (either the caller passed - // us bad parameters, or we messed up our shader generation). - return INVALID_OPERATION; + if (drawFence != nullptr) { + *drawFence = flush(); + } + // If flush failed or we don't support native fences, we need to force the + // gl command stream to be executed. + if (drawFence == nullptr || drawFence->get() < 0) { + bool success = finish(); + if (!success) { + ALOGE("Failed to flush RenderEngine commands"); + checkErrors(); + // Chances are, something illegal happened (either the caller passed + // us bad parameters, or we messed up our shader generation). + return INVALID_OPERATION; + } } - } - checkErrors(); + checkErrors(); + } return NO_ERROR; } @@ -1321,6 +1358,20 @@ bool GLESRenderEngine::needsXYZTransformMatrix() const { return (isInputHdrDataSpace || isOutputHdrDataSpace) && inputTransfer != outputTransfer; } +bool GLESRenderEngine::isImageCachedForTesting(uint64_t bufferId) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + const auto& cachedImage = mImageCache.find(bufferId); + return cachedImage != mImageCache.end(); +} + +bool GLESRenderEngine::isFramebufferImageCachedForTesting(uint64_t bufferId) { + std::lock_guard<std::mutex> lock(mRenderingMutex); + return std::any_of(mFramebufferImageCache.cbegin(), mFramebufferImageCache.cend(), + [=](std::pair<uint64_t, EGLImageKHR> image) { + return image.first == bufferId; + }); +} + // FlushTracer implementation GLESRenderEngine::FlushTracer::FlushTracer(GLESRenderEngine* engine) : mEngine(engine) { mThread = std::thread(&GLESRenderEngine::FlushTracer::loop, this); diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index 7b72666f02..efb6ef0043 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -21,16 +21,17 @@ #include <stdint.h> #include <sys/types.h> #include <condition_variable> +#include <deque> #include <mutex> #include <queue> #include <thread> +#include <unordered_map> #include <EGL/egl.h> #include <EGL/eglext.h> #include <GLES2/gl2.h> #include <renderengine/RenderEngine.h> #include <renderengine/private/Description.h> -#include <unordered_map> #define EGL_NO_CONFIG ((EGLConfig)0) @@ -47,13 +48,15 @@ class GLImage; class GLESRenderEngine : public impl::RenderEngine { public: - static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags); + static std::unique_ptr<GLESRenderEngine> create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize); static EGLConfig chooseEglConfig(EGLDisplay display, int format, bool logConfig); GLESRenderEngine(uint32_t featureFlags, // See RenderEngine::FeatureFlag EGLDisplay display, EGLConfig config, EGLContext ctxt, EGLSurface dummy, - EGLContext protectedContext, EGLSurface protectedDummy); - ~GLESRenderEngine() override; + EGLContext protectedContext, EGLSurface protectedDummy, + uint32_t imageCacheSize); + ~GLESRenderEngine() override EXCLUDES(mRenderingMutex); std::unique_ptr<Framebuffer> createFramebuffer() override; std::unique_ptr<Image> createImage() override; @@ -71,8 +74,9 @@ public: void genTextures(size_t count, uint32_t* names) override; void deleteTextures(size_t count, uint32_t const* names) override; void bindExternalTextureImage(uint32_t texName, const Image& image) override; - status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence, - bool readCache); + status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence) + EXCLUDES(mRenderingMutex); + void unbindExternalTextureBuffer(uint64_t bufferId) EXCLUDES(mRenderingMutex); status_t bindFrameBuffer(Framebuffer* framebuffer) override; void unbindFrameBuffer(Framebuffer* framebuffer) override; void checkErrors() const override; @@ -81,11 +85,20 @@ public: bool supportsProtectedContent() const override; bool useProtectedContext(bool useProtectedContext) override; status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, - ANativeWindowBuffer* buffer, base::unique_fd* drawFence) override; + ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence, + base::unique_fd* drawFence) EXCLUDES(mRenderingMutex) override; // internal to RenderEngine EGLDisplay getEGLDisplay() const { return mEGLDisplay; } EGLConfig getEGLConfig() const { return mEGLConfig; } + // Creates an output image for rendering to + EGLImageKHR createFramebufferImageIfNeeded(ANativeWindowBuffer* nativeBuffer, bool isProtected); + + // Test-only methods + // Returns true iff mImageCache contains an image keyed by bufferId + bool isImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); + // Returns true iff mFramebufferImageCache contains an image keyed by bufferId + bool isFramebufferImageCachedForTesting(uint64_t bufferId) EXCLUDES(mRenderingMutex); protected: Framebuffer* getFramebufferForDrawing() override; @@ -137,8 +150,6 @@ private: // Defines the viewport, and sets the projection matrix to the projection // defined by the clip. void setViewportAndProjection(Rect viewport, Rect clip); - status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, sp<Fence> fence, - bool readCache, bool persistCache); // Evicts stale images from the buffer cache. void evictImages(const std::vector<LayerSettings>& layers); // Computes the cropping window for the layer and sets up cropping @@ -175,6 +186,12 @@ private: // If set to true, then enables tracing flush() and finish() to systrace. bool mTraceGpuCompletion = false; int32_t mFboHeight = 0; + // Maximum size of mFramebufferImageCache. If more images would be cached, then (approximately) + // the last recently used buffer should be kicked out. + uint32_t mFramebufferImageCacheSize = 0; + + // Cache of output images, keyed by corresponding GraphicBuffer ID. + std::deque<std::pair<uint64_t, EGLImageKHR>> mFramebufferImageCache; // Current dataspace of layer being rendered ui::Dataspace mDataSpace = ui::Dataspace::UNKNOWN; @@ -187,10 +204,17 @@ private: const bool mUseColorManagement = false; // Cache of GL images that we'll store per GraphicBuffer ID - // TODO: Layer should call back on destruction instead to clean this up, - // as it has better system utilization at the potential expense of a - // more complicated interface. - std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache; + std::unordered_map<uint64_t, std::unique_ptr<Image>> mImageCache GUARDED_BY(mRenderingMutex); + // Mutex guarding rendering operations, so that: + // 1. GL operations aren't interleaved, and + // 2. Internal state related to rendering that is potentially modified by + // multiple threads is guaranteed thread-safe. + std::mutex mRenderingMutex; + + // See bindExternalTextureBuffer above, but requiring that mRenderingMutex + // is held. + status_t bindExternalTextureBufferLocked(uint32_t texName, sp<GraphicBuffer> buffer, + sp<Fence> fence) REQUIRES(mRenderingMutex); std::unique_ptr<Framebuffer> mDrawingBuffer; diff --git a/libs/renderengine/gl/GLFramebuffer.cpp b/libs/renderengine/gl/GLFramebuffer.cpp index 0e3b40568c..c45598cc16 100644 --- a/libs/renderengine/gl/GLFramebuffer.cpp +++ b/libs/renderengine/gl/GLFramebuffer.cpp @@ -30,8 +30,8 @@ namespace android { namespace renderengine { namespace gl { -GLFramebuffer::GLFramebuffer(const GLESRenderEngine& engine) - : mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { +GLFramebuffer::GLFramebuffer(GLESRenderEngine& engine) + : mEngine(engine), mEGLDisplay(engine.getEGLDisplay()), mEGLImage(EGL_NO_IMAGE_KHR) { glGenTextures(1, &mTextureName); glGenFramebuffers(1, &mFramebufferName); } @@ -39,26 +39,18 @@ GLFramebuffer::GLFramebuffer(const GLESRenderEngine& engine) GLFramebuffer::~GLFramebuffer() { glDeleteFramebuffers(1, &mFramebufferName); glDeleteTextures(1, &mTextureName); - eglDestroyImageKHR(mEGLDisplay, mEGLImage); } bool GLFramebuffer::setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) { ATRACE_CALL(); if (mEGLImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mEGLDisplay, mEGLImage); mEGLImage = EGL_NO_IMAGE_KHR; mBufferWidth = 0; mBufferHeight = 0; } if (nativeBuffer) { - EGLint attributes[] = { - isProtected ? EGL_PROTECTED_CONTENT_EXT : EGL_NONE, - isProtected ? EGL_TRUE : EGL_NONE, - EGL_NONE, - }; - mEGLImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, - nativeBuffer, attributes); + mEGLImage = mEngine.createFramebufferImageIfNeeded(nativeBuffer, isProtected); if (mEGLImage == EGL_NO_IMAGE_KHR) { return false; } diff --git a/libs/renderengine/gl/GLFramebuffer.h b/libs/renderengine/gl/GLFramebuffer.h index 5043c590a9..1289fbff19 100644 --- a/libs/renderengine/gl/GLFramebuffer.h +++ b/libs/renderengine/gl/GLFramebuffer.h @@ -32,7 +32,7 @@ class GLESRenderEngine; class GLFramebuffer : public renderengine::Framebuffer { public: - explicit GLFramebuffer(const GLESRenderEngine& engine); + explicit GLFramebuffer(GLESRenderEngine& engine); ~GLFramebuffer() override; bool setNativeWindowBuffer(ANativeWindowBuffer* nativeBuffer, bool isProtected) override; @@ -43,6 +43,7 @@ public: int32_t getBufferWidth() const { return mBufferWidth; } private: + GLESRenderEngine& mEngine; EGLDisplay mEGLDisplay; EGLImageKHR mEGLImage; uint32_t mTextureName, mFramebufferName; diff --git a/libs/renderengine/include/renderengine/LayerSettings.h b/libs/renderengine/include/renderengine/LayerSettings.h index aa45ed8353..b8bf8019de 100644 --- a/libs/renderengine/include/renderengine/LayerSettings.h +++ b/libs/renderengine/include/renderengine/LayerSettings.h @@ -32,16 +32,6 @@ namespace renderengine { // Metadata describing the input buffer to render from. struct Buffer { - // Hint for whether to use the Image cache or not. - // If NO_CACHE is specified, then upload the contents of the GraphicBuffer - // to the GPU, without checking against any implementation defined cache. - // If USE_CACHE is specified, then check against an implementation defined - // cache first. If there is an Image cached for the given GraphicBuffer id, - // then use that instead of the provided buffer contents. If there is no - // cached image or the RenderEngine implementation does not support caching, - // then use the GraphicBuffer contents. - enum class CachingHint { NO_CACHE, USE_CACHE }; - // Buffer containing the image that we will render. // If buffer == nullptr, then the rest of the fields in this struct will be // ignored. @@ -50,9 +40,6 @@ struct Buffer { // Fence that will fire when the buffer is ready to be bound. sp<Fence> fence = nullptr; - // Caching hint to use when uploading buffer contents. - CachingHint cacheHint = CachingHint::NO_CACHE; - // Texture identifier to bind the external texture to. // TODO(alecmouri): This is GL-specific...make the type backend-agnostic. uint32_t textureName = 0; diff --git a/libs/renderengine/include/renderengine/RenderEngine.h b/libs/renderengine/include/renderengine/RenderEngine.h index b51ed22ef0..ab342744c2 100644 --- a/libs/renderengine/include/renderengine/RenderEngine.h +++ b/libs/renderengine/include/renderengine/RenderEngine.h @@ -65,7 +65,8 @@ public: USE_HIGH_PRIORITY_CONTEXT = 1 << 1, // Use high priority context }; - static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags); + static std::unique_ptr<impl::RenderEngine> create(int hwcFormat, uint32_t featureFlags, + uint32_t imageCacheSize); virtual ~RenderEngine() = 0; @@ -109,7 +110,11 @@ public: virtual void deleteTextures(size_t count, uint32_t const* names) = 0; virtual void bindExternalTextureImage(uint32_t texName, const Image& image) = 0; virtual status_t bindExternalTextureBuffer(uint32_t texName, sp<GraphicBuffer> buffer, - sp<Fence> fence, bool cleanCache) = 0; + sp<Fence> fence) = 0; + // Removes internal resources referenced by the bufferId. This method should be + // invoked when the caller will no longer hold a reference to a GraphicBuffer + // and needs to clean up its resources. + virtual void unbindExternalTextureBuffer(uint64_t bufferId) = 0; // When binding a native buffer, it must be done before setViewportAndProjection // Returns NO_ERROR when binds successfully, NO_MEMORY when there's no memory for allocation. virtual status_t bindFrameBuffer(Framebuffer* framebuffer) = 0; @@ -167,7 +172,9 @@ public: // drawing any layers. // @param layers The layers to draw onto the display, in Z-order. // @param buffer The buffer which will be drawn to. This buffer will be - // ready once displayFence fires. + // ready once drawFence fires. + // @param bufferFence Fence signalling that the buffer is ready to be drawn + // to. // @param drawFence A pointer to a fence, which will fire when the buffer // has been drawn to and is ready to be examined. The fence will be // initialized by this method. The caller will be responsible for owning the @@ -176,7 +183,8 @@ public: // now, this always returns NO_ERROR. virtual status_t drawLayers(const DisplaySettings& display, const std::vector<LayerSettings>& layers, - ANativeWindowBuffer* buffer, base::unique_fd* drawFence) = 0; + ANativeWindowBuffer* buffer, base::unique_fd&& bufferFence, + base::unique_fd* drawFence) = 0; protected: // Gets a framebuffer to render to. This framebuffer may or may not be diff --git a/libs/renderengine/include/renderengine/mock/RenderEngine.h b/libs/renderengine/include/renderengine/mock/RenderEngine.h index 800eac356e..ddf742098a 100644 --- a/libs/renderengine/include/renderengine/mock/RenderEngine.h +++ b/libs/renderengine/include/renderengine/mock/RenderEngine.h @@ -53,7 +53,8 @@ public: MOCK_METHOD2(genTextures, void(size_t, uint32_t*)); MOCK_METHOD2(deleteTextures, void(size_t, uint32_t const*)); MOCK_METHOD2(bindExternalTextureImage, void(uint32_t, const renderengine::Image&)); - MOCK_METHOD4(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>, bool)); + MOCK_METHOD3(bindExternalTextureBuffer, status_t(uint32_t, sp<GraphicBuffer>, sp<Fence>)); + MOCK_METHOD1(unbindExternalTextureBuffer, void(uint64_t)); MOCK_CONST_METHOD0(checkErrors, void()); MOCK_METHOD4(setViewportAndProjection, void(size_t, size_t, Rect, ui::Transform::orientation_flags)); @@ -78,9 +79,9 @@ public: MOCK_CONST_METHOD0(isProtected, bool()); MOCK_CONST_METHOD0(supportsProtectedContent, bool()); MOCK_METHOD1(useProtectedContext, bool(bool)); - MOCK_METHOD4(drawLayers, + MOCK_METHOD5(drawLayers, status_t(const DisplaySettings&, const std::vector<LayerSettings>&, - ANativeWindowBuffer*, base::unique_fd*)); + ANativeWindowBuffer*, base::unique_fd&&, base::unique_fd*)); }; } // namespace mock diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index f82beeb4c5..8c93cf432c 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -19,6 +19,7 @@ #include <renderengine/RenderEngine.h> #include <sync/sync.h> #include <ui/PixelFormat.h> +#include "../gl/GLESRenderEngine.h" constexpr int DEFAULT_DISPLAY_WIDTH = 128; constexpr int DEFAULT_DISPLAY_HEIGHT = 256; @@ -27,6 +28,19 @@ constexpr int DEFAULT_DISPLAY_OFFSET = 64; namespace android { struct RenderEngineTest : public ::testing::Test { + static void SetUpTestSuite() { + sRE = renderengine::gl::GLESRenderEngine::create(static_cast<int32_t>( + ui::PixelFormat::RGBA_8888), + 0, 1); + } + + static void TearDownTestSuite() { + // The ordering here is important - sCurrentBuffer must live longer + // than RenderEngine to avoid a null reference on tear-down. + sRE = nullptr; + sCurrentBuffer = nullptr; + } + static sp<GraphicBuffer> allocateDefaultBuffer() { return new GraphicBuffer(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT, HAL_PIXEL_FORMAT_RGBA_8888, 1, @@ -101,11 +115,12 @@ struct RenderEngineTest : public ::testing::Test { DEFAULT_DISPLAY_HEIGHT - DEFAULT_DISPLAY_OFFSET); } - static void invokeDraw(renderengine::DisplaySettings settings, - std::vector<renderengine::LayerSettings> layers, - sp<GraphicBuffer> buffer) { + void invokeDraw(renderengine::DisplaySettings settings, + std::vector<renderengine::LayerSettings> layers, sp<GraphicBuffer> buffer) { base::unique_fd fence; - status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), &fence); + status_t status = sRE->drawLayers(settings, layers, buffer->getNativeBuffer(), + base::unique_fd(), &fence); + sCurrentBuffer = buffer; int fd = fence.release(); if (fd >= 0) { @@ -114,9 +129,12 @@ struct RenderEngineTest : public ::testing::Test { } ASSERT_EQ(NO_ERROR, status); + if (layers.size() > 0) { + ASSERT_TRUE(sRE->isFramebufferImageCachedForTesting(buffer->getId())); + } } - static void drawEmptyLayers() { + void drawEmptyLayers() { renderengine::DisplaySettings settings; std::vector<renderengine::LayerSettings> layers; // Meaningless buffer since we don't do any drawing @@ -199,17 +217,22 @@ struct RenderEngineTest : public ::testing::Test { void clearRegion(); - // Dumb hack to get aroud the fact that tear-down for renderengine isn't - // well defined right now, so we can't create multiple instances - static std::unique_ptr<renderengine::RenderEngine> sRE; + // Keep around the same renderengine object to save on initialization time. + // For now, exercise the GL backend directly so that some caching specifics + // can be tested without changing the interface. + static std::unique_ptr<renderengine::gl::GLESRenderEngine> sRE; + // Dumb hack to avoid NPE in the EGL driver: the GraphicBuffer needs to + // be freed *after* RenderEngine is destroyed, so that the EGL image is + // destroyed first. + static sp<GraphicBuffer> sCurrentBuffer; sp<GraphicBuffer> mBuffer; std::vector<uint32_t> mTexNames; }; -std::unique_ptr<renderengine::RenderEngine> RenderEngineTest::sRE = - renderengine::RenderEngine::create(static_cast<int32_t>(ui::PixelFormat::RGBA_8888), 0); +std::unique_ptr<renderengine::gl::GLESRenderEngine> RenderEngineTest::sRE = nullptr; +sp<GraphicBuffer> RenderEngineTest::sCurrentBuffer = nullptr; struct ColorSourceVariant { static void fillColor(renderengine::LayerSettings& layer, half r, half g, half b, @@ -244,7 +267,7 @@ struct BufferSourceVariant { RenderEngineTest* fixture) { sp<GraphicBuffer> buf = RenderEngineTest::allocateSourceBuffer(1, 1); uint32_t texName; - RenderEngineTest::sRE->genTextures(1, &texName); + fixture->sRE->genTextures(1, &texName); fixture->mTexNames.push_back(texName); uint8_t* pixels; @@ -739,6 +762,38 @@ TEST_F(RenderEngineTest, drawLayers_noLayersToDraw) { drawEmptyLayers(); } +TEST_F(RenderEngineTest, drawLayers_nullOutputBuffer) { + renderengine::DisplaySettings settings; + std::vector<renderengine::LayerSettings> layers; + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layers.push_back(layer); + base::unique_fd fence; + status_t status = sRE->drawLayers(settings, layers, nullptr, base::unique_fd(), &fence); + + ASSERT_EQ(BAD_VALUE, status); +} + +TEST_F(RenderEngineTest, drawLayers_nullOutputFence) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + layer.alpha = 1.0; + layers.push_back(layer); + + status_t status = sRE->drawLayers(settings, layers, mBuffer->getNativeBuffer(), + base::unique_fd(), nullptr); + sCurrentBuffer = mBuffer; + ASSERT_EQ(NO_ERROR, status); + expectBufferColor(fullscreenRect(), 255, 0, 0, 255); +} + TEST_F(RenderEngineTest, drawLayers_fillRedBuffer_colorSource) { fillRedBuffer<ColorSourceVariant>(); } @@ -911,4 +966,41 @@ TEST_F(RenderEngineTest, drawLayers_clearRegion) { clearRegion(); } +TEST_F(RenderEngineTest, drawLayers_fillsBufferAndCachesImages) { + renderengine::DisplaySettings settings; + settings.physicalDisplay = fullscreenRect(); + settings.clip = fullscreenRect(); + + std::vector<renderengine::LayerSettings> layers; + + renderengine::LayerSettings layer; + layer.geometry.boundaries = fullscreenRect().toFloatRect(); + BufferSourceVariant<ForceOpaqueBufferVariant>::fillColor(layer, 1.0f, 0.0f, 0.0f, this); + + layers.push_back(layer); + invokeDraw(settings, layers, mBuffer); + uint64_t bufferId = layer.source.buffer.buffer->getId(); + EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); + sRE->unbindExternalTextureBuffer(bufferId); + EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); +} + +TEST_F(RenderEngineTest, drawLayers_bindExternalBufferWithNullBuffer) { + status_t result = sRE->bindExternalTextureBuffer(0, nullptr, nullptr); + ASSERT_EQ(BAD_VALUE, result); +} + +TEST_F(RenderEngineTest, drawLayers_bindExternalBufferCachesImages) { + sp<GraphicBuffer> buf = allocateSourceBuffer(1, 1); + uint32_t texName; + sRE->genTextures(1, &texName); + mTexNames.push_back(texName); + + sRE->bindExternalTextureBuffer(texName, buf, nullptr); + uint64_t bufferId = buf->getId(); + EXPECT_TRUE(sRE->isImageCachedForTesting(bufferId)); + sRE->unbindExternalTextureBuffer(bufferId); + EXPECT_FALSE(sRE->isImageCachedForTesting(bufferId)); +} + } // namespace android diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp index 00e227fe20..e521b613a0 100644 --- a/libs/ui/Android.bp +++ b/libs/ui/Android.bp @@ -45,6 +45,8 @@ cc_library_shared { // Don't warn about struct padding "-Wno-padded", + + "-Wno-switch-enum", ], sanitize: { @@ -68,6 +70,7 @@ cc_library_shared { "GraphicBufferMapper.cpp", "HdrCapabilities.cpp", "PixelFormat.cpp", + "PublicFormat.cpp", "Rect.cpp", "Region.cpp", "Size.cpp", @@ -87,8 +90,6 @@ cc_library_shared { "android.hardware.graphics.mapper@2.0", "android.hardware.graphics.mapper@2.1", "android.hardware.graphics.mapper@3.0", - "android.hardware.configstore@1.2", - "android.hardware.configstore-utils", "libbase", "libcutils", "libhardware", @@ -102,7 +103,6 @@ cc_library_shared { ], export_shared_lib_headers: [ - "android.hardware.configstore@1.2", "android.hardware.graphics.common@1.2", ], @@ -124,7 +124,6 @@ cc_library_shared { exclude_header_libs: [ "libbufferhub_headers", "libdvr_headers", - "libnativewindow_headers", ], exclude_shared_libs: [ "android.frameworks.bufferhub@1.0", @@ -152,6 +151,7 @@ cc_library_shared { export_header_lib_headers: [ "libbase_headers", "libnativebase_headers", + "libnativewindow_headers", "libhardware_headers", "libui_headers", ], diff --git a/libs/ui/GraphicBuffer.cpp b/libs/ui/GraphicBuffer.cpp index f487dfa81b..f800627ef7 100644 --- a/libs/ui/GraphicBuffer.cpp +++ b/libs/ui/GraphicBuffer.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "GraphicBuffer" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS #include <ui/GraphicBuffer.h> @@ -29,6 +30,7 @@ #include <ui/Gralloc2.h> #include <ui/GraphicBufferAllocator.h> #include <ui/GraphicBufferMapper.h> +#include <utils/Trace.h> namespace android { @@ -47,6 +49,22 @@ sp<GraphicBuffer> GraphicBuffer::from(ANativeWindowBuffer* anwb) { return static_cast<GraphicBuffer *>(anwb); } +GraphicBuffer* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer* buffer) { + return reinterpret_cast<GraphicBuffer*>(buffer); +} + +GraphicBuffer const* GraphicBuffer::fromAHardwareBuffer(AHardwareBuffer const* buffer) { + return reinterpret_cast<GraphicBuffer const*>(buffer); +} + +AHardwareBuffer* GraphicBuffer::toAHardwareBuffer() { + return reinterpret_cast<AHardwareBuffer*>(this); +} + +AHardwareBuffer const* GraphicBuffer::toAHardwareBuffer() const { + return reinterpret_cast<AHardwareBuffer const*>(this); +} + GraphicBuffer::GraphicBuffer() : BASE(), mOwner(ownData), mBufferMapper(GraphicBufferMapper::get()), mInitCheck(NO_ERROR), mId(getUniqueId()), mGenerationNumber(0) @@ -111,6 +129,7 @@ GraphicBuffer::GraphicBuffer(std::unique_ptr<BufferHubBuffer> buffer) : GraphicB GraphicBuffer::~GraphicBuffer() { + ATRACE_CALL(); if (handle) { free_handle(); } diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp new file mode 100644 index 0000000000..70e3ce768c --- /dev/null +++ b/libs/ui/PublicFormat.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <ui/GraphicTypes.h> // ui::Dataspace +#include <ui/PublicFormat.h> + +// ---------------------------------------------------------------------------- +namespace android { +// ---------------------------------------------------------------------------- + +using ui::Dataspace; + +int mapPublicFormatToHalFormat(PublicFormat f) { + switch (f) { + case PublicFormat::JPEG: + case PublicFormat::DEPTH_POINT_CLOUD: + case PublicFormat::DEPTH_JPEG: + case PublicFormat::HEIC: + return HAL_PIXEL_FORMAT_BLOB; + case PublicFormat::DEPTH16: + return HAL_PIXEL_FORMAT_Y16; + case PublicFormat::RAW_SENSOR: + case PublicFormat::RAW_DEPTH: + return HAL_PIXEL_FORMAT_RAW16; + default: + // Most formats map 1:1 + return static_cast<int>(f); + } +} + +android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) { + Dataspace dataspace; + switch (f) { + case PublicFormat::JPEG: + dataspace = Dataspace::V0_JFIF; + break; + case PublicFormat::DEPTH_POINT_CLOUD: + case PublicFormat::DEPTH16: + case PublicFormat::RAW_DEPTH: + dataspace = Dataspace::DEPTH; + break; + case PublicFormat::RAW_SENSOR: + case PublicFormat::RAW_PRIVATE: + case PublicFormat::RAW10: + case PublicFormat::RAW12: + dataspace = Dataspace::ARBITRARY; + break; + case PublicFormat::YUV_420_888: + case PublicFormat::NV21: + case PublicFormat::YV12: + dataspace = Dataspace::V0_JFIF; + break; + case PublicFormat::DEPTH_JPEG: + dataspace = Dataspace::DYNAMIC_DEPTH; + break; + case PublicFormat::HEIC: + dataspace = Dataspace::HEIF; + break; + default: + // Most formats map to UNKNOWN + dataspace = Dataspace::UNKNOWN; + break; + } + return static_cast<android_dataspace>(dataspace); +} + +PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) { + Dataspace ds = static_cast<Dataspace>(dataSpace); + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGBA_FP16: + case HAL_PIXEL_FORMAT_RGBA_1010102: + case HAL_PIXEL_FORMAT_RGB_888: + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_Y8: + case HAL_PIXEL_FORMAT_RAW10: + case HAL_PIXEL_FORMAT_RAW12: + case HAL_PIXEL_FORMAT_YCbCr_420_888: + case HAL_PIXEL_FORMAT_YV12: + // Enums overlap in both name and value + return static_cast<PublicFormat>(format); + case HAL_PIXEL_FORMAT_RAW16: + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::RAW_DEPTH; + default: + return PublicFormat::RAW_SENSOR; + } + case HAL_PIXEL_FORMAT_RAW_OPAQUE: + // Name differs, though value is the same + return PublicFormat::RAW_PRIVATE; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + // Name differs, though the value is the same + return PublicFormat::NV16; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + // Name differs, though the value is the same + return PublicFormat::NV21; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + // Name differs, though the value is the same + return PublicFormat::YUY2; + case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED: + // Name differs, though the value is the same + return PublicFormat::PRIVATE; + case HAL_PIXEL_FORMAT_Y16: + // Dataspace-dependent + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::DEPTH16; + default: + // Assume non-depth Y16 is just Y16. + return PublicFormat::Y16; + } + case HAL_PIXEL_FORMAT_BLOB: + // Dataspace-dependent + switch (ds) { + case Dataspace::DEPTH: + return PublicFormat::DEPTH_POINT_CLOUD; + case Dataspace::V0_JFIF: + return PublicFormat::JPEG; + case Dataspace::HEIF: + return PublicFormat::HEIC; + default: + if (dataSpace == static_cast<android_dataspace>(HAL_DATASPACE_DYNAMIC_DEPTH)) { + return PublicFormat::DEPTH_JPEG; + } else { + // Assume otherwise-marked blobs are also JPEG + return PublicFormat::JPEG; + } + } + case HAL_PIXEL_FORMAT_BGRA_8888: + // Not defined in public API + return PublicFormat::UNKNOWN; + + default: + return PublicFormat::UNKNOWN; + } +} + +// ---------------------------------------------------------------------------- +}; // namespace android +// ---------------------------------------------------------------------------- diff --git a/libs/ui/Transform.cpp b/libs/ui/Transform.cpp index d13942dca4..28c3f7bdd9 100644 --- a/libs/ui/Transform.cpp +++ b/libs/ui/Transform.cpp @@ -381,6 +381,37 @@ bool Transform::preserveRects() const return (getOrientation() & ROT_INVALID) ? false : true; } +mat4 Transform::asMatrix4() const { + // Internally Transform uses a 3x3 matrix since the transform is meant for + // two-dimensional values. An equivalent 4x4 matrix means inserting an extra + // row and column which adds as an identity transform on the third + // dimension. + + mat4 m = mat4{mat4::NO_INIT}; // NO_INIT since we explicitly set every element + + m[0][0] = mMatrix[0][0]; + m[0][1] = mMatrix[0][1]; + m[0][2] = 0.f; + m[0][3] = mMatrix[0][2]; + + m[1][0] = mMatrix[1][0]; + m[1][1] = mMatrix[1][1]; + m[1][2] = 0.f; + m[1][3] = mMatrix[1][2]; + + m[2][0] = 0.f; + m[2][1] = 0.f; + m[2][2] = 1.f; + m[2][3] = 0.f; + + m[3][0] = mMatrix[2][0]; + m[3][1] = mMatrix[2][1]; + m[3][2] = 0.f; + m[3][3] = mMatrix[2][2]; + + return m; +} + void Transform::dump(std::string& out, const char* name) const { using android::base::StringAppendF; diff --git a/libs/ui/include/ui/ConfigStoreTypes.h b/libs/ui/include/ui/ConfigStoreTypes.h index 37a2bd5451..4445ae97c9 100644 --- a/libs/ui/include/ui/ConfigStoreTypes.h +++ b/libs/ui/include/ui/ConfigStoreTypes.h @@ -16,14 +16,22 @@ #pragma once -#include <android/hardware/configstore/1.2/types.h> - // android::ui::* in this header file will alias different types as // the HIDL interface is updated. namespace android { namespace ui { -using android::hardware::configstore::V1_2::DisplayPrimaries; +struct CieXyz { + float X; + float Y; + float Z; +}; +struct DisplayPrimaries { + CieXyz red; + CieXyz green; + CieXyz blue; + CieXyz white; +}; } // namespace ui } // namespace android diff --git a/libs/ui/include/ui/Fence.h b/libs/ui/include/ui/Fence.h index ec67fa972c..6efecd3c0e 100644 --- a/libs/ui/include/ui/Fence.h +++ b/libs/ui/include/ui/Fence.h @@ -99,6 +99,12 @@ public: // be returned and errno will indicate the problem. int dup() const; + // Return the underlying file descriptor without giving up ownership. The + // returned file descriptor is only valid for as long as the owning Fence + // object lives. (If the situation is unclear, dup() is always a safer + // option.) + int get() const { return mFenceFd.get(); } + // getSignalTime returns the system monotonic clock time at which the // fence transitioned to the signaled state. If the fence is not signaled // then SIGNAL_TIME_PENDING is returned. If the fence is invalid or if an diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index 4d4ee68194..e0c655813d 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -22,6 +22,7 @@ #include <string> +#include <android/hardware_buffer.h> #include <ui/ANativeObjectBase.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> @@ -78,6 +79,10 @@ public: static sp<GraphicBuffer> from(ANativeWindowBuffer *); + static GraphicBuffer* fromAHardwareBuffer(AHardwareBuffer*); + static GraphicBuffer const* fromAHardwareBuffer(AHardwareBuffer const*); + AHardwareBuffer* toAHardwareBuffer(); + AHardwareBuffer const* toAHardwareBuffer() const; // Create a GraphicBuffer to be unflatten'ed into or be reallocated. GraphicBuffer(); diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h new file mode 100644 index 0000000000..1152cc5526 --- /dev/null +++ b/libs/ui/include/ui/PublicFormat.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UI_PUBLICFORMAT_H +#define UI_PUBLICFORMAT_H + +#include <system/graphics.h> + +namespace android { + +/** + * Enum mirroring the public API definitions for image and pixel formats. + * Some of these are hidden in the public API + * + * Keep up to date with android.graphics.ImageFormat and + * android.graphics.PixelFormat + * + * TODO: PublicFormat is going to be deprecated(b/126594675) + */ +enum class PublicFormat { + UNKNOWN = 0x0, + RGBA_8888 = 0x1, + RGBX_8888 = 0x2, + RGB_888 = 0x3, + RGB_565 = 0x4, + NV16 = 0x10, + NV21 = 0x11, + YUY2 = 0x14, + RGBA_FP16 = 0x16, + RAW_SENSOR = 0x20, + PRIVATE = 0x22, + YUV_420_888 = 0x23, + RAW_PRIVATE = 0x24, + RAW10 = 0x25, + RAW12 = 0x26, + RGBA_1010102 = 0x2b, + JPEG = 0x100, + DEPTH_POINT_CLOUD = 0x101, + RAW_DEPTH = 0x1002, // @hide + YV12 = 0x32315659, + Y8 = 0x20203859, + Y16 = 0x20363159, // @hide + DEPTH16 = 0x44363159, + DEPTH_JPEG = 0x69656963, + HEIC = 0x48454946, +}; + +/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL + * format */ +extern int mapPublicFormatToHalFormat(PublicFormat f); + +/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL + * dataspace */ +extern android_dataspace mapPublicFormatToHalDataspace(PublicFormat f); + +/* Convert from HAL format, dataspace pair to + * android.graphics.ImageFormat/PixelFormat. + * For unknown/unspecified pairs, returns PublicFormat::UNKNOWN */ +extern PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace); + +}; // namespace android + +#endif // UI_PUBLICFORMAT_H diff --git a/libs/ui/include/ui/Transform.h b/libs/ui/include/ui/Transform.h index dcb26cf5f4..f29a370194 100644 --- a/libs/ui/include/ui/Transform.h +++ b/libs/ui/include/ui/Transform.h @@ -22,6 +22,7 @@ #include <string> #include <hardware/hardware.h> +#include <math/mat4.h> #include <math/vec2.h> #include <math/vec3.h> #include <ui/Point.h> @@ -88,6 +89,9 @@ public: vec2 transform(const vec2& v) const; vec3 transform(const vec3& v) const; + // Expands from the internal 3x3 matrix to an equivalent 4x4 matrix + mat4 asMatrix4() const; + Transform inverse() const; // for debugging diff --git a/libs/ui/include_vndk/ui/ConfigStoreTypes.h b/libs/ui/include_vndk/ui/ConfigStoreTypes.h index 37a2bd5451..4445ae97c9 100644 --- a/libs/ui/include_vndk/ui/ConfigStoreTypes.h +++ b/libs/ui/include_vndk/ui/ConfigStoreTypes.h @@ -16,14 +16,22 @@ #pragma once -#include <android/hardware/configstore/1.2/types.h> - // android::ui::* in this header file will alias different types as // the HIDL interface is updated. namespace android { namespace ui { -using android::hardware::configstore::V1_2::DisplayPrimaries; +struct CieXyz { + float X; + float Y; + float Z; +}; +struct DisplayPrimaries { + CieXyz red; + CieXyz green; + CieXyz blue; + CieXyz white; +}; } // namespace ui } // namespace android diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp index c80b79a319..c0bace8486 100644 --- a/opengl/libs/Android.bp +++ b/opengl/libs/Android.bp @@ -154,8 +154,8 @@ cc_library_shared { "libbase", "libhidlbase", "libhidltransport", - "libnativebridge", - "libnativeloader", + "libnativebridge_lazy", + "libnativeloader_lazy", "libutils", ], static_libs: [ diff --git a/opengl/libs/EGL/Loader.cpp b/opengl/libs/EGL/Loader.cpp index 4cafe2b819..259242b459 100644 --- a/opengl/libs/EGL/Loader.cpp +++ b/opengl/libs/EGL/Loader.cpp @@ -27,6 +27,7 @@ #include <android/dlext.h> #include <cutils/properties.h> #include <log/log.h> +#include <utils/Timers.h> #ifndef __ANDROID_VNDK__ #include <graphicsenv/GraphicsEnv.h> @@ -217,6 +218,7 @@ static void setEmulatorGlesValue(void) { void* Loader::open(egl_connection_t* cnx) { ATRACE_CALL(); + const nsecs_t openTime = systemTime(); void* dso; driver_t* hnd = nullptr; @@ -234,6 +236,8 @@ void* Loader::open(egl_connection_t* cnx) if (dso) { hnd = new driver_t(dso); } else { + android::GraphicsEnv::getInstance().clearDriverLoadingInfo( + android::GraphicsEnv::Api::API_GL); // Always load EGL first dso = load_driver("EGL", cnx, EGL); if (dso) { @@ -243,19 +247,30 @@ void* Loader::open(egl_connection_t* cnx) } } + if (!hnd) { + android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, + false, systemTime() - openTime); + } + LOG_ALWAYS_FATAL_IF(!hnd, "couldn't find an OpenGL ES implementation"); cnx->libEgl = load_wrapper(EGL_WRAPPER_DIR "/libEGL.so"); cnx->libGles2 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv2.so"); cnx->libGles1 = load_wrapper(EGL_WRAPPER_DIR "/libGLESv1_CM.so"); + if (!cnx->libEgl || !cnx->libGles2 || !cnx->libGles1) { + android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, + false, systemTime() - openTime); + } + LOG_ALWAYS_FATAL_IF(!cnx->libEgl, "couldn't load system EGL wrapper libraries"); LOG_ALWAYS_FATAL_IF(!cnx->libGles2 || !cnx->libGles1, "couldn't load system OpenGL ES wrapper libraries"); - android::GraphicsEnv::getInstance().sendGpuStats(); + android::GraphicsEnv::getInstance().setDriverLoaded(android::GraphicsEnv::Api::API_GL, true, + systemTime() - openTime); return (void*)hnd; } @@ -591,17 +606,21 @@ void *Loader::load_driver(const char* kind, void* dso = nullptr; android_namespace_t* ns = android::GraphicsEnv::getInstance().getAngleNamespace(); if (ns) { + android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::ANGLE); dso = load_angle(kind, ns, cnx); } #ifndef __ANDROID_VNDK__ if (!dso) { android_namespace_t* ns = android::GraphicsEnv::getInstance().getDriverNamespace(); if (ns) { + android::GraphicsEnv::getInstance().setDriverToLoad( + android::GraphicsEnv::Driver::GL_UPDATED); dso = load_updated_driver(kind, ns); } } #endif if (!dso) { + android::GraphicsEnv::getInstance().setDriverToLoad(android::GraphicsEnv::Driver::GL); dso = load_system_driver(kind); if (!dso) return nullptr; diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index 9fa58e2b58..da9bb49495 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -90,6 +90,10 @@ fi rm src/*.class +# Add UnsupportedAppUsage.java to known sources. +mkdir -p out/android/annotation +cp ../../../../base/core/java/android/annotation/UnsupportedAppUsage.java out/android/annotation + pushd out > /dev/null mkdir classes javac -d classes android/opengl/EGL14.java \ @@ -109,7 +113,8 @@ javac -d classes android/opengl/EGL14.java \ android/opengl/GLES30.java \ android/opengl/GLES31.java \ android/opengl/GLES31Ext.java \ - android/opengl/GLES32.java + android/opengl/GLES32.java \ + android/annotation/UnsupportedAppUsage.java popd > /dev/null JAVA_RESULT=$? if [ $JAVA_RESULT -ne 0 ]; then @@ -142,7 +147,7 @@ compareGenerated() { echo SAID_PLEASE=1 fi - echo " cp $2/$3 $1" + echo " cp $2/$3 $1/$3" echo " (cd $1; git add $3)" KEEP_GENERATED=1 fi diff --git a/opengl/tools/glgen/src/JniCodeEmitter.java b/opengl/tools/glgen/src/JniCodeEmitter.java index 6697189695..9c80212011 100644 --- a/opengl/tools/glgen/src/JniCodeEmitter.java +++ b/opengl/tools/glgen/src/JniCodeEmitter.java @@ -775,6 +775,19 @@ public class JniCodeEmitter { } } + String getJniDefaultReturn(JType jType) { + if (jType.isPrimitive()) { + String baseType = jType.getBaseType(); + if (baseType.equals("boolean")) { + return "JNI_FALSE"; + } else { + return "(" + getJniType(jType) + ")0"; + } + } else { + return "nullptr"; + } + } + String getJniMangledName(String name) { name = name.replaceAll("_", "_1"); name = name.replaceAll(";", "_2"); @@ -943,15 +956,15 @@ public class JniCodeEmitter { "jniThrowException(_env, \"java/lang/UnsupportedOperationException\","); out.println(indent + " \"" + cfunc.getName() + "\");"); - if (!isVoid) { - String retval = getErrorReturnValue(cfunc); + if (isVoid) { + out.println(indent + "return;"); + } else { if (cfunc.getType().isEGLHandle()) { String baseType = cfunc.getType().getBaseType().toLowerCase(); - out.println(indent + - "return toEGLHandle(_env, " + baseType + "Class, " + - baseType + "Constructor, " + retval + ");"); + out.println(indent + indent + "return nullptr;"); } else { - out.println(indent + "return " + retval + ";"); + out.println(indent + indent + "return " + + getJniDefaultReturn(jfunc.getType()) + ";"); } } out.println("}"); @@ -1595,8 +1608,17 @@ public class JniCodeEmitter { out.println(indent + "if (_exception) {"); out.println(indent + indent + "jniThrowException(_env, _exceptionType, _exceptionMessage);"); - out.println(indent + "}"); + if (!isVoid) { + if (cfunc.getType().isEGLHandle()) { + String baseType = cfunc.getType().getBaseType().toLowerCase(); + out.println(indent + indent + "return nullptr;"); + } else { + out.println(indent + indent + "return " + + getJniDefaultReturn(jfunc.getType()) + ";"); + } + } + out.println(indent + "}"); } diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if index f3bf220bf9..12728f588f 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if +++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if @@ -18,6 +18,7 @@ package android.opengl; +import android.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; import android.view.Surface; import android.view.SurfaceView; diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp index f90e3ec590..93203fd529 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp @@ -35,8 +35,6 @@ #include <ui/ANativeObjectBase.h> -static int initialized = 0; - static jclass egldisplayClass; static jclass eglcontextClass; static jclass eglsurfaceClass; @@ -107,6 +105,7 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { if (obj == NULL){ jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + return nullptr; } jlong handle = _env->CallLongMethod(obj, mid); diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp index 70b46f7585..1c53c9e8e2 100644 --- a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp @@ -14,21 +14,21 @@ ** limitations under the License. */ +// This source file is automatically generated + #pragma GCC diagnostic ignored "-Wunused-variable" #pragma GCC diagnostic ignored "-Wunused-function" -#include <android_runtime/AndroidRuntime.h> +#include "jni.h" #include <nativehelper/JNIHelp.h> +#include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> -#include "jni.h" -#include <EGL/egl.h> #include <assert.h> +#include <EGL/egl.h> #include <ui/ANativeObjectBase.h> -static int initialized = 0; - // classes from EGL 1.4 static jclass egldisplayClass; static jclass eglsurfaceClass; @@ -74,16 +74,18 @@ static jobject eglNoSyncObject; /* Cache method IDs each time the class is loaded. */ -static void nativeClassInit(JNIEnv *_env, jclass glImplClass) { +static void +nativeClassInit(JNIEnv *_env, jclass glImplClass) +{ // EGL 1.4 Init jclass eglconfigClassLocal = _env->FindClass("android/opengl/EGLConfig"); - eglconfigClass = (jclass)_env->NewGlobalRef(eglconfigClassLocal); + eglconfigClass = (jclass) _env->NewGlobalRef(eglconfigClassLocal); jclass eglcontextClassLocal = _env->FindClass("android/opengl/EGLContext"); - eglcontextClass = (jclass)_env->NewGlobalRef(eglcontextClassLocal); + eglcontextClass = (jclass) _env->NewGlobalRef(eglcontextClassLocal); jclass egldisplayClassLocal = _env->FindClass("android/opengl/EGLDisplay"); - egldisplayClass = (jclass)_env->NewGlobalRef(egldisplayClassLocal); + egldisplayClass = (jclass) _env->NewGlobalRef(egldisplayClassLocal); jclass eglsurfaceClassLocal = _env->FindClass("android/opengl/EGLSurface"); - eglsurfaceClass = (jclass)_env->NewGlobalRef(eglsurfaceClassLocal); + eglsurfaceClass = (jclass) _env->NewGlobalRef(eglsurfaceClassLocal); eglconfigGetHandleID = _env->GetMethodID(eglconfigClass, "getNativeHandle", "()J"); eglcontextGetHandleID = _env->GetMethodID(eglcontextClass, "getNativeHandle", "()J"); @@ -95,51 +97,46 @@ static void nativeClassInit(JNIEnv *_env, jclass glImplClass) { egldisplayConstructor = _env->GetMethodID(egldisplayClass, "<init>", "(J)V"); eglsurfaceConstructor = _env->GetMethodID(eglsurfaceClass, "<init>", "(J)V"); - jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, - reinterpret_cast<jlong>(EGL_NO_CONTEXT)); + jobject localeglNoContextObject = _env->NewObject(eglcontextClass, eglcontextConstructor, reinterpret_cast<jlong>(EGL_NO_CONTEXT)); eglNoContextObject = _env->NewGlobalRef(localeglNoContextObject); - jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, - reinterpret_cast<jlong>(EGL_NO_DISPLAY)); + jobject localeglNoDisplayObject = _env->NewObject(egldisplayClass, egldisplayConstructor, reinterpret_cast<jlong>(EGL_NO_DISPLAY)); eglNoDisplayObject = _env->NewGlobalRef(localeglNoDisplayObject); - jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, - reinterpret_cast<jlong>(EGL_NO_SURFACE)); + jobject localeglNoSurfaceObject = _env->NewObject(eglsurfaceClass, eglsurfaceConstructor, reinterpret_cast<jlong>(EGL_NO_SURFACE)); eglNoSurfaceObject = _env->NewGlobalRef(localeglNoSurfaceObject); jclass eglClass = _env->FindClass("android/opengl/EGL15"); - jfieldID noContextFieldID = - _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); + jfieldID noContextFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_CONTEXT", "Landroid/opengl/EGLContext;"); _env->SetStaticObjectField(eglClass, noContextFieldID, eglNoContextObject); - jfieldID noDisplayFieldID = - _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;"); + jfieldID noDisplayFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_DISPLAY", "Landroid/opengl/EGLDisplay;"); _env->SetStaticObjectField(eglClass, noDisplayFieldID, eglNoDisplayObject); - jfieldID noSurfaceFieldID = - _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;"); + jfieldID noSurfaceFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SURFACE", "Landroid/opengl/EGLSurface;"); _env->SetStaticObjectField(eglClass, noSurfaceFieldID, eglNoSurfaceObject); // EGL 1.5 init jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); - nioAccessClass = (jclass)_env->NewGlobalRef(nioAccessClassLocal); + nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); - bufferClass = (jclass)_env->NewGlobalRef(bufferClassLocal); + bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); - getBasePointerID = - _env->GetStaticMethodID(nioAccessClass, "getBasePointer", "(Ljava/nio/Buffer;)J"); - getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, "getBaseArray", - "(Ljava/nio/Buffer;)Ljava/lang/Object;"); - getBaseArrayOffsetID = - _env->GetStaticMethodID(nioAccessClass, "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); + getBasePointerID = _env->GetStaticMethodID(nioAccessClass, + "getBasePointer", "(Ljava/nio/Buffer;)J"); + getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); + getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, + "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); positionID = _env->GetFieldID(bufferClass, "position", "I"); limitID = _env->GetFieldID(bufferClass, "limit", "I"); - elementSizeShiftID = _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); + elementSizeShiftID = + _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); jclass eglimageClassLocal = _env->FindClass("android/opengl/EGLImage"); - eglimageClass = (jclass)_env->NewGlobalRef(eglimageClassLocal); + eglimageClass = (jclass) _env->NewGlobalRef(eglimageClassLocal); jclass eglsyncClassLocal = _env->FindClass("android/opengl/EGLSync"); - eglsyncClass = (jclass)_env->NewGlobalRef(eglsyncClassLocal); + eglsyncClass = (jclass) _env->NewGlobalRef(eglsyncClassLocal); eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); @@ -147,17 +144,16 @@ static void nativeClassInit(JNIEnv *_env, jclass glImplClass) { eglimageConstructor = _env->GetMethodID(eglimageClass, "<init>", "(J)V"); eglsyncConstructor = _env->GetMethodID(eglsyncClass, "<init>", "(J)V"); - jfieldID noImageFieldID = - _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;"); + jfieldID noImageFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_IMAGE", "Landroid/opengl/EGLImage;"); _env->SetStaticObjectField(eglClass, noImageFieldID, eglNoImageObject); - jfieldID noSyncFieldID = - _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;"); + jfieldID noSyncFieldID = _env->GetStaticFieldID(eglClass, "EGL_NO_SYNC", "Landroid/opengl/EGLSync;"); _env->SetStaticObjectField(eglClass, noSyncFieldID, eglNoSyncObject); } -static void *getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, - jint *offset) { +static void * +getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset) +{ jint position; jint limit; jint elementSizeShift; @@ -167,34 +163,42 @@ static void *getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remai limit = _env->GetIntField(buffer, limitID); elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); *remaining = (limit - position) << elementSizeShift; - pointer = _env->CallStaticLongMethod(nioAccessClass, getBasePointerID, buffer); + pointer = _env->CallStaticLongMethod(nioAccessClass, + getBasePointerID, buffer); if (pointer != 0L) { *array = NULL; - return reinterpret_cast<void *>(pointer); + return reinterpret_cast<void*>(pointer); } - eglimageGetHandleID = _env->GetMethodID(eglimageClass, "getNativeHandle", "()J"); - eglsyncGetHandleID = _env->GetMethodID(eglsyncClass, "getNativeHandle", "()J"); - *array = (jarray)_env->CallStaticObjectMethod(nioAccessClass, getBaseArrayID, buffer); - *offset = _env->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetID, buffer); + *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, + getBaseArrayID, buffer); + *offset = _env->CallStaticIntMethod(nioAccessClass, + getBaseArrayOffsetID, buffer); return NULL; } -static void releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) { - _env->ReleasePrimitiveArrayCritical(array, data, commit ? 0 : JNI_ABORT); +static void +releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) +{ + _env->ReleasePrimitiveArrayCritical(array, data, + commit ? 0 : JNI_ABORT); } -static void *fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { +static void * +fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { if (obj == NULL) { - jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + jniThrowException(_env, "java/lang/IllegalArgumentException", + "Object is set to null."); + return nullptr; } jlong handle = _env->CallLongMethod(obj, mid); - return reinterpret_cast<void *>(handle); + return reinterpret_cast<void*>(handle); } -static jobject toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) { +static jobject +toEGLHandle(JNIEnv *_env, jclass cls, jmethodID con, void *handle) { if (cls == eglimageClass && (EGLImage)handle == EGL_NO_IMAGE) { return eglNoImageObject; } diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp index 12b96f40d7..b3b069082a 100644 --- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp @@ -36,8 +36,6 @@ #include <ui/ANativeObjectBase.h> -static int initialized = 0; - static jclass egldisplayClass; static jclass eglcontextClass; static jclass eglsurfaceClass; @@ -104,6 +102,7 @@ fromEGLHandle(JNIEnv *_env, jmethodID mid, jobject obj) { if (obj == NULL){ jniThrowException(_env, "java/lang/IllegalArgumentException", "Object is set to null."); + return nullptr; } return reinterpret_cast<void*>(_env->CallLongMethod(obj, mid)); diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp index 497d284727..f22986054e 100755 --- a/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp +++ b/opengl/tools/glgen/stubs/egl/eglCreatePbufferFromClientBuffer.cpp @@ -54,6 +54,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } diff --git a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp index 3eacf3c3cd..2e146a8044 100644 --- a/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp +++ b/opengl/tools/glgen/stubs/egl/eglCreatePixmapSurface.cpp @@ -4,6 +4,6 @@ android_eglCreatePixmapSurface (JNIEnv *_env, jobject _this, jobject dpy, jobject config, jint pixmap, jintArray attrib_list_ref, jint offset) { jniThrowException(_env, "java/lang/UnsupportedOperationException", "eglCreatePixmapSurface"); - return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, (EGLSurface) 0); + return nullptr; } diff --git a/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp b/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp index 355c4b0456..7c255ed106 100644 --- a/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp +++ b/opengl/tools/glgen/stubs/egl/eglCreateWindowSurface.cpp @@ -67,6 +67,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } @@ -149,6 +150,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, eglsurfaceClass, eglsurfaceConstructor, _returnValue); } diff --git a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java index 7532abf3a1..85f743d815 100755 --- a/opengl/tools/glgen/stubs/egl/eglGetDisplay.java +++ b/opengl/tools/glgen/stubs/egl/eglGetDisplay.java @@ -7,6 +7,7 @@ /** * {@hide} */ + @UnsupportedAppUsage public static native EGLDisplay eglGetDisplay( long display_id ); diff --git a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp index fd44498e43..3a6176f09c 100644 --- a/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp +++ b/opengl/tools/glgen/stubs/egl/eglGetPlatformDisplay.cpp @@ -40,6 +40,7 @@ exit: } if (_exception) { jniThrowException(_env, _exceptionType, _exceptionMessage); + return nullptr; } return toEGLHandle(_env, egldisplayClass, egldisplayConstructor, _returnValue); } diff --git a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if index 9ce6728e04..c2711aa4db 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if +++ b/opengl/tools/glgen/stubs/gles11/GLES20Header.java-if @@ -19,6 +19,8 @@ package android.opengl; +import android.annotation.UnsupportedAppUsage; + /** OpenGL ES 2.0 */ public class GLES20 { diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp index 2163d7600d..51e62ed393 100644 --- a/opengl/tools/glgen/stubs/gles11/common.cpp +++ b/opengl/tools/glgen/stubs/gles11/common.cpp @@ -4,8 +4,6 @@ #include <utils/misc.h> #include <assert.h> -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jmethodID getBasePointerID; diff --git a/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java b/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java index d66200f4f0..b297b7a821 100644 --- a/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java +++ b/opengl/tools/glgen/stubs/gles11/glGetActiveAttrib.java @@ -17,6 +17,7 @@ // C function void glGetActiveAttrib ( GLuint program, GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, char *name ) /** @hide Method is broken, but used to be public (b/6006380) */ + @UnsupportedAppUsage public static native void glGetActiveAttrib( int program, int index, diff --git a/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java b/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java index 8c8d5a2bf9..f211440365 100644 --- a/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java +++ b/opengl/tools/glgen/stubs/gles11/glGetActiveUniform.java @@ -17,6 +17,7 @@ // C function void glGetActiveUniform ( GLuint program, GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, char *name ) /** @hide Method is broken, but used to be public (b/6006380) */ + @UnsupportedAppUsage public static native void glGetActiveUniform( int program, int index, diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp index 29296ff6a9..c808fe9681 100644 --- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp +++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp @@ -64,8 +64,6 @@ GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, GLsizei stride, const GLvoid *pointer, GLsizei count); } -static int initialized = 0; - static jclass nioAccessClass; static jclass bufferClass; static jclass G11ImplClass; diff --git a/services/bufferhub/tests/Android.bp b/services/bufferhub/tests/Android.bp index 8d29923069..3033e705a9 100644 --- a/services/bufferhub/tests/Android.bp +++ b/services/bufferhub/tests/Android.bp @@ -11,6 +11,7 @@ cc_test { "-Wall", "-Werror", ], + compile_multilib: "first", header_libs: [ "libdvr_headers", "libnativewindow_headers", diff --git a/services/gpuservice/Android.bp b/services/gpuservice/Android.bp index e21d8e779d..dbb6ba6719 100644 --- a/services/gpuservice/Android.bp +++ b/services/gpuservice/Android.bp @@ -2,6 +2,7 @@ filegroup { name: "gpuservice_sources", srcs: [ "GpuService.cpp", + "gpustats/GpuStats.cpp" ], } @@ -29,6 +30,7 @@ cc_defaults { "frameworks/native/vulkan/include", ], shared_libs: [ + "libbase", "libbinder", "libcutils", "libgraphicsenv", @@ -37,6 +39,7 @@ cc_defaults { "libvulkan", ], static_libs: [ + "libserviceutils", "libvkjson", ], } diff --git a/services/gpuservice/GpuService.cpp b/services/gpuservice/GpuService.cpp index 68c185c119..59fa1c0c33 100644 --- a/services/gpuservice/GpuService.cpp +++ b/services/gpuservice/GpuService.cpp @@ -18,38 +18,61 @@ #include "GpuService.h" +#include <android-base/stringprintf.h> +#include <binder/IPCThreadState.h> #include <binder/IResultReceiver.h> #include <binder/Parcel.h> +#include <binder/PermissionCache.h> +#include <cutils/properties.h> +#include <private/android_filesystem_config.h> #include <utils/String8.h> #include <utils/Trace.h> #include <vkjson.h> +#include "gpustats/GpuStats.h" + namespace android { +using base::StringAppendF; namespace { - status_t cmd_help(int out); - status_t cmd_vkjson(int out, int err); -} +status_t cmdHelp(int out); +status_t cmdVkjson(int out, int err); +void dumpGameDriverInfo(std::string* result); +} // namespace + +const String16 sDump("android.permission.DUMP"); const char* const GpuService::SERVICE_NAME = "gpu"; -GpuService::GpuService() = default; +GpuService::GpuService() : mGpuStats(std::make_unique<GpuStats>()){}; void GpuService::setGpuStats(const std::string& driverPackageName, - const std::string& driverVersionName, const uint64_t driverVersionCode, - const std::string& appPackageName) { + const std::string& driverVersionName, uint64_t driverVersionCode, + int64_t driverBuildTime, const std::string& appPackageName, + GraphicsEnv::Driver driver, bool isDriverLoaded, + int64_t driverLoadingTime) { + ATRACE_CALL(); + + mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime, + appPackageName, driver, isDriverLoaded, driverLoadingTime); +} + +status_t GpuService::getGpuStatsGlobalInfo(std::vector<GpuStatsGlobalInfo>* outStats) const { + ATRACE_CALL(); + + mGpuStats->pullGlobalStats(outStats); + + return OK; +} + +status_t GpuService::getGpuStatsAppInfo(std::vector<GpuStatsAppInfo>* outStats) const { ATRACE_CALL(); - std::lock_guard<std::mutex> lock(mStateLock); - ALOGV("Received:\n" - "\tdriverPackageName[%s]\n" - "\tdriverVersionName[%s]\n" - "\tdriverVersionCode[%llu]\n" - "\tappPackageName[%s]\n", - driverPackageName.c_str(), driverVersionName.c_str(), - (unsigned long long)driverVersionCode, appPackageName.c_str()); + mGpuStats->pullAppStats(outStats); + + return OK; } status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<String16>& args) { @@ -60,28 +83,60 @@ status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector<Stri ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string()); if (args.size() >= 1) { - if (args[0] == String16("vkjson")) - return cmd_vkjson(out, err); - if (args[0] == String16("help")) - return cmd_help(out); + if (args[0] == String16("vkjson")) return cmdVkjson(out, err); + if (args[0] == String16("help")) return cmdHelp(out); } // no command, or unrecognized command - cmd_help(err); + cmdHelp(err); return BAD_VALUE; } +status_t GpuService::doDump(int fd, const Vector<String16>& args, bool /*asProto*/) { + std::string result; + + IPCThreadState* ipc = IPCThreadState::self(); + const int pid = ipc->getCallingPid(); + const int uid = ipc->getCallingUid(); + + if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { + StringAppendF(&result, "Permission Denial: can't dump gpu from pid=%d, uid=%d\n", pid, uid); + } else { + bool dumpAll = true; + size_t index = 0; + size_t numArgs = args.size(); + + if (numArgs) { + if ((index < numArgs) && (args[index] == String16("--gpustats"))) { + index++; + mGpuStats->dump(args, &result); + dumpAll = false; + } + } + + if (dumpAll) { + dumpGameDriverInfo(&result); + result.append("\n"); + + mGpuStats->dump(Vector<String16>(), &result); + result.append("\n"); + } + } + + write(fd, result.c_str(), result.size()); + return NO_ERROR; +} + namespace { -status_t cmd_help(int out) { +status_t cmdHelp(int out) { FILE* outs = fdopen(out, "w"); if (!outs) { - ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno), - errno); + ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno), errno); return BAD_VALUE; } fprintf(outs, - "GPU Service commands:\n" - " vkjson dump Vulkan properties as JSON\n"); + "GPU Service commands:\n" + " vkjson dump Vulkan properties as JSON\n"); fclose(outs); return NO_ERROR; } @@ -92,7 +147,7 @@ void vkjsonPrint(FILE* out) { fputc('\n', out); } -status_t cmd_vkjson(int out, int /*err*/) { +status_t cmdVkjson(int out, int /*err*/) { FILE* outs = fdopen(out, "w"); if (!outs) { int errnum = errno; @@ -104,6 +159,18 @@ status_t cmd_vkjson(int out, int /*err*/) { return NO_ERROR; } +void dumpGameDriverInfo(std::string* result) { + if (!result) return; + + char stableGameDriver[PROPERTY_VALUE_MAX] = {}; + property_get("ro.gfx.driver.0", stableGameDriver, "unsupported"); + StringAppendF(result, "Stable Game Driver: %s\n", stableGameDriver); + + char preReleaseGameDriver[PROPERTY_VALUE_MAX] = {}; + property_get("ro.gfx.driver.1", preReleaseGameDriver, "unsupported"); + StringAppendF(result, "Pre-release Game Driver: %s\n", preReleaseGameDriver); +} + } // anonymous namespace } // namespace android diff --git a/services/gpuservice/GpuService.h b/services/gpuservice/GpuService.h index 5304fa1279..7a9b2d4dbf 100644 --- a/services/gpuservice/GpuService.h +++ b/services/gpuservice/GpuService.h @@ -19,14 +19,18 @@ #include <binder/IInterface.h> #include <cutils/compiler.h> +#include <graphicsenv/GpuStatsInfo.h> #include <graphicsenv/IGpuService.h> +#include <serviceutils/PriorityDumper.h> #include <mutex> #include <vector> namespace android { -class GpuService : public BnGpuService { +class GpuStats; + +class GpuService : public BnGpuService, public PriorityDumper { public: static const char* const SERVICE_NAME ANDROID_API; @@ -36,12 +40,38 @@ protected: status_t shellCommand(int in, int out, int err, std::vector<String16>& args) override; private: - // IGpuService interface + /* + * IGpuService interface + */ void setGpuStats(const std::string& driverPackageName, const std::string& driverVersionName, - const uint64_t driverVersionCode, const std::string& appPackageName); + uint64_t driverVersionCode, int64_t driverBuildTime, + 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 + */ + status_t dump(int fd, const Vector<String16>& args) override { return priorityDump(fd, args); } + + /* + * Debugging & dumpsys + */ + status_t dumpCritical(int fd, const Vector<String16>& /*args*/, bool asProto) override { + return doDump(fd, Vector<String16>(), asProto); + } + + status_t dumpAll(int fd, const Vector<String16>& args, bool asProto) override { + return doDump(fd, args, asProto); + } + + status_t doDump(int fd, const Vector<String16>& args, bool asProto); - // GpuStats access must be protected by mStateLock - std::mutex mStateLock; + /* + * Attributes + */ + std::unique_ptr<GpuStats> mGpuStats; }; } // namespace android diff --git a/services/gpuservice/gpustats/GpuStats.cpp b/services/gpuservice/gpustats/GpuStats.cpp new file mode 100644 index 0000000000..618530523a --- /dev/null +++ b/services/gpuservice/gpustats/GpuStats.cpp @@ -0,0 +1,217 @@ +/* + * 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 "GpuStats" +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + +#include "GpuStats.h" + +#include <unordered_set> + +#include <log/log.h> +#include <utils/Trace.h> + +namespace android { + +static bool addLoadingCount(GraphicsEnv::Driver driver, bool isDriverLoaded, + GpuStatsGlobalInfo* const outGlobalInfo) { + switch (driver) { + case GraphicsEnv::Driver::GL: + case GraphicsEnv::Driver::GL_UPDATED: + outGlobalInfo->glLoadingCount++; + if (!isDriverLoaded) outGlobalInfo->glLoadingFailureCount++; + break; + case GraphicsEnv::Driver::VULKAN: + case GraphicsEnv::Driver::VULKAN_UPDATED: + outGlobalInfo->vkLoadingCount++; + if (!isDriverLoaded) outGlobalInfo->vkLoadingFailureCount++; + break; + default: + // Currently we don't support GraphicsEnv::Driver::ANGLE because the + // basic driver package info only belongs to system or updated driver. + return false; + } + + return true; +} + +static void addLoadingTime(GraphicsEnv::Driver driver, int64_t driverLoadingTime, + GpuStatsAppInfo* const outAppInfo) { + 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: + break; + } +} + +void GpuStats::insert(const std::string& driverPackageName, const std::string& driverVersionName, + uint64_t driverVersionCode, int64_t driverBuildTime, + const std::string& appPackageName, GraphicsEnv::Driver driver, + bool isDriverLoaded, int64_t driverLoadingTime) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mLock); + ALOGV("Received:\n" + "\tdriverPackageName[%s]\n" + "\tdriverVersionName[%s]\n" + "\tdriverVersionCode[%" PRIu64 "]\n" + "\tdriverBuildTime[%" PRId64 "]\n" + "\tappPackageName[%s]\n" + "\tdriver[%d]\n" + "\tisDriverLoaded[%d]\n" + "\tdriverLoadingTime[%" PRId64 "]", + driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime, + appPackageName.c_str(), static_cast<int32_t>(driver), isDriverLoaded, driverLoadingTime); + + if (!mGlobalStats.count(driverVersionCode)) { + GpuStatsGlobalInfo globalInfo; + if (!addLoadingCount(driver, isDriverLoaded, &globalInfo)) { + return; + } + globalInfo.driverPackageName = driverPackageName; + globalInfo.driverVersionName = driverVersionName; + globalInfo.driverVersionCode = driverVersionCode; + globalInfo.driverBuildTime = driverBuildTime; + mGlobalStats.insert({driverVersionCode, globalInfo}); + } else if (!addLoadingCount(driver, isDriverLoaded, &mGlobalStats[driverVersionCode])) { + return; + } + + if (mAppStats.size() >= MAX_NUM_APP_RECORDS) { + ALOGV("GpuStatsAppInfo has reached maximum size. Ignore new stats."); + return; + } + + const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode); + if (!mAppStats.count(appStatsKey)) { + GpuStatsAppInfo appInfo; + addLoadingTime(driver, driverLoadingTime, &appInfo); + appInfo.appPackageName = appPackageName; + appInfo.driverVersionCode = driverVersionCode; + mAppStats.insert({appStatsKey, appInfo}); + return; + } + + addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]); +} + +void GpuStats::dump(const Vector<String16>& args, std::string* result) { + ATRACE_CALL(); + + if (!result) { + ALOGE("Dump result shouldn't be nullptr."); + return; + } + + std::lock_guard<std::mutex> lock(mLock); + bool dumpAll = true; + + std::unordered_set<std::string> argsSet; + for (size_t i = 0; i < args.size(); i++) { + argsSet.insert(String8(args[i]).c_str()); + } + + const bool dumpGlobal = argsSet.count("--global") != 0; + if (dumpGlobal) { + dumpGlobalLocked(result); + dumpAll = false; + } + + const bool dumpApp = argsSet.count("--app") != 0; + if (dumpApp) { + dumpAppLocked(result); + dumpAll = false; + } + + if (argsSet.count("--clear")) { + bool clearAll = true; + + if (dumpGlobal) { + mGlobalStats.clear(); + clearAll = false; + } + + if (dumpApp) { + mAppStats.clear(); + clearAll = false; + } + + if (clearAll) { + mGlobalStats.clear(); + mAppStats.clear(); + } + + dumpAll = false; + } + + if (dumpAll) { + dumpGlobalLocked(result); + dumpAppLocked(result); + } +} + +void GpuStats::dumpGlobalLocked(std::string* result) { + for (const auto& ele : mGlobalStats) { + result->append(ele.second.toString()); + result->append("\n"); + } +} + +void GpuStats::dumpAppLocked(std::string* result) { + for (const auto& ele : mAppStats) { + result->append(ele.second.toString()); + result->append("\n"); + } +} + +void GpuStats::pullGlobalStats(std::vector<GpuStatsGlobalInfo>* outStats) { + ATRACE_CALL(); + + std::lock_guard<std::mutex> lock(mLock); + outStats->clear(); + outStats->reserve(mGlobalStats.size()); + + for (const auto& ele : mGlobalStats) { + outStats->emplace_back(ele.second); + } + + 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 new file mode 100644 index 0000000000..d942154cbb --- /dev/null +++ b/services/gpuservice/gpustats/GpuStats.h @@ -0,0 +1,67 @@ +/* + * 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 <mutex> +#include <unordered_map> +#include <vector> + +#include <graphicsenv/GpuStatsInfo.h> +#include <graphicsenv/GraphicsEnv.h> +#include <utils/String16.h> +#include <utils/Vector.h> + +namespace android { + +class GpuStats { +public: + GpuStats() = default; + ~GpuStats() = default; + + // Insert new gpu stats into global stats and app stats. + void insert(const std::string& driverPackageName, const std::string& driverVersionName, + uint64_t driverVersionCode, int64_t driverBuildTime, + const std::string& appPackageName, GraphicsEnv::Driver driver, bool isDriverLoaded, + int64_t driverLoadingTime); + // dumpsys interface + 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 + void dumpGlobalLocked(std::string* result); + // Dump app stats + void dumpAppLocked(std::string* result); + + // 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. + std::unordered_map<uint64_t, GpuStatsGlobalInfo> mGlobalStats; + // Key is <app package name>+<driver version code>. + std::unordered_map<std::string, GpuStatsAppInfo> mAppStats; +}; + +} // namespace android diff --git a/services/inputflinger/Android.bp b/services/inputflinger/Android.bp index f73d498bb2..63e759c0f8 100644 --- a/services/inputflinger/Android.bp +++ b/services/inputflinger/Android.bp @@ -12,8 +12,20 @@ // See the License for the specific language governing permissions and // limitations under the License. +cc_defaults { + name: "inputflinger_defaults", + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + "-Wno-unused-parameter", + "-Wthread-safety", + ], +} + cc_library_shared { name: "libinputflinger", + defaults: ["inputflinger_defaults"], srcs: [ "InputClassifier.cpp", @@ -23,10 +35,10 @@ cc_library_shared { shared_libs: [ "android.hardware.input.classifier@1.0", + "libbase", "libinputflinger_base", "libinputreporter", "libinputreader", - "libbase", "libbinder", "libcutils", "libhidlbase", @@ -38,12 +50,6 @@ cc_library_shared { ], cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter", - // TODO(b/123097103): annotate InputDispatcher and uncomment the following line - //"-Wthread-safety", // TODO(b/23084678): Move inputflinger to its own process and mark it hidden //-fvisibility=hidden ], @@ -55,15 +61,14 @@ cc_library_shared { } - cc_library_headers { - name: "libinputflinger_headers", - - export_include_dirs: ["include"], + name: "libinputflinger_headers", + export_include_dirs: ["include"], } cc_library_shared { name: "libinputreader", + defaults: ["inputflinger_defaults"], srcs: [ "EventHub.cpp", @@ -73,17 +78,16 @@ cc_library_shared { ], shared_libs: [ - "libinputflinger_base", "libbase", + "libinputflinger_base", "libcrypto", "libcutils", "libinput", "liblog", - "libutils", "libui", + "libutils", "libhardware_legacy", "libstatslog", - "libutils", ], header_libs: [ @@ -93,17 +97,11 @@ cc_library_shared { export_header_lib_headers: [ "libinputflinger_headers", ], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter", - ], } cc_library_shared { name: "libinputflinger_base", + defaults: ["inputflinger_defaults"], srcs: [ "InputListener.cpp", @@ -124,24 +122,17 @@ cc_library_shared { export_header_lib_headers: [ "libinputflinger_headers", ], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter", - ], } cc_library_shared { name: "libinputreporter", + defaults: ["inputflinger_defaults"], srcs: [ "InputReporter.cpp", ], shared_libs: [ - "libbase", "liblog", "libutils", ], @@ -153,13 +144,6 @@ cc_library_shared { export_header_lib_headers: [ "libinputflinger_headers", ], - - cflags: [ - "-Wall", - "-Wextra", - "-Werror", - "-Wno-unused-parameter", - ], } subdirs = [ diff --git a/services/inputflinger/BlockingQueue.h b/services/inputflinger/BlockingQueue.h index c9eb683fcb..db9f26ec12 100644 --- a/services/inputflinger/BlockingQueue.h +++ b/services/inputflinger/BlockingQueue.h @@ -17,6 +17,7 @@ #ifndef _UI_INPUT_BLOCKING_QUEUE_H #define _UI_INPUT_BLOCKING_QUEUE_H +#include "android-base/thread_annotations.h" #include <condition_variable> #include <mutex> #include <vector> @@ -42,11 +43,15 @@ public: * Blocks execution while queue is empty. */ T pop() { - std::unique_lock<std::mutex> lock(mLock); - mHasElements.wait(lock, [this]{ return !this->mQueue.empty(); }); + std::unique_lock lock(mLock); + android::base::ScopedLockAssertion assumeLock(mLock); + mHasElements.wait(lock, [this]{ + android::base::ScopedLockAssertion assumeLock(mLock); + return !this->mQueue.empty(); + }); T t = std::move(mQueue.front()); mQueue.erase(mQueue.begin()); - return std::move(t); + return t; }; /** @@ -56,17 +61,19 @@ public: * Return false if the queue is full. */ bool push(T&& t) { - std::unique_lock<std::mutex> lock(mLock); - if (mQueue.size() == mCapacity) { - return false; + { + std::scoped_lock lock(mLock); + if (mQueue.size() == mCapacity) { + return false; + } + mQueue.push_back(std::move(t)); } - mQueue.push_back(std::move(t)); mHasElements.notify_one(); return true; }; void erase(const std::function<bool(const T&)>& lambda) { - std::unique_lock<std::mutex> lock(mLock); + std::scoped_lock lock(mLock); mQueue.erase(std::remove_if(mQueue.begin(), mQueue.end(), [&lambda](const T& t) { return lambda(t); }), mQueue.end()); } @@ -91,7 +98,7 @@ public: } private: - size_t mCapacity; + const size_t mCapacity; /** * Used to signal that mQueue is non-empty. */ @@ -100,7 +107,7 @@ private: * Lock for accessing and waiting on elements. */ std::mutex mLock; - std::vector<T> mQueue; //GUARDED_BY(mLock) + std::vector<T> mQueue GUARDED_BY(mLock); }; diff --git a/services/inputflinger/EventHub.cpp b/services/inputflinger/EventHub.cpp index c13bac6a12..4da1473904 100644 --- a/services/inputflinger/EventHub.cpp +++ b/services/inputflinger/EventHub.cpp @@ -202,7 +202,6 @@ EventHub::Device::Device(int fd, int32_t id, const std::string& path, EventHub::Device::~Device() { close(); delete configuration; - delete virtualKeyMap; } void EventHub::Device::close() { @@ -1364,8 +1363,8 @@ status_t EventHub::openDeviceLocked(const char* devicePath) { if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { // Load the virtual keys for the touch screen, if any. // We do this now so that we can make sure to load the keymap if necessary. - status_t status = loadVirtualKeyMapLocked(device); - if (!status) { + bool success = loadVirtualKeyMapLocked(device); + if (success) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; } } @@ -1614,15 +1613,16 @@ void EventHub::loadConfigurationLocked(Device* device) { } } -status_t EventHub::loadVirtualKeyMapLocked(Device* device) { +bool EventHub::loadVirtualKeyMapLocked(Device* device) { // The virtual key map is supplied by the kernel as a system board property file. std::string path; path += "/sys/board_properties/virtualkeys."; - path += device->identifier.name; + path += device->identifier.getCanonicalName(); if (access(path.c_str(), R_OK)) { - return NAME_NOT_FOUND; + return false; } - return VirtualKeyMap::load(path, &device->virtualKeyMap); + device->virtualKeyMap = VirtualKeyMap::load(path); + return device->virtualKeyMap != nullptr; } status_t EventHub::loadKeyMapLocked(Device* device) { diff --git a/services/inputflinger/EventHub.h b/services/inputflinger/EventHub.h index d176648b04..44f7b3715a 100644 --- a/services/inputflinger/EventHub.h +++ b/services/inputflinger/EventHub.h @@ -345,7 +345,7 @@ private: std::string configurationFile; PropertyMap* configuration; - VirtualKeyMap* virtualKeyMap; + std::unique_ptr<VirtualKeyMap> virtualKeyMap; KeyMap keyMap; sp<KeyCharacterMap> overlayKeyMap; @@ -416,7 +416,7 @@ private: bool hasKeycodeLocked(Device* device, int keycode) const; void loadConfigurationLocked(Device* device); - status_t loadVirtualKeyMapLocked(Device* device); + bool loadVirtualKeyMapLocked(Device* device); status_t loadKeyMapLocked(Device* device); bool isExternalDeviceLocked(Device* device); diff --git a/services/inputflinger/InputClassifier.cpp b/services/inputflinger/InputClassifier.cpp index 3905ed05b0..b4db338687 100644 --- a/services/inputflinger/InputClassifier.cpp +++ b/services/inputflinger/InputClassifier.cpp @@ -40,6 +40,7 @@ using android::base::StringPrintf; using android::hardware::hidl_bitfield; using android::hardware::hidl_vec; +using android::hardware::Return; using namespace android::hardware::input; namespace android { @@ -548,23 +549,28 @@ void MotionClassifier::callInputClassifierHal() { ensureHalThread(__func__); while (true) { ClassifierEvent event = mEvents.pop(); + bool halResponseOk = true; switch (event.type) { case ClassifierEventType::MOTION: { NotifyMotionArgs* motionArgs = static_cast<NotifyMotionArgs*>(event.args.get()); common::V1_0::MotionEvent motionEvent = getMotionEvent(*motionArgs); - common::V1_0::Classification halClassification = mService->classify(motionEvent); - updateClassification(motionArgs->deviceId, motionArgs->eventTime, - getMotionClassification(halClassification)); + Return<common::V1_0::Classification> response = mService->classify(motionEvent); + halResponseOk = response.isOk(); + if (halResponseOk) { + common::V1_0::Classification halClassification = response; + updateClassification(motionArgs->deviceId, motionArgs->eventTime, + getMotionClassification(halClassification)); + } break; } case ClassifierEventType::DEVICE_RESET: { const int32_t deviceId = *(event.getDeviceId()); - mService->resetDevice(deviceId); + halResponseOk = mService->resetDevice(deviceId).isOk(); setClassification(deviceId, MotionClassification::NONE); break; } case ClassifierEventType::HAL_RESET: { - mService->reset(); + halResponseOk = mService->reset().isOk(); clearClassifications(); break; } @@ -573,6 +579,21 @@ void MotionClassifier::callInputClassifierHal() { return; } } + if (!halResponseOk) { + ALOGE("Error communicating with InputClassifier HAL. " + "Exiting MotionClassifier HAL thread"); + clearClassifications(); + return; + } + } +} + +void MotionClassifier::enqueueEvent(ClassifierEvent&& event) { + bool eventAdded = mEvents.push(std::move(event)); + if (!eventAdded) { + // If the queue is full, suspect the HAL is slow in processing the events. + ALOGE("Dropped event with eventTime %" PRId64, event.args->eventTime); + reset(); } } @@ -617,22 +638,12 @@ void MotionClassifier::updateLastDownTime(int32_t deviceId, nsecs_t downTime) { } MotionClassification MotionClassifier::classify(const NotifyMotionArgs& args) { - if (!mService) { - // If HAL is not present, do nothing - return MotionClassification::NONE; - } if ((args.action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN) { updateLastDownTime(args.deviceId, args.downTime); } ClassifierEvent event(std::make_unique<NotifyMotionArgs>(args)); - bool elementAdded = mEvents.push(std::move(event)); - if (!elementAdded) { - // Queue should not ever overfill. Suspect HAL is slow. - ALOGE("Dropped element with eventTime %" PRIu64, args.eventTime); - reset(); - return MotionClassification::NONE; - } + enqueueEvent(std::move(event)); return getClassification(args.deviceId); } @@ -652,7 +663,7 @@ void MotionClassifier::reset(const NotifyDeviceResetArgs& args) { std::optional<int32_t> eventDeviceId = event.getDeviceId(); return eventDeviceId && (*eventDeviceId == deviceId); }); - mEvents.push(std::make_unique<NotifyDeviceResetArgs>(args)); + enqueueEvent(std::make_unique<NotifyDeviceResetArgs>(args)); } void MotionClassifier::dump(std::string& dump) { @@ -679,20 +690,39 @@ void MotionClassifier::dump(std::string& dump) { } } + // --- InputClassifier --- InputClassifier::InputClassifier(const sp<InputListenerInterface>& listener) : mListener(listener) { - if (deepPressEnabled()) { - sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = - classifier::V1_0::IInputClassifier::getService(); - if (service) { - mMotionClassifier = std::make_unique<MotionClassifier>(service); - } else { - ALOGI("Could not obtain InputClassifier HAL"); - } + // The rest of the initialization is done in onFirstRef, because we need to obtain + // an sp to 'this' in order to register for HAL death notifications +} + +void InputClassifier::onFirstRef() { + std::scoped_lock lock(mLock); + if (!deepPressEnabled()) { + // If feature is not enabled, the InputClassifier will just be in passthrough + // mode, and will forward all events to the next InputListener, unmodified + return; + } + + sp<android::hardware::input::classifier::V1_0::IInputClassifier> service = + classifier::V1_0::IInputClassifier::getService(); + if (!service) { + // Not really an error, maybe the device does not have this HAL, + // but somehow the feature flag is flipped + ALOGI("Could not obtain InputClassifier HAL"); + return; + } + const bool linked = service->linkToDeath(this, 0 /* cookie */).withDefault(false); + if (!linked) { + ALOGE("Could not link android::InputClassifier to the HAL death"); + return; } -}; + + mMotionClassifier = std::make_unique<MotionClassifier>(service); +} void InputClassifier::notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) { // pass through @@ -705,12 +735,17 @@ void InputClassifier::notifyKey(const NotifyKeyArgs* args) { } void InputClassifier::notifyMotion(const NotifyMotionArgs* args) { - NotifyMotionArgs copyArgs = NotifyMotionArgs(*args); - if (mMotionClassifier && isTouchEvent(*args)) { - // We only cover touch events, for now. - copyArgs.classification = mMotionClassifier->classify(copyArgs); + std::scoped_lock lock(mLock); + // MotionClassifier is only used for touch events, for now + const bool sendToMotionClassifier = mMotionClassifier && isTouchEvent(*args); + if (!sendToMotionClassifier) { + mListener->notifyMotion(args); + return; } - mListener->notifyMotion(©Args); + + NotifyMotionArgs newArgs(*args); + newArgs.classification = mMotionClassifier->classify(newArgs); + mListener->notifyMotion(&newArgs); } void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { @@ -719,6 +754,7 @@ void InputClassifier::notifySwitch(const NotifySwitchArgs* args) { } void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { + std::scoped_lock lock(mLock); if (mMotionClassifier) { mMotionClassifier->reset(*args); } @@ -726,7 +762,19 @@ void InputClassifier::notifyDeviceReset(const NotifyDeviceResetArgs* args) { mListener->notifyDeviceReset(args); } +void InputClassifier::serviceDied(uint64_t /*cookie*/, + const wp<android::hidl::base::V1_0::IBase>& who) { + std::scoped_lock lock(mLock); + ALOGE("InputClassifier HAL has died. Setting mMotionClassifier to null"); + mMotionClassifier = nullptr; + sp<android::hidl::base::V1_0::IBase> service = who.promote(); + if (service) { + service->unlinkToDeath(this); + } +} + void InputClassifier::dump(std::string& dump) { + std::scoped_lock lock(mLock); dump += "Input Classifier State:\n"; dump += INDENT1 "Motion Classifier:\n"; diff --git a/services/inputflinger/InputClassifier.h b/services/inputflinger/InputClassifier.h index 4b9dae275b..0b1483fdd5 100644 --- a/services/inputflinger/InputClassifier.h +++ b/services/inputflinger/InputClassifier.h @@ -17,6 +17,7 @@ #ifndef _UI_INPUT_CLASSIFIER_H #define _UI_INPUT_CLASSIFIER_H +#include <android-base/thread_annotations.h> #include <utils/RefBase.h> #include <unordered_map> #include <thread> @@ -68,7 +69,13 @@ public: * provide a MotionClassification for the current gesture. */ virtual MotionClassification classify(const NotifyMotionArgs& args) = 0; + /** + * Reset all internal HAL state. + */ virtual void reset() = 0; + /** + * Reset HAL state for a specific device. + */ virtual void reset(const NotifyDeviceResetArgs& args) = 0; /** @@ -106,6 +113,9 @@ protected: */ class MotionClassifier final : public MotionClassifierInterface { public: + /** + * The provided pointer to the service cannot be null. + */ MotionClassifier(sp<android::hardware::input::classifier::V1_0::IInputClassifier> service); ~MotionClassifier(); /** @@ -127,6 +137,10 @@ private: // The events that need to be sent to the HAL. BlockingQueue<ClassifierEvent> mEvents; /** + * Add an event to the queue mEvents. + */ + void enqueueEvent(ClassifierEvent&& event); + /** * Thread that will communicate with InputClassifier HAL. * This should be the only thread that communicates with InputClassifier HAL, * because this thread is allowed to block on the HAL calls. @@ -142,16 +156,16 @@ private: */ void callInputClassifierHal(); /** - * Access to the InputClassifier HAL + * Access to the InputClassifier HAL. Can always be safely dereferenced. */ - sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService; + const sp<android::hardware::input::classifier::V1_0::IInputClassifier> mService; std::mutex mLock; /** * Per-device input classifications. Should only be accessed using the * getClassification / setClassification methods. */ std::unordered_map<int32_t /*deviceId*/, MotionClassification> - mClassifications; //GUARDED_BY(mLock); + mClassifications GUARDED_BY(mLock); /** * Set the current classification for a given device. */ @@ -161,7 +175,7 @@ private: */ MotionClassification getClassification(int32_t deviceId); void updateClassification(int32_t deviceId, nsecs_t eventTime, - MotionClassification classification); + MotionClassification classification); /** * Clear all current classifications */ @@ -172,8 +186,8 @@ private: * * Accessed indirectly by both InputClassifier thread and the thread that receives notifyMotion. */ - std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/> - mLastDownTimes; //GUARDED_BY(mLock); + std::unordered_map<int32_t /*deviceId*/, nsecs_t /*downTime*/> mLastDownTimes GUARDED_BY(mLock); + void updateLastDownTime(int32_t deviceId, nsecs_t downTime); /** @@ -183,15 +197,19 @@ private: void requestExit(); }; + /** * Implementation of the InputClassifierInterface. * Represents a separate stage of input processing. All of the input events go through this stage. * Acts as a passthrough for all input events except for motion events. * The events of motion type are sent to MotionClassifier. */ -class InputClassifier : public InputClassifierInterface { +class InputClassifier : public InputClassifierInterface, + public android::hardware::hidl_death_recipient { public: explicit InputClassifier(const sp<InputListenerInterface>& listener); + // Some of the constructor logic is finished in onFirstRef + virtual void onFirstRef() override; virtual void notifyConfigurationChanged(const NotifyConfigurationChangedArgs* args) override; virtual void notifyKey(const NotifyKeyArgs* args) override; @@ -199,10 +217,15 @@ public: virtual void notifySwitch(const NotifySwitchArgs* args) override; virtual void notifyDeviceReset(const NotifyDeviceResetArgs* args) override; + virtual void serviceDied(uint64_t cookie, + const wp<android::hidl::base::V1_0::IBase>& who) override; + virtual void dump(std::string& dump) override; private: - std::unique_ptr<MotionClassifierInterface> mMotionClassifier = nullptr; + // Protect access to mMotionClassifier, since it may become null via a hidl callback + std::mutex mLock; + std::unique_ptr<MotionClassifierInterface> mMotionClassifier GUARDED_BY(mLock); // The next stage to pass input events to sp<InputListenerInterface> mListener; }; diff --git a/services/inputflinger/InputDispatcher.cpp b/services/inputflinger/InputDispatcher.cpp index 7ff8b20a7e..bcb1ec5fb8 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(const sp<InputDispatcherPolicyInterface>& polic InputDispatcher::~InputDispatcher() { { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); resetKeyRepeatLocked(); releasePendingEventLocked(); @@ -277,8 +278,8 @@ InputDispatcher::~InputDispatcher() { 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. @@ -401,7 +402,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { case EventEntry::TYPE_KEY: { KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); if (isAppSwitchDue) { - if (isAppSwitchKeyEventLocked(typedEntry)) { + if (isAppSwitchKeyEvent(typedEntry)) { resetPendingAppSwitchLocked(true); isAppSwitchDue = false; } else if (dropReason == DROP_REASON_NOT_DROPPED) { @@ -409,7 +410,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { } } if (dropReason == DROP_REASON_NOT_DROPPED - && isStaleEventLocked(currentTime, typedEntry)) { + && isStaleEvent(currentTime, typedEntry)) { dropReason = DROP_REASON_STALE; } if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { @@ -425,7 +426,7 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) { dropReason = DROP_REASON_APP_SWITCH; } if (dropReason == DROP_REASON_NOT_DROPPED - && isStaleEventLocked(currentTime, typedEntry)) { + && isStaleEvent(currentTime, typedEntry)) { dropReason = DROP_REASON_STALE; } if (dropReason == DROP_REASON_NOT_DROPPED && mNextUnblockedEvent) { @@ -463,7 +464,7 @@ bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { // If the application takes too long to catch up then we drop all events preceding // the app switch key. KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); - if (isAppSwitchKeyEventLocked(keyEntry)) { + if (isAppSwitchKeyEvent(keyEntry)) { if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) { mAppSwitchSawKeyDown = true; } else if (keyEntry->action == AKEY_EVENT_ACTION_UP) { @@ -615,13 +616,13 @@ void InputDispatcher::dropInboundEventLocked(EventEntry* entry, DropReason dropR } } -bool InputDispatcher::isAppSwitchKeyCode(int32_t keyCode) { +static bool isAppSwitchKeyCode(int32_t keyCode) { return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL || keyCode == AKEYCODE_APP_SWITCH; } -bool InputDispatcher::isAppSwitchKeyEventLocked(KeyEntry* keyEntry) { +bool InputDispatcher::isAppSwitchKeyEvent(KeyEntry* keyEntry) { return ! (keyEntry->flags & AKEY_EVENT_FLAG_CANCELED) && isAppSwitchKeyCode(keyEntry->keyCode) && (keyEntry->policyFlags & POLICY_FLAG_TRUSTED) @@ -644,7 +645,7 @@ void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { #endif } -bool InputDispatcher::isStaleEventLocked(nsecs_t currentTime, EventEntry* entry) { +bool InputDispatcher::isStaleEvent(nsecs_t currentTime, EventEntry* entry) { return currentTime - entry->eventTime >= STALE_EVENT_TIMEOUT; } @@ -697,7 +698,7 @@ void InputDispatcher::releaseInboundEventLocked(EventEntry* entry) { #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; @@ -756,7 +757,7 @@ bool InputDispatcher::dispatchConfigurationChangedLocked( // Enqueue a command to run outside the lock to tell the policy that the configuration changed. CommandEntry* commandEntry = postCommandLocked( - & InputDispatcher::doNotifyConfigurationChangedInterruptible); + & InputDispatcher::doNotifyConfigurationChangedLockedInterruptible); commandEntry->eventTime = entry->eventTime; return true; } @@ -811,7 +812,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, entry->dispatchInProgress = true; - logOutboundKeyDetailsLocked("dispatchKey - ", entry); + logOutboundKeyDetails("dispatchKey - ", entry); } // Handle case where the policy asked us to try again later last time. @@ -851,7 +852,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, // 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 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, return false; } - setInjectionResultLocked(entry, injectionResult); + setInjectionResult(entry, injectionResult); if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { return true; } @@ -878,7 +879,7 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, KeyEntry* entry, return true; } -void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) { +void InputDispatcher::logOutboundKeyDetails(const char* prefix, const KeyEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", " "policyFlags=0x%x, action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, " @@ -896,12 +897,12 @@ bool InputDispatcher::dispatchMotionLocked( if (! entry->dispatchInProgress) { entry->dispatchInProgress = true; - logOutboundMotionDetailsLocked("dispatchMotion - ", entry); + logOutboundMotionDetails("dispatchMotion - ", entry); } // 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 @@ bool InputDispatcher::dispatchMotionLocked( 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 ? @@ -968,7 +969,7 @@ bool InputDispatcher::dispatchMotionLocked( } -void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) { +void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32 ", policyFlags=0x%x, " @@ -1049,7 +1050,7 @@ int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { #if DEBUG_FOCUS ALOGD("Waiting for application to become ready for input: %s. Reason: %s", - getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str(), + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), reason); #endif nsecs_t timeout; @@ -1233,8 +1234,7 @@ int32_t InputDispatcher::findFocusedWindowTargetsLocked(nsecs_t currentTime, Failed: Unresponsive: nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatisticsLocked(currentTime, entry, - injectionResult, timeSpentWaitingForApplication); + updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS ALOGD("findFocusedWindow finished: injectionResult=%d, " "timeSpentWaitingForApplication=%0.1fms", @@ -1654,8 +1654,7 @@ Unresponsive: mTempTouchState.reset(); nsecs_t timeSpentWaitingForApplication = getTimeSpentWaitingForApplicationLocked(currentTime); - updateDispatchStatisticsLocked(currentTime, entry, - injectionResult, timeSpentWaitingForApplication); + updateDispatchStatistics(currentTime, entry, injectionResult, timeSpentWaitingForApplication); #if DEBUG_FOCUS ALOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d, " "timeSpentWaitingForApplication=%0.1fms", @@ -1857,7 +1856,7 @@ std::string InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentT return ""; } -std::string InputDispatcher::getApplicationWindowLabelLocked( +std::string InputDispatcher::getApplicationWindowLabel( const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle) { if (applicationHandle != nullptr) { @@ -1956,7 +1955,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, #if DEBUG_FOCUS ALOGD("channel '%s' ~ Split motion event.", connection->getInputChannelName().c_str()); - logOutboundMotionDetailsLocked(" ", splitMotionEntry); + logOutboundMotionDetails(" ", splitMotionEntry); #endif enqueueDispatchEntriesLocked(currentTime, connection, splitMotionEntry, inputTarget); @@ -1974,17 +1973,17 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, 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. @@ -1993,7 +1992,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime, } } -void InputDispatcher::enqueueDispatchEntryLocked( +void InputDispatcher::enqueueDispatchEntry( const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode) { int32_t inputTargetFlags = inputTarget->flags; @@ -2076,12 +2075,12 @@ void InputDispatcher::enqueueDispatchEntryLocked( // Remember that we are waiting for this dispatch to complete. if (dispatchEntry->hasForegroundTarget()) { - incrementPendingForegroundDispatchesLocked(eventEntry); + incrementPendingForegroundDispatches(eventEntry); } // Enqueue the dispatch entry. connection->outboundQueue.enqueueAtTail(dispatchEntry); - traceOutboundQueueLengthLocked(connection); + traceOutboundQueueLength(connection); } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, @@ -2196,9 +2195,9 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, // Re-enqueue the event on the wait queue. connection->outboundQueue.dequeue(dispatchEntry); - traceOutboundQueueLengthLocked(connection); + traceOutboundQueueLength(connection); connection->waitQueue.enqueueAtTail(dispatchEntry); - traceWaitQueueLengthLocked(connection); + traceWaitQueueLength(connection); } } @@ -2228,10 +2227,10 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, #endif // Clear the dispatch queues. - drainDispatchQueueLocked(&connection->outboundQueue); - traceOutboundQueueLengthLocked(connection); - drainDispatchQueueLocked(&connection->waitQueue); - traceWaitQueueLengthLocked(connection); + drainDispatchQueue(&connection->outboundQueue); + traceOutboundQueueLength(connection); + drainDispatchQueue(&connection->waitQueue); + traceWaitQueueLength(connection); // The connection appears to be unrecoverably broken. // Ignore already broken or zombie connections. @@ -2245,16 +2244,16 @@ void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime, } } -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; } @@ -2263,7 +2262,7 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { 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) { @@ -2323,7 +2322,7 @@ int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) { } // release lock } -void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( +void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked ( const CancelationOptions& options) { for (size_t i = 0; i < mConnectionsByFd.size(); i++) { synthesizeCancelationEventsForConnectionLocked( @@ -2331,7 +2330,7 @@ void InputDispatcher::synthesizeCancelationEventsForAllConnectionsLocked( } } -void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked( +void InputDispatcher::synthesizeCancelationEventsForMonitorsLocked ( const CancelationOptions& options) { for (auto& it : mMonitoringChannelsByDisplay) { const Vector<sp<InputChannel>>& monitoringChannels = it.second; @@ -2374,11 +2373,11 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( EventEntry* cancelationEventEntry = cancelationEvents.itemAt(i); switch (cancelationEventEntry->type) { case EventEntry::TYPE_KEY: - logOutboundKeyDetailsLocked("cancel - ", + logOutboundKeyDetails("cancel - ", static_cast<KeyEntry*>(cancelationEventEntry)); break; case EventEntry::TYPE_MOTION: - logOutboundMotionDetailsLocked("cancel - ", + logOutboundMotionDetails("cancel - ", static_cast<MotionEntry*>(cancelationEventEntry)); break; } @@ -2401,7 +2400,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( 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(); @@ -2511,7 +2510,7 @@ void InputDispatcher::notifyConfigurationChanged(const NotifyConfigurationChange bool needWake; { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); ConfigurationChangedEntry* newEntry = new ConfigurationChangedEntry(args->sequenceNum, args->eventTime); @@ -2539,7 +2538,7 @@ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int3 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; @@ -2549,7 +2548,7 @@ void InputDispatcher::accelerateMetaShortcuts(const int32_t deviceId, const int3 // 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) { @@ -2744,7 +2743,7 @@ void InputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) { bool needWake; { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); DeviceResetEntry* newEntry = new DeviceResetEntry(args->sequenceNum, args->eventTime, args->deviceId); @@ -2898,7 +2897,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, 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; @@ -2919,7 +2918,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, break; } - mInjectionResultAvailableCondition.waitRelative(mLock, remainingTimeout); + mInjectionResultAvailable.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); } if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED @@ -2939,7 +2938,7 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, break; } - mInjectionSyncFinishedCondition.waitRelative(mLock, remainingTimeout); + mInjectionSyncFinished.wait_for(_l, std::chrono::nanoseconds(remainingTimeout)); } } } @@ -2961,7 +2960,7 @@ bool InputDispatcher::hasInjectionPermission(int32_t injectorPid, int32_t inject || 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 @@ -2990,31 +2989,31 @@ void InputDispatcher::setInjectionResultLocked(EventEntry* entry, int32_t inject } injectionState->injectionResult = injectionResult; - mInjectionResultAvailableCondition.broadcast(); + mInjectionResultAvailable.notify_all(); } } -void InputDispatcher::incrementPendingForegroundDispatchesLocked(EventEntry* entry) { +void InputDispatcher::incrementPendingForegroundDispatches(EventEntry* entry) { InjectionState* injectionState = entry->injectionState; if (injectionState) { injectionState->pendingForegroundDispatches += 1; } } -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(); } } } Vector<sp<InputWindowHandle>> InputDispatcher::getWindowHandlesLocked(int32_t displayId) const { std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>>::const_iterator it = - mWindowHandlesByDisplay.find(displayId); + mWindowHandlesByDisplay.find(displayId); if(it != mWindowHandlesByDisplay.end()) { return it->second; } @@ -3038,8 +3037,7 @@ sp<InputWindowHandle> InputDispatcher::getWindowHandleLocked( return nullptr; } -bool InputDispatcher::hasWindowHandleLocked( - const sp<InputWindowHandle>& windowHandle) const { +bool InputDispatcher::hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const { for (auto& it : mWindowHandlesByDisplay) { const Vector<sp<InputWindowHandle>> windowHandles = it.second; size_t numWindows = windowHandles.size(); @@ -3075,12 +3073,12 @@ sp<InputChannel> InputDispatcher::getInputChannelLocked(const sp<IBinder>& token * For removed handle, check if need to send a cancel event if already in touch. */ void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle>>& inputWindowHandles, - int32_t displayId) { + int32_t displayId, const sp<ISetInputWindowsListener>& setInputWindowsListener) { #if DEBUG_FOCUS 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); @@ -3225,6 +3223,10 @@ void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle>>& input // Wake up poll loop since it may need to make new input dispatching choices. mLooper->wake(); + + if (setInputWindowsListener) { + setInputWindowsListener->onSetInputWindowsFinished(); + } } void InputDispatcher::setFocusedApplication( @@ -3233,7 +3235,7 @@ void InputDispatcher::setFocusedApplication( ALOGD("setFocusedApplication displayId=%" PRId32, displayId); #endif { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); sp<InputApplicationHandle> oldFocusedApplicationHandle = getValueByKey(mFocusedApplicationHandlesByDisplay, displayId); @@ -3275,7 +3277,7 @@ void InputDispatcher::setFocusedDisplay(int32_t displayId) { ALOGD("setFocusedDisplay displayId=%" PRId32, displayId); #endif { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); if (mFocusedDisplayId != displayId) { sp<InputWindowHandle> oldFocusedWindowHandle = @@ -3327,7 +3329,7 @@ void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { bool changed; { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { if (mDispatchFrozen && !frozen) { @@ -3362,7 +3364,7 @@ void InputDispatcher::setInputFilterEnabled(bool enabled) { #endif { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); if (mInputFilterEnabled == enabled) { return; @@ -3385,7 +3387,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< } { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); sp<InputWindowHandle> fromWindowHandle = getWindowHandleLocked(fromToken); sp<InputWindowHandle> toWindowHandle = getWindowHandleLocked(toToken); @@ -3729,7 +3731,7 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan #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. @@ -3771,7 +3773,7 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh #endif { // acquire lock - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); status_t status = unregisterInputChannelLocked(inputChannel, false /*notify*/); if (status) { @@ -3884,7 +3886,7 @@ void InputDispatcher::onANRLocked( float waitDuration = (currentTime - waitStartTime) * 0.000001f; ALOGI("Application is not responding: %s. " "It has been %0.1fms since event, %0.1fms since wait started. Reason: %s", - getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str(), + getApplicationWindowLabel(applicationHandle, windowHandle).c_str(), dispatchLatency, waitDuration, reason); // Capture a record of the InputDispatcher state at the time of the ANR. @@ -3897,7 +3899,7 @@ void InputDispatcher::onANRLocked( mLastANRState += INDENT "ANR:\n"; mLastANRState += StringPrintf(INDENT2 "Time: %s\n", timestr); mLastANRState += StringPrintf(INDENT2 "Window: %s\n", - getApplicationWindowLabelLocked(applicationHandle, windowHandle).c_str()); + getApplicationWindowLabel(applicationHandle, windowHandle).c_str()); mLastANRState += StringPrintf(INDENT2 "DispatchLatency: %0.1fms\n", dispatchLatency); mLastANRState += StringPrintf(INDENT2 "WaitDuration: %0.1fms\n", waitDuration); mLastANRState += StringPrintf(INDENT2 "Reason: %s\n", reason); @@ -3911,7 +3913,7 @@ void InputDispatcher::onANRLocked( commandEntry->reason = reason; } -void InputDispatcher::doNotifyConfigurationChangedInterruptible( +void InputDispatcher::doNotifyConfigurationChangedLockedInterruptible ( CommandEntry* commandEntry) { mLock.unlock(); @@ -4027,12 +4029,12 @@ void InputDispatcher::doDispatchCycleFinishedLockedInterruptible( // a few things. if (dispatchEntry == connection->findWaitQueueEntry(seq)) { connection->waitQueue.dequeue(dispatchEntry); - traceWaitQueueLengthLocked(connection); + traceWaitQueueLength(connection); if (restartEvent && connection->status == Connection::STATUS_NORMAL) { connection->outboundQueue.enqueueAtHead(dispatchEntry); - traceOutboundQueueLengthLocked(connection); + traceOutboundQueueLength(connection); } else { - releaseDispatchEntryLocked(dispatchEntry); + releaseDispatchEntry(dispatchEntry); } } @@ -4242,7 +4244,7 @@ void InputDispatcher::initializeKeyEvent(KeyEvent* event, const KeyEntry* entry) entry->downTime, entry->eventTime); } -void InputDispatcher::updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, +void InputDispatcher::updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication) { // TODO Write some statistics about how long we spend waiting. } @@ -4253,7 +4255,7 @@ void InputDispatcher::traceInboundQueueLengthLocked() { } } -void InputDispatcher::traceOutboundQueueLengthLocked(const sp<Connection>& connection) { +void InputDispatcher::traceOutboundQueueLength(const sp<Connection>& connection) { if (ATRACE_ENABLED()) { char counterName[40]; snprintf(counterName, sizeof(counterName), "oq:%s", connection->getWindowName().c_str()); @@ -4261,7 +4263,7 @@ void InputDispatcher::traceOutboundQueueLengthLocked(const sp<Connection>& conne } } -void InputDispatcher::traceWaitQueueLengthLocked(const sp<Connection>& connection) { +void InputDispatcher::traceWaitQueueLength(const sp<Connection>& connection) { if (ATRACE_ENABLED()) { char counterName[40]; snprintf(counterName, sizeof(counterName), "wq:%s", connection->getWindowName().c_str()); @@ -4270,7 +4272,7 @@ void InputDispatcher::traceWaitQueueLengthLocked(const sp<Connection>& connectio } void InputDispatcher::dump(std::string& dump) { - AutoMutex _l(mLock); + std::scoped_lock _l(mLock); dump += "Input Dispatcher State:\n"; dumpDispatchStateLocked(dump); @@ -4283,10 +4285,9 @@ void InputDispatcher::dump(std::string& dump) { 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 595b01d46c..24ae32fcf5 100644 --- a/services/inputflinger/InputDispatcher.h +++ b/services/inputflinger/InputDispatcher.h @@ -17,10 +17,12 @@ #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> #include <input/InputWindow.h> +#include <input/ISetInputWindowsListener.h> #include <utils/KeyedVector.h> #include <utils/Vector.h> #include <utils/threads.h> @@ -39,7 +41,6 @@ #include "InputListener.h" #include "InputReporterInterface.h" - namespace android { /* @@ -315,7 +316,8 @@ public: * This method may be called on any thread (usually by the input manager). */ virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles, - int32_t displayId) = 0; + int32_t displayId, + const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr) = 0; /* Sets the focused application on the given display. * @@ -406,7 +408,8 @@ public: uint32_t policyFlags); virtual void setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles, - int32_t displayId); + int32_t displayId, + const sp<ISetInputWindowsListener>& setInputWindowsListener = nullptr); virtual void setFocusedApplication(int32_t displayId, const sp<InputApplicationHandle>& inputApplicationHandle); virtual void setFocusedDisplay(int32_t displayId); @@ -882,81 +885,82 @@ private: sp<InputDispatcherPolicyInterface> mPolicy; InputDispatcherConfiguration mConfig; - Mutex mLock; + std::mutex mLock; - Condition mDispatcherIsAliveCondition; + std::condition_variable mDispatcherIsAlive; sp<Looper> mLooper; - EventEntry* mPendingEvent; - Queue<EventEntry> mInboundQueue; - Queue<EventEntry> mRecentQueue; - Queue<CommandEntry> mCommandQueue; + EventEntry* mPendingEvent GUARDED_BY(mLock); + Queue<EventEntry> mInboundQueue GUARDED_BY(mLock); + Queue<EventEntry> mRecentQueue GUARDED_BY(mLock); + Queue<CommandEntry> mCommandQueue GUARDED_BY(mLock); - DropReason mLastDropReason; + DropReason mLastDropReason GUARDED_BY(mLock); - void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime); + void dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) REQUIRES(mLock); // Enqueues an inbound event. Returns true if mLooper->wake() should be called. - bool enqueueInboundEventLocked(EventEntry* entry); + bool enqueueInboundEventLocked(EventEntry* entry) REQUIRES(mLock); // Cleans up input state when dropping an inbound event. - void dropInboundEventLocked(EventEntry* entry, DropReason dropReason); + void dropInboundEventLocked(EventEntry* entry, DropReason dropReason) REQUIRES(mLock); // Adds an event to a queue of recent events for debugging purposes. - void addRecentEventLocked(EventEntry* entry); + void addRecentEventLocked(EventEntry* entry) REQUIRES(mLock); // App switch latency optimization. - bool mAppSwitchSawKeyDown; - nsecs_t mAppSwitchDueTime; + bool mAppSwitchSawKeyDown GUARDED_BY(mLock); + nsecs_t mAppSwitchDueTime GUARDED_BY(mLock); - static bool isAppSwitchKeyCode(int32_t keyCode); - bool isAppSwitchKeyEventLocked(KeyEntry* keyEntry); - bool isAppSwitchPendingLocked(); - void resetPendingAppSwitchLocked(bool handled); + bool isAppSwitchKeyEvent(KeyEntry* keyEntry); + bool isAppSwitchPendingLocked() REQUIRES(mLock); + void resetPendingAppSwitchLocked(bool handled) REQUIRES(mLock); // Stale event latency optimization. - static bool isStaleEventLocked(nsecs_t currentTime, EventEntry* entry); + static bool isStaleEvent(nsecs_t currentTime, EventEntry* entry); // Blocked event latency optimization. Drops old events when the user intends // to transfer focus to a new application. - EventEntry* mNextUnblockedEvent; + EventEntry* mNextUnblockedEvent GUARDED_BY(mLock); sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y, - bool addOutsideTargets = false, bool addPortalWindows = false); + bool addOutsideTargets = false, bool addPortalWindows = false) REQUIRES(mLock); // All registered connections mapped by channel file descriptor. - KeyedVector<int, sp<Connection> > mConnectionsByFd; + KeyedVector<int, sp<Connection> > mConnectionsByFd GUARDED_BY(mLock); struct IBinderHash { std::size_t operator()(const sp<IBinder>& b) const { return std::hash<IBinder *>{}(b.get()); } }; - std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken; + std::unordered_map<sp<IBinder>, sp<InputChannel>, IBinderHash> mInputChannelsByToken + GUARDED_BY(mLock); - ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel); + ssize_t getConnectionIndexLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); // Input channels that will receive a copy of all input events sent to the provided display. - std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay; + std::unordered_map<int32_t, Vector<sp<InputChannel>>> mMonitoringChannelsByDisplay + 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; - void incrementPendingForegroundDispatchesLocked(EventEntry* entry); - void decrementPendingForegroundDispatchesLocked(EventEntry* entry); + std::condition_variable mInjectionSyncFinished; + void incrementPendingForegroundDispatches(EventEntry* entry); + void decrementPendingForegroundDispatches(EventEntry* entry); // Key repeat tracking. struct KeyRepeatState { KeyEntry* lastKeyEntry; // or null if no repeat nsecs_t nextRepeatTime; - } mKeyRepeatState; + } mKeyRepeatState GUARDED_BY(mLock); - void resetKeyRepeatLocked(); - KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime); + void resetKeyRepeatLocked() REQUIRES(mLock); + KeyEntry* synthesizeKeyRepeatLocked(nsecs_t currentTime) REQUIRES(mLock); // Key replacement tracking struct KeyReplacement { @@ -970,39 +974,42 @@ private: } }; // Maps the key code replaced, device id tuple to the key code it was replaced with - KeyedVector<KeyReplacement, int32_t> mReplacedKeys; + KeyedVector<KeyReplacement, int32_t> mReplacedKeys GUARDED_BY(mLock); // Process certain Meta + Key combinations void accelerateMetaShortcuts(const int32_t deviceId, const int32_t action, int32_t& keyCode, int32_t& metaState); // Deferred command processing. - bool haveCommandsLocked() const; - bool runCommandsLockedInterruptible(); - CommandEntry* postCommandLocked(Command command); + bool haveCommandsLocked() const REQUIRES(mLock); + bool runCommandsLockedInterruptible() REQUIRES(mLock); + CommandEntry* postCommandLocked(Command command) REQUIRES(mLock); // Input filter processing. - bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args); - bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args); + bool shouldSendKeyToInputFilterLocked(const NotifyKeyArgs* args) REQUIRES(mLock); + bool shouldSendMotionToInputFilterLocked(const NotifyMotionArgs* args) REQUIRES(mLock); // Inbound event processing. - void drainInboundQueueLocked(); - void releasePendingEventLocked(); - void releaseInboundEventLocked(EventEntry* entry); + void drainInboundQueueLocked() REQUIRES(mLock); + void releasePendingEventLocked() REQUIRES(mLock); + void releaseInboundEventLocked(EventEntry* entry) REQUIRES(mLock); // Dispatch state. - bool mDispatchEnabled; - bool mDispatchFrozen; - bool mInputFilterEnabled; + bool mDispatchEnabled GUARDED_BY(mLock); + bool mDispatchFrozen GUARDED_BY(mLock); + bool mInputFilterEnabled GUARDED_BY(mLock); - std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay; + std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> mWindowHandlesByDisplay + GUARDED_BY(mLock); // Get window handles by display, return an empty vector if not found. - Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const; - sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const; - sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const; - bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const; + Vector<sp<InputWindowHandle>> getWindowHandlesLocked(int32_t displayId) const REQUIRES(mLock); + sp<InputWindowHandle> getWindowHandleLocked(const sp<IBinder>& windowHandleToken) const + REQUIRES(mLock); + sp<InputChannel> getInputChannelLocked(const sp<IBinder>& windowToken) const REQUIRES(mLock); + bool hasWindowHandleLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); // Focus tracking for keys, trackball, etc. - std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay; + std::unordered_map<int32_t, sp<InputWindowHandle>> mFocusedWindowHandlesByDisplay + GUARDED_BY(mLock); // Focus tracking for touch. struct TouchedWindow { @@ -1037,34 +1044,35 @@ private: bool isSlippery() const; }; - KeyedVector<int32_t, TouchState> mTouchStatesByDisplay; - TouchState mTempTouchState; + KeyedVector<int32_t, TouchState> mTouchStatesByDisplay GUARDED_BY(mLock); + TouchState mTempTouchState GUARDED_BY(mLock); // Focused applications. - std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay; + std::unordered_map<int32_t, sp<InputApplicationHandle>> mFocusedApplicationHandlesByDisplay + GUARDED_BY(mLock); // Top focused display. - int32_t mFocusedDisplayId; + int32_t mFocusedDisplayId GUARDED_BY(mLock); // Dispatcher state at time of last ANR. - std::string mLastANRState; + std::string mLastANRState GUARDED_BY(mLock); // Dispatch inbound events. bool dispatchConfigurationChangedLocked( - nsecs_t currentTime, ConfigurationChangedEntry* entry); + nsecs_t currentTime, ConfigurationChangedEntry* entry) REQUIRES(mLock); bool dispatchDeviceResetLocked( - nsecs_t currentTime, DeviceResetEntry* entry); + nsecs_t currentTime, DeviceResetEntry* entry) REQUIRES(mLock); bool dispatchKeyLocked( nsecs_t currentTime, KeyEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime); + DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); bool dispatchMotionLocked( nsecs_t currentTime, MotionEntry* entry, - DropReason* dropReason, nsecs_t* nextWakeupTime); + DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock); void dispatchEventLocked(nsecs_t currentTime, EventEntry* entry, - const Vector<InputTarget>& inputTargets); + const Vector<InputTarget>& inputTargets) REQUIRES(mLock); - void logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry); - void logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry); + void logOutboundKeyDetails(const char* prefix, const KeyEntry* entry); + void logOutboundMotionDetails(const char* prefix, const MotionEntry* entry); // Keeping track of ANR timeouts. enum InputTargetWaitCause { @@ -1073,130 +1081,133 @@ private: INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY, }; - InputTargetWaitCause mInputTargetWaitCause; - nsecs_t mInputTargetWaitStartTime; - nsecs_t mInputTargetWaitTimeoutTime; - bool mInputTargetWaitTimeoutExpired; - sp<IBinder> mInputTargetWaitApplicationToken; + InputTargetWaitCause mInputTargetWaitCause GUARDED_BY(mLock); + nsecs_t mInputTargetWaitStartTime GUARDED_BY(mLock); + nsecs_t mInputTargetWaitTimeoutTime GUARDED_BY(mLock); + bool mInputTargetWaitTimeoutExpired GUARDED_BY(mLock); + sp<IBinder> mInputTargetWaitApplicationToken GUARDED_BY(mLock); // Contains the last window which received a hover event. - sp<InputWindowHandle> mLastHoverWindowHandle; + sp<InputWindowHandle> mLastHoverWindowHandle GUARDED_BY(mLock); // Finding targets for input events. int32_t handleTargetsNotReadyLocked(nsecs_t currentTime, const EventEntry* entry, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, - nsecs_t* nextWakeupTime, const char* reason); + nsecs_t* nextWakeupTime, const char* reason) REQUIRES(mLock); - void removeWindowByTokenLocked(const sp<IBinder>& token); + void removeWindowByTokenLocked(const sp<IBinder>& token) REQUIRES(mLock); void resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout, - const sp<InputChannel>& inputChannel); - nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime); - void resetANRTimeoutsLocked(); + const sp<InputChannel>& inputChannel) REQUIRES(mLock); + nsecs_t getTimeSpentWaitingForApplicationLocked(nsecs_t currentTime) REQUIRES(mLock); + void resetANRTimeoutsLocked() REQUIRES(mLock); int32_t getTargetDisplayId(const EventEntry* entry); int32_t findFocusedWindowTargetsLocked(nsecs_t currentTime, const EventEntry* entry, - Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime); + Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime) REQUIRES(mLock); int32_t findTouchedWindowTargetsLocked(nsecs_t currentTime, const MotionEntry* entry, Vector<InputTarget>& inputTargets, nsecs_t* nextWakeupTime, - bool* outConflictingPointerActions); + bool* outConflictingPointerActions) REQUIRES(mLock); void addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle, - int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets); + int32_t targetFlags, BitSet32 pointerIds, Vector<InputTarget>& inputTargets) + REQUIRES(mLock); void addMonitoringTargetsLocked(Vector<InputTarget>& inputTargets, int32_t displayId, - float xOffset = 0, float yOffset = 0); + float xOffset = 0, float yOffset = 0) REQUIRES(mLock); - void pokeUserActivityLocked(const EventEntry* eventEntry); + void pokeUserActivityLocked(const EventEntry* eventEntry) REQUIRES(mLock); bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle, const InjectionState* injectionState); bool isWindowObscuredAtPointLocked(const sp<InputWindowHandle>& windowHandle, - int32_t x, int32_t y) const; - bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const; - std::string getApplicationWindowLabelLocked(const sp<InputApplicationHandle>& applicationHandle, + int32_t x, int32_t y) const REQUIRES(mLock); + bool isWindowObscuredLocked(const sp<InputWindowHandle>& windowHandle) const REQUIRES(mLock); + std::string getApplicationWindowLabel(const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle); std::string checkWindowReadyForMoreInputLocked(nsecs_t currentTime, const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry, - const char* targetType); + const char* targetType) REQUIRES(mLock); // Manage the dispatch cycle for a single connection. // These methods are deliberately not Interruptible because doing all of the work // with the mutex held makes it easier to ensure that connection invariants are maintained. // If needed, the methods post commands to run later once the critical bits are done. void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, const InputTarget* inputTarget); + EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection, - EventEntry* eventEntry, const InputTarget* inputTarget); - void enqueueDispatchEntryLocked(const sp<Connection>& connection, + EventEntry* eventEntry, const InputTarget* inputTarget) REQUIRES(mLock); + void enqueueDispatchEntry(const sp<Connection>& connection, EventEntry* eventEntry, const InputTarget* inputTarget, int32_t dispatchMode); - void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection); + void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection) + REQUIRES(mLock); void finishDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - uint32_t seq, bool handled); + uint32_t seq, bool handled) REQUIRES(mLock); void abortBrokenDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, - bool notify); - void drainDispatchQueueLocked(Queue<DispatchEntry>* queue); - void releaseDispatchEntryLocked(DispatchEntry* dispatchEntry); + bool notify) REQUIRES(mLock); + void drainDispatchQueue(Queue<DispatchEntry>* queue); + void releaseDispatchEntry(DispatchEntry* dispatchEntry); static int handleReceiveCallback(int fd, int events, void* data); void synthesizeCancelationEventsForAllConnectionsLocked( - const CancelationOptions& options); - void synthesizeCancelationEventsForMonitorsLocked(const CancelationOptions& options); + const CancelationOptions& options) REQUIRES(mLock); + void synthesizeCancelationEventsForMonitorsLocked( + const CancelationOptions& options) REQUIRES(mLock); void synthesizeCancelationEventsForInputChannelLocked(const sp<InputChannel>& channel, - const CancelationOptions& options); + const CancelationOptions& options) REQUIRES(mLock); void synthesizeCancelationEventsForConnectionLocked(const sp<Connection>& connection, - const CancelationOptions& options); + const CancelationOptions& options) REQUIRES(mLock); // Splitting motion events across windows. MotionEntry* splitMotionEvent(const MotionEntry* originalMotionEntry, BitSet32 pointerIds); // Reset and drop everything the dispatcher is doing. - void resetAndDropEverythingLocked(const char* reason); + void resetAndDropEverythingLocked(const char* reason) REQUIRES(mLock); // Dump state. - void dumpDispatchStateLocked(std::string& dump); - void logDispatchStateLocked(); + void dumpDispatchStateLocked(std::string& dump) REQUIRES(mLock); + void logDispatchStateLocked() REQUIRES(mLock); // Registration. - void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel); - status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify); - - // Add or remove a connection to the mActiveConnections vector. - void activateConnectionLocked(Connection* connection); - void deactivateConnectionLocked(Connection* connection); + void removeMonitorChannelLocked(const sp<InputChannel>& inputChannel) REQUIRES(mLock); + status_t unregisterInputChannelLocked(const sp<InputChannel>& inputChannel, bool notify) + REQUIRES(mLock); // Interesting events that we might like to log or tell the framework about. void onDispatchCycleFinishedLocked( - nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled); + nsecs_t currentTime, const sp<Connection>& connection, uint32_t seq, bool handled) + REQUIRES(mLock); void onDispatchCycleBrokenLocked( - nsecs_t currentTime, const sp<Connection>& connection); + nsecs_t currentTime, const sp<Connection>& connection) REQUIRES(mLock); void onFocusChangedLocked(const sp<InputWindowHandle>& oldFocus, - const sp<InputWindowHandle>& newFocus); + const sp<InputWindowHandle>& newFocus) REQUIRES(mLock); void onANRLocked( nsecs_t currentTime, const sp<InputApplicationHandle>& applicationHandle, const sp<InputWindowHandle>& windowHandle, - nsecs_t eventTime, nsecs_t waitStartTime, const char* reason); + nsecs_t eventTime, nsecs_t waitStartTime, const char* reason) REQUIRES(mLock); // Outbound policy interactions. - void doNotifyConfigurationChangedInterruptible(CommandEntry* commandEntry); - void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry); - void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry); - void doNotifyANRLockedInterruptible(CommandEntry* commandEntry); - void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry); - void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry); + void doNotifyConfigurationChangedLockedInterruptible(CommandEntry* commandEntry) + REQUIRES(mLock); + void doNotifyInputChannelBrokenLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyFocusChangedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doNotifyANRLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); + void doInterceptKeyBeforeDispatchingLockedInterruptible(CommandEntry* commandEntry) + REQUIRES(mLock); + void doDispatchCycleFinishedLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); bool afterKeyEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled); + DispatchEntry* dispatchEntry, KeyEntry* keyEntry, bool handled) REQUIRES(mLock); bool afterMotionEventLockedInterruptible(const sp<Connection>& connection, - DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled); - void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry); + DispatchEntry* dispatchEntry, MotionEntry* motionEntry, bool handled) REQUIRES(mLock); + void doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) REQUIRES(mLock); void initializeKeyEvent(KeyEvent* event, const KeyEntry* entry); // Statistics gathering. - void updateDispatchStatisticsLocked(nsecs_t currentTime, const EventEntry* entry, + void updateDispatchStatistics(nsecs_t currentTime, const EventEntry* entry, int32_t injectionResult, nsecs_t timeSpentWaitingForApplication); - void traceInboundQueueLengthLocked(); - void traceOutboundQueueLengthLocked(const sp<Connection>& connection); - void traceWaitQueueLengthLocked(const sp<Connection>& connection); + void traceInboundQueueLengthLocked() REQUIRES(mLock); + void traceOutboundQueueLength(const sp<Connection>& connection); + void traceWaitQueueLength(const sp<Connection>& connection); sp<InputReporterInterface> mReporter; }; diff --git a/services/inputflinger/InputManager.cpp b/services/inputflinger/InputManager.cpp index a7fd9bab95..b0157a166f 100644 --- a/services/inputflinger/InputManager.cpp +++ b/services/inputflinger/InputManager.cpp @@ -103,7 +103,8 @@ public: } }; -void InputManager::setInputWindows(const Vector<InputWindowInfo>& infos) { +void InputManager::setInputWindows(const Vector<InputWindowInfo>& infos, + const sp<ISetInputWindowsListener>& setInputWindowsListener) { std::unordered_map<int32_t, Vector<sp<InputWindowHandle>>> handlesPerDisplay; Vector<sp<InputWindowHandle>> handles; @@ -112,7 +113,7 @@ void InputManager::setInputWindows(const Vector<InputWindowInfo>& infos) { handlesPerDisplay[info.displayId].add(new BinderWindowHandle(info)); } for (auto const& i : handlesPerDisplay) { - mDispatcher->setInputWindows(i.second, i.first); + mDispatcher->setInputWindows(i.second, i.first, setInputWindowsListener); } } diff --git a/services/inputflinger/InputManager.h b/services/inputflinger/InputManager.h index e632da3f63..ff9a0800da 100644 --- a/services/inputflinger/InputManager.h +++ b/services/inputflinger/InputManager.h @@ -29,6 +29,7 @@ #include <input/Input.h> #include <input/InputTransport.h> +#include <input/ISetInputWindowsListener.h> #include <input/IInputFlinger.h> #include <utils/Errors.h> @@ -93,7 +94,8 @@ public: virtual sp<InputClassifierInterface> getClassifier(); virtual sp<InputDispatcherInterface> getDispatcher(); - virtual void setInputWindows(const Vector<InputWindowInfo>& handles); + virtual void setInputWindows(const Vector<InputWindowInfo>& handles, + const sp<ISetInputWindowsListener>& setInputWindowsListener); virtual void transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken); virtual void registerInputChannel(const sp<InputChannel>& channel); diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp index 1a1ae21123..21ba029854 100644 --- a/services/inputflinger/InputReader.cpp +++ b/services/inputflinger/InputReader.cpp @@ -6565,6 +6565,8 @@ void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32 const int32_t displayId = getAssociatedDisplay().value_or(ADISPLAY_ID_NONE); const int32_t deviceId = getDeviceId(); std::vector<TouchVideoFrame> frames = mDevice->getEventHub()->getVideoFrames(deviceId); + std::for_each(frames.begin(), frames.end(), + [this](TouchVideoFrame& frame) { frame.rotate(this->mSurfaceOrientation); }); NotifyMotionArgs args(mContext->getNextSequenceNum(), when, deviceId, source, displayId, policyFlags, action, actionButton, flags, metaState, buttonState, MotionClassification::NONE, diff --git a/services/inputflinger/TouchVideoDevice.cpp b/services/inputflinger/TouchVideoDevice.cpp index ad70ccc46d..19c1313dcd 100644 --- a/services/inputflinger/TouchVideoDevice.cpp +++ b/services/inputflinger/TouchVideoDevice.cpp @@ -37,10 +37,10 @@ using android::base::unique_fd; namespace android { TouchVideoDevice::TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, - uint32_t width, uint32_t height, + uint32_t height, uint32_t width, const std::array<const int16_t*, NUM_BUFFERS>& readLocations) : mFd(fd), mName(std::move(name)), mPath(std::move(devicePath)), - mWidth(width), mHeight(height), + mHeight(height), mWidth(width), mReadLocations(readLocations) { mFrames.reserve(MAX_QUEUE_SIZE); }; @@ -68,6 +68,7 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat std::string name = reinterpret_cast<const char*>(cap.card); struct v4l2_input v4l2_input_struct; + v4l2_input_struct.index = 0; result = ioctl(fd.get(), VIDIOC_ENUMINPUT, &v4l2_input_struct); if (result == -1) { ALOGE("VIDIOC_ENUMINPUT failed: %s", strerror(errno)); @@ -87,14 +88,15 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat ALOGE("VIDIOC_G_FMT failed: %s", strerror(errno)); return nullptr; } - const uint32_t width = v4l2_fmt.fmt.pix.width; const uint32_t height = v4l2_fmt.fmt.pix.height; - ALOGI("Frame dimensions: width = %" PRIu32 " height = %" PRIu32, width, height); + const uint32_t width = v4l2_fmt.fmt.pix.width; + ALOGI("Frame dimensions: height = %" PRIu32 " width = %" PRIu32, height, width); - struct v4l2_requestbuffers req; + struct v4l2_requestbuffers req = {}; req.count = NUM_BUFFERS; req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req.memory = V4L2_MEMORY_MMAP; + // req.reserved is zeroed during initialization, which is required per v4l docs result = ioctl(fd.get(), VIDIOC_REQBUFS, &req); if (result == -1) { ALOGE("VIDIOC_REQBUFS failed: %s", strerror(errno)); @@ -108,6 +110,7 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat struct v4l2_buffer buf = {}; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf.memory = V4L2_MEMORY_MMAP; + // buf.reserved and buf.reserved2 are zeroed during initialization, required per v4l docs std::array<const int16_t*, NUM_BUFFERS> readLocations; for (size_t i = 0; i < NUM_BUFFERS; i++) { buf.index = i; @@ -116,7 +119,7 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat ALOGE("VIDIOC_QUERYBUF failed: %s", strerror(errno)); return nullptr; } - if (buf.length != width * height * sizeof(int16_t)) { + if (buf.length != height * width * sizeof(int16_t)) { ALOGE("Unexpected value of buf.length = %i (offset = %" PRIu32 ")", buf.length, buf.m.offset); return nullptr; @@ -148,7 +151,7 @@ std::unique_ptr<TouchVideoDevice> TouchVideoDevice::create(std::string devicePat } // Using 'new' to access a non-public constructor. return std::unique_ptr<TouchVideoDevice>(new TouchVideoDevice( - fd.release(), std::move(name), std::move(devicePath), width, height, readLocations)); + fd.release(), std::move(name), std::move(devicePath), height, width, readLocations)); } size_t TouchVideoDevice::readAndQueueFrames() { @@ -193,10 +196,10 @@ std::optional<TouchVideoFrame> TouchVideoDevice::readFrame() { ALOGW("The timestamp %ld.%ld was not acquired using CLOCK_MONOTONIC", buf.timestamp.tv_sec, buf.timestamp.tv_usec); } - std::vector<int16_t> data(mWidth * mHeight); + std::vector<int16_t> data(mHeight * mWidth); const int16_t* readFrom = mReadLocations[buf.index]; - std::copy(readFrom, readFrom + mWidth * mHeight, data.begin()); - TouchVideoFrame frame(mWidth, mHeight, std::move(data), buf.timestamp); + std::copy(readFrom, readFrom + mHeight * mWidth, data.begin()); + TouchVideoFrame frame(mHeight, mWidth, std::move(data), buf.timestamp); result = ioctl(mFd.get(), VIDIOC_QBUF, &buf); if (result == -1) { @@ -230,7 +233,7 @@ TouchVideoDevice::~TouchVideoDevice() { } for (const int16_t* buffer : mReadLocations) { void* bufferAddress = static_cast<void*>(const_cast<int16_t*>(buffer)); - result = munmap(bufferAddress, mWidth * mHeight * sizeof(int16_t)); + result = munmap(bufferAddress, mHeight * mWidth * sizeof(int16_t)); if (result == -1) { ALOGE("%s: Couldn't unmap: [%s]", __func__, strerror(errno)); } @@ -238,9 +241,9 @@ TouchVideoDevice::~TouchVideoDevice() { } std::string TouchVideoDevice::dump() const { - return StringPrintf("Video device %s (%s) : width=%" PRIu32 ", height=%" PRIu32 + return StringPrintf("Video device %s (%s) : height=%" PRIu32 ", width=%" PRIu32 ", fd=%i, hasValidFd=%s", - mName.c_str(), mPath.c_str(), mWidth, mHeight, mFd.get(), + mName.c_str(), mPath.c_str(), mHeight, mWidth, mFd.get(), hasValidFd() ? "true" : "false"); } diff --git a/services/inputflinger/TouchVideoDevice.h b/services/inputflinger/TouchVideoDevice.h index 3d5c8c6f48..0e7e2ef496 100644 --- a/services/inputflinger/TouchVideoDevice.h +++ b/services/inputflinger/TouchVideoDevice.h @@ -54,14 +54,14 @@ public: */ const std::string& getPath() const { return mPath; } /** - * Get the width of the heatmap frame - */ - uint32_t getWidth() const { return mWidth; } - /** * Get the height of the heatmap frame */ uint32_t getHeight() const { return mHeight; } /** + * Get the width of the heatmap frame + */ + uint32_t getWidth() const { return mWidth; } + /** * Direct read of the frame. Stores the frame into internal buffer. * Return the number of frames that were successfully read. * @@ -87,8 +87,8 @@ private: std::string mName; std::string mPath; - uint32_t mWidth; uint32_t mHeight; + uint32_t mWidth; static constexpr int INVALID_FD = -1; /** @@ -110,7 +110,7 @@ private: * To get a new TouchVideoDevice, use 'create' instead. */ explicit TouchVideoDevice(int fd, std::string&& name, std::string&& devicePath, - uint32_t width, uint32_t height, + uint32_t height, uint32_t width, const std::array<const int16_t*, NUM_BUFFERS>& readLocations); /** * Read all currently available frames. diff --git a/services/inputflinger/host/InputFlinger.h b/services/inputflinger/host/InputFlinger.h index 9d0be95976..a00b5fbc13 100644 --- a/services/inputflinger/host/InputFlinger.h +++ b/services/inputflinger/host/InputFlinger.h @@ -24,6 +24,7 @@ #include <cutils/compiler.h> #include <input/IInputFlinger.h> +#include <input/ISetInputWindowsListener.h> #include <utils/String8.h> #include <utils/String16.h> #include <utils/StrongPointer.h> @@ -39,7 +40,7 @@ public: InputFlinger() ANDROID_API; virtual status_t dump(int fd, const Vector<String16>& args); - void setInputWindows(const Vector<InputWindowInfo>&) {} + void setInputWindows(const Vector<InputWindowInfo>&, const sp<ISetInputWindowsListener>&) {} void transferTouchFocus(const sp<IBinder>&, const sp<IBinder>&) {} void registerInputChannel(const sp<InputChannel>&) {} void unregisterInputChannel(const sp<InputChannel>&) {} diff --git a/services/inputflinger/tests/InputClassifier_test.cpp b/services/inputflinger/tests/InputClassifier_test.cpp index 20699c9288..16510577bf 100644 --- a/services/inputflinger/tests/InputClassifier_test.cpp +++ b/services/inputflinger/tests/InputClassifier_test.cpp @@ -29,20 +29,20 @@ namespace android { 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; - - PointerCoords coords[pointerCount]; - coords[0].setAxisValue(AMOTION_EVENT_AXIS_X, 1); - coords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, 1); + PointerProperties properties; + properties.id = 0; + properties.toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + 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/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index fbacb9bdb8..80a55f1a65 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -335,6 +335,7 @@ class FakeEventHub : public EventHubInterface { KeyedVector<int32_t, Device*> mDevices; std::vector<std::string> mExcludedDevices; List<RawEvent> mEvents; + std::unordered_map<int32_t /*deviceId*/, std::vector<TouchVideoFrame>> mVideoFrames; protected: virtual ~FakeEventHub() { @@ -499,6 +500,11 @@ public: } } + void setVideoFrames(std::unordered_map<int32_t /*deviceId*/, + std::vector<TouchVideoFrame>> videoFrames) { + mVideoFrames = std::move(videoFrames); + } + void assertQueueIsEmpty() { ASSERT_EQ(size_t(0), mEvents.size()) << "Expected the event queue to be empty (fully consumed)."; @@ -614,6 +620,12 @@ private: } virtual std::vector<TouchVideoFrame> getVideoFrames(int32_t deviceId) { + auto it = mVideoFrames.find(deviceId); + if (it != mVideoFrames.end()) { + std::vector<TouchVideoFrame> frames = std::move(it->second); + mVideoFrames.erase(deviceId); + return frames; + } return {}; } @@ -6415,4 +6427,78 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { ASSERT_EQ(size_t(2), iter->second.size()); } +TEST_F(MultiTouchInputMapperTest, VideoFrames_ReceivedByListener) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + prepareAxes(POSITION); + addConfigurationProperty("touch.deviceType", "touchScreen"); + prepareDisplay(DISPLAY_ORIENTATION_0); + addMapperAndConfigure(mapper); + + NotifyMotionArgs motionArgs; + // Unrotated video frame + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); + std::vector<TouchVideoFrame> frames{frame}; + mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(frames, motionArgs.videoFrames); + + // Subsequent touch events should not have any videoframes + // This is implemented separately in FakeEventHub, + // but that should match the behaviour of TouchVideoDevice. + processPosition(mapper, 200, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + ASSERT_EQ(std::vector<TouchVideoFrame>(), motionArgs.videoFrames); +} + +TEST_F(MultiTouchInputMapperTest, VideoFrames_AreRotated) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + prepareAxes(POSITION); + addConfigurationProperty("touch.deviceType", "touchScreen"); + addMapperAndConfigure(mapper); + // Unrotated video frame + TouchVideoFrame frame(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); + NotifyMotionArgs motionArgs; + + // Test all 4 orientations + for (int32_t orientation : {DISPLAY_ORIENTATION_0, DISPLAY_ORIENTATION_90, + DISPLAY_ORIENTATION_180, DISPLAY_ORIENTATION_270}) { + SCOPED_TRACE("Orientation " + StringPrintf("%i", orientation)); + clearViewports(); + prepareDisplay(orientation); + std::vector<TouchVideoFrame> frames{frame}; + mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + frames[0].rotate(orientation); + ASSERT_EQ(frames, motionArgs.videoFrames); + } +} + +TEST_F(MultiTouchInputMapperTest, VideoFrames_MultipleFramesAreRotated) { + MultiTouchInputMapper* mapper = new MultiTouchInputMapper(mDevice); + prepareAxes(POSITION); + addConfigurationProperty("touch.deviceType", "touchScreen"); + addMapperAndConfigure(mapper); + // Unrotated video frames. There's no rule that they must all have the same dimensions, + // so mix these. + TouchVideoFrame frame1(3, 2, {1, 2, 3, 4, 5, 6}, {1, 2}); + TouchVideoFrame frame2(3, 3, {0, 1, 2, 3, 4, 5, 6, 7, 8}, {1, 3}); + TouchVideoFrame frame3(2, 2, {10, 20, 10, 0}, {1, 4}); + std::vector<TouchVideoFrame> frames{frame1, frame2, frame3}; + NotifyMotionArgs motionArgs; + + prepareDisplay(DISPLAY_ORIENTATION_90); + mFakeEventHub->setVideoFrames({{mDevice->getId(), frames}}); + processPosition(mapper, 100, 200); + processSync(mapper); + ASSERT_NO_FATAL_FAILURE(mFakeListener->assertNotifyMotionWasCalled(&motionArgs)); + std::for_each(frames.begin(), frames.end(), + [](TouchVideoFrame& frame) { frame.rotate(DISPLAY_ORIENTATION_90); }); + ASSERT_EQ(frames, motionArgs.videoFrames); +} + } // namespace android diff --git a/services/sensorservice/SensorDirectConnection.cpp b/services/sensorservice/SensorDirectConnection.cpp index 538d72822e..cd0ea5d22c 100644 --- a/services/sensorservice/SensorDirectConnection.cpp +++ b/services/sensorservice/SensorDirectConnection.cpp @@ -100,7 +100,7 @@ int32_t SensorService::SensorDirectConnection::configureChannel(int handle, int 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 9a37ff1902..3fbd61e483 100644 --- a/services/sensorservice/SensorService.cpp +++ b/services/sensorservice/SensorService.cpp @@ -1691,13 +1691,13 @@ bool SensorService::isWhiteListedPackage(const String8& packageName) { 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 136ee27131..fbfe05ddc7 100644 --- a/services/sensorservice/SensorService.h +++ b/services/sensorservice/SensorService.h @@ -272,7 +272,7 @@ private: // 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/AllowedDisplayConfigs.h b/services/surfaceflinger/AllowedDisplayConfigs.h new file mode 100644 index 0000000000..7ca62ea550 --- /dev/null +++ b/services/surfaceflinger/AllowedDisplayConfigs.h @@ -0,0 +1,68 @@ +/* + * 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 <log/log.h> +#include <vector> + +/* + * Used to represent the Display Configurations allowed to be set by SurfaceFlinger + */ +class AllowedDisplayConfigs { +private: + // Defining ConstructorTag as private to prevent instantiating this class from outside + // while still allowing it to be constructed by std::make_unique + struct ConstructorTag {}; + +public: + AllowedDisplayConfigs(ConstructorTag) {} + + class Builder { + public: + Builder() + : mAllowedDisplayConfigs(std::make_unique<AllowedDisplayConfigs>(ConstructorTag{})) {} + + std::unique_ptr<const AllowedDisplayConfigs> build() { + return std::move(mAllowedDisplayConfigs); + } + + // add a config to the allowed config set + Builder& addConfig(int32_t config) { + mAllowedDisplayConfigs->addConfig(config); + return *this; + } + + private: + std::unique_ptr<AllowedDisplayConfigs> mAllowedDisplayConfigs; + }; + + bool isConfigAllowed(int32_t config) const { + return (std::find(mConfigs.begin(), mConfigs.end(), config) != mConfigs.end()); + } + + void getAllowedConfigs(std::vector<int32_t>* outConfigs) const { + if (outConfigs) { + *outConfigs = mConfigs; + } + } + +private: + // add a config to the allowed config set + void addConfig(int32_t config) { mConfigs.push_back(config); } + + std::vector<int32_t> mConfigs; +}; diff --git a/services/surfaceflinger/Android.bp b/services/surfaceflinger/Android.bp index 734ed6c6ca..ac1d492adf 100644 --- a/services/surfaceflinger/Android.bp +++ b/services/surfaceflinger/Android.bp @@ -23,7 +23,6 @@ cc_defaults { "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.configstore@1.1", - "android.hardware.configstore@1.2", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", "android.hardware.graphics.common@1.2", @@ -57,7 +56,6 @@ cc_defaults { "libui", "libinput", "libutils", - "libutilscallstack", "libSurfaceFlingerProperties", ], static_libs: [ @@ -134,7 +132,6 @@ filegroup { "EventLog/EventLog.cpp", "FrameTracker.cpp", "Layer.cpp", - "LayerBE.cpp", "LayerProtoHelper.cpp", "LayerRejecter.cpp", "LayerStats.cpp", @@ -142,6 +139,7 @@ filegroup { "MonitoredProducer.cpp", "NativeWindowSurface.cpp", "RenderArea.cpp", + "RegionSamplingThread.cpp", "Scheduler/DispSync.cpp", "Scheduler/DispSyncSource.cpp", "Scheduler/EventControlThread.cpp", @@ -189,7 +187,6 @@ cc_defaults { "android.frameworks.displayservice@1.0", "android.hardware.configstore-utils", "android.hardware.configstore@1.0", - "android.hardware.configstore@1.2", "android.hardware.graphics.allocator@2.0", "android.hardware.graphics.allocator@3.0", "libbinder", @@ -244,13 +241,15 @@ cc_library_shared { "android.hardware.configstore-utils", "android.hardware.configstore@1.0", "android.hardware.configstore@1.1", - "android.hardware.configstore@1.2", + "android.hardware.graphics.common@1.2", "libhidlbase", "libhidltransport", "libhwbinder", + "libui", "libutils", ], export_shared_lib_headers: [ + "android.hardware.graphics.common@1.2", "libhidlbase", "libhidltransport", "libhwbinder", diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp index c077b6841f..1b2b180c52 100644 --- a/services/surfaceflinger/BufferLayer.cpp +++ b/services/surfaceflinger/BufferLayer.cpp @@ -69,13 +69,6 @@ BufferLayer::BufferLayer(const LayerCreationArgs& args) BufferLayer::~BufferLayer() { mFlinger->deleteTextureAsync(mTextureName); - - if (destroyAllHwcLayersPlusChildren()) { - ALOGE("Found stale hardware composer layers when destroying " - "surface flinger layer %s", - mName.string()); - } - mFlinger->mTimeStats->onDestroy(getSequence()); } @@ -112,6 +105,10 @@ bool BufferLayer::isFixedSize() const { return getEffectiveScalingMode() != NATIVE_WINDOW_SCALING_MODE_FREEZE; } +bool BufferLayer::usesSourceCrop() const { + return true; +} + static constexpr mat4 inverseOrientation(uint32_t transform) { const mat4 flipH(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1); const mat4 flipV(1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1); @@ -132,9 +129,11 @@ static constexpr mat4 inverseOrientation(uint32_t transform) { bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform, Region& clearRegion, + const bool supportProtectedContent, renderengine::LayerSettings& layer) { ATRACE_CALL(); - Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer); + Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, + supportProtectedContent, layer); if (CC_UNLIKELY(mActiveBuffer == 0)) { // the texture has not been created yet, this Layer has // in fact never been drawn into. This happens frequently with @@ -161,15 +160,13 @@ bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& } return false; } - bool blackOutLayer = isProtected() || (isSecure() && !renderArea.isSecure()); + bool blackOutLayer = + (isProtected() && !supportProtectedContent) || (isSecure() && !renderArea.isSecure()); const State& s(getDrawingState()); if (!blackOutLayer) { layer.source.buffer.buffer = mActiveBuffer; layer.source.buffer.isOpaque = isOpaque(s); layer.source.buffer.fence = mActiveBufferFence; - layer.source.buffer.cacheHint = useCachedBufferForClientComposition() - ? renderengine::Buffer::CachingHint::USE_CACHE - : renderengine::Buffer::CachingHint::NO_CACHE; layer.source.buffer.textureName = mTextureName; layer.source.buffer.usePremultipliedAlpha = getPremultipledAlpha(); layer.source.buffer.isY410BT2020 = isHdrY410(); @@ -211,8 +208,16 @@ bool BufferLayer::prepareClientLayer(const RenderArea& renderArea, const Region& } const Rect win{getBounds()}; - const float bufferWidth = getBufferSize(s).getWidth(); - const float bufferHeight = getBufferSize(s).getHeight(); + float bufferWidth = getBufferSize(s).getWidth(); + float bufferHeight = getBufferSize(s).getHeight(); + + // BufferStateLayers can have a "buffer size" of [0, 0, -1, -1] when no display frame has + // been set and there is no parent layer bounds. In that case, the scale is meaningless so + // ignore them. + if (!getBufferSize(s).isValid()) { + bufferWidth = float(win.right) - float(win.left); + bufferHeight = float(win.bottom) - float(win.top); + } const float scaleHeight = (float(win.bottom) - float(win.top)) / bufferHeight; const float scaleWidth = (float(win.right) - float(win.left)) / bufferWidth; @@ -246,7 +251,8 @@ bool BufferLayer::isHdrY410() const { void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice, const ui::Transform& transform, const Rect& viewport, - int32_t supportedPerFrameMetadata) { + int32_t supportedPerFrameMetadata, + const ui::Dataspace targetDataspace) { RETURN_IF_NO_HWC_LAYER(displayDevice); // Apply this display's projection's viewport to the visible region @@ -298,10 +304,12 @@ void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice, setCompositionType(displayDevice, Hwc2::IComposerClient::Composition::DEVICE); } - ALOGV("setPerFrameData: dataspace = %d", mCurrentDataSpace); - error = hwcLayer->setDataspace(mCurrentDataSpace); + ui::Dataspace dataspace = isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN + ? targetDataspace + : mCurrentDataSpace; + error = hwcLayer->setDataspace(dataspace); if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace, + ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace, to_string(error).c_str(), static_cast<int32_t>(error)); } @@ -380,8 +388,7 @@ bool BufferLayer::onPostComposition(const std::optional<DisplayId>& displayId, return true; } -bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, - const sp<Fence>& releaseFence) { +bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) { ATRACE_CALL(); bool refreshRequired = latchSidebandStream(recomputeVisibleRegions); @@ -420,7 +427,7 @@ bool BufferLayer::latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, return false; } - status_t err = updateTexImage(recomputeVisibleRegions, latchTime, releaseFence); + status_t err = updateTexImage(recomputeVisibleRegions, latchTime); if (err != NO_ERROR) { return false; } diff --git a/services/surfaceflinger/BufferLayer.h b/services/surfaceflinger/BufferLayer.h index d358baeae0..dc103cbada 100644 --- a/services/surfaceflinger/BufferLayer.h +++ b/services/surfaceflinger/BufferLayer.h @@ -78,10 +78,13 @@ public: // isFixedSize - true if content has a fixed size bool isFixedSize() const override; + bool usesSourceCrop() const override; + bool isHdrY410() const override; void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, - const Rect& viewport, int32_t supportedPerFrameMetadata) override; + const Rect& viewport, int32_t supportedPerFrameMetadata, + const ui::Dataspace targetDataspace) override; bool onPreComposition(nsecs_t refreshStartTime) override; bool onPostComposition(const std::optional<DisplayId>& displayId, @@ -93,11 +96,7 @@ public: // the visible regions need to be recomputed (this is a fairly heavy // operation, so this should be set only if needed). Typically this is used // to figure out if the content or size of a surface has changed. - // If there was a GL composition step rendering the previous frame, then - // releaseFence will be populated with a native fence that fires when - // composition has completed. - bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime, - const sp<Fence>& releaseFence) override; + bool latchBuffer(bool& recomputeVisibleRegions, nsecs_t latchTime) override; bool isBufferLatched() const override { return mRefreshPending; } @@ -142,8 +141,7 @@ private: virtual void setFilteringEnabled(bool enabled) = 0; virtual status_t bindTextureImage() = 0; - virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - const sp<Fence>& flushFence) = 0; + virtual status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) = 0; virtual status_t updateActiveBuffer() = 0; virtual status_t updateFrameNumber(nsecs_t latchTime) = 0; @@ -166,14 +164,11 @@ protected: bool mRefreshPending{false}; - // Returns true if, when drawing the active buffer during gpu compositon, we - // should use a cached buffer or not. - virtual bool useCachedBufferForClientComposition() const = 0; - // prepareClientLayer - constructs a RenderEngine layer for GPU composition. bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform, Region& clearRegion, - renderengine::LayerSettings& layer); + const bool supportProtectedContent, + renderengine::LayerSettings& layer) override; private: // Returns true if this layer requires filtering @@ -188,6 +183,8 @@ private: // main thread. bool mBufferLatched{false}; // TODO: Use mActiveBuffer? + // BufferStateLayers can return Rect::INVALID_RECT if the layer does not have a display frame + // and its parent layer is not bounded Rect getBufferSize(const State& s) const override; std::shared_ptr<compositionengine::Layer> mCompositionLayer; diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp index 7ed818454c..cc78ece2a8 100644 --- a/services/surfaceflinger/BufferLayerConsumer.cpp +++ b/services/surfaceflinger/BufferLayerConsumer.cpp @@ -100,8 +100,7 @@ void BufferLayerConsumer::setContentsChangedListener(const wp<ContentsChangedLis status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime, bool* autoRefresh, bool* queuedBuffer, - uint64_t maxFrameNumber, - const sp<Fence>& releaseFence) { + uint64_t maxFrameNumber) { ATRACE_CALL(); BLC_LOGV("updateTexImage"); Mutex::Autolock lock(mMutex); @@ -146,7 +145,7 @@ status_t BufferLayerConsumer::updateTexImage(BufferRejecter* rejecter, nsecs_t e } // Release the previous buffer. - err = updateAndReleaseLocked(item, &mPendingRelease, releaseFence); + err = updateAndReleaseLocked(item, &mPendingRelease); if (err != NO_ERROR) { return err; } @@ -179,7 +178,8 @@ void BufferLayerConsumer::setReleaseFence(const sp<Fence>& fence) { return; } - auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer : mCurrentTextureBuffer; + auto buffer = mPendingRelease.isPending ? mPendingRelease.graphicBuffer + : mCurrentTextureBuffer->graphicBuffer(); auto err = addReleaseFence(slot, buffer, fence); if (err != OK) { BLC_LOGE("setReleaseFence: failed to add the fence: %s (%d)", strerror(-err), err); @@ -215,47 +215,36 @@ status_t BufferLayerConsumer::acquireBufferLocked(BufferItem* item, nsecs_t pres } // If item->mGraphicBuffer is not null, this buffer has not been acquired - // before. + // before, so we need to clean up old references. if (item->mGraphicBuffer != nullptr) { - mImages[item->mSlot] = nullptr; + mImages[item->mSlot] = std::make_shared<Image>(item->mGraphicBuffer, mRE); } return NO_ERROR; } status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, - PendingRelease* pendingRelease, - const sp<Fence>& releaseFence) { + PendingRelease* pendingRelease) { status_t err = NO_ERROR; int slot = item.mSlot; - // Do whatever sync ops we need to do before releasing the old slot. - if (slot != mCurrentTexture) { - err = syncForReleaseLocked(releaseFence); - if (err != NO_ERROR) { - // Release the buffer we just acquired. It's not safe to - // release the old buffer, so instead we just drop the new frame. - // As we are still under lock since acquireBuffer, it is safe to - // release by slot. - releaseBufferLocked(slot, mSlots[slot].mGraphicBuffer); - return err; - } - } - BLC_LOGV("updateAndRelease: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, - mCurrentTextureBuffer != nullptr ? mCurrentTextureBuffer->handle : 0, slot, - mSlots[slot].mGraphicBuffer->handle); + (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) + ? mCurrentTextureBuffer->graphicBuffer()->handle + : 0, + slot, mSlots[slot].mGraphicBuffer->handle); // Hang onto the pointer so that it isn't freed in the call to // releaseBufferLocked() if we're in shared buffer mode and both buffers are // the same. - sp<GraphicBuffer> nextTextureBuffer = mSlots[slot].mGraphicBuffer; + std::shared_ptr<Image> nextTextureBuffer = mImages[slot]; // release old buffer if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { if (pendingRelease == nullptr) { - status_t status = releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer); + status_t status = + releaseBufferLocked(mCurrentTexture, mCurrentTextureBuffer->graphicBuffer()); if (status < NO_ERROR) { BLC_LOGE("updateAndRelease: failed to release buffer: %s (%d)", strerror(-status), status); @@ -264,7 +253,7 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, } } else { pendingRelease->currentTexture = mCurrentTexture; - pendingRelease->graphicBuffer = mCurrentTextureBuffer; + pendingRelease->graphicBuffer = mCurrentTextureBuffer->graphicBuffer(); pendingRelease->isPending = true; } } @@ -272,8 +261,6 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, // Update the BufferLayerConsumer state. mCurrentTexture = slot; mCurrentTextureBuffer = nextTextureBuffer; - mCurrentTextureBufferStaleForGpu = false; - mCurrentTextureImageFreed = nullptr; mCurrentCrop = item.mCrop; mCurrentTransform = item.mTransform; mCurrentScalingMode = item.mScalingMode; @@ -295,31 +282,12 @@ status_t BufferLayerConsumer::updateAndReleaseLocked(const BufferItem& item, status_t BufferLayerConsumer::bindTextureImageLocked() { ATRACE_CALL(); - return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer, mCurrentFence, false); -} - -status_t BufferLayerConsumer::syncForReleaseLocked(const sp<Fence>& releaseFence) { - BLC_LOGV("syncForReleaseLocked"); - - if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) { - if (mRE.useNativeFenceSync() && releaseFence != Fence::NO_FENCE) { - // TODO(alecmouri): fail further upstream if the fence is invalid - if (!releaseFence->isValid()) { - BLC_LOGE("syncForReleaseLocked: failed to flush RenderEngine"); - return UNKNOWN_ERROR; - } - status_t err = - addReleaseFenceLocked(mCurrentTexture, mCurrentTextureBuffer, releaseFence); - if (err != OK) { - BLC_LOGE("syncForReleaseLocked: error adding release fence: " - "%s (%d)", - strerror(-err), err); - return err; - } - } + if (mCurrentTextureBuffer != nullptr && mCurrentTextureBuffer->graphicBuffer() != nullptr) { + return mRE.bindExternalTextureBuffer(mTexName, mCurrentTextureBuffer->graphicBuffer(), + mCurrentFence); } - return OK; + return NO_INIT; } void BufferLayerConsumer::getTransformMatrix(float mtx[16]) { @@ -347,12 +315,15 @@ void BufferLayerConsumer::setFilteringEnabled(bool enabled) { void BufferLayerConsumer::computeCurrentTransformMatrixLocked() { BLC_LOGV("computeCurrentTransformMatrixLocked"); - if (mCurrentTextureBuffer == nullptr) { + if (mCurrentTextureBuffer == nullptr || mCurrentTextureBuffer->graphicBuffer() == nullptr) { BLC_LOGD("computeCurrentTransformMatrixLocked: " "mCurrentTextureBuffer is nullptr"); } - GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, mCurrentTextureBuffer, mCurrentCrop, - mCurrentTransform, mFilteringEnabled); + GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, + mCurrentTextureBuffer == nullptr + ? nullptr + : mCurrentTextureBuffer->graphicBuffer(), + mCurrentCrop, mCurrentTransform, mFilteringEnabled); } nsecs_t BufferLayerConsumer::getTimestamp() { @@ -404,16 +375,7 @@ sp<GraphicBuffer> BufferLayerConsumer::getCurrentBuffer(int* outSlot, sp<Fence>* *outFence = mCurrentFence; } - return mCurrentTextureBuffer; -} - -bool BufferLayerConsumer::getAndSetCurrentBufferCacheHint() { - Mutex::Autolock lock(mMutex); - bool useCache = mCurrentTextureBufferStaleForGpu; - // Set the staleness bit here, as this function is only called during a - // client composition path. - mCurrentTextureBufferStaleForGpu = true; - return useCache; + return mCurrentTextureBuffer == nullptr ? nullptr : mCurrentTextureBuffer->graphicBuffer(); } Rect BufferLayerConsumer::getCurrentCrop() const { @@ -471,10 +433,8 @@ void BufferLayerConsumer::freeBufferLocked(int slotIndex) { BLC_LOGV("freeBufferLocked: slotIndex=%d", slotIndex); if (slotIndex == mCurrentTexture) { mCurrentTexture = BufferQueue::INVALID_BUFFER_SLOT; - mCurrentTextureImageFreed = std::move(mImages[slotIndex]); - } else { - mImages[slotIndex] = nullptr; } + mImages[slotIndex] = nullptr; ConsumerBase::freeBufferLocked(slotIndex); } @@ -513,7 +473,10 @@ void BufferLayerConsumer::addAndGetFrameTimestamps(const NewFrameEventsEntry* ne void BufferLayerConsumer::abandonLocked() { BLC_LOGV("abandonLocked"); - mCurrentTextureBuffer.clear(); + mCurrentTextureBuffer = nullptr; + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + mImages[i] = nullptr; + } ConsumerBase::abandonLocked(); } @@ -531,4 +494,11 @@ void BufferLayerConsumer::dumpLocked(String8& result, const char* prefix) const ConsumerBase::dumpLocked(result, prefix); } +BufferLayerConsumer::Image::~Image() { + if (mGraphicBuffer != nullptr) { + ALOGV("Destroying buffer: %" PRId64, mGraphicBuffer->getId()); + mRE.unbindExternalTextureBuffer(mGraphicBuffer->getId()); + } +} + }; // namespace android diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h index e2ef399599..32ccfbb14d 100644 --- a/services/surfaceflinger/BufferLayerConsumer.h +++ b/services/surfaceflinger/BufferLayerConsumer.h @@ -92,8 +92,7 @@ public: // used to reject the newly acquired buffer. It also does not bind the // RenderEngine texture until bindTextureImage is called. status_t updateTexImage(BufferRejecter* rejecter, nsecs_t expectedPresentTime, - bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber, - const sp<Fence>& releaseFence); + bool* autoRefresh, bool* queuedBuffer, uint64_t maxFrameNumber); // See BufferLayerConsumer::bindTextureImageLocked(). status_t bindTextureImage(); @@ -150,11 +149,6 @@ public: // for use with bilinear filtering. void setFilteringEnabled(bool enabled); - // Sets mCurrentTextureBufferStaleForGpu to true to indicate that the - // buffer is now "stale" for GPU composition, and returns the old staleness - // bit as a caching hint. - bool getAndSetCurrentBufferCacheHint(); - // getCurrentBuffer returns the buffer associated with the current image. // When outSlot is not nullptr, the current buffer slot index is also // returned. Simiarly, when outFence is not nullptr, the current output @@ -212,14 +206,29 @@ protected: // completion of the method will instead be returned to the caller, so that // it may call releaseBufferLocked itself later. status_t updateAndReleaseLocked(const BufferItem& item, - PendingRelease* pendingRelease = nullptr, - const sp<Fence>& releaseFence = Fence::NO_FENCE); + PendingRelease* pendingRelease = nullptr); // Binds mTexName and the current buffer to TEXTURE_EXTERNAL target. // If the bind succeeds, this calls doFenceWait. status_t bindTextureImageLocked(); private: + // Utility class for managing GraphicBuffer references into renderengine + class Image { + public: + Image(sp<GraphicBuffer> graphicBuffer, renderengine::RenderEngine& engine) + : mGraphicBuffer(graphicBuffer), mRE(engine) {} + virtual ~Image(); + const sp<GraphicBuffer>& graphicBuffer() { return mGraphicBuffer; } + + private: + // mGraphicBuffer is the buffer that was used to create this image. + sp<GraphicBuffer> mGraphicBuffer; + // Back-reference into renderengine to initiate cleanup. + renderengine::RenderEngine& mRE; + DISALLOW_COPY_AND_ASSIGN(Image); + }; + // freeBufferLocked frees up the given buffer slot. If the slot has been // initialized this will release the reference to the GraphicBuffer in // that slot. Otherwise it has no effect. @@ -244,26 +253,16 @@ private: // access the current texture buffer. status_t doFenceWaitLocked() const; - // syncForReleaseLocked performs the synchronization needed to release the - // current slot from RenderEngine. If needed it will set the current - // slot's fence to guard against a producer accessing the buffer before - // the outstanding accesses have completed. - status_t syncForReleaseLocked(const sp<Fence>& releaseFence); - // The default consumer usage flags that BufferLayerConsumer always sets on its // BufferQueue instance; these will be OR:d with any additional flags passed // from the BufferLayerConsumer user. In particular, BufferLayerConsumer will always // consume buffers as hardware textures. static const uint64_t DEFAULT_USAGE_FLAGS = GraphicBuffer::USAGE_HW_TEXTURE; - // mCurrentTextureImage is the buffer containing the current texture. It's + // mCurrentTextureBuffer is the buffer containing the current texture. It's // possible that this buffer is not associated with any buffer slot, so we // must track it separately in order to support the getCurrentBuffer method. - sp<GraphicBuffer> mCurrentTextureBuffer; - - // True if the buffer was used for the previous client composition frame, - // and false otherwise. - bool mCurrentTextureBufferStaleForGpu; + std::shared_ptr<Image> mCurrentTextureBuffer; // mCurrentCrop is the crop rectangle that applies to the current texture. // It gets set each time updateTexImage is called. @@ -341,16 +340,8 @@ private: // reset mCurrentTexture to INVALID_BUFFER_SLOT. int mCurrentTexture; - // Cached image used for rendering the current texture through GPU - // composition, which contains the cached image after freeBufferLocked is - // called on the current buffer. Whenever latchBuffer is called, this is - // expected to be cleared. Then, if bindTexImage is called before the next - // buffer is acquired, then this image is bound. - std::unique_ptr<renderengine::Image> mCurrentTextureImageFreed; - - // Cached images used for rendering the current texture through GPU - // composition. - std::unique_ptr<renderengine::Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS]; + // Shadow buffer cache for cleaning up renderengine references. + std::shared_ptr<Image> mImages[BufferQueueDefs::NUM_BUFFER_SLOTS]; // A release that is pending on the receipt of a new release fence from // presentDisplay diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp index 5a60fcd306..e4179ee43a 100644 --- a/services/surfaceflinger/BufferQueueLayer.cpp +++ b/services/surfaceflinger/BufferQueueLayer.cpp @@ -199,7 +199,6 @@ bool BufferQueueLayer::latchSidebandStream(bool& recomputeVisibleRegions) { bool sidebandStreamChanged = true; if (mSidebandStreamChanged.compare_exchange_strong(sidebandStreamChanged, false)) { // mSidebandStreamChanged was changed to false - // replicated in LayerBE until FE/BE is ready to be synchronized auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; layerCompositionState.sidebandStream = mConsumer->getSidebandStream(); if (layerCompositionState.sidebandStream != nullptr) { @@ -225,8 +224,7 @@ status_t BufferQueueLayer::bindTextureImage() { return mConsumer->bindTextureImage(); } -status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - const sp<Fence>& releaseFence) { +status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) { // This boolean is used to make sure that SurfaceFlinger's shadow copy // of the buffer queue isn't modified when the buffer queue is returning // BufferItem's that weren't actually queued. This can happen in shared @@ -237,9 +235,7 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t getProducerStickyTransform() != 0, mName.string(), mOverrideScalingMode, getTransformToDisplayInverse(), mFreezeGeometryUpdates); - nsecs_t expectedPresentTime = mFlinger->mUseScheduler - ? mFlinger->mScheduler->expectedPresentTime() - : mFlinger->mPrimaryDispSync->expectedPresentTime(); + nsecs_t expectedPresentTime = mFlinger->mScheduler->expectedPresentTime(); if (isRemovedFromCurrentState()) { expectedPresentTime = 0; @@ -264,9 +260,8 @@ status_t BufferQueueLayer::updateTexImage(bool& recomputeVisibleRegions, nsecs_t const uint64_t maxFrameNumberToAcquire = std::min(mLastFrameNumberReceived.load(), lastSignaledFrameNumber); - status_t updateResult = - mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh, &queuedBuffer, - maxFrameNumberToAcquire, releaseFence); + status_t updateResult = mConsumer->updateTexImage(&r, expectedPresentTime, &mAutoRefresh, + &queuedBuffer, maxFrameNumberToAcquire); if (updateResult == BufferQueue::PRESENT_LATER) { // Producer doesn't want buffer to be displayed yet. Signal a // layer update so we check again at the next opportunity. @@ -347,10 +342,6 @@ status_t BufferQueueLayer::updateActiveBuffer() { return NO_ERROR; } -bool BufferQueueLayer::useCachedBufferForClientComposition() const { - return mConsumer->getAndSetCurrentBufferCacheHint(); -} - status_t BufferQueueLayer::updateFrameNumber(nsecs_t latchTime) { mPreviousFrameNumber = mCurrentFrameNumber; mCurrentFrameNumber = mConsumer->getFrameNumber(); @@ -371,7 +362,7 @@ void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) uint32_t hwcSlot = 0; sp<GraphicBuffer> hwcBuffer; (*outputLayer->editState().hwc) - .hwcBufferCache.getHwcBuffer(mActiveBufferSlot, mActiveBuffer, &hwcSlot, &hwcBuffer); + .hwcBufferCache.getHwcBuffer(mActiveBuffer, &hwcSlot, &hwcBuffer); auto acquireFence = mConsumer->getCurrentFence(); auto error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence); @@ -393,7 +384,7 @@ void BufferQueueLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) void BufferQueueLayer::fakeVsync() { mRefreshPending = false; bool ignored = false; - latchBuffer(ignored, systemTime(), Fence::NO_FENCE); + latchBuffer(ignored, systemTime()); usleep(16000); releasePendingBuffer(systemTime()); } @@ -401,10 +392,9 @@ void BufferQueueLayer::fakeVsync() { void BufferQueueLayer::onFrameAvailable(const BufferItem& item) { // Add this buffer from our internal queue tracker { // Autolock scope - // Report the requested present time to the Scheduler. - if (mFlinger->mUseScheduler) { - 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/BufferQueueLayer.h b/services/surfaceflinger/BufferQueueLayer.h index 30107e24ab..a2aad17497 100644 --- a/services/surfaceflinger/BufferQueueLayer.h +++ b/services/surfaceflinger/BufferQueueLayer.h @@ -62,9 +62,6 @@ public: public: bool fenceHasSignaled() const override; -protected: - bool useCachedBufferForClientComposition() const override; - private: nsecs_t getDesiredPresentTime() override; std::shared_ptr<FenceTime> getCurrentFenceTime() const override; @@ -91,8 +88,7 @@ private: void setFilteringEnabled(bool enabled) override; status_t bindTextureImage() override; - status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - const sp<Fence>& releaseFence) override; + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override; status_t updateActiveBuffer() override; status_t updateFrameNumber(nsecs_t latchTime) override; diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index a3d5b89be2..1ca0b029df 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -48,7 +48,12 @@ BufferStateLayer::BufferStateLayer(const LayerCreationArgs& args) : BufferLayer( mOverrideScalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; mCurrentState.dataspace = ui::Dataspace::V0_SRGB; } -BufferStateLayer::~BufferStateLayer() = default; +BufferStateLayer::~BufferStateLayer() { + if (mActiveBuffer != nullptr) { + auto& engine(mFlinger->getRenderEngine()); + engine.unbindExternalTextureBuffer(mActiveBuffer->getId()); + } +} // ----------------------------------------------------------------------- // Interface implementation for Layer @@ -151,9 +156,22 @@ bool BufferStateLayer::setTransformToDisplayInverse(bool transformToDisplayInver } bool BufferStateLayer::setCrop(const Rect& crop) { - if (mCurrentState.crop == crop) return false; + Rect c = crop; + if (c.left < 0) { + c.left = 0; + } + if (c.top < 0) { + c.top = 0; + } + // If the width and/or height are < 0, make it [0, 0, -1, -1] so the equality comparision below + // treats all invalid rectangles the same. + if (!c.isValid()) { + c.makeInvalid(); + } + + if (mCurrentState.crop == c) return false; mCurrentState.sequence++; - mCurrentState.crop = crop; + mCurrentState.crop = c; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); return true; @@ -198,7 +216,6 @@ bool BufferStateLayer::setBuffer(const sp<GraphicBuffer>& buffer) { mReleasePreviousBuffer = true; } - mCurrentState.sequence++; mCurrentState.buffer = buffer; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -217,7 +234,6 @@ bool BufferStateLayer::setAcquireFence(const sp<Fence>& fence) { bool BufferStateLayer::setDataspace(ui::Dataspace dataspace) { if (mCurrentState.dataspace == dataspace) return false; - mCurrentState.sequence++; mCurrentState.dataspace = dataspace; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -324,10 +340,6 @@ Rect BufferStateLayer::getBufferSize(const State& s) const { } } - // if there is no parent layer, use the buffer's bounds as the buffer size - if (s.buffer) { - return s.buffer->getBounds(); - } return Rect::INVALID_RECT; } @@ -342,6 +354,14 @@ FloatRect BufferStateLayer::computeSourceBounds(const FloatRect& parentBounds) c return parentBounds; } +void BufferStateLayer::setPostTime(nsecs_t postTime) { + mFlinger->mTimeStats->setPostTime(getSequence(), getFrameNumber(), getName().c_str(), postTime); +} + +void BufferStateLayer::setDesiredPresentTime(nsecs_t desiredPresentTime) { + mDesiredPresentTime = desiredPresentTime; +} + // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- @@ -356,8 +376,7 @@ bool BufferStateLayer::fenceHasSignaled() const { } nsecs_t BufferStateLayer::getDesiredPresentTime() { - // TODO(marissaw): support an equivalent to desiredPresentTime for timestats metrics - return 0; + return mDesiredPresentTime; } std::shared_ptr<FenceTime> BufferStateLayer::getCurrentFenceTime() const { @@ -470,11 +489,10 @@ status_t BufferStateLayer::bindTextureImage() { const State& s(getDrawingState()); auto& engine(mFlinger->getRenderEngine()); - return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence, false); + return engine.bindExternalTextureBuffer(mTextureName, s.buffer, s.acquireFence); } -status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime, - const sp<Fence>& releaseFence) { +status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nsecs_t latchTime) { const State& s(getDrawingState()); if (!s.buffer) { @@ -516,59 +534,7 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse handle->latchTime = latchTime; } - // Handle sync fences - if (SyncFeatures::getInstance().useNativeFenceSync() && releaseFence != Fence::NO_FENCE) { - // TODO(alecmouri): Fail somewhere upstream if the fence is invalid. - if (!releaseFence->isValid()) { - mFlinger->mTimeStats->onDestroy(layerID); - return UNKNOWN_ERROR; - } - - // Check status of fences first because merging is expensive. - // Merging an invalid fence with any other fence results in an - // invalid fence. - auto currentStatus = s.acquireFence->getStatus(); - if (currentStatus == Fence::Status::Invalid) { - ALOGE("Existing fence has invalid state"); - mFlinger->mTimeStats->onDestroy(layerID); - return BAD_VALUE; - } - - auto incomingStatus = releaseFence->getStatus(); - if (incomingStatus == Fence::Status::Invalid) { - ALOGE("New fence has invalid state"); - mDrawingState.acquireFence = releaseFence; - mFlinger->mTimeStats->onDestroy(layerID); - return BAD_VALUE; - } - - // If both fences are signaled or both are unsignaled, we need to merge - // them to get an accurate timestamp. - if (currentStatus == incomingStatus) { - char fenceName[32] = {}; - snprintf(fenceName, 32, "%.28s:%d", mName.string(), mFrameNumber); - sp<Fence> mergedFence = - Fence::merge(fenceName, mDrawingState.acquireFence, releaseFence); - if (!mergedFence.get()) { - ALOGE("failed to merge release fences"); - // synchronization is broken, the best we can do is hope fences - // signal in order so the new fence will act like a union - mDrawingState.acquireFence = releaseFence; - mFlinger->mTimeStats->onDestroy(layerID); - return BAD_VALUE; - } - mDrawingState.acquireFence = mergedFence; - } else if (incomingStatus == Fence::Status::Unsignaled) { - // If one fence has signaled and the other hasn't, the unsignaled - // fence will approximately correspond with the correct timestamp. - // There's a small race if both fences signal at about the same time - // and their statuses are retrieved with unfortunate timing. However, - // by this point, they will have both signaled and only the timestamp - // will be slightly off; any dependencies after this point will - // already have been met. - mDrawingState.acquireFence = releaseFence; - } - } else { + if (!SyncFeatures::getInstance().useNativeFenceSync()) { // Bind the new buffer to the GL texture. // // Older devices require the "implicit" synchronization provided @@ -582,8 +548,6 @@ status_t BufferStateLayer::updateTexImage(bool& /*recomputeVisibleRegions*/, nse } } - // TODO(marissaw): properly support mTimeStats - mFlinger->mTimeStats->setPostTime(layerID, getFrameNumber(), getName().c_str(), latchTime); mFlinger->mTimeStats->setAcquireFence(layerID, getFrameNumber(), getCurrentFenceTime()); mFlinger->mTimeStats->setLatchTime(layerID, getFrameNumber(), latchTime); @@ -597,6 +561,11 @@ status_t BufferStateLayer::updateActiveBuffer() { return BAD_VALUE; } + if (mActiveBuffer != nullptr) { + // todo: get this to work with BufferStateLayerCache + auto& engine(mFlinger->getRenderEngine()); + engine.unbindExternalTextureBuffer(mActiveBuffer->getId()); + } mActiveBuffer = s.buffer; mActiveBufferFence = s.acquireFence; auto& layerCompositionState = getCompositionLayer()->editState().frontEnd; @@ -606,11 +575,6 @@ status_t BufferStateLayer::updateActiveBuffer() { return NO_ERROR; } -bool BufferStateLayer::useCachedBufferForClientComposition() const { - // TODO: Store a proper staleness bit to support EGLImage caching. - return false; -} - status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) { // TODO(marissaw): support frame history events mCurrentFrameNumber = mFrameNumber; @@ -620,14 +584,17 @@ status_t BufferStateLayer::updateFrameNumber(nsecs_t /*latchTime*/) { void BufferStateLayer::setHwcLayerBuffer(const sp<const DisplayDevice>& display) { const auto outputLayer = findOutputLayerForDisplay(display); LOG_FATAL_IF(!outputLayer || !outputLayer->getState().hwc); - auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer; + auto& hwcInfo = *outputLayer->editState().hwc; + auto& hwcLayer = hwcInfo.hwcLayer; const State& s(getDrawingState()); - // TODO(marissaw): support more than one slot + // obtain slot uint32_t hwcSlot = 0; + sp<GraphicBuffer> buffer; + hwcInfo.hwcBufferCache.getHwcBuffer(s.buffer, &hwcSlot, &buffer); - auto error = hwcLayer->setBuffer(hwcSlot, s.buffer, s.acquireFence); + auto error = hwcLayer->setBuffer(hwcSlot, buffer, s.acquireFence); if (error != HWC2::Error::None) { ALOGE("[%s] Failed to set buffer %p: %s (%d)", mName.string(), s.buffer->handle, to_string(error).c_str(), static_cast<int32_t>(error)); diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index a97f0a47fc..668830a3fe 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -89,6 +89,9 @@ public: Rect getBufferSize(const State& s) const override; FloatRect computeSourceBounds(const FloatRect& parentBounds) const override; + + void setPostTime(nsecs_t postTime) override; + void setDesiredPresentTime(nsecs_t desiredPresentTime) override; // ----------------------------------------------------------------------- // ----------------------------------------------------------------------- @@ -96,9 +99,6 @@ public: // ----------------------------------------------------------------------- bool fenceHasSignaled() const override; -protected: - bool useCachedBufferForClientComposition() const override; - private: nsecs_t getDesiredPresentTime() override; std::shared_ptr<FenceTime> getCurrentFenceTime() const override; @@ -125,8 +125,7 @@ private: void setFilteringEnabled(bool enabled) override; status_t bindTextureImage() override; - status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime, - const sp<Fence>& releaseFence) override; + status_t updateTexImage(bool& recomputeVisibleRegions, nsecs_t latchTime) override; status_t updateActiveBuffer() override; status_t updateFrameNumber(nsecs_t latchTime) override; @@ -153,6 +152,8 @@ private: bool mReleasePreviousBuffer = false; nsecs_t mCallbackHandleAcquireTime = -1; + nsecs_t mDesiredPresentTime = -1; + // TODO(marissaw): support sticky transform for LEGACY camera mode }; diff --git a/services/surfaceflinger/ColorLayer.cpp b/services/surfaceflinger/ColorLayer.cpp index 9ea0a466a5..fcc2d97ca3 100644 --- a/services/surfaceflinger/ColorLayer.cpp +++ b/services/surfaceflinger/ColorLayer.cpp @@ -50,8 +50,10 @@ ColorLayer::~ColorLayer() = default; bool ColorLayer::prepareClientLayer(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform, Region& clearRegion, + const bool supportProtectedContent, renderengine::LayerSettings& layer) { - Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, layer); + Layer::prepareClientLayer(renderArea, clip, useIdentityTransform, clearRegion, + supportProtectedContent, layer); half4 color(getColor()); half3 solidColor(color.r, color.g, color.b); layer.source.solidColor = solidColor; @@ -77,9 +79,22 @@ bool ColorLayer::setColor(const half3& color) { return true; } +bool ColorLayer::setDataspace(ui::Dataspace dataspace) { + if (mCurrentState.dataspace == dataspace) { + return false; + } + + mCurrentState.sequence++; + mCurrentState.dataspace = dataspace; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, const Rect& viewport, - int32_t /* supportedPerFrameMetadata */) { + int32_t /* supportedPerFrameMetadata */, + const ui::Dataspace targetDataspace) { RETURN_IF_NO_HWC_LAYER(display); Region visible = transform.transform(visibleRegion.intersect(viewport)); @@ -99,9 +114,12 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display, setCompositionType(display, Hwc2::IComposerClient::Composition::SOLID_COLOR); - error = hwcLayer->setDataspace(mCurrentDataSpace); + const ui::Dataspace dataspace = + isColorSpaceAgnostic() && targetDataspace != ui::Dataspace::UNKNOWN ? targetDataspace + : mCurrentDataSpace; + error = hwcLayer->setDataspace(dataspace); if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace, + ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), dataspace, to_string(error).c_str(), static_cast<int32_t>(error)); } @@ -144,6 +162,11 @@ void ColorLayer::setPerFrameData(const sp<const DisplayDevice>& display, layerCompositionState.surfaceDamage = surfaceDamageRegion; } +void ColorLayer::commitTransaction(const State& stateToCommit) { + Layer::commitTransaction(stateToCommit); + mCurrentDataSpace = mDrawingState.dataspace; +} + std::shared_ptr<compositionengine::Layer> ColorLayer::getCompositionLayer() const { return mCompositionLayer; } diff --git a/services/surfaceflinger/ColorLayer.h b/services/surfaceflinger/ColorLayer.h index df0adac1dd..53d5b5b605 100644 --- a/services/surfaceflinger/ColorLayer.h +++ b/services/surfaceflinger/ColorLayer.h @@ -35,15 +35,20 @@ public: bool setColor(const half3& color) override; + bool setDataspace(ui::Dataspace dataspace) override; + void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, - const Rect& viewport, int32_t supportedPerFrameMetadata) override; + 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 {}; } virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform, Region& clearRegion, + const bool supportProtectedContent, renderengine::LayerSettings& layer); private: diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index f5376a59b9..e86d35dde8 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -91,6 +91,7 @@ cc_test { "tests/DisplayTest.cpp", "tests/HwcBufferCacheTest.cpp", "tests/LayerTest.cpp", + "tests/MockHWC2.cpp", "tests/MockHWComposer.cpp", "tests/OutputTest.cpp", "tests/OutputLayerTest.cpp", diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h index f9a36241c8..9f635b9a35 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFE.h @@ -18,10 +18,28 @@ #include <utils/RefBase.h> -namespace android::compositionengine { +namespace android { + +class Fence; + +namespace compositionengine { + +struct LayerFECompositionState; // Defines the interface used by the CompositionEngine to make requests // of the front-end layer -class LayerFE : public virtual RefBase {}; +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 android::compositionengine +} // namespace compositionengine +} // namespace android diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 2201bdd6d2..e6ee078624 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -36,6 +36,26 @@ namespace android::compositionengine { * Used by LayerFE::getCompositionState */ struct LayerFECompositionState { + // TODO(lpique): b/121291683 Remove this one we are sure we don't need the + // value recomputed / set every frame. + 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/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h index 84b2423def..54e6bd6af7 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/Output.h @@ -17,6 +17,7 @@ #pragma once #include <cstdint> +#include <optional> #include <string> #include <math/mat4.h> @@ -25,6 +26,8 @@ #include <ui/Transform.h> #include <utils/StrongPointer.h> +#include "DisplayHardware/DisplayIdentification.h" + namespace android::compositionengine { class DisplayColorProfile; @@ -117,7 +120,8 @@ public: // Gets the OutputLayer corresponding to the input Layer instance from the // current ordered set of output layers. If there is no such layer, a new // one is created and returned. - virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::shared_ptr<Layer>, + virtual std::unique_ptr<OutputLayer> getOrCreateOutputLayer(std::optional<DisplayId>, + std::shared_ptr<Layer>, sp<LayerFE>) = 0; // Sets the new ordered set of output layers for this output diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h index 48cb581ccd..cd63b57d86 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/OutputLayer.h @@ -16,14 +16,18 @@ #pragma once +#include <optional> #include <string> #include <utils/StrongPointer.h> +#include "DisplayHardware/DisplayIdentification.h" + namespace android { namespace compositionengine { +class CompositionEngine; class Output; class Layer; class LayerFE; @@ -60,6 +64,15 @@ public: // TODO(lpique): Make this protected once it is only internally called. virtual CompositionState& editState() = 0; + // Recalculates the state of the output layer from the output-independent + // layer. If includeGeometry is false, the geometry state can be skipped. + virtual void updateCompositionState(bool includeGeometry) = 0; + + // Writes the geometry state to the HWC, or does nothing if this layer does + // not use the HWC. If includeGeometry is false, the geometry state can be + // skipped. + virtual void writeStateToHWC(bool includeGeometry) const = 0; + // Debugging virtual void dump(std::string& result) const = 0; }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h index 20093808cb..e69b99f0f5 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/RenderSurface.h @@ -71,7 +71,7 @@ public: virtual status_t prepareFrame() = 0; // Allocates a buffer as scratch space for GPU composition - virtual sp<GraphicBuffer> dequeueBuffer() = 0; + virtual sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) = 0; // Queues the drawn buffer for consumption by HWC. readyFence is the fence // which will fire when the buffer is ready for consumption. diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h index b45de5a619..02d7890c9d 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/HwcBufferCache.h @@ -19,6 +19,7 @@ #include <cstdint> #include <vector> +#include <gui/BufferQueue.h> #include <utils/StrongPointer.h> namespace android { @@ -39,19 +40,28 @@ namespace compositionengine::impl { class HwcBufferCache { public: HwcBufferCache(); - - // Given a buffer queue slot and buffer, return the HWC cache slot and + // Given a buffer, return the HWC cache slot and // buffer to be sent to HWC. // // outBuffer is set to buffer when buffer is not in the HWC cache; // otherwise, outBuffer is set to nullptr. - void getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot, + void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot, sp<GraphicBuffer>* outBuffer); +protected: + bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot); + uint32_t getLeastRecentlyUsedSlot(); + uint64_t getCounter(); + private: - // a vector as we expect "slot" to be in the range of [0, 63] (that is, - // less than BufferQueue::NUM_BUFFER_SLOTS). - std::vector<sp<GraphicBuffer>> mBuffers; + // an array where the index corresponds to a slot and the value corresponds to a (counter, + // buffer) pair. "counter" is a unique value that indicates the last time this slot was updated + // or used and allows us to keep track of the least-recently used buffer. + std::pair<uint64_t, wp<GraphicBuffer>> mBuffers[BufferQueue::NUM_BUFFER_SLOTS]; + + // The cache increments this counter value when a slot is updated or used. + // Used to track the least recently-used buffer + uint64_t mCounter = 1; }; } // namespace compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h index 3fd057c79f..b1d1f42f46 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/Output.h @@ -67,7 +67,8 @@ public: compositionengine::OutputLayer* getOutputLayerForLayer( compositionengine::Layer*) const override; std::unique_ptr<compositionengine::OutputLayer> getOrCreateOutputLayer( - std::shared_ptr<compositionengine::Layer>, sp<LayerFE>) override; + std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>, + sp<LayerFE>) override; void setOutputLayersOrderedByZ(OutputLayers&&) override; const OutputLayers& getOutputLayersOrderedByZ() const override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h index 5798540792..6a4818f10f 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayer.h @@ -21,6 +21,10 @@ #include <compositionengine/OutputLayer.h> #include <compositionengine/impl/OutputLayerCompositionState.h> +#include <ui/FloatRect.h> +#include <ui/Rect.h> + +#include "DisplayHardware/DisplayIdentification.h" namespace android::compositionengine::impl { @@ -30,6 +34,8 @@ public: sp<compositionengine::LayerFE>); ~OutputLayer() override; + void initialize(const CompositionEngine&, std::optional<DisplayId>); + const compositionengine::Output& getOutput() const override; compositionengine::Layer& getLayer() const override; compositionengine::LayerFE& getLayerFE() const override; @@ -37,9 +43,18 @@ public: const OutputLayerCompositionState& getState() const override; OutputLayerCompositionState& editState() override; + void updateCompositionState(bool) override; + void writeStateToHWC(bool) const override; + void dump(std::string& result) const override; + virtual FloatRect calculateOutputSourceCrop() const; + virtual Rect calculateOutputDisplayFrame() const; + virtual uint32_t calculateOutputRelativeBufferTransform() const; + private: + Rect calculateInitialCrop() const; + const compositionengine::Output& mOutput; std::shared_ptr<compositionengine::Layer> mLayer; sp<compositionengine::LayerFE> mLayerFE; @@ -48,7 +63,7 @@ private: }; std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( - const compositionengine::Output&, std::shared_ptr<compositionengine::Layer>, - sp<compositionengine::LayerFE>); + const CompositionEngine&, std::optional<DisplayId>, const compositionengine::Output&, + std::shared_ptr<compositionengine::Layer>, sp<compositionengine::LayerFE>); } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h index 58b13ed603..3c79084073 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/RenderSurface.h @@ -52,7 +52,7 @@ public: void setProtected(bool useProtected) override; status_t beginFrame(bool mustRecompose) override; status_t prepareFrame() override; - sp<GraphicBuffer> dequeueBuffer() override; + sp<GraphicBuffer> dequeueBuffer(base::unique_fd* bufferFence) override; void queueBuffer(base::unique_fd&& readyFence) override; void onPresentDisplayCompleted() override; void setViewportAndProjection() override; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h index 92e007081b..aab18db3c9 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/LayerFE.h @@ -17,7 +17,9 @@ #pragma once #include <compositionengine/LayerFE.h> +#include <compositionengine/LayerFECompositionState.h> #include <gmock/gmock.h> +#include <ui/Fence.h> namespace android::compositionengine::mock { @@ -27,6 +29,11 @@ class LayerFE : public compositionengine::LayerFE { public: LayerFE(); virtual ~LayerFE(); + + MOCK_CONST_METHOD2(latchCompositionState, void(LayerFECompositionState&, bool)); + MOCK_METHOD1(onLayerDisplayed, void(const sp<Fence>&)); + + MOCK_CONST_METHOD0(getDebugName, const char*()); }; } // namespace android::compositionengine::mock diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h index 2972ad7346..d0e7b195a8 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/Output.h @@ -61,9 +61,9 @@ public: MOCK_CONST_METHOD1(getOutputLayerForLayer, compositionengine::OutputLayer*(compositionengine::Layer*)); - MOCK_METHOD2(getOrCreateOutputLayer, + MOCK_METHOD3(getOrCreateOutputLayer, std::unique_ptr<compositionengine::OutputLayer>( - std::shared_ptr<compositionengine::Layer>, + std::optional<DisplayId>, std::shared_ptr<compositionengine::Layer>, sp<compositionengine::LayerFE>)); MOCK_METHOD1(setOutputLayersOrderedByZ, void(OutputLayers&&)); MOCK_CONST_METHOD0(getOutputLayersOrderedByZ, OutputLayers&()); diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h index 6bd61ee795..29cd08a681 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/OutputLayer.h @@ -16,6 +16,7 @@ #pragma once +#include <compositionengine/CompositionEngine.h> #include <compositionengine/Layer.h> #include <compositionengine/LayerFE.h> #include <compositionengine/Output.h> @@ -37,6 +38,9 @@ public: MOCK_CONST_METHOD0(getState, const impl::OutputLayerCompositionState&()); MOCK_METHOD0(editState, impl::OutputLayerCompositionState&()); + MOCK_METHOD1(updateCompositionState, void(bool)); + MOCK_CONST_METHOD1(writeStateToHWC, void(bool)); + MOCK_CONST_METHOD1(dump, void(std::string&)); }; diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h index 8442bef6fc..1562f58709 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/mock/RenderSurface.h @@ -37,7 +37,7 @@ public: MOCK_METHOD1(setBufferDataspace, void(ui::Dataspace)); MOCK_METHOD1(beginFrame, status_t(bool mustRecompose)); MOCK_METHOD0(prepareFrame, status_t()); - MOCK_METHOD0(dequeueBuffer, sp<GraphicBuffer>()); + MOCK_METHOD1(dequeueBuffer, sp<GraphicBuffer>(base::unique_fd*)); MOCK_METHOD1(queueBuffer, void(base::unique_fd&&)); MOCK_METHOD0(onPresentDisplayCompleted, void()); MOCK_METHOD0(setViewportAndProjection, void()); diff --git a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp index c49701315b..959843050c 100644 --- a/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp +++ b/services/surfaceflinger/CompositionEngine/src/DumpHelpers.cpp @@ -24,7 +24,7 @@ namespace android::compositionengine::impl { using android::base::StringAppendF; void dumpVal(std::string& out, const char* name, bool value) { - StringAppendF(&out, "%s=%c ", name, value ? 'T' : 'F'); + StringAppendF(&out, "%s=%s ", name, value ? "true" : "false"); } void dumpVal(std::string& out, const char* name, const void* value) { @@ -56,7 +56,7 @@ void dumpVal(std::string& out, const char* name, const std::string& value) { } void dumpVal(std::string& out, const char* name, const char* valueName, int value) { - StringAppendF(&out, "%s=%s (%d)", name, valueName, value); + StringAppendF(&out, "%s=%s (%d) ", name, valueName, value); } void dumpVal(std::string& out, const char* name, const std::string& valueName, int value) { diff --git a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp index 6f340b96d0..8613210cfc 100644 --- a/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp +++ b/services/surfaceflinger/CompositionEngine/src/HwcBufferCache.cpp @@ -21,31 +21,52 @@ namespace android::compositionengine::impl { HwcBufferCache::HwcBufferCache() { - mBuffers.reserve(BufferQueue::NUM_BUFFER_SLOTS); + std::fill(std::begin(mBuffers), std::end(mBuffers), + std::pair<uint64_t, wp<GraphicBuffer>>(0, nullptr)); } - -void HwcBufferCache::getHwcBuffer(int slot, const sp<GraphicBuffer>& buffer, uint32_t* outSlot, - sp<GraphicBuffer>* outBuffer) { - if (slot == BufferQueue::INVALID_BUFFER_SLOT || slot < 0) { - // default to slot 0 - slot = 0; +bool HwcBufferCache::getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) { + // search for cached buffer first + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + // Weak pointers in the cache may have had their object destroyed. + // Comparisons between weak pointers will accurately reflect this case, + // but comparisons between weak and strong may not. Thus, we create a weak + // pointer from strong pointer buffer + wp<GraphicBuffer> weakCopy(buffer); + if (mBuffers[i].second == weakCopy) { + *outSlot = i; + return true; + } } - if (static_cast<size_t>(slot) >= mBuffers.size()) { - mBuffers.resize(slot + 1); - } + // use the least-recently used slot + *outSlot = getLeastRecentlyUsedSlot(); + return false; +} - *outSlot = slot; +uint32_t HwcBufferCache::getLeastRecentlyUsedSlot() { + auto iter = std::min_element(std::begin(mBuffers), std::end(mBuffers)); + return std::distance(std::begin(mBuffers), iter); +} + +void HwcBufferCache::getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot, + sp<GraphicBuffer>* outBuffer) { + bool cached = getSlot(buffer, outSlot); - if (mBuffers[slot] == buffer) { + auto& [currentCounter, currentBuffer] = mBuffers[*outSlot]; + if (cached) { // already cached in HWC, skip sending the buffer *outBuffer = nullptr; + currentCounter = getCounter(); } else { *outBuffer = buffer; // update cache - mBuffers[slot] = buffer; + currentBuffer = buffer; + currentCounter = getCounter(); } } +uint64_t HwcBufferCache::getCounter() { + return mCounter++; +} } // namespace android::compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Layer.cpp b/services/surfaceflinger/CompositionEngine/src/Layer.cpp index 109e9f8438..96e9731768 100644 --- a/services/surfaceflinger/CompositionEngine/src/Layer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Layer.cpp @@ -52,7 +52,9 @@ LayerCompositionState& Layer::editState() { } void Layer::dump(std::string& out) const { - android::base::StringAppendF(&out, " Layer %p\n", this); + auto layerFE = getLayerFE(); + android::base::StringAppendF(&out, "* compositionengine::Layer %p (%s)\n", this, + layerFE ? layerFE->getDebugName() : "<unknown>"); mState.dump(out); } diff --git a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp index 517b641594..40c4da97a8 100644 --- a/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/LayerCompositionState.cpp @@ -31,6 +31,25 @@ void dumpVal(std::string& out, const char* name, Hwc2::IComposerClient::Color va void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) { out.append(" "); + dumpVal(out, "isSecure", state.isSecure); + dumpVal(out, "geomUsesSourceCrop", state.geomUsesSourceCrop); + dumpVal(out, "geomBufferUsesDisplayInverseTransform", + state.geomBufferUsesDisplayInverseTransform); + dumpVal(out, "geomLayerTransform", state.geomLayerTransform); + + out.append("\n "); + dumpVal(out, "geomBufferSize", state.geomBufferSize); + dumpVal(out, "geomContentCrop", state.geomContentCrop); + dumpVal(out, "geomCrop", state.geomCrop); + dumpVal(out, "geomBufferTransform", state.geomBufferTransform); + + out.append("\n "); + dumpVal(out, "geomActiveTransparentRegion", state.geomActiveTransparentRegion); + + out.append(" "); + dumpVal(out, "geomLayerBounds", state.geomLayerBounds); + + out.append("\n "); dumpVal(out, "blend", toString(state.blendMode), state.blendMode); dumpVal(out, "alpha", state.alpha); @@ -61,7 +80,7 @@ void dumpFrontEnd(std::string& out, const LayerFECompositionState& state) { } // namespace void LayerCompositionState::dump(std::string& out) const { - out.append(" frontend:\n"); + out.append(" frontend:\n"); dumpFrontEnd(out, frontEnd); } diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index f97add4f17..d22bdaf625 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -147,7 +147,7 @@ void Output::dumpBase(std::string& out) const { out.append(" No render surface!\n"); } - out.append("\n %d Layers", mOutputLayersOrderedByZ.size()); + android::base::StringAppendF(&out, "\n %zu Layers\b", mOutputLayersOrderedByZ.size()); for (const auto& outputLayer : mOutputLayersOrderedByZ) { if (!outputLayer) { continue; @@ -217,13 +217,14 @@ compositionengine::OutputLayer* Output::getOutputLayerForLayer( } std::unique_ptr<compositionengine::OutputLayer> Output::getOrCreateOutputLayer( - std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) { + std::optional<DisplayId> displayId, std::shared_ptr<compositionengine::Layer> layer, + sp<compositionengine::LayerFE> layerFE) { for (auto& outputLayer : mOutputLayersOrderedByZ) { if (outputLayer && &outputLayer->getLayer() == layer.get()) { return std::move(outputLayer); } } - return createOutputLayer(*this, layer, layerFE); + return createOutputLayer(mCompositionEngine, displayId, *this, layer, layerFE); } void Output::setOutputLayersOrderedByZ(OutputLayers&& layers) { diff --git a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp index 78807ffa9f..9549054bd6 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputCompositionState.cpp @@ -20,6 +20,7 @@ namespace android::compositionengine::impl { void OutputCompositionState::dump(std::string& out) const { + out.append(" "); dumpVal(out, "isEnabled", isEnabled); dumpVal(out, "isSecure", isSecure); @@ -37,7 +38,7 @@ void OutputCompositionState::dump(std::string& out) const { dumpVal(out, "scissor", scissor); dumpVal(out, "needsFiltering", needsFiltering); - out.append("\n"); + out.append("\n "); dumpVal(out, "colorMode", toString(colorMode), colorMode); dumpVal(out, "renderIntent", toString(renderIntent), renderIntent); diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp index 78c140316e..5ce72b0879 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp @@ -15,10 +15,16 @@ */ #include <android-base/stringprintf.h> +#include <compositionengine/CompositionEngine.h> #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" namespace android::compositionengine { @@ -26,10 +32,25 @@ OutputLayer::~OutputLayer() = default; namespace impl { +namespace { + +FloatRect reduce(const FloatRect& win, const Region& exclude) { + if (CC_LIKELY(exclude.isEmpty())) { + return win; + } + // Convert through Rect (by rounding) for lack of FloatRegion + return Region(Rect{win}).subtract(exclude).getBounds().toFloatRect(); +} + +} // namespace + std::unique_ptr<compositionengine::OutputLayer> createOutputLayer( - const compositionengine::Output& display, std::shared_ptr<compositionengine::Layer> layer, + const CompositionEngine& compositionEngine, std::optional<DisplayId> displayId, + const compositionengine::Output& output, std::shared_ptr<compositionengine::Layer> layer, sp<compositionengine::LayerFE> layerFE) { - return std::make_unique<OutputLayer>(display, layer, layerFE); + auto result = std::make_unique<OutputLayer>(output, layer, layerFE); + result->initialize(compositionEngine, displayId); + return result; } OutputLayer::OutputLayer(const Output& output, std::shared_ptr<Layer> layer, sp<LayerFE> layerFE) @@ -37,6 +58,20 @@ OutputLayer::OutputLayer(const Output& output, std::shared_ptr<Layer> layer, sp< OutputLayer::~OutputLayer() = default; +void OutputLayer::initialize(const CompositionEngine& compositionEngine, + std::optional<DisplayId> displayId) { + if (!displayId) { + return; + } + + auto& hwc = compositionEngine.getHwComposer(); + + mState.hwc.emplace(std::shared_ptr<HWC2::Layer>(hwc.createLayer(*displayId), + [&hwc, displayId](HWC2::Layer* layer) { + hwc.destroyLayer(*displayId, layer); + })); +} + const compositionengine::Output& OutputLayer::getOutput() const { return mOutput; } @@ -57,10 +92,295 @@ OutputLayerCompositionState& OutputLayer::editState() { return mState; } +Rect OutputLayer::calculateInitialCrop() const { + const auto& layerState = mLayer->getState().frontEnd; + + // apply the projection's clipping to the window crop in + // layerstack space, and convert-back to layer space. + // if there are no window scaling involved, this operation will map to full + // pixels in the buffer. + + FloatRect activeCropFloat = + reduce(layerState.geomLayerBounds, layerState.geomActiveTransparentRegion); + + const Rect& viewport = mOutput.getState().viewport; + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; + // Transform to screen space. + activeCropFloat = layerTransform.transform(activeCropFloat); + activeCropFloat = activeCropFloat.intersect(viewport.toFloatRect()); + // Back to layer space to work with the content crop. + activeCropFloat = inverseLayerTransform.transform(activeCropFloat); + + // This needs to be here as transform.transform(Rect) computes the + // transformed rect and then takes the bounding box of the result before + // returning. This means + // transform.inverse().transform(transform.transform(Rect)) != Rect + // in which case we need to make sure the final rect is clipped to the + // display bounds. + Rect activeCrop{activeCropFloat}; + if (!activeCrop.intersect(layerState.geomBufferSize, &activeCrop)) { + activeCrop.clear(); + } + return activeCrop; +} + +FloatRect OutputLayer::calculateOutputSourceCrop() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + if (!layerState.geomUsesSourceCrop) { + return {}; + } + + // the content crop is the area of the content that gets scaled to the + // layer's size. This is in buffer space. + FloatRect crop = layerState.geomContentCrop.toFloatRect(); + + // In addition there is a WM-specified crop we pull from our drawing state. + Rect activeCrop = calculateInitialCrop(); + const Rect& bufferSize = layerState.geomBufferSize; + + int winWidth = bufferSize.getWidth(); + int winHeight = bufferSize.getHeight(); + + // The bufferSize for buffer state layers can be unbounded ([0, 0, -1, -1]) + // if display frame hasn't been set and the parent is an unbounded layer. + if (winWidth < 0 && winHeight < 0) { + return crop; + } + + // Transform the window crop to match the buffer coordinate system, + // which means using the inverse of the current transform set on the + // SurfaceFlingerConsumer. + uint32_t invTransform = layerState.geomBufferTransform; + if (layerState.geomBufferUsesDisplayInverseTransform) { + /* + * the code below applies the primary display's inverse transform to the + * buffer + */ + uint32_t invTransformOrient = outputState.orientation; + // calculate the inverse transform + if (invTransformOrient & HAL_TRANSFORM_ROT_90) { + invTransformOrient ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + // and apply to the current transform + invTransform = + (ui::Transform(invTransformOrient) * ui::Transform(invTransform)).getOrientation(); + } + + if (invTransform & HAL_TRANSFORM_ROT_90) { + // If the activeCrop has been rotate the ends are rotated but not + // the space itself so when transforming ends back we can't rely on + // a modification of the axes of rotation. To account for this we + // need to reorient the inverse rotation in terms of the current + // axes of rotation. + bool is_h_flipped = (invTransform & HAL_TRANSFORM_FLIP_H) != 0; + bool is_v_flipped = (invTransform & HAL_TRANSFORM_FLIP_V) != 0; + if (is_h_flipped == is_v_flipped) { + invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + std::swap(winWidth, winHeight); + } + const Rect winCrop = + activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight()); + + // below, crop is intersected with winCrop expressed in crop's coordinate space + float xScale = crop.getWidth() / float(winWidth); + float yScale = crop.getHeight() / float(winHeight); + + float insetL = winCrop.left * xScale; + float insetT = winCrop.top * yScale; + float insetR = (winWidth - winCrop.right) * xScale; + float insetB = (winHeight - winCrop.bottom) * yScale; + + crop.left += insetL; + crop.top += insetT; + crop.right -= insetR; + crop.bottom -= insetB; + + return crop; +} + +Rect OutputLayer::calculateOutputDisplayFrame() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + // apply the layer's transform, followed by the display's global transform + // here we're guaranteed that the layer's transform preserves rects + Region activeTransparentRegion = layerState.geomActiveTransparentRegion; + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform& inverseLayerTransform = layerState.geomInverseLayerTransform; + const Rect& bufferSize = layerState.geomBufferSize; + Rect activeCrop = layerState.geomCrop; + if (!activeCrop.isEmpty() && bufferSize.isValid()) { + activeCrop = layerTransform.transform(activeCrop); + if (!activeCrop.intersect(outputState.viewport, &activeCrop)) { + activeCrop.clear(); + } + activeCrop = inverseLayerTransform.transform(activeCrop, true); + // This needs to be here as transform.transform(Rect) computes the + // transformed rect and then takes the bounding box of the result before + // returning. This means + // transform.inverse().transform(transform.transform(Rect)) != Rect + // in which case we need to make sure the final rect is clipped to the + // display bounds. + if (!activeCrop.intersect(bufferSize, &activeCrop)) { + activeCrop.clear(); + } + // mark regions outside the crop as transparent + activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top)); + activeTransparentRegion.orSelf( + Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight())); + activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); + activeTransparentRegion.orSelf( + Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom)); + } + + // reduce uses a FloatRect to provide more accuracy during the + // transformation. We then round upon constructing 'frame'. + Rect frame{ + layerTransform.transform(reduce(layerState.geomLayerBounds, activeTransparentRegion))}; + if (!frame.intersect(outputState.viewport, &frame)) { + frame.clear(); + } + const ui::Transform displayTransform{outputState.transform}; + + return displayTransform.transform(frame); +} + +uint32_t OutputLayer::calculateOutputRelativeBufferTransform() const { + const auto& layerState = mLayer->getState().frontEnd; + const auto& outputState = mOutput.getState(); + + /* + * Transformations are applied in this order: + * 1) buffer orientation/flip/mirror + * 2) state transformation (window manager) + * 3) layer orientation (screen orientation) + * (NOTE: the matrices are multiplied in reverse order) + */ + const ui::Transform& layerTransform = layerState.geomLayerTransform; + const ui::Transform displayTransform{outputState.orientation}; + const ui::Transform bufferTransform{layerState.geomBufferTransform}; + ui::Transform transform(displayTransform * layerTransform * bufferTransform); + + if (layerState.geomBufferUsesDisplayInverseTransform) { + /* + * the code below applies the primary display's inverse transform to the + * buffer + */ + uint32_t invTransform = outputState.orientation; + // calculate the inverse transform + if (invTransform & HAL_TRANSFORM_ROT_90) { + invTransform ^= HAL_TRANSFORM_FLIP_V | HAL_TRANSFORM_FLIP_H; + } + + /* + * Here we cancel out the orientation component of the WM transform. + * The scaling and translate components are already included in our bounds + * computation so it's enough to just omit it in the composition. + * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why. + */ + transform = ui::Transform(invTransform) * displayTransform * bufferTransform; + } + + // this gives us only the "orientation" component of the transform + return transform.getOrientation(); +} // namespace impl + +void OutputLayer::updateCompositionState(bool includeGeometry) { + if (includeGeometry) { + mState.displayFrame = calculateOutputDisplayFrame(); + mState.sourceCrop = calculateOutputSourceCrop(); + mState.bufferTransform = + static_cast<Hwc2::Transform>(calculateOutputRelativeBufferTransform()); + + if ((mLayer->getState().frontEnd.isSecure && !mOutput.getState().isSecure) || + (mState.bufferTransform & ui::Transform::ROT_INVALID)) { + mState.forceClientComposition = true; + } + } +} + +void OutputLayer::writeStateToHWC(bool includeGeometry) const { + // Skip doing this if there is no HWC interface + if (!mState.hwc) { + return; + } + + auto& hwcLayer = (*mState.hwc).hwcLayer; + if (!hwcLayer) { + ALOGE("[%s] failed to write composition state to HWC -- no hwcLayer for output %s", + mLayerFE->getDebugName(), mOutput.getName().c_str()); + return; + } + + if (includeGeometry) { + // Output dependent state + + if (auto error = hwcLayer->setDisplayFrame(mState.displayFrame); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", + mLayerFE->getDebugName(), mState.displayFrame.left, mState.displayFrame.top, + mState.displayFrame.right, mState.displayFrame.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setSourceCrop(mState.sourceCrop); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " + "%s (%d)", + mLayerFE->getDebugName(), mState.sourceCrop.left, mState.sourceCrop.top, + mState.sourceCrop.right, mState.sourceCrop.bottom, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setZOrder(mState.z); error != HWC2::Error::None) { + ALOGE("[%s] Failed to set Z %u: %s (%d)", mLayerFE->getDebugName(), mState.z, + to_string(error).c_str(), static_cast<int32_t>(error)); + } + + if (auto error = + hwcLayer->setTransform(static_cast<HWC2::Transform>(mState.bufferTransform)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set transform %s: %s (%d)", mLayerFE->getDebugName(), + toString(mState.bufferTransform).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + // Output independent state + + const auto& outputIndependentState = mLayer->getState().frontEnd; + + if (auto error = hwcLayer->setBlendMode( + static_cast<HWC2::BlendMode>(outputIndependentState.blendMode)); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set blend mode %s: %s (%d)", mLayerFE->getDebugName(), + toString(outputIndependentState.blendMode).c_str(), to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = hwcLayer->setPlaneAlpha(outputIndependentState.alpha); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set plane alpha %.3f: %s (%d)", mLayerFE->getDebugName(), + outputIndependentState.alpha, to_string(error).c_str(), + static_cast<int32_t>(error)); + } + + if (auto error = + hwcLayer->setInfo(outputIndependentState.type, outputIndependentState.appId); + error != HWC2::Error::None) { + ALOGE("[%s] Failed to set info %s (%d)", mLayerFE->getDebugName(), + to_string(error).c_str(), static_cast<int32_t>(error)); + } + } +} + void OutputLayer::dump(std::string& out) const { using android::base::StringAppendF; - StringAppendF(&out, " Output Layer %p\n", this); + StringAppendF(&out, " - Output Layer %p (Composition layer %p) (%s)\n", this, mLayer.get(), + mLayerFE->getDebugName()); mState.dump(out); } diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp index 10f27b86f9..861ea5757b 100644 --- a/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp +++ b/services/surfaceflinger/CompositionEngine/src/OutputLayerCompositionState.cpp @@ -46,7 +46,7 @@ void OutputLayerCompositionState::dump(std::string& out) const { dumpVal(out, "clearClientTarget", clearClientTarget); dumpVal(out, "displayFrame", displayFrame); dumpVal(out, "sourceCrop", sourceCrop); - dumpVal(out, "bufferTransform%", toString(bufferTransform), bufferTransform); + dumpVal(out, "bufferTransform", toString(bufferTransform), bufferTransform); dumpVal(out, "z-index", z); if (hwc) { diff --git a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp index ebb1bc2e5b..b4dfba1d07 100644 --- a/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp +++ b/services/surfaceflinger/CompositionEngine/src/RenderSurface.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#define ATRACE_TAG ATRACE_TAG_GRAPHICS + #include <android-base/stringprintf.h> #include <android/native_window.h> #include <compositionengine/CompositionEngine.h> @@ -28,6 +30,7 @@ #include <system/window.h> #include <ui/GraphicBuffer.h> #include <ui/Rect.h> +#include <utils/Trace.h> #include "DisplayHardware/HWComposer.h" @@ -127,7 +130,8 @@ status_t RenderSurface::prepareFrame() { return mDisplaySurface->prepareFrame(compositionType); } -sp<GraphicBuffer> RenderSurface::dequeueBuffer() { +sp<GraphicBuffer> RenderSurface::dequeueBuffer(base::unique_fd* bufferFence) { + ATRACE_CALL(); int fd = -1; ANativeWindowBuffer* buffer = nullptr; @@ -145,13 +149,7 @@ sp<GraphicBuffer> RenderSurface::dequeueBuffer() { mGraphicBuffer->getNativeBuffer()->handle); mGraphicBuffer = GraphicBuffer::from(buffer); - // Block until the buffer is ready - // TODO(alecmouri): it's perhaps more appropriate to block renderengine so - // that the gl driver can block instead. - if (fd >= 0) { - sync_wait(fd, -1); - close(fd); - } + *bufferFence = base::unique_fd(fd); return mGraphicBuffer; } @@ -172,7 +170,8 @@ void RenderSurface::queueBuffer(base::unique_fd&& readyFence) { // We shouldn't deadlock here, since mGraphicBuffer == nullptr only // after a successful call to queueBuffer, or if dequeueBuffer has // never been called. - dequeueBuffer(); + base::unique_fd unused; + dequeueBuffer(&unused); } if (mGraphicBuffer == nullptr) { diff --git a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp index f2a1aad2fc..ac04cb3f32 100644 --- a/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/HwcBufferCacheTest.cpp @@ -22,60 +22,80 @@ namespace android::compositionengine { namespace { +class TestableHwcBufferCache : public impl::HwcBufferCache { +public: + void getHwcBuffer(const sp<GraphicBuffer>& buffer, uint32_t* outSlot, + sp<GraphicBuffer>* outBuffer) { + HwcBufferCache::getHwcBuffer(buffer, outSlot, outBuffer); + } + bool getSlot(const sp<GraphicBuffer>& buffer, uint32_t* outSlot) { + return HwcBufferCache::getSlot(buffer, outSlot); + } + uint32_t getLeastRecentlyUsedSlot() { return HwcBufferCache::getLeastRecentlyUsedSlot(); } +}; + class HwcBufferCacheTest : public testing::Test { public: ~HwcBufferCacheTest() override = default; - void testSlot(const int inSlot, const uint32_t expectedSlot) { - uint32_t outSlot; - sp<GraphicBuffer> outBuffer; - - // The first time, the output is the same as the input - mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer); - EXPECT_EQ(expectedSlot, outSlot); - EXPECT_EQ(mBuffer1, outBuffer); - - // The second time with the same buffer, the outBuffer is nullptr. - mCache.getHwcBuffer(inSlot, mBuffer1, &outSlot, &outBuffer); - EXPECT_EQ(expectedSlot, outSlot); - EXPECT_EQ(nullptr, outBuffer.get()); - - // With a new buffer, the outBuffer is the input. - mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer); - EXPECT_EQ(expectedSlot, outSlot); - EXPECT_EQ(mBuffer2, outBuffer); - - // Again, the second request with the same buffer sets outBuffer to nullptr. - mCache.getHwcBuffer(inSlot, mBuffer2, &outSlot, &outBuffer); - EXPECT_EQ(expectedSlot, outSlot); - EXPECT_EQ(nullptr, outBuffer.get()); - - // Setting a slot to use nullptr lookslike works, but note that - // the output values make it look like no new buffer is being set.... - mCache.getHwcBuffer(inSlot, sp<GraphicBuffer>(), &outSlot, &outBuffer); - EXPECT_EQ(expectedSlot, outSlot); - EXPECT_EQ(nullptr, outBuffer.get()); - } - - impl::HwcBufferCache mCache; + TestableHwcBufferCache mCache; sp<GraphicBuffer> mBuffer1{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; sp<GraphicBuffer> mBuffer2{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; }; -TEST_F(HwcBufferCacheTest, cacheWorksForSlotZero) { - testSlot(0, 0); -} +TEST_F(HwcBufferCacheTest, testSlot) { + uint32_t outSlot; + sp<GraphicBuffer> outBuffer; -TEST_F(HwcBufferCacheTest, cacheWorksForMaxSlot) { - testSlot(BufferQueue::NUM_BUFFER_SLOTS - 1, BufferQueue::NUM_BUFFER_SLOTS - 1); -} + // The first time, the output is the same as the input + mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer); + EXPECT_EQ(0, outSlot); + EXPECT_EQ(mBuffer1, outBuffer); -TEST_F(HwcBufferCacheTest, cacheMapsNegativeSlotToZero) { - testSlot(-123, 0); + // The second time with the same buffer, the outBuffer is nullptr. + mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer); + EXPECT_EQ(0, outSlot); + EXPECT_EQ(nullptr, outBuffer.get()); + + // With a new buffer, the outBuffer is the input. + mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer); + EXPECT_EQ(1, outSlot); + EXPECT_EQ(mBuffer2, outBuffer); + + // Again, the second request with the same buffer sets outBuffer to nullptr. + mCache.getHwcBuffer(mBuffer2, &outSlot, &outBuffer); + EXPECT_EQ(1, outSlot); + EXPECT_EQ(nullptr, outBuffer.get()); + + // Setting a slot to use nullptr lookslike works, but note that + // the output values make it look like no new buffer is being set.... + mCache.getHwcBuffer(sp<GraphicBuffer>(), &outSlot, &outBuffer); + EXPECT_EQ(2, outSlot); + EXPECT_EQ(nullptr, outBuffer.get()); } -TEST_F(HwcBufferCacheTest, cacheMapsInvalidBufferSlotToZero) { - testSlot(BufferQueue::INVALID_BUFFER_SLOT, 0); +TEST_F(HwcBufferCacheTest, testGetLeastRecentlyUsedSlot) { + int slot; + uint32_t outSlot; + sp<GraphicBuffer> outBuffer; + + // fill up cache + for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { + sp<GraphicBuffer> buf{new GraphicBuffer(1, 1, HAL_PIXEL_FORMAT_RGBA_8888, 1, 0)}; + mCache.getHwcBuffer(buf, &outSlot, &outBuffer); + EXPECT_EQ(buf, outBuffer); + EXPECT_EQ(i, outSlot); + } + + slot = mCache.getLeastRecentlyUsedSlot(); + EXPECT_EQ(0, slot); + + mCache.getHwcBuffer(mBuffer1, &outSlot, &outBuffer); + EXPECT_EQ(0, outSlot); + EXPECT_EQ(mBuffer1, outBuffer); + + slot = mCache.getLeastRecentlyUsedSlot(); + EXPECT_EQ(1, slot); } } // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp new file mode 100644 index 0000000000..8c103410c6 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.cpp @@ -0,0 +1,32 @@ +/* + * Copyright 2019 The Android Open Source Project + + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "MockHWC2.h" + +namespace HWC2 { + +// This will go away once HWC2::Layer is moved into the "backend" library +Layer::~Layer() = default; + +namespace mock { + +// The Google Mock documentation recommends explicit non-header instantiations +// for better compile time performance. +Layer::Layer() = default; +Layer::~Layer() = default; + +} // namespace mock +} // namespace HWC2 diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h new file mode 100644 index 0000000000..7fd6541a62 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWC2.h @@ -0,0 +1,63 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> +#include <ui/Fence.h> +#include <ui/FloatRect.h> +#include <ui/GraphicBuffer.h> +#include <ui/GraphicTypes.h> +#include <ui/Rect.h> +#include <ui/Region.h> +#include <ui/Transform.h> + +#include "DisplayHardware/HWC2.h" + +namespace HWC2 { +namespace mock { + +class Layer : public HWC2::Layer { +public: + Layer(); + ~Layer() override; + + MOCK_CONST_METHOD0(getId, hwc2_layer_t()); + + MOCK_METHOD2(setCursorPosition, Error(int32_t, int32_t)); + MOCK_METHOD3(setBuffer, + Error(uint32_t, const android::sp<android::GraphicBuffer>&, + const android::sp<android::Fence>&)); + MOCK_METHOD1(setSurfaceDamage, Error(const android::Region&)); + MOCK_METHOD1(setBlendMode, Error(BlendMode)); + MOCK_METHOD1(setColor, Error(hwc_color_t)); + MOCK_METHOD1(setCompositionType, Error(Composition)); + MOCK_METHOD1(setDataspace, Error(android::ui::Dataspace)); + MOCK_METHOD2(setPerFrameMetadata, Error(const int32_t, const android::HdrMetadata&)); + MOCK_METHOD1(setDisplayFrame, Error(const android::Rect&)); + MOCK_METHOD1(setPlaneAlpha, Error(float)); + MOCK_METHOD1(setSidebandStream, Error(const native_handle_t*)); + MOCK_METHOD1(setSourceCrop, Error(const android::FloatRect&)); + MOCK_METHOD1(setTransform, Error(Transform)); + MOCK_METHOD1(setVisibleRegion, Error(const android::Region&)); + MOCK_METHOD1(setZOrder, Error(uint32_t)); + MOCK_METHOD2(setInfo, Error(uint32_t, uint32_t)); + + MOCK_METHOD1(setColorTransform, Error(const android::mat4&)); +}; + +} // namespace mock +} // namespace HWC2 diff --git a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h index 885cdd4ec4..94349dea14 100644 --- a/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h +++ b/services/surfaceflinger/CompositionEngine/tests/MockHWComposer.h @@ -65,6 +65,8 @@ public: MOCK_METHOD4(setDisplayContentSamplingEnabled, status_t(DisplayId, bool, uint8_t, uint64_t)); MOCK_METHOD4(getDisplayedContentSample, status_t(DisplayId, uint64_t, uint64_t, DisplayedFrameStats*)); + MOCK_METHOD2(setDisplayBrightness, status_t(DisplayId, float)); + MOCK_METHOD2(getDisplayBrightnessSupport, status_t(DisplayId, bool*)); MOCK_METHOD2(onHotplug, std::optional<DisplayIdentificationInfo>(hwc2_display_t, HWC2::Connection)); diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp index 0a929ac1a3..2060c5aaff 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputLayerTest.cpp @@ -15,18 +15,45 @@ */ #include <compositionengine/impl/OutputLayer.h> +#include <compositionengine/mock/CompositionEngine.h> #include <compositionengine/mock/Layer.h> #include <compositionengine/mock/LayerFE.h> #include <compositionengine/mock/Output.h> #include <gtest/gtest.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; @@ -35,13 +62,253 @@ public: sp<compositionengine::mock::LayerFE> mLayerFE{ new StrictMock<compositionengine::mock::LayerFE>()}; impl::OutputLayer mOutputLayer{mOutput, mLayer, mLayerFE}; + + impl::LayerCompositionState mLayerState; + impl::OutputCompositionState mOutputState; }; -/* ------------------------------------------------------------------------ +/* * Basic construction */ TEST_F(OutputLayerTest, canInstantiateOutputLayer) {} +/* + * OutputLayer::initialize() + */ + +TEST_F(OutputLayerTest, initializingOutputLayerWithoutHwcDoesNothingInteresting) { + StrictMock<compositionengine::mock::CompositionEngine> compositionEngine; + + mOutputLayer.initialize(compositionEngine, std::nullopt); + + EXPECT_FALSE(mOutputLayer.getState().hwc); +} + +TEST_F(OutputLayerTest, initializingOutputLayerWithHwcDisplayCreatesHwcLayer) { + StrictMock<compositionengine::mock::CompositionEngine> compositionEngine; + StrictMock<android::mock::HWComposer> hwc; + StrictMock<HWC2::mock::Layer> hwcLayer; + + EXPECT_CALL(compositionEngine, getHwComposer()).WillOnce(ReturnRef(hwc)); + EXPECT_CALL(hwc, createLayer(DEFAULT_DISPLAY_ID)).WillOnce(Return(&hwcLayer)); + + mOutputLayer.initialize(compositionEngine, DEFAULT_DISPLAY_ID); + + const auto& outputLayerState = mOutputLayer.getState(); + ASSERT_TRUE(outputLayerState.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::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, geomLayerSnapToBoundsAffectsFrame) { + 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; + } +} + +/* + * 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/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index cb718218de..a84af3a82f 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -338,7 +338,7 @@ TEST_F(OutputTest, getOrCreateOutputLayerWorks) { // If there is no OutputLayer corresponding to the input layer, a // new OutputLayer is constructed and returned. EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(otherLayer)); - auto result = mOutput.getOrCreateOutputLayer(layer, layerFE); + auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE); EXPECT_NE(existingOutputLayer, result.get()); EXPECT_TRUE(result.get() != nullptr); EXPECT_EQ(layer.get(), &result->getLayer()); @@ -354,7 +354,7 @@ TEST_F(OutputTest, getOrCreateOutputLayerWorks) { // If there is an existing OutputLayer for the requested layer, an owned // pointer is returned EXPECT_CALL(*existingOutputLayer, getLayer()).WillOnce(ReturnRef(*layer)); - auto result = mOutput.getOrCreateOutputLayer(layer, layerFE); + auto result = mOutput.getOrCreateOutputLayer(std::nullopt, layer, layerFE); EXPECT_EQ(existingOutputLayer, result.get()); // The corresponding entry in the ordered array should be cleared. diff --git a/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h new file mode 100644 index 0000000000..d4c76bc7e8 --- /dev/null +++ b/services/surfaceflinger/CompositionEngine/tests/RectMatcher.h @@ -0,0 +1,45 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <string> + +#include <android-base/stringprintf.h> +#include <gmock/gmock.h> + +namespace { + +using android::base::StringAppendF; +using Rect = android::Rect; + +void dumpRect(const Rect& rect, std::string& result, const char* name) { + StringAppendF(&result, "%s (%d %d %d %d) ", name, rect.left, rect.top, rect.right, rect.bottom); +} + +// Checks for a region match +MATCHER_P(RectEq, expected, "") { + std::string buf; + buf.append("Rects are not equal\n"); + dumpRect(expected, buf, "expected rect"); + dumpRect(arg, buf, "actual rect"); + *result_listener << buf; + + return (expected.left == arg.left) && (expected.top == arg.top) && + (expected.right == arg.right) && (expected.bottom == arg.bottom); +} + +} // namespace diff --git a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp index 0a7c46254a..84af9b967e 100644 --- a/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/RenderSurfaceTest.cpp @@ -358,7 +358,8 @@ TEST_F(RenderSurfaceTest, dequeueBufferObtainsABuffer) { .WillOnce( DoAll(SetArgPointee<0>(buffer.get()), SetArgPointee<1>(-1), Return(NO_ERROR))); - EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer().get()); + base::unique_fd fence; + EXPECT_EQ(buffer.get(), mSurface.dequeueBuffer(&fence).get()); EXPECT_EQ(buffer.get(), mSurface.mutableGraphicBufferForTest().get()); } diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp index 22c40d79a0..7927fa95b6 100644 --- a/services/surfaceflinger/ContainerLayer.cpp +++ b/services/surfaceflinger/ContainerLayer.cpp @@ -26,7 +26,7 @@ ContainerLayer::ContainerLayer(const LayerCreationArgs& args) : Layer(args) {} ContainerLayer::~ContainerLayer() = default; -bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&, +bool ContainerLayer::prepareClientLayer(const RenderArea&, const Region&, bool, Region&, const bool, renderengine::LayerSettings&) { return false; } @@ -40,6 +40,6 @@ bool ContainerLayer::canReceiveInput() const { } void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&, const ui::Transform&, - const Rect&, int32_t) {} + const Rect&, int32_t, const ui::Dataspace) {} } // namespace android diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h index c69997dd3b..7222a3e15a 100644 --- a/services/surfaceflinger/ContainerLayer.h +++ b/services/surfaceflinger/ContainerLayer.h @@ -34,7 +34,8 @@ public: bool canReceiveInput() const override; void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, - const Rect& viewport, int32_t supportedPerFrameMetadata) override; + const Rect& viewport, int32_t supportedPerFrameMetadata, + const ui::Dataspace targetDataspace) override; bool isCreatedFromMainThread() const override { return true; } @@ -43,7 +44,8 @@ public: protected: bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform, Region& clearRegion, - renderengine::LayerSettings& layer); + const bool supportProtectedContent, + renderengine::LayerSettings& layer) override; }; } // namespace android diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h index a827c47ba9..c80925ee78 100644 --- a/services/surfaceflinger/DisplayDevice.h +++ b/services/surfaceflinger/DisplayDevice.h @@ -255,17 +255,18 @@ public: device->getCompositionDataSpace(), rotation) {} DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, ui::Dataspace reqDataSpace, - ui::Transform::orientation_flags rotation) + ui::Transform::orientation_flags rotation, bool allowSecureLayers = true) : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE, reqDataSpace, getDisplayRotation(rotation, device->getInstallOrientation())), mDevice(device), - mSourceCrop(sourceCrop) {} + mSourceCrop(sourceCrop), + mAllowSecureLayers(allowSecureLayers) {} const ui::Transform& getTransform() const override { return mDevice->getTransform(); } Rect getBounds() const override { return mDevice->getBounds(); } int getHeight() const override { return mDevice->getHeight(); } int getWidth() const override { return mDevice->getWidth(); } - bool isSecure() const override { return mDevice->isSecure(); } + bool isSecure() const override { return mAllowSecureLayers && mDevice->isSecure(); } const sp<const DisplayDevice> getDisplayDevice() const override { return mDevice; } bool needsFiltering() const override { @@ -356,6 +357,7 @@ private: const sp<const DisplayDevice> mDevice; const Rect mSourceCrop; + const bool mAllowSecureLayers; }; }; // namespace android diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp index 8343e5ab91..9cb43bccfa 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.cpp +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.cpp @@ -214,7 +214,6 @@ std::vector<IComposer::Capability> Composer::getCapabilities() [&](const auto& tmpCapabilities) { capabilities = tmpCapabilities; }); - return capabilities; } @@ -1159,6 +1158,30 @@ Error Composer::setLayerPerFrameMetadataBlobs( return Error::NONE; } +Error Composer::getDisplayBrightnessSupport(Display display, bool* outSupport) { + if (!mClient_2_3) { + return Error::UNSUPPORTED; + } + Error error = kDefaultError; + mClient_2_3->getDisplayBrightnessSupport(display, + [&](const auto& tmpError, const auto& tmpSupport) { + error = tmpError; + if (error != Error::NONE) { + return; + } + + *outSupport = tmpSupport; + }); + return error; +} + +Error Composer::setDisplayBrightness(Display display, float brightness) { + if (!mClient_2_3) { + return Error::UNSUPPORTED; + } + return mClient_2_3->setDisplayBrightness(display, brightness); +} + CommandReader::~CommandReader() { resetData(); diff --git a/services/surfaceflinger/DisplayHardware/ComposerHal.h b/services/surfaceflinger/DisplayHardware/ComposerHal.h index 542e840251..e24db152e9 100644 --- a/services/surfaceflinger/DisplayHardware/ComposerHal.h +++ b/services/surfaceflinger/DisplayHardware/ComposerHal.h @@ -203,6 +203,8 @@ public: std::vector<DisplayCapability>* outCapabilities) = 0; virtual Error setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<PerFrameMetadataBlob>& metadata) = 0; + virtual Error getDisplayBrightnessSupport(Display display, bool* outSupport) = 0; + virtual Error setDisplayBrightness(Display display, float brightness) = 0; }; namespace impl { @@ -414,6 +416,8 @@ public: Error setLayerPerFrameMetadataBlobs( Display display, Layer layer, const std::vector<IComposerClient::PerFrameMetadataBlob>& metadata) override; + Error getDisplayBrightnessSupport(Display display, bool* outSupport) override; + Error setDisplayBrightness(Display display, float brightness) override; private: class CommandWriter : public CommandWriterBase { diff --git a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp index 27812f7492..775fb806b8 100644 --- a/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp +++ b/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp @@ -111,8 +111,7 @@ status_t FramebufferSurface::nextBuffer(uint32_t& outSlot, BufferItem item; status_t err = acquireBufferLocked(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { - mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, - &outSlot, &outBuffer); + mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer); return NO_ERROR; } else if (err != NO_ERROR) { ALOGE("error acquiring buffer: %s (%d)", strerror(-err), err); @@ -138,8 +137,7 @@ status_t FramebufferSurface::nextBuffer(uint32_t& outSlot, mCurrentFence = item.mFence; outFence = item.mFence; - mHwcBufferCache.getHwcBuffer(mCurrentBufferSlot, mCurrentBuffer, - &outSlot, &outBuffer); + mHwcBufferCache.getHwcBuffer(mCurrentBuffer, &outSlot, &outBuffer); outDataspace = static_cast<Dataspace>(item.mDataSpace); status_t result = mHwc.setClientTarget(mDisplayId, outSlot, outFence, outBuffer, outDataspace); if (result != NO_ERROR) { diff --git a/services/surfaceflinger/DisplayHardware/HWC2.cpp b/services/surfaceflinger/DisplayHardware/HWC2.cpp index 68e78768e8..62073b6ef4 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.cpp +++ b/services/surfaceflinger/DisplayHardware/HWC2.cpp @@ -143,8 +143,8 @@ Error Device::createVirtualDisplay(uint32_t width, uint32_t height, return error; } - auto display = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor, mCapabilities, - displayId, DisplayType::Virtual); + auto display = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, displayId, + DisplayType::Virtual); display->setConnected(true); *outDisplay = display.get(); mDisplays.emplace(displayId, std::move(display)); @@ -182,8 +182,8 @@ void Device::onHotplug(hwc2_display_t displayId, Connection connection) { return; } - auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mPowerAdvisor, - mCapabilities, displayId, displayType); + auto newDisplay = std::make_unique<impl::Display>(*mComposer.get(), mCapabilities, + displayId, displayType); newDisplay->setConnected(true); mDisplays.emplace(displayId, std::move(newDisplay)); } else if (connection == Connection::Disconnected) { @@ -254,11 +254,10 @@ float Display::Config::Builder::getDefaultDensity() { } namespace impl { -Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor, +Display::Display(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities, hwc2_display_t id, DisplayType type) : mComposer(composer), - mPowerAdvisor(advisor), mCapabilities(capabilities), mId(id), mIsConnected(false), @@ -278,6 +277,11 @@ Display::Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& 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); } @@ -308,8 +312,7 @@ Error Display::acceptChanges() return static_cast<Error>(intError); } -Error Display::createLayer(Layer** outLayer) -{ +Error Display::createLayer(HWC2::Layer** outLayer) { if (!outLayer) { return Error::BadParameter; } @@ -320,15 +323,13 @@ Error Display::createLayer(Layer** outLayer) return error; } - auto layer = std::make_unique<Layer>( - mComposer, mCapabilities, mId, layerId); + auto layer = std::make_unique<impl::Layer>(mComposer, mCapabilities, mId, layerId); *outLayer = layer.get(); mLayers.emplace(layerId, std::move(layer)); return Error::None; } -Error Display::destroyLayer(Layer* layer) -{ +Error Display::destroyLayer(HWC2::Layer* layer) { if (!layer) { return Error::BadParameter; } @@ -388,9 +389,7 @@ Error Display::getActiveConfigIndex(int* outIndex) const { return Error::None; } -Error Display::getChangedCompositionTypes( - std::unordered_map<Layer*, Composition>* outTypes) -{ +Error Display::getChangedCompositionTypes(std::unordered_map<HWC2::Layer*, Composition>* outTypes) { std::vector<Hwc2::Layer> layerIds; std::vector<Hwc2::IComposerClient::Composition> types; auto intError = mComposer.getChangedCompositionTypes( @@ -492,8 +491,7 @@ Error Display::getName(std::string* outName) const } Error Display::getRequests(HWC2::DisplayRequest* outDisplayRequests, - std::unordered_map<Layer*, LayerRequest>* outLayerRequests) -{ + std::unordered_map<HWC2::Layer*, LayerRequest>* outLayerRequests) { uint32_t intDisplayRequests; std::vector<Hwc2::Layer> layerIds; std::vector<uint32_t> layerRequests; @@ -574,9 +572,7 @@ Error Display::getDisplayedContentSample(uint64_t maxFrames, uint64_t timestamp, return static_cast<Error>(intError); } -Error Display::getReleaseFences( - std::unordered_map<Layer*, sp<Fence>>* outFences) const -{ +Error Display::getReleaseFences(std::unordered_map<HWC2::Layer*, sp<Fence>>* outFences) const { std::vector<Hwc2::Layer> layerIds; std::vector<int> fenceFds; auto intError = mComposer.getReleaseFences(mId, &layerIds, &fenceFds); @@ -586,7 +582,7 @@ Error Display::getReleaseFences( return error; } - std::unordered_map<Layer*, sp<Fence>> releaseFences; + std::unordered_map<HWC2::Layer*, sp<Fence>> releaseFences; releaseFences.reserve(numElements); for (uint32_t element = 0; element < numElements; ++element) { auto layer = getLayerById(layerIds[element]); @@ -644,12 +640,6 @@ Error Display::setClientTarget(uint32_t slot, const sp<GraphicBuffer>& target, Error Display::setColorMode(ColorMode mode, RenderIntent renderIntent) { - // When the color mode is switched to DISPLAY_P3, we want to boost the GPU frequency - // so that GPU composition can finish in time. When color mode is switched from - // DISPLAY_P3, we want to reset GPU frequency. - const bool expensiveRenderingExpected = (mode == ColorMode::DISPLAY_P3); - mPowerAdvisor.setExpensiveRenderingExpected(mId, expensiveRenderingExpected); - auto intError = mComposer.setColorMode(mId, mode, renderIntent); return static_cast<Error>(intError); } @@ -725,6 +715,11 @@ Error Display::presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests return error; } +Error Display::setDisplayBrightness(float brightness) const { + auto intError = mComposer.setDisplayBrightness(mId, brightness); + return static_cast<Error>(intError); +} + // For use by Device void Display::setConnected(bool connected) { @@ -787,8 +782,7 @@ void Display::loadConfigs() // Other Display methods -Layer* Display::getLayerById(hwc2_layer_t id) const -{ +HWC2::Layer* Display::getLayerById(hwc2_layer_t id) const { if (mLayers.count(id) == 0) { return nullptr; } @@ -799,6 +793,10 @@ Layer* Display::getLayerById(hwc2_layer_t id) const // Layer methods +Layer::~Layer() = default; + +namespace impl { + Layer::Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities, hwc2_display_t displayId, hwc2_layer_t layerId) : mComposer(composer), @@ -817,15 +815,6 @@ Layer::~Layer() ALOGE_IF(error != Error::None, "destroyLayer(%" PRIu64 ", %" PRIu64 ")" " failed: %s (%d)", mDisplayId, mId, to_string(error).c_str(), intError); - if (mLayerDestroyedListener) { - mLayerDestroyedListener(this); - } -} - -void Layer::setLayerDestroyedListener(std::function<void(Layer*)> listener) { - LOG_ALWAYS_FATAL_IF(mLayerDestroyedListener && listener, - "Attempt to set layer destroyed listener multiple times"); - mLayerDestroyedListener = listener; } Error Layer::setCursorPosition(int32_t x, int32_t y) @@ -1033,4 +1022,6 @@ Error Layer::setColorTransform(const android::mat4& matrix) { auto intError = mComposer.setLayerColorTransform(mDisplayId, mId, matrix.asArray()); return static_cast<Error>(intError); } + +} // namespace impl } // namespace HWC2 diff --git a/services/surfaceflinger/DisplayHardware/HWC2.h b/services/surfaceflinger/DisplayHardware/HWC2.h index c1f481ade9..4209e45175 100644 --- a/services/surfaceflinger/DisplayHardware/HWC2.h +++ b/services/surfaceflinger/DisplayHardware/HWC2.h @@ -37,8 +37,6 @@ #include <unordered_set> #include <vector> -#include "PowerAdvisor.h" - namespace android { struct DisplayedFrameStats; class Fence; @@ -125,7 +123,6 @@ private: std::unique_ptr<android::Hwc2::Composer> mComposer; std::unordered_set<Capability> mCapabilities; std::unordered_map<hwc2_display_t, std::unique_ptr<Display>> mDisplays; - android::Hwc2::impl::PowerAdvisor mPowerAdvisor; bool mRegisteredCallback = false; }; @@ -267,15 +264,15 @@ public: [[clang::warn_unused_result]] virtual Error presentOrValidate( uint32_t* outNumTypes, uint32_t* outNumRequests, android::sp<android::Fence>* outPresentFence, uint32_t* state) = 0; + [[clang::warn_unused_result]] virtual Error setDisplayBrightness(float brightness) const = 0; }; namespace impl { class Display : public HWC2::Display { public: - Display(android::Hwc2::Composer& composer, android::Hwc2::PowerAdvisor& advisor, - const std::unordered_set<Capability>& capabilities, hwc2_display_t id, - DisplayType type); + Display(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities, + hwc2_display_t id, DisplayType type); ~Display() override; // Required by HWC2 @@ -326,6 +323,7 @@ public: Error validate(uint32_t* outNumTypes, uint32_t* outNumRequests) override; Error presentOrValidate(uint32_t* outNumTypes, uint32_t* outNumRequests, android::sp<android::Fence>* outPresentFence, uint32_t* state) override; + Error setDisplayBrightness(float brightness) const override; // Other Display methods hwc2_display_t getId() const override { return mId; } @@ -352,7 +350,6 @@ private: // this HWC2::Display, so these references are guaranteed to be valid for // the lifetime of this object. android::Hwc2::Composer& mComposer; - android::Hwc2::PowerAdvisor& mPowerAdvisor; const std::unordered_set<Capability>& mCapabilities; hwc2_display_t mId; @@ -364,52 +361,73 @@ private: }; } // namespace impl +class Layer { +public: + virtual ~Layer(); + + virtual hwc2_layer_t getId() const = 0; + + [[clang::warn_unused_result]] virtual Error setCursorPosition(int32_t x, int32_t y) = 0; + [[clang::warn_unused_result]] virtual Error setBuffer( + uint32_t slot, const android::sp<android::GraphicBuffer>& buffer, + const android::sp<android::Fence>& acquireFence) = 0; + [[clang::warn_unused_result]] virtual Error setSurfaceDamage(const android::Region& damage) = 0; + + [[clang::warn_unused_result]] virtual Error setBlendMode(BlendMode mode) = 0; + [[clang::warn_unused_result]] virtual Error setColor(hwc_color_t color) = 0; + [[clang::warn_unused_result]] virtual Error setCompositionType(Composition type) = 0; + [[clang::warn_unused_result]] virtual Error setDataspace(android::ui::Dataspace dataspace) = 0; + [[clang::warn_unused_result]] virtual Error setPerFrameMetadata( + const int32_t supportedPerFrameMetadata, const android::HdrMetadata& metadata) = 0; + [[clang::warn_unused_result]] virtual Error setDisplayFrame(const android::Rect& frame) = 0; + [[clang::warn_unused_result]] virtual Error setPlaneAlpha(float alpha) = 0; + [[clang::warn_unused_result]] virtual Error setSidebandStream( + const native_handle_t* stream) = 0; + [[clang::warn_unused_result]] virtual Error setSourceCrop(const android::FloatRect& crop) = 0; + [[clang::warn_unused_result]] virtual Error setTransform(Transform transform) = 0; + [[clang::warn_unused_result]] virtual Error setVisibleRegion(const android::Region& region) = 0; + [[clang::warn_unused_result]] virtual Error setZOrder(uint32_t z) = 0; + [[clang::warn_unused_result]] virtual Error setInfo(uint32_t type, uint32_t appId) = 0; + + // Composer HAL 2.3 + [[clang::warn_unused_result]] virtual Error setColorTransform(const android::mat4& matrix) = 0; +}; + +namespace impl { + // Convenience C++ class to access hwc2_device_t Layer functions directly. -class Layer -{ + +class Layer : public HWC2::Layer { public: Layer(android::Hwc2::Composer& composer, const std::unordered_set<Capability>& capabilities, hwc2_display_t displayId, hwc2_layer_t layerId); - ~Layer(); - - hwc2_layer_t getId() const { return mId; } - - // Register a listener to be notified when the layer is destroyed. When the - // listener function is called, the Layer will be in the process of being - // destroyed, so it's not safe to call methods on it. - void setLayerDestroyedListener(std::function<void(Layer*)> listener); - - [[clang::warn_unused_result]] Error setCursorPosition(int32_t x, int32_t y); - [[clang::warn_unused_result]] Error setBuffer(uint32_t slot, - const android::sp<android::GraphicBuffer>& buffer, - const android::sp<android::Fence>& acquireFence); - [[clang::warn_unused_result]] Error setSurfaceDamage( - const android::Region& damage); - - [[clang::warn_unused_result]] Error setBlendMode(BlendMode mode); - [[clang::warn_unused_result]] Error setColor(hwc_color_t color); - [[clang::warn_unused_result]] Error setCompositionType(Composition type); - [[clang::warn_unused_result]] Error setDataspace( - android::ui::Dataspace dataspace); - [[clang::warn_unused_result]] Error setPerFrameMetadata( - const int32_t supportedPerFrameMetadata, - const android::HdrMetadata& metadata); - [[clang::warn_unused_result]] Error setDisplayFrame( - const android::Rect& frame); - [[clang::warn_unused_result]] Error setPlaneAlpha(float alpha); - [[clang::warn_unused_result]] Error setSidebandStream( - const native_handle_t* stream); - [[clang::warn_unused_result]] Error setSourceCrop( - const android::FloatRect& crop); - [[clang::warn_unused_result]] Error setTransform(Transform transform); - [[clang::warn_unused_result]] Error setVisibleRegion( - const android::Region& region); - [[clang::warn_unused_result]] Error setZOrder(uint32_t z); - [[clang::warn_unused_result]] Error setInfo(uint32_t type, uint32_t appId); + ~Layer() override; + + hwc2_layer_t getId() const override { return mId; } + + Error setCursorPosition(int32_t x, int32_t y) override; + Error setBuffer(uint32_t slot, const android::sp<android::GraphicBuffer>& buffer, + const android::sp<android::Fence>& acquireFence) override; + Error setSurfaceDamage(const android::Region& damage) override; + + Error setBlendMode(BlendMode mode) override; + Error setColor(hwc_color_t color) override; + Error setCompositionType(Composition type) override; + Error setDataspace(android::ui::Dataspace dataspace) override; + Error setPerFrameMetadata(const int32_t supportedPerFrameMetadata, + const android::HdrMetadata& metadata) override; + Error setDisplayFrame(const android::Rect& frame) override; + Error setPlaneAlpha(float alpha) override; + Error setSidebandStream(const native_handle_t* stream) override; + Error setSourceCrop(const android::FloatRect& crop) override; + Error setTransform(Transform transform) override; + Error setVisibleRegion(const android::Region& region) override; + Error setZOrder(uint32_t z) override; + Error setInfo(uint32_t type, uint32_t appId) override; // Composer HAL 2.3 - [[clang::warn_unused_result]] Error setColorTransform(const android::mat4& matrix); + Error setColorTransform(const android::mat4& matrix) override; private: // These are references to data owned by HWC2::Device, which will outlive @@ -422,10 +440,11 @@ private: hwc2_layer_t mId; android::ui::Dataspace mDataSpace = android::ui::Dataspace::UNKNOWN; android::HdrMetadata mHdrMetadata; - std::function<void(Layer*)> mLayerDestroyedListener; android::mat4 mColorMatrix; }; +} // namespace impl + } // namespace HWC2 #endif // ANDROID_SF_HWC2_H diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 3b9e0e6c83..1099041b4b 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -784,6 +784,19 @@ status_t HWComposer::getDisplayedContentSample(DisplayId displayId, uint64_t max return NO_ERROR; } +status_t HWComposer::setDisplayBrightness(DisplayId displayId, float brightness) { + RETURN_IF_INVALID_DISPLAY(displayId, BAD_INDEX); + const auto error = mDisplayData[displayId].hwcDisplay->setDisplayBrightness(brightness); + if (error == HWC2::Error::Unsupported) { + RETURN_IF_HWC_ERROR(error, displayId, INVALID_OPERATION); + } + if (error == HWC2::Error::BadParameter) { + RETURN_IF_HWC_ERROR(error, displayId, BAD_VALUE); + } + RETURN_IF_HWC_ERROR(error, displayId, UNKNOWN_ERROR); + return NO_ERROR; +} + bool HWComposer::isUsingVrComposer() const { return getComposer()->isUsingVrComposer(); } diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index ca59a2645a..de863b8d6c 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -140,6 +140,9 @@ public: uint64_t timestamp, DisplayedFrameStats* outStats) = 0; + // Sets the brightness of a display. + virtual status_t setDisplayBrightness(DisplayId displayId, float brightness) = 0; + // Events handling --------------------------------------------------------- // Returns stable display ID (and display name on connection of new or previously disconnected @@ -271,6 +274,7 @@ public: uint8_t componentMask, uint64_t maxFrames) override; status_t getDisplayedContentSample(DisplayId displayId, uint64_t maxFrames, uint64_t timestamp, DisplayedFrameStats* outStats) override; + status_t setDisplayBrightness(DisplayId displayId, float brightness) override; // Events handling --------------------------------------------------------- diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp index 12bbae207b..039db73928 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.cpp @@ -38,7 +38,7 @@ PowerAdvisor::~PowerAdvisor() = default; PowerAdvisor::PowerAdvisor() = default; -void PowerAdvisor::setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) { +void PowerAdvisor::setExpensiveRenderingExpected(DisplayId displayId, bool expected) { if (expected) { mExpensiveDisplays.insert(displayId); } else { diff --git a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h index 573a1a9ad3..5aa1f22b9e 100644 --- a/services/surfaceflinger/DisplayHardware/PowerAdvisor.h +++ b/services/surfaceflinger/DisplayHardware/PowerAdvisor.h @@ -22,10 +22,12 @@ #undef HWC2_INCLUDE_STRINGIFICATION #undef HWC2_USE_CPP11 +#include <unordered_set> + #include <android/hardware/power/1.3/IPower.h> #include <utils/StrongPointer.h> -#include <unordered_set> +#include "DisplayIdentification.h" namespace android { namespace Hwc2 { @@ -34,7 +36,7 @@ class PowerAdvisor { public: virtual ~PowerAdvisor(); - virtual void setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) = 0; + virtual void setExpensiveRenderingExpected(DisplayId displayId, bool expected) = 0; }; namespace impl { @@ -48,12 +50,12 @@ public: PowerAdvisor(); ~PowerAdvisor() override; - void setExpensiveRenderingExpected(hwc2_display_t displayId, bool expected) override; + void setExpensiveRenderingExpected(DisplayId displayId, bool expected) override; private: sp<V1_3::IPower> getPowerHal(); - std::unordered_set<hwc2_display_t> mExpensiveDisplays; + std::unordered_set<DisplayId> mExpensiveDisplays; bool mNotifiedExpensiveRendering = false; bool mReconnectPowerHal = false; }; diff --git a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp index 1c2853a857..613dc777ee 100644 --- a/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp +++ b/services/surfaceflinger/DisplayHardware/VirtualDisplaySurface.cpp @@ -224,8 +224,7 @@ status_t VirtualDisplaySurface::advanceFrame() { if (fbBuffer != nullptr) { uint32_t hwcSlot = 0; sp<GraphicBuffer> hwcBuffer; - mHwcBufferCache.getHwcBuffer(mFbProducerSlot, fbBuffer, - &hwcSlot, &hwcBuffer); + mHwcBufferCache.getHwcBuffer(fbBuffer, &hwcSlot, &hwcBuffer); // TODO: Correctly propagate the dataspace from GL composition result = mHwc.setClientTarget(*mDisplayId, hwcSlot, mFbFence, hwcBuffer, diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 7b8ad7129a..512a0b4f10 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> @@ -69,10 +70,7 @@ using base::StringAppendF; std::atomic<int32_t> Layer::sSequence{1}; Layer::Layer(const LayerCreationArgs& args) - : mFlinger(args.flinger), - mName(args.name), - mClientRef(args.client), - mBE{this, args.name.string()} { + : mFlinger(args.flinger), mName(args.name), mClientRef(args.client) { mCurrentCrop.makeInvalid(); uint32_t layerFlags = 0; @@ -106,6 +104,8 @@ Layer::Layer(const LayerCreationArgs& args) mCurrentState.cornerRadius = 0.0f; mCurrentState.api = -1; mCurrentState.hasColorTransform = false; + mCurrentState.colorSpaceAgnostic = false; + mCurrentState.metadata = args.metadata; // drawing state & current state are identical mDrawingState = mCurrentState; @@ -126,8 +126,6 @@ Layer::~Layer() { mFrameTracker.logAndResetStats(mName); - destroyAllHwcLayersPlusChildren(); - mFlinger->onLayerDestroyed(); } @@ -152,7 +150,7 @@ void Layer::onRemovedFromCurrentState() { strongRelative->removeZOrderRelative(this); mFlinger->setTransactionFlags(eTraversalNeeded); } - mCurrentState.zOrderRelativeOf = nullptr; + setZOrderRelativeOf(nullptr); } // Since we are no longer reachable from CurrentState SurfaceFlinger @@ -209,61 +207,6 @@ sp<IBinder> Layer::getHandle() { // h/w composer set-up // --------------------------------------------------------------------------- -bool Layer::createHwcLayer(HWComposer* hwc, const sp<DisplayDevice>& displayDevice) { - LOG_ALWAYS_FATAL_IF(!displayDevice->getId()); - auto displayId = *displayDevice->getId(); - auto outputLayer = findOutputLayerForDisplay(displayDevice); - LOG_ALWAYS_FATAL_IF(!outputLayer); - - LOG_ALWAYS_FATAL_IF(outputLayer->getState().hwc.has_value(), - "Already have a layer for display %s", - displayDevice->getDisplayName().c_str()); - - auto layer = std::shared_ptr<HWC2::Layer>( - hwc->createLayer(displayId), - [hwc, displayId](HWC2::Layer* layer) { - hwc->destroyLayer(displayId, layer); }); - if (!layer) { - return false; - } - auto& state = outputLayer->editState(); - state.hwc.emplace(layer); - return true; -} - -bool Layer::destroyHwcLayer(const sp<DisplayDevice>& displayDevice) { - auto outputLayer = findOutputLayerForDisplay(displayDevice); - if (outputLayer == nullptr) { - return false; - } - auto& state = outputLayer->editState(); - bool result = state.hwc.has_value(); - state.hwc.reset(); - return result; -} - -bool Layer::destroyHwcLayersForAllDisplays() { - bool destroyedAnyLayers = false; - - for (const auto& [token, displayDevice] : mFlinger->mDisplays) { - if (destroyHwcLayer(displayDevice)) { - destroyedAnyLayers = true; - } - } - - return destroyedAnyLayers; -} - -bool Layer::destroyAllHwcLayersPlusChildren() { - bool result = destroyHwcLayersForAllDisplays(); - - for (const sp<Layer>& child : mDrawingChildren) { - result |= child->destroyAllHwcLayersPlusChildren(); - } - - return result; -} - bool Layer::hasHwcLayer(const sp<const DisplayDevice>& displayDevice) { auto outputLayer = findOutputLayerForDisplay(displayDevice); LOG_FATAL_IF(!outputLayer); @@ -335,30 +278,53 @@ FloatRect Layer::getBounds(const Region& activeTransparentRegion) const { return reduce(mBounds, activeTransparentRegion); } -ui::Transform Layer::getTransformWithScale() const { +ui::Transform Layer::getBufferScaleTransform() const { // If the layer is not using NATIVE_WINDOW_SCALING_MODE_FREEZE (e.g. // it isFixedSize) then there may be additional scaling not accounted - // for in the transform. We need to mirror this scaling to child surfaces - // or we will break the contract where WM can treat child surfaces as - // pixels in the parent surface. + // for in the layer transform. if (!isFixedSize() || !mActiveBuffer) { - return mEffectiveTransform; + return {}; } - int bufferWidth; - int bufferHeight; - if ((mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) == 0) { - bufferWidth = mActiveBuffer->getWidth(); - bufferHeight = mActiveBuffer->getHeight(); - } else { - bufferHeight = mActiveBuffer->getWidth(); - bufferWidth = mActiveBuffer->getHeight(); + // 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 {}; } - float sx = getActiveWidth(getDrawingState()) / static_cast<float>(bufferWidth); - float sy = getActiveHeight(getDrawingState()) / static_cast<float>(bufferHeight); + + int bufferWidth = mActiveBuffer->getWidth(); + int bufferHeight = mActiveBuffer->getHeight(); + + if (mCurrentTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { + std::swap(bufferWidth, 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; + return extraParentScaling; +} + +ui::Transform Layer::getTransformWithScale(const ui::Transform& bufferScaleTransform) const { + // We need to mirror this scaling to child surfaces or we will break the contract where WM can + // treat child surfaces as pixels in the parent surface. + if (!isFixedSize() || !mActiveBuffer) { + return mEffectiveTransform; + } + return mEffectiveTransform * bufferScaleTransform; +} + +FloatRect Layer::getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const { + // We need the pre scaled layer bounds when computing child bounds to make sure the child is + // cropped to its parent layer after any buffer transform scaling is applied. + if (!isFixedSize() || !mActiveBuffer) { + return mBounds; + } + return bufferScaleTransform.inverse().transform(mBounds); } void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) { @@ -370,7 +336,7 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) // Transform parent bounds to layer space parentBounds = getActiveTransform(s).inverse().transform(parentBounds); - // Calculate display frame + // Calculate source bounds mSourceBounds = computeSourceBounds(parentBounds); // Calculate bounds by croping diplay frame with layer crop and parent bounds @@ -383,13 +349,15 @@ void Layer::computeBounds(FloatRect parentBounds, ui::Transform parentTransform) mBounds = bounds; mScreenBounds = mEffectiveTransform.transform(mBounds); + + // Add any buffer scaling to the layer's children. + ui::Transform bufferScaleTransform = getBufferScaleTransform(); for (const sp<Layer>& child : mDrawingChildren) { - child->computeBounds(mBounds, getTransformWithScale()); + child->computeBounds(getBoundsPreScaling(bufferScaleTransform), + getTransformWithScale(bufferScaleTransform)); } } - - Rect Layer::getCroppedBufferSize(const State& s) const { Rect size = getBufferSize(s); Rect crop = getCrop(s); @@ -401,36 +369,6 @@ Rect Layer::getCroppedBufferSize(const State& s) const { return size; } -Rect Layer::computeInitialCrop(const sp<const DisplayDevice>& display) const { - // the crop is the area of the window that gets cropped, but not - // scaled in any ways. - const State& s(getDrawingState()); - - // apply the projection's clipping to the window crop in - // layerstack space, and convert-back to layer space. - // if there are no window scaling involved, this operation will map to full - // pixels in the buffer. - - FloatRect activeCropFloat = getBounds(); - ui::Transform t = getTransform(); - // Transform to screen space. - activeCropFloat = t.transform(activeCropFloat); - activeCropFloat = activeCropFloat.intersect(display->getViewport().toFloatRect()); - // Back to layer space to work with the content crop. - activeCropFloat = t.inverse().transform(activeCropFloat); - // This needs to be here as transform.transform(Rect) computes the - // transformed rect and then takes the bounding box of the result before - // returning. This means - // transform.inverse().transform(transform.transform(Rect)) != Rect - // in which case we need to make sure the final rect is clipped to the - // display bounds. - Rect activeCrop{activeCropFloat}; - if (!activeCrop.intersect(getBufferSize(s), &activeCrop)) { - activeCrop.clear(); - } - return activeCrop; -} - void Layer::setupRoundedCornersCropCoordinates(Rect win, const FloatRect& roundedCornersCrop) const { // Translate win by the rounded corners rect coordinates, to have all values in @@ -448,182 +386,17 @@ void Layer::setupRoundedCornersCropCoordinates(Rect win, cropCoords[3] = vec2(win.right, win.top); } -FloatRect Layer::computeCrop(const sp<const DisplayDevice>& display) const { - // the content crop is the area of the content that gets scaled to the - // layer's size. This is in buffer space. - FloatRect crop = getContentCrop().toFloatRect(); - - // In addition there is a WM-specified crop we pull from our drawing state. - const State& s(getDrawingState()); - - Rect activeCrop = computeInitialCrop(display); - Rect bufferSize = getBufferSize(s); - - // Transform the window crop to match the buffer coordinate system, - // which means using the inverse of the current transform set on the - // SurfaceFlingerConsumer. - uint32_t invTransform = mCurrentTransform; - if (getTransformToDisplayInverse()) { - /* - * the code below applies the primary display's inverse transform to the - * buffer - */ - uint32_t invTransformOrient = DisplayDevice::getPrimaryDisplayOrientationTransform(); - // calculate the inverse transform - if (invTransformOrient & NATIVE_WINDOW_TRANSFORM_ROT_90) { - invTransformOrient ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } - // and apply to the current transform - invTransform = (ui::Transform(invTransformOrient) * - ui::Transform(invTransform)).getOrientation(); - } - - int winWidth = bufferSize.getWidth(); - int winHeight = bufferSize.getHeight(); - if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - // If the activeCrop has been rotate the ends are rotated but not - // the space itself so when transforming ends back we can't rely on - // a modification of the axes of rotation. To account for this we - // need to reorient the inverse rotation in terms of the current - // axes of rotation. - bool is_h_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_H) != 0; - bool is_v_flipped = (invTransform & NATIVE_WINDOW_TRANSFORM_FLIP_V) != 0; - if (is_h_flipped == is_v_flipped) { - invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } - std::swap(winWidth, winHeight); - } - const Rect winCrop = - activeCrop.transform(invTransform, bufferSize.getWidth(), bufferSize.getHeight()); - - // below, crop is intersected with winCrop expressed in crop's coordinate space - float xScale = crop.getWidth() / float(winWidth); - float yScale = crop.getHeight() / float(winHeight); - - float insetL = winCrop.left * xScale; - float insetT = winCrop.top * yScale; - float insetR = (winWidth - winCrop.right) * xScale; - float insetB = (winHeight - winCrop.bottom) * yScale; - - crop.left += insetL; - crop.top += insetT; - crop.right -= insetR; - crop.bottom -= insetB; - - return crop; -} - -void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { - const auto outputLayer = findOutputLayerForDisplay(display); - LOG_FATAL_IF(!outputLayer); - LOG_FATAL_IF(!outputLayer->getState().hwc); - auto& hwcLayer = (*outputLayer->getState().hwc).hwcLayer; - - if (!hasHwcLayer(display)) { - ALOGE("[%s] failed to setGeometry: no HWC layer found (%s)", mName.string(), - display->getDebugName().c_str()); - return; - } - - LOG_FATAL_IF(!getCompositionLayer()); - auto& commonCompositionState = getCompositionLayer()->editState().frontEnd; - auto& compositionState = outputLayer->editState(); - - // enable this layer - compositionState.forceClientComposition = false; - - if (isSecure() && !display->isSecure()) { - compositionState.forceClientComposition = true; - } - - // this gives us only the "orientation" component of the transform - const State& s(getDrawingState()); - const Rect bufferSize = getBufferSize(s); +void Layer::latchGeometry(compositionengine::LayerFECompositionState& compositionState) const { + const auto& drawingState{getDrawingState()}; + auto alpha = static_cast<float>(getAlpha()); auto blendMode = HWC2::BlendMode::None; - if (!isOpaque(s) || getAlpha() != 1.0f) { + if (!isOpaque(drawingState) || alpha != 1.0f) { blendMode = mPremultipliedAlpha ? HWC2::BlendMode::Premultiplied : HWC2::BlendMode::Coverage; } - auto error = hwcLayer->setBlendMode(blendMode); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set blend mode %s:" - " %s (%d)", - mName.string(), to_string(blendMode).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - commonCompositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); - - // apply the layer's transform, followed by the display's global transform - // here we're guaranteed that the layer's transform preserves rects - Region activeTransparentRegion(getActiveTransparentRegion(s)); - ui::Transform t = getTransform(); - Rect activeCrop = getCrop(s); - if (!activeCrop.isEmpty() && bufferSize.isValid()) { - activeCrop = t.transform(activeCrop); - if (!activeCrop.intersect(display->getViewport(), &activeCrop)) { - activeCrop.clear(); - } - activeCrop = t.inverse().transform(activeCrop, true); - // This needs to be here as transform.transform(Rect) computes the - // transformed rect and then takes the bounding box of the result before - // returning. This means - // transform.inverse().transform(transform.transform(Rect)) != Rect - // in which case we need to make sure the final rect is clipped to the - // display bounds. - if (!activeCrop.intersect(bufferSize, &activeCrop)) { - activeCrop.clear(); - } - // mark regions outside the crop as transparent - activeTransparentRegion.orSelf(Rect(0, 0, bufferSize.getWidth(), activeCrop.top)); - activeTransparentRegion.orSelf( - Rect(0, activeCrop.bottom, bufferSize.getWidth(), bufferSize.getHeight())); - activeTransparentRegion.orSelf(Rect(0, activeCrop.top, activeCrop.left, activeCrop.bottom)); - activeTransparentRegion.orSelf( - Rect(activeCrop.right, activeCrop.top, bufferSize.getWidth(), activeCrop.bottom)); - } - - // getBounds returns a FloatRect to provide more accuracy during the - // transformation. We then round upon constructing 'frame'. - Rect frame{t.transform(getBounds(activeTransparentRegion))}; - if (!frame.intersect(display->getViewport(), &frame)) { - frame.clear(); - } - const ui::Transform& tr = display->getTransform(); - Rect transformedFrame = tr.transform(frame); - error = hwcLayer->setDisplayFrame(transformedFrame); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set display frame [%d, %d, %d, %d]: %s (%d)", mName.string(), - transformedFrame.left, transformedFrame.top, transformedFrame.right, - transformedFrame.bottom, to_string(error).c_str(), static_cast<int32_t>(error)); - } else { - compositionState.displayFrame = transformedFrame; - } - - FloatRect sourceCrop = computeCrop(display); - error = hwcLayer->setSourceCrop(sourceCrop); - if (error != HWC2::Error::None) { - ALOGE("[%s] Failed to set source crop [%.3f, %.3f, %.3f, %.3f]: " - "%s (%d)", - mName.string(), sourceCrop.left, sourceCrop.top, sourceCrop.right, sourceCrop.bottom, - to_string(error).c_str(), static_cast<int32_t>(error)); - } else { - compositionState.sourceCrop = sourceCrop; - } - float alpha = static_cast<float>(getAlpha()); - error = hwcLayer->setPlaneAlpha(alpha); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set plane alpha %.3f: " - "%s (%d)", - mName.string(), alpha, to_string(error).c_str(), static_cast<int32_t>(error)); - commonCompositionState.alpha = alpha; - - error = hwcLayer->setZOrder(z); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set Z %u: %s (%d)", mName.string(), z, - to_string(error).c_str(), static_cast<int32_t>(error)); - compositionState.z = z; - - int type = s.metadata.getInt32(METADATA_WINDOW_TYPE, 0); - int appId = s.metadata.getInt32(METADATA_OWNER_UID, 0); + int type = drawingState.metadata.getInt32(METADATA_WINDOW_TYPE, 0); + int appId = drawingState.metadata.getInt32(METADATA_OWNER_UID, 0); sp<Layer> parent = mDrawingParent.promote(); if (parent.get()) { auto& parentState = parent->getDrawingState(); @@ -635,62 +408,35 @@ void Layer::setGeometry(const sp<const DisplayDevice>& display, uint32_t z) { } } - error = hwcLayer->setInfo(type, appId); - ALOGE_IF(error != HWC2::Error::None, "[%s] Failed to set info (%d)", mName.string(), - static_cast<int32_t>(error)); + compositionState.geomLayerTransform = getTransform(); + compositionState.geomInverseLayerTransform = compositionState.geomLayerTransform.inverse(); + compositionState.geomBufferSize = getBufferSize(drawingState); + compositionState.geomContentCrop = getContentCrop(); + compositionState.geomCrop = getCrop(drawingState); + compositionState.geomBufferTransform = mCurrentTransform; + compositionState.geomBufferUsesDisplayInverseTransform = getTransformToDisplayInverse(); + compositionState.geomActiveTransparentRegion = getActiveTransparentRegion(drawingState); + compositionState.geomLayerBounds = mBounds; + compositionState.geomUsesSourceCrop = usesSourceCrop(); + compositionState.isSecure = isSecure(); - commonCompositionState.type = type; - commonCompositionState.appId = appId; - - /* - * Transformations are applied in this order: - * 1) buffer orientation/flip/mirror - * 2) state transformation (window manager) - * 3) layer orientation (screen orientation) - * (NOTE: the matrices are multiplied in reverse order) - */ - - const ui::Transform bufferOrientation(mCurrentTransform); - ui::Transform transform(tr * t * bufferOrientation); - - if (getTransformToDisplayInverse()) { - /* - * the code below applies the primary display's inverse transform to the - * buffer - */ - uint32_t invTransform = DisplayDevice::getPrimaryDisplayOrientationTransform(); - // calculate the inverse transform - if (invTransform & NATIVE_WINDOW_TRANSFORM_ROT_90) { - invTransform ^= NATIVE_WINDOW_TRANSFORM_FLIP_V | NATIVE_WINDOW_TRANSFORM_FLIP_H; - } + compositionState.blendMode = static_cast<Hwc2::IComposerClient::BlendMode>(blendMode); + compositionState.alpha = alpha; + compositionState.type = type; + compositionState.appId = appId; +} - /* - * Here we cancel out the orientation component of the WM transform. - * The scaling and translate components are already included in our bounds - * computation so it's enough to just omit it in the composition. - * See comment in BufferLayer::prepareClientLayer with ref to b/36727915 for why. - */ - transform = ui::Transform(invTransform) * tr * bufferOrientation; - } - - // this gives us only the "orientation" component of the transform - const uint32_t orientation = transform.getOrientation(); - if (orientation & ui::Transform::ROT_INVALID) { - // we can only handle simple transformation - compositionState.forceClientComposition = true; - (*compositionState.hwc).hwcCompositionType = Hwc2::IComposerClient::Composition::CLIENT; - } else { - auto transform = static_cast<HWC2::Transform>(orientation); - auto error = hwcLayer->setTransform(transform); - ALOGE_IF(error != HWC2::Error::None, - "[%s] Failed to set transform %s: " - "%s (%d)", - mName.string(), to_string(transform).c_str(), to_string(error).c_str(), - static_cast<int32_t>(error)); - compositionState.bufferTransform = static_cast<Hwc2::Transform>(transform); +void Layer::latchCompositionState(compositionengine::LayerFECompositionState& compositionState, + bool includeGeometry) const { + if (includeGeometry) { + latchGeometry(compositionState); } } +const char* Layer::getDebugName() const { + return mName.string(); +} + void Layer::forceClientComposition(const sp<DisplayDevice>& display) { const auto outputLayer = findOutputLayerForDisplay(display); LOG_FATAL_IF(!outputLayer); @@ -740,18 +486,21 @@ void Layer::updateCursorPosition(const sp<const DisplayDevice>& display) { // --------------------------------------------------------------------------- bool Layer::prepareClientLayer(const RenderArea& renderArea, const Region& clip, - Region& clearRegion, renderengine::LayerSettings& layer) { - return prepareClientLayer(renderArea, clip, false, clearRegion, layer); + Region& clearRegion, const bool supportProtectedContent, + renderengine::LayerSettings& layer) { + return prepareClientLayer(renderArea, clip, false, clearRegion, supportProtectedContent, layer); } bool Layer::prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform, - Region& clearRegion, renderengine::LayerSettings& layer) { + Region& clearRegion, const bool supportProtectedContent, + renderengine::LayerSettings& layer) { return prepareClientLayer(renderArea, Region(renderArea.getBounds()), useIdentityTransform, - clearRegion, layer); + clearRegion, supportProtectedContent, layer); } bool Layer::prepareClientLayer(const RenderArea& /*renderArea*/, const Region& /*clip*/, bool useIdentityTransform, Region& /*clearRegion*/, + const bool /*supportProtectedContent*/, renderengine::LayerSettings& layer) { FloatRect bounds = getBounds(); half alpha = getAlpha(); @@ -1128,13 +877,6 @@ uint32_t Layer::doTransaction(uint32_t flags) { mNeedsFiltering = (!getActiveTransform(c).preserveRects() || type >= ui::Transform::SCALE); } - // If the layer is hidden, signal and clear out all local sync points so - // that transactions for layers depending on this layer's frames becoming - // visible are not blocked - if (c.flags & layer_state_t::eLayerHidden) { - clearSyncPoints(); - } - if (mCurrentState.inputInfoChanged) { flags |= eInputInfoChanged; mCurrentState.inputInfoChanged = false; @@ -1221,7 +963,7 @@ bool Layer::setLayer(int32_t z) { if (strongRelative != nullptr) { strongRelative->removeZOrderRelative(this); } - mCurrentState.zOrderRelativeOf = nullptr; + setZOrderRelativeOf(nullptr); } setTransactionFlags(eTransactionNeeded); return true; @@ -1241,6 +983,13 @@ void Layer::addZOrderRelative(const wp<Layer>& relative) { setTransactionFlags(eTransactionNeeded); } +void Layer::setZOrderRelativeOf(const wp<Layer>& relativeOf) { + mCurrentState.zOrderRelativeOf = relativeOf; + mCurrentState.sequence++; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); +} + bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relativeZ) { sp<Handle> handle = static_cast<Handle*>(relativeToHandle.get()); if (handle == nullptr) { @@ -1264,7 +1013,7 @@ bool Layer::setRelativeLayer(const sp<IBinder>& relativeToHandle, int32_t relati if (oldZOrderRelativeOf != nullptr) { oldZOrderRelativeOf->removeZOrderRelative(this); } - mCurrentState.zOrderRelativeOf = relative; + setZOrderRelativeOf(relative); relative->addZOrderRelative(this); setTransactionFlags(eTransactionNeeded); @@ -1306,8 +1055,8 @@ bool Layer::setBackgroundColor(const half3& color, float alpha, ui::Dataspace da // create background color layer if one does not yet exist uint32_t flags = ISurfaceComposerClient::eFXSurfaceColor; const String8& name = mName + "BackgroundColorLayer"; - mCurrentState.bgColorLayer = - new ColorLayer(LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags)); + mCurrentState.bgColorLayer = new ColorLayer( + LayerCreationArgs(mFlinger.get(), nullptr, name, 0, 0, flags, LayerMetadata())); // add to child list addChild(mCurrentState.bgColorLayer); @@ -1395,10 +1144,8 @@ bool Layer::setOverrideScalingMode(int32_t scalingMode) { return true; } -bool Layer::setMetadata(LayerMetadata data) { - bool changed = data.mMap != mCurrentState.metadata.mMap; - if (!changed) return false; - mCurrentState.metadata = std::move(data); +bool Layer::setMetadata(const LayerMetadata& data) { + if (!mCurrentState.metadata.merge(data, true /* eraseEmpty */)) return false; mCurrentState.sequence++; mCurrentState.modified = true; setTransactionFlags(eTransactionNeeded); @@ -1414,6 +1161,17 @@ bool Layer::setLayerStack(uint32_t layerStack) { return true; } +bool Layer::setColorSpaceAgnostic(const bool agnostic) { + if (mCurrentState.colorSpaceAgnostic == agnostic) { + return false; + } + mCurrentState.sequence++; + mCurrentState.colorSpaceAgnostic = agnostic; + mCurrentState.modified = true; + setTransactionFlags(eTransactionNeeded); + return true; +} + uint32_t Layer::getLayerStack() const { auto p = mDrawingParent.promote(); if (p == nullptr) { @@ -1696,7 +1454,9 @@ bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) { void Layer::setChildrenDrawingParent(const sp<Layer>& newParent) { for (const sp<Layer>& child : mDrawingChildren) { child->mDrawingParent = newParent; - child->computeBounds(newParent->mBounds, newParent->getTransformWithScale()); + child->computeBounds(newParent->mBounds, + newParent->getTransformWithScale( + newParent->getBufferScaleTransform())); } } @@ -1823,18 +1583,6 @@ void Layer::setParent(const sp<Layer>& layer) { mCurrentParent = layer; } -void Layer::clearSyncPoints() { - for (const auto& child : mCurrentChildren) { - child->clearSyncPoints(); - } - - Mutex::Autolock lock(mLocalSyncPointMutex); - for (auto& point : mLocalSyncPoints) { - point->setFrameAvailable(); - } - mLocalSyncPoints.clear(); -} - int32_t Layer::getZ() const { return mDrawingState.z; } @@ -2068,8 +1816,24 @@ void Layer::commitChildList() { mDrawingParent = mCurrentParent; } +static wp<Layer> extractLayerFromBinder(const wp<IBinder>& weakBinderHandle) { + if (weakBinderHandle == nullptr) { + return nullptr; + } + sp<IBinder> binderHandle = weakBinderHandle.promote(); + if (binderHandle == nullptr) { + return nullptr; + } + sp<Layer::Handle> handle = static_cast<Layer::Handle*>(binderHandle.get()); + if (handle == nullptr) { + return nullptr; + } + return handle->owner; +} + void Layer::setInputInfo(const InputWindowInfo& info) { mCurrentState.inputInfo = info; + mCurrentState.touchableRegionCrop = extractLayerFromBinder(info.touchableRegionCropHandle); mCurrentState.modified = true; mCurrentState.inputInfoChanged = true; setTransactionFlags(eTransactionNeeded); @@ -2099,26 +1863,26 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) } LayerProtoHelper::writeToProto(state.activeTransparentRegion_legacy, - layerInfo->mutable_transparent_region()); - LayerProtoHelper::writeToProto(visibleRegion, layerInfo->mutable_visible_region()); - LayerProtoHelper::writeToProto(surfaceDamageRegion, layerInfo->mutable_damage_region()); + [&]() { return layerInfo->mutable_transparent_region(); }); + LayerProtoHelper::writeToProto(visibleRegion, + [&]() { return layerInfo->mutable_visible_region(); }); + LayerProtoHelper::writeToProto(surfaceDamageRegion, + [&]() { return layerInfo->mutable_damage_region(); }); layerInfo->set_layer_stack(getLayerStack()); layerInfo->set_z(state.z); - PositionProto* position = layerInfo->mutable_position(); - position->set_x(transform.tx()); - position->set_y(transform.ty()); + LayerProtoHelper::writePositionToProto(transform.tx(), transform.ty(), + [&]() { return layerInfo->mutable_position(); }); - PositionProto* requestedPosition = layerInfo->mutable_requested_position(); - requestedPosition->set_x(requestedTransform.tx()); - requestedPosition->set_y(requestedTransform.ty()); + LayerProtoHelper::writePositionToProto(requestedTransform.tx(), requestedTransform.ty(), [&]() { + return layerInfo->mutable_requested_position(); + }); - SizeProto* size = layerInfo->mutable_size(); - size->set_w(state.active_legacy.w); - size->set_h(state.active_legacy.h); + LayerProtoHelper::writeSizeToProto(state.active_legacy.w, state.active_legacy.h, + [&]() { return layerInfo->mutable_size(); }); - LayerProtoHelper::writeToProto(state.crop_legacy, layerInfo->mutable_crop()); + LayerProtoHelper::writeToProto(state.crop_legacy, [&]() { return layerInfo->mutable_crop(); }); layerInfo->set_corner_radius(getRoundedCornerState().radius); layerInfo->set_is_opaque(isOpaque(state)); @@ -2128,8 +1892,9 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) layerInfo->set_dataspace(dataspaceDetails(static_cast<android_dataspace>(mCurrentDataSpace))); layerInfo->set_pixel_format(decodePixelFormat(getPixelFormat())); - LayerProtoHelper::writeToProto(getColor(), layerInfo->mutable_color()); - LayerProtoHelper::writeToProto(state.color, layerInfo->mutable_requested_color()); + LayerProtoHelper::writeToProto(getColor(), [&]() { return layerInfo->mutable_color(); }); + LayerProtoHelper::writeToProto(state.color, + [&]() { return layerInfo->mutable_requested_color(); }); layerInfo->set_flags(state.flags); LayerProtoHelper::writeToProto(transform, layerInfo->mutable_transform()); @@ -2138,16 +1903,21 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) auto parent = useDrawing ? mDrawingParent.promote() : mCurrentParent.promote(); if (parent != nullptr) { layerInfo->set_parent(parent->sequence); + } else { + layerInfo->set_parent(-1); } auto zOrderRelativeOf = state.zOrderRelativeOf.promote(); if (zOrderRelativeOf != nullptr) { layerInfo->set_z_order_relative_of(zOrderRelativeOf->sequence); + } else { + layerInfo->set_z_order_relative_of(-1); } auto buffer = mActiveBuffer; if (buffer != nullptr) { - LayerProtoHelper::writeToProto(buffer, layerInfo->mutable_active_buffer()); + LayerProtoHelper::writeToProto(buffer, + [&]() { return layerInfo->mutable_active_buffer(); }); LayerProtoHelper::writeToProto(ui::Transform(mCurrentTransform), layerInfo->mutable_buffer_transform()); } @@ -2171,9 +1941,11 @@ void Layer::writeToProto(LayerProto* layerInfo, LayerVector::StateSet stateSet) (*protoMap)[entry.first] = std::string(entry.second.cbegin(), entry.second.cend()); } LayerProtoHelper::writeToProto(mEffectiveTransform, layerInfo->mutable_effective_transform()); - LayerProtoHelper::writeToProto(mSourceBounds, layerInfo->mutable_source_bounds()); - LayerProtoHelper::writeToProto(mScreenBounds, layerInfo->mutable_screen_bounds()); - LayerProtoHelper::writeToProto(mBounds, layerInfo->mutable_bounds()); + LayerProtoHelper::writeToProto(mSourceBounds, + [&]() { return layerInfo->mutable_source_bounds(); }); + LayerProtoHelper::writeToProto(mScreenBounds, + [&]() { return layerInfo->mutable_screen_bounds(); }); + LayerProtoHelper::writeToProto(mBounds, [&]() { return layerInfo->mutable_bounds(); }); } void Layer::writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& displayDevice) { @@ -2187,10 +1959,10 @@ void Layer::writeToProto(LayerProto* layerInfo, const sp<DisplayDevice>& display const auto& compositionState = outputLayer->getState(); const Rect& frame = compositionState.displayFrame; - LayerProtoHelper::writeToProto(frame, layerInfo->mutable_hwc_frame()); + LayerProtoHelper::writeToProto(frame, [&]() { return layerInfo->mutable_hwc_frame(); }); const FloatRect& crop = compositionState.sourceCrop; - LayerProtoHelper::writeToProto(crop, layerInfo->mutable_hwc_crop()); + LayerProtoHelper::writeToProto(crop, [&]() { return layerInfo->mutable_hwc_crop(); }); const int32_t transform = getCompositionLayer() ? static_cast<int32_t>(compositionState.bufferTransform) : 0; @@ -2250,6 +2022,18 @@ InputWindowInfo Layer::fillInputInfo() { // bounds. info.touchableRegion = info.touchableRegion.translate(info.frameLeft, info.frameTop); info.visible = canReceiveInput(); + + auto cropLayer = mDrawingState.touchableRegionCrop.promote(); + if (info.replaceTouchableRegionWithCrop) { + if (cropLayer == nullptr) { + info.touchableRegion = Region(Rect{mScreenBounds}); + } else { + info.touchableRegion = Region(Rect{cropLayer->mScreenBounds}); + } + } else if (cropLayer != nullptr) { + info.touchableRegion = info.touchableRegion.intersect(Rect{cropLayer->mScreenBounds}); + } + return info; } diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index 8ae057f804..89063da251 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -45,7 +45,6 @@ #include "Client.h" #include "FrameTracker.h" -#include "LayerBE.h" #include "LayerVector.h" #include "MonitoredProducer.h" #include "SurfaceFlinger.h" @@ -67,11 +66,11 @@ class DisplayDevice; class GraphicBuffer; class SurfaceFlinger; class LayerDebugInfo; -class LayerBE; namespace compositionengine { class Layer; class OutputLayer; +struct LayerFECompositionState; } namespace impl { @@ -82,8 +81,9 @@ class SurfaceInterceptor; struct LayerCreationArgs { LayerCreationArgs(SurfaceFlinger* flinger, const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags) - : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags) {} + uint32_t w, uint32_t h, uint32_t flags, LayerMetadata metadata) + : flinger(flinger), client(client), name(name), w(w), h(h), flags(flags), + metadata(std::move(metadata)) {} SurfaceFlinger* flinger; const sp<Client>& client; @@ -91,15 +91,13 @@ struct LayerCreationArgs { uint32_t w; uint32_t h; uint32_t flags; + LayerMetadata metadata; }; class Layer : public virtual compositionengine::LayerFE { static std::atomic<int32_t> sSequence; public: - friend class LayerBE; - LayerBE& getBE() { return mBE; } - LayerBE& getBE() const { return mBE; } mutable bool contentDirty{false}; // regions below are in window-manager space Region visibleRegion; @@ -186,6 +184,10 @@ public: bool inputInfoChanged; 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; @@ -198,7 +200,6 @@ public: sp<GraphicBuffer> buffer; sp<Fence> acquireFence; - ui::Dataspace dataspace; HdrMetadata hdrMetadata; Region surfaceDamageRegion; int32_t api; @@ -215,6 +216,7 @@ public: // The deque of callback handles for this frame. The back of the deque contains the most // recent callback handle. std::deque<sp<CallbackHandle>> callbackHandles; + bool colorSpaceAgnostic; }; explicit Layer(const LayerCreationArgs& args); @@ -292,7 +294,7 @@ public: uint64_t frameNumber); virtual void deferTransactionUntil_legacy(const sp<Layer>& barrierLayer, uint64_t frameNumber); virtual bool setOverrideScalingMode(int32_t overrideScalingMode); - virtual bool setMetadata(LayerMetadata data); + virtual bool setMetadata(const LayerMetadata& data); virtual bool reparentChildren(const sp<IBinder>& layer); virtual void setChildrenDrawingParent(const sp<Layer>& layer); virtual bool reparent(const sp<IBinder>& newParentHandle); @@ -302,6 +304,7 @@ public: virtual bool setColorTransform(const mat4& matrix); virtual mat4 getColorTransform() const; virtual bool hasColorTransform() const; + virtual bool isColorSpaceAgnostic() const { return mDrawingState.colorSpaceAgnostic; } // Used only to set BufferStateLayer state virtual bool setTransform(uint32_t /*transform*/) { return false; }; @@ -320,6 +323,7 @@ public: return false; }; virtual bool setBackgroundColor(const half3& color, float alpha, ui::Dataspace dataspace); + virtual bool setColorSpaceAgnostic(const bool agnostic); ui::Dataspace getDataSpace() const { return mCurrentDataSpace; } @@ -357,9 +361,15 @@ public: // Compute bounds for the layer and cache the results. void computeBounds(FloatRect parentBounds, ui::Transform parentTransform); + // Returns the buffer scale transform if a scaling mode is set. + ui::Transform getBufferScaleTransform() const; + // Get effective layer transform, taking into account all its parent transform with any // scaling if the parent scaling more is not NATIVE_WINDOW_SCALING_MODE_FREEZE. - ui::Transform getTransformWithScale() const; + ui::Transform getTransformWithScale(const ui::Transform& bufferScaleTransform) const; + + // Returns the bounds of the layer without any buffer scaling. + FloatRect getBoundsPreScaling(const ui::Transform& bufferScaleTransform) const; int32_t getSequence() const { return sequence; } @@ -411,6 +421,11 @@ public: */ virtual bool isFixedSize() const { return true; } + /* + * usesSourceCrop - true if content should use a source crop + */ + virtual bool usesSourceCrop() const { return false; } + // Most layers aren't created from the main thread, and therefore need to // grab the SF state lock to access HWC, but ContainerLayer does, so we need // to avoid grabbing the lock again to avoid deadlock @@ -434,22 +449,38 @@ public: } virtual Rect getCrop(const Layer::State& s) const { return s.crop_legacy; } + virtual void setPostTime(nsecs_t /*postTime*/) {} + virtual void setDesiredPresentTime(nsecs_t /*desiredPresentTime*/) {} + protected: virtual bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, bool useIdentityTransform, Region& clearRegion, - renderengine::LayerSettings& layer) = 0; + const bool supportProtectedContent, + renderengine::LayerSettings& layer); + +public: + /* + * compositionengine::LayerFE overrides + */ + void latchCompositionState(compositionengine::LayerFECompositionState&, + bool includeGeometry) const override; + void onLayerDisplayed(const sp<Fence>& releaseFence) override; + const char* getDebugName() const override; + +protected: + void latchGeometry(compositionengine::LayerFECompositionState& outState) const; public: virtual void setDefaultBufferSize(uint32_t /*w*/, uint32_t /*h*/) {} virtual bool isHdrY410() const { return false; } - void setGeometry(const sp<const DisplayDevice>& display, uint32_t z); void forceClientComposition(const sp<DisplayDevice>& display); bool getForceClientComposition(const sp<DisplayDevice>& display); virtual void setPerFrameData(const sp<const DisplayDevice>& display, const ui::Transform& transform, const Rect& viewport, - int32_t supportedPerFrameMetadata) = 0; + int32_t supportedPerFrameMetadata, + const ui::Dataspace targetDataspace) = 0; // callIntoHwc exists so we can update our local state and call // acceptDisplayChanges without unnecessarily updating the device's state @@ -460,11 +491,6 @@ public: bool getClearClientTarget(const sp<const DisplayDevice>& display) const; void updateCursorPosition(const sp<const DisplayDevice>& display); - /* - * called after page-flip - */ - virtual void onLayerDisplayed(const sp<Fence>& releaseFence); - virtual bool shouldPresentNow(nsecs_t /*expectedPresentTime*/) const { return false; } virtual void setTransformHint(uint32_t /*orientation*/) const { } @@ -494,9 +520,10 @@ public: * false otherwise. */ bool prepareClientLayer(const RenderArea& renderArea, const Region& clip, Region& clearRegion, - renderengine::LayerSettings& layer); + const bool supportProtectedContent, renderengine::LayerSettings& layer); bool prepareClientLayer(const RenderArea& renderArea, bool useIdentityTransform, - Region& clearRegion, renderengine::LayerSettings& layer); + Region& clearRegion, const bool supportProtectedContent, + renderengine::LayerSettings& layer); /* * doTransaction - process the transaction. This is a good place to figure @@ -534,8 +561,7 @@ public: * operation, so this should be set only if needed). Typically this is used * to figure out if the content or size of a surface has changed. */ - virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/, - const sp<Fence>& /*releaseFence*/) { + virtual bool latchBuffer(bool& /*recomputeVisibleRegions*/, nsecs_t /*latchTime*/) { return {}; } @@ -571,11 +597,6 @@ public: // ----------------------------------------------------------------------- - bool createHwcLayer(HWComposer* hwc, const sp<DisplayDevice>& display); - bool destroyHwcLayer(const sp<DisplayDevice>& display); - bool destroyHwcLayersForAllDisplays(); - bool destroyAllHwcLayersPlusChildren(); - bool hasHwcLayer(const sp<const DisplayDevice>& displayDevice); HWC2::Layer* getHwcLayer(const sp<const DisplayDevice>& displayDevice); @@ -696,16 +717,10 @@ protected: // For unit tests friend class TestableSurfaceFlinger; - void commitTransaction(const State& stateToCommit); + virtual void commitTransaction(const State& stateToCommit); uint32_t getEffectiveUsage(uint32_t usage) const; - virtual FloatRect computeCrop(const sp<const DisplayDevice>& display) const; - // Compute the initial crop as specified by parent layers and the - // SurfaceControl for this layer. Does not include buffer crop from the - // IGraphicBufferProducer client, as that should not affect child clipping. - // Returns in screen space. - Rect computeInitialCrop(const sp<const DisplayDevice>& display) const; /** * Setup rounded corners coordinates of this layer, taking into account the layer bounds and * crop coordinates, transforming them into layer space. @@ -759,8 +774,6 @@ protected: virtual bool applyPendingStates(State* stateToCommit); virtual uint32_t doTransactionResize(uint32_t flags, Layer::State* stateToCommit); - void clearSyncPoints(); - // Returns mCurrentScaling mode (originating from the // Client) or mOverrideScalingMode mode (originating from // the Surface Controller) if set. @@ -866,8 +879,6 @@ protected: wp<Layer> mCurrentParent; wp<Layer> mDrawingParent; - mutable LayerBE mBE; - // Can only be accessed with the SF state lock held. bool mLayerDetached{false}; // Can only be accessed with the SF state lock held. @@ -910,6 +921,8 @@ private: // Layer bounds in screen space. FloatRect mScreenBounds; + + void setZOrderRelativeOf(const wp<Layer>& relativeOf); }; } // namespace android diff --git a/services/surfaceflinger/LayerBE.cpp b/services/surfaceflinger/LayerBE.cpp deleted file mode 100644 index 9f634408c7..0000000000 --- a/services/surfaceflinger/LayerBE.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_NDEBUG 0 -#undef LOG_TAG -#define LOG_TAG "LayerBE" -#define ATRACE_TAG ATRACE_TAG_GRAPHICS - -#include "Layer.h" - -namespace android { - -LayerBE::LayerBE(Layer* layer, std::string layerName) : mLayer(layer) { - compositionInfo.layer = std::make_shared<LayerBE>(*this); - compositionInfo.layerName = layerName; -} - -LayerBE::LayerBE(const LayerBE& layer) : mLayer(layer.mLayer) { - compositionInfo.layer = layer.compositionInfo.layer; - compositionInfo.layerName = layer.mLayer->getName().string(); -} - -void LayerBE::onLayerDisplayed(const sp<Fence>& releaseFence) { - mLayer->onLayerDisplayed(releaseFence); -} - -}; // namespace android diff --git a/services/surfaceflinger/LayerBE.h b/services/surfaceflinger/LayerBE.h deleted file mode 100644 index 51f7857356..0000000000 --- a/services/surfaceflinger/LayerBE.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2018 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 <stdint.h> -#include <string.h> - -#include <ui/Fence.h> -#include <utils/StrongPointer.h> - -#include "DisplayHardware/DisplayIdentification.h" - -namespace android { - -class LayerBE; - -struct CompositionInfo { - std::string layerName; - std::shared_ptr<LayerBE> layer; - struct { - DisplayId displayId; - } hwc; -}; - -class LayerBE { -public: - friend class Layer; - friend class BufferLayer; - friend class BufferQueueLayer; - friend class BufferStateLayer; - friend class ColorLayer; - friend class SurfaceFlinger; - - // For unit tests - friend class TestableSurfaceFlinger; - - LayerBE(Layer* layer, std::string layerName); - explicit LayerBE(const LayerBE& layer); - - void onLayerDisplayed(const sp<Fence>& releaseFence); - - Layer*const mLayer; - -private: - CompositionInfo compositionInfo; -}; - -}; // namespace android - diff --git a/services/surfaceflinger/LayerProtoHelper.cpp b/services/surfaceflinger/LayerProtoHelper.cpp index 3289e8f583..c25c418dc5 100644 --- a/services/surfaceflinger/LayerProtoHelper.cpp +++ b/services/surfaceflinger/LayerProtoHelper.cpp @@ -18,53 +18,105 @@ namespace android { namespace surfaceflinger { -void LayerProtoHelper::writeToProto(const Region& region, RegionProto* regionProto) { + +void LayerProtoHelper::writePositionToProto(const float x, const float y, + std::function<PositionProto*()> getPositionProto) { + if (x != 0 || y != 0) { + // Use a lambda do avoid writing the object header when the object is empty + PositionProto* position = getPositionProto(); + position->set_x(x); + position->set_y(y); + } +} + +void LayerProtoHelper::writeSizeToProto(const uint32_t w, const uint32_t h, + std::function<SizeProto*()> getSizeProto) { + if (w != 0 || h != 0) { + // Use a lambda do avoid writing the object header when the object is empty + SizeProto* size = getSizeProto(); + size->set_w(w); + size->set_h(h); + } +} + +void LayerProtoHelper::writeToProto(const Region& region, + std::function<RegionProto*()> getRegionProto) { + if (region.isEmpty()) { + return; + } + Region::const_iterator head = region.begin(); Region::const_iterator const tail = region.end(); - uint64_t address = reinterpret_cast<uint64_t>(®ion); - regionProto->set_id(address); + // Use a lambda do avoid writing the object header when the object is empty + RegionProto* regionProto = getRegionProto(); while (head != tail) { - RectProto* rectProto = regionProto->add_rect(); - writeToProto(*head, rectProto); + std::function<RectProto*()> getProtoRect = [&]() { return regionProto->add_rect(); }; + writeToProto(*head, getProtoRect); head++; } } -void LayerProtoHelper::writeToProto(const Rect& rect, RectProto* rectProto) { - rectProto->set_left(rect.left); - rectProto->set_top(rect.top); - rectProto->set_bottom(rect.bottom); - rectProto->set_right(rect.right); +void LayerProtoHelper::writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto) { + if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) { + // Use a lambda do avoid writing the object header when the object is empty + RectProto* rectProto = getRectProto(); + rectProto->set_left(rect.left); + rectProto->set_top(rect.top); + rectProto->set_bottom(rect.bottom); + rectProto->set_right(rect.right); + } } -void LayerProtoHelper::writeToProto(const FloatRect& rect, FloatRectProto* rectProto) { - rectProto->set_left(rect.left); - rectProto->set_top(rect.top); - rectProto->set_bottom(rect.bottom); - rectProto->set_right(rect.right); +void LayerProtoHelper::writeToProto(const FloatRect& rect, + std::function<FloatRectProto*()> getFloatRectProto) { + if (rect.left != 0 || rect.right != 0 || rect.top != 0 || rect.bottom != 0) { + // Use a lambda do avoid writing the object header when the object is empty + FloatRectProto* rectProto = getFloatRectProto(); + rectProto->set_left(rect.left); + rectProto->set_top(rect.top); + rectProto->set_bottom(rect.bottom); + rectProto->set_right(rect.right); + } } -void LayerProtoHelper::writeToProto(const half4 color, ColorProto* colorProto) { - colorProto->set_r(color.r); - colorProto->set_g(color.g); - colorProto->set_b(color.b); - colorProto->set_a(color.a); +void LayerProtoHelper::writeToProto(const half4 color, std::function<ColorProto*()> getColorProto) { + if (color.r != 0 || color.g != 0 || color.b != 0 || color.a != 0) { + // Use a lambda do avoid writing the object header when the object is empty + ColorProto* colorProto = getColorProto(); + colorProto->set_r(color.r); + colorProto->set_g(color.g); + colorProto->set_b(color.b); + colorProto->set_a(color.a); + } } void LayerProtoHelper::writeToProto(const ui::Transform& transform, TransformProto* transformProto) { - transformProto->set_dsdx(transform[0][0]); - transformProto->set_dtdx(transform[0][1]); - transformProto->set_dsdy(transform[1][0]); - transformProto->set_dtdy(transform[1][1]); + const uint32_t type = transform.getType() | (transform.getOrientation() << 8); + transformProto->set_type(type); + + // Rotations that are 90/180/270 have their own type so the transform matrix can be + // reconstructed later. All other rotation have the type UKNOWN so we need to save the transform + // values in that case. + if (type & (ui::Transform::SCALE | ui::Transform::UNKNOWN)) { + transformProto->set_dsdx(transform[0][0]); + transformProto->set_dtdx(transform[0][1]); + transformProto->set_dsdy(transform[1][0]); + transformProto->set_dtdy(transform[1][1]); + } } void LayerProtoHelper::writeToProto(const sp<GraphicBuffer>& buffer, - ActiveBufferProto* activeBufferProto) { - activeBufferProto->set_width(buffer->getWidth()); - activeBufferProto->set_height(buffer->getHeight()); - activeBufferProto->set_stride(buffer->getStride()); - activeBufferProto->set_format(buffer->format); + std::function<ActiveBufferProto*()> getActiveBufferProto) { + if (buffer->getWidth() != 0 || buffer->getHeight() != 0 || buffer->getStride() != 0 || + buffer->format != 0) { + // Use a lambda do avoid writing the object header when the object is empty + ActiveBufferProto* activeBufferProto = getActiveBufferProto(); + activeBufferProto->set_width(buffer->getWidth()); + activeBufferProto->set_height(buffer->getHeight()); + activeBufferProto->set_stride(buffer->getStride()); + activeBufferProto->set_format(buffer->format); + } } } // namespace surfaceflinger diff --git a/services/surfaceflinger/LayerProtoHelper.h b/services/surfaceflinger/LayerProtoHelper.h index 6df5aeaebf..dca9a5e41f 100644 --- a/services/surfaceflinger/LayerProtoHelper.h +++ b/services/surfaceflinger/LayerProtoHelper.h @@ -26,12 +26,18 @@ namespace android { namespace surfaceflinger { class LayerProtoHelper { public: - static void writeToProto(const Rect& rect, RectProto* rectProto); - static void writeToProto(const FloatRect& rect, FloatRectProto* rectProto); - static void writeToProto(const Region& region, RegionProto* regionProto); - static void writeToProto(const half4 color, ColorProto* colorProto); + static void writePositionToProto(const float x, const float y, + std::function<PositionProto*()> getPositionProto); + static void writeSizeToProto(const uint32_t w, const uint32_t h, + std::function<SizeProto*()> getSizeProto); + static void writeToProto(const Rect& rect, std::function<RectProto*()> getRectProto); + static void writeToProto(const FloatRect& rect, + std::function<FloatRectProto*()> getFloatRectProto); + static void writeToProto(const Region& region, std::function<RegionProto*()> getRegionProto); + static void writeToProto(const half4 color, std::function<ColorProto*()> getColorProto); static void writeToProto(const ui::Transform& transform, TransformProto* transformProto); - static void writeToProto(const sp<GraphicBuffer>& buffer, ActiveBufferProto* activeBufferProto); + static void writeToProto(const sp<GraphicBuffer>& buffer, + std::function<ActiveBufferProto*()> getActiveBufferProto); }; } // namespace surfaceflinger diff --git a/services/surfaceflinger/RegionSamplingThread.cpp b/services/surfaceflinger/RegionSamplingThread.cpp new file mode 100644 index 0000000000..718e996dae --- /dev/null +++ b/services/surfaceflinger/RegionSamplingThread.cpp @@ -0,0 +1,432 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#undef LOG_TAG +#define LOG_TAG "RegionSamplingThread" + +#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()); } +}; + +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; + mCondition.notify_one(); + } + + std::lock_guard threadLock(mThreadMutex); + if (mThread.joinable()) { + mThread.join(); + } +} + +void RegionSamplingThread::addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener) { + wp<Layer> stopLayer = stopLayerHandle != nullptr + ? static_cast<Layer::Handle*>(stopLayerHandle.get())->owner + : nullptr; + + sp<IBinder> asBinder = IInterface::asBinder(listener); + asBinder->linkToDeath(this); + std::lock_guard lock(mMutex); + mDescriptors.emplace(wp<IBinder>(asBinder), Descriptor{samplingArea, stopLayer, listener}); +} + +void RegionSamplingThread::removeListener(const sp<IRegionSamplingListener>& listener) { + std::lock_guard lock(mMutex); + mDescriptors.erase(wp<IBinder>(IInterface::asBinder(listener))); +} + +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(); +} + +void RegionSamplingThread::binderDied(const wp<IBinder>& who) { + std::lock_guard lock(mMutex); + mDescriptors.erase(who); +} + +namespace { +// Using Rec. 709 primaries +float getLuma(float r, float g, float b) { + constexpr auto rec709_red_primary = 0.2126f; + constexpr auto rec709_green_primary = 0.7152f; + constexpr auto rec709_blue_primary = 0.0722f; + return rec709_red_primary * r + rec709_green_primary * g + rec709_blue_primary * b; +} + +float sampleArea(const uint32_t* data, int32_t stride, const Rect& area) { + std::array<int32_t, 256> brightnessBuckets = {}; + const int32_t majoritySampleNum = area.getWidth() * area.getHeight() / 2; + + for (int32_t row = area.top; row < area.bottom; ++row) { + const uint32_t* rowBase = data + row * stride; + for (int32_t column = area.left; column < area.right; ++column) { + uint32_t pixel = rowBase[column]; + const float r = (pixel & 0xFF) / 255.0f; + const float g = ((pixel >> 8) & 0xFF) / 255.0f; + const float b = ((pixel >> 16) & 0xFF) / 255.0f; + const uint8_t luma = std::round(getLuma(r, g, b) * 255.0f); + ++brightnessBuckets[luma]; + if (brightnessBuckets[luma] > majoritySampleNum) return luma / 255.0f; + } + } + + int32_t accumulated = 0; + size_t bucket = 0; + while (bucket++ < brightnessBuckets.size()) { + accumulated += brightnessBuckets[bucket]; + if (accumulated > majoritySampleNum) break; + } + + return bucket / 255.0f; +} +} // anonymous namespace + +std::vector<float> RegionSamplingThread::sampleBuffer( + const sp<GraphicBuffer>& buffer, const Point& leftTop, + const std::vector<RegionSamplingThread::Descriptor>& descriptors) { + void* data_raw = nullptr; + buffer->lock(GRALLOC_USAGE_SW_READ_OFTEN, &data_raw); + std::shared_ptr<uint32_t> data(reinterpret_cast<uint32_t*>(data_raw), + [&buffer](auto) { buffer->unlock(); }); + if (!data) return {}; + + const int32_t stride = buffer->getStride(); + std::vector<float> lumas(descriptors.size()); + std::transform(descriptors.begin(), descriptors.end(), lumas.begin(), + [&](auto const& descriptor) { + return sampleArea(data.get(), stride, descriptor.area - leftTop); + }); + return lumas; +} + +void RegionSamplingThread::captureSample() { + ATRACE_CALL(); + + if (mDescriptors.empty()) { + return; + } + + std::vector<RegionSamplingThread::Descriptor> descriptors; + Region sampleRegion; + for (const auto& [listener, descriptor] : mDescriptors) { + sampleRegion.orSelf(descriptor.area); + descriptors.emplace_back(descriptor); + } + + const Rect sampledArea = sampleRegion.bounds(); + + sp<const DisplayDevice> device = mFlinger.getDefaultDisplayDevice(); + DisplayRenderArea renderArea(device, sampledArea, sampledArea.getWidth(), + sampledArea.getHeight(), ui::Dataspace::V0_SRGB, + ui::Transform::ROT_0); + + std::unordered_set<sp<IRegionSamplingListener>, SpHash<IRegionSamplingListener>> listeners; + + auto traverseLayers = [&](const LayerVector::Visitor& visitor) { + bool stopLayerFound = false; + auto filterVisitor = [&](Layer* layer) { + // We don't want to capture any layers beyond the stop layer + if (stopLayerFound) return; + + // Likewise if we just found a stop layer, set the flag and abort + for (const auto& [area, stopLayer, listener] : descriptors) { + if (layer == stopLayer.promote().get()) { + stopLayerFound = true; + return; + } + } + + // Compute the layer's position on the screen + const Rect bounds = Rect(layer->getBounds()); + const ui::Transform transform = layer->getTransform(); + constexpr bool roundOutwards = true; + Rect transformed = transform.transform(bounds, roundOutwards); + + // If this layer doesn't intersect with the larger sampledArea, skip capturing it + Rect ignore; + if (!transformed.intersect(sampledArea, &ignore)) return; + + // If the layer doesn't intersect a sampling area, skip capturing it + bool intersectsAnyArea = false; + for (const auto& [area, stopLayer, listener] : descriptors) { + if (transformed.intersect(area, &ignore)) { + intersectsAnyArea = true; + listeners.insert(listener); + } + } + if (!intersectsAnyArea) return; + + ALOGV("Traversing [%s] [%d, %d, %d, %d]", layer->getName().string(), bounds.left, + bounds.top, bounds.right, bounds.bottom); + visitor(layer); + }; + mFlinger.traverseLayersInDisplay(device, filterVisitor); + }; + + const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER; + sp<GraphicBuffer> buffer = + new GraphicBuffer(sampledArea.getWidth(), sampledArea.getHeight(), + PIXEL_FORMAT_RGBA_8888, 1, usage, "RegionSamplingThread"); + + // When calling into SF, we post a message into the SF message queue (so the + // screen capture runs on the main thread). This message blocks until the + // screenshot is actually captured, but before the capture occurs, the main + // thread may perform a normal refresh cycle. At the end of this cycle, it + // can request another sample (because layers changed), which triggers a + // call into sampleNow. When sampleNow attempts to grab the mutex, we can + // deadlock. + // + // To avoid this, we drop the mutex while we call into SF. + mMutex.unlock(); + mFlinger.captureScreenCommon(renderArea, traverseLayers, buffer, false); + mMutex.lock(); + + std::vector<Descriptor> activeDescriptors; + for (const auto& descriptor : descriptors) { + if (listeners.count(descriptor.listener) != 0) { + activeDescriptors.emplace_back(descriptor); + } + } + + ALOGV("Sampling %zu descriptors", activeDescriptors.size()); + std::vector<float> lumas = sampleBuffer(buffer, sampledArea.leftTop(), activeDescriptors); + + if (lumas.size() != activeDescriptors.size()) { + ALOGW("collected %zu median luma values for %zu descriptors", lumas.size(), + activeDescriptors.size()); + return; + } + + 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() { + std::lock_guard lock(mMutex); + while (mRunning) { + if (mSampleRequested) { + mSampleRequested = false; + captureSample(); + } + mCondition.wait(mMutex, + [this]() REQUIRES(mMutex) { return mSampleRequested || !mRunning; }); + } +} + +} // namespace android diff --git a/services/surfaceflinger/RegionSamplingThread.h b/services/surfaceflinger/RegionSamplingThread.h new file mode 100644 index 0000000000..d4e57bfc7b --- /dev/null +++ b/services/surfaceflinger/RegionSamplingThread.h @@ -0,0 +1,124 @@ +/* + * 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 <chrono> +#include <condition_variable> +#include <mutex> +#include <thread> +#include <unordered_map> + +#include <android-base/thread_annotations.h> +#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: + 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 + // report the median luma for the layers under the stopLayerHandle, in the samplingArea region. + void addListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener); + // Remove the listener to stop receiving median luma notifications. + void removeListener(const sp<IRegionSamplingListener>& listener); + + // 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 { + Rect area = Rect::EMPTY_RECT; + wp<Layer> stopLayer; + sp<IRegionSamplingListener> listener; + }; + + struct WpHash { + size_t operator()(const wp<IBinder>& p) const { + return std::hash<IBinder*>()(p.unsafe_get()); + } + }; + std::vector<float> sampleBuffer( + 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); + + std::mutex mMutex; + std::condition_variable_any mCondition; + bool mRunning GUARDED_BY(mMutex) = true; + 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 075e2382d9..5296da9652 100644 --- a/services/surfaceflinger/Scheduler/DispSync.cpp +++ b/services/surfaceflinger/Scheduler/DispSync.cpp @@ -43,6 +43,7 @@ using std::min; namespace android { DispSync::~DispSync() = default; +DispSync::Callback::~Callback() = default; namespace impl { @@ -75,9 +76,23 @@ public: 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); + ATRACE_INT64("DispSync:Reference Time", mReferenceTime); + } ALOGV("[%s] updateModel: mPeriod = %" PRId64 ", mPhase = %" PRId64 " mReferenceTime = %" PRId64, mName, ns2us(mPeriod), ns2us(mPhase), ns2us(mReferenceTime)); @@ -170,7 +185,8 @@ public: return false; } - status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback) { + status_t addEventListener(const char* name, nsecs_t phase, DispSync::Callback* callback, + nsecs_t lastCallbackTime) { if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); @@ -186,8 +202,34 @@ public: listener.mCallback = callback; // We want to allow the firstmost future event to fire without - // allowing any past events to fire - listener.mLastEventTime = systemTime() - mPeriod / 2 + mPhase - mWakeupLatency; + // allowing any past events to fire. To do this extrapolate from + // mReferenceTime the most recent hardware vsync, and pin the + // last event time there. + const nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (mPeriod != 0) { + const nsecs_t baseTime = now - mReferenceTime; + const nsecs_t numPeriodsSinceReference = baseTime / mPeriod; + const nsecs_t predictedReference = mReferenceTime + numPeriodsSinceReference * mPeriod; + const nsecs_t phaseCorrection = mPhase + listener.mPhase; + const nsecs_t predictedLastEventTime = predictedReference + phaseCorrection; + if (predictedLastEventTime >= now) { + // Make sure that the last event time does not exceed the current time. + // If it would, then back the last event time by a period. + listener.mLastEventTime = predictedLastEventTime - mPeriod; + } else { + listener.mLastEventTime = predictedLastEventTime; + } + } else { + listener.mLastEventTime = now + mPhase - mWakeupLatency; + } + + if (lastCallbackTime <= 0) { + // If there is no prior callback time, try to infer one based on the + // logical last event time. + listener.mLastCallbackTime = listener.mLastEventTime + mWakeupLatency; + } else { + listener.mLastCallbackTime = lastCallbackTime; + } mEventListeners.push_back(listener); @@ -196,13 +238,14 @@ public: return NO_ERROR; } - status_t removeEventListener(DispSync::Callback* callback) { + status_t removeEventListener(DispSync::Callback* callback, nsecs_t* outLastCallback) { if (mTraceDetailedInfo) ATRACE_CALL(); Mutex::Autolock lock(mMutex); for (std::vector<EventListener>::iterator it = mEventListeners.begin(); it != mEventListeners.end(); ++it) { if (it->mCallback == callback) { + *outLastCallback = it->mLastCallbackTime; mEventListeners.erase(it); mCond.signal(); return NO_ERROR; @@ -244,6 +287,7 @@ private: const char* mName; nsecs_t mPhase; nsecs_t mLastEventTime; + nsecs_t mLastCallbackTime; DispSync::Callback* mCallback; }; @@ -268,6 +312,13 @@ private: return nextEventTime; } + // Sanity check that the duration is close enough in length to a period without + // falling into double-rate vsyncs. + bool isCloseToPeriod(nsecs_t duration) { + // Ratio of 3/5 is arbitrary, but it must be greater than 1/2. + return duration < (3 * mPeriod) / 5; + } + std::vector<CallbackInvocation> gatherCallbackInvocationsLocked(nsecs_t now) { if (mTraceDetailedInfo) ATRACE_CALL(); ALOGV("[%s] gatherCallbackInvocationsLocked @ %" PRId64, mName, ns2us(now)); @@ -279,12 +330,21 @@ private: nsecs_t t = computeListenerNextEventTimeLocked(eventListener, onePeriodAgo); if (t < now) { + if (isCloseToPeriod(now - eventListener.mLastCallbackTime)) { + eventListener.mLastEventTime = t; + eventListener.mLastCallbackTime = now; + ALOGV("[%s] [%s] Skipping event due to model error", mName, + eventListener.mName); + continue; + } CallbackInvocation ci; ci.mCallback = eventListener.mCallback; ci.mEventTime = t; - ALOGV("[%s] [%s] Preparing to fire", mName, eventListener.mName); + ALOGV("[%s] [%s] Preparing to fire, latency: %" PRId64, mName, eventListener.mName, + t - eventListener.mLastEventTime); callbackInvocations.push_back(ci); eventListener.mLastEventTime = t; + eventListener.mLastCallbackTime = now; } } @@ -331,7 +391,7 @@ private: // Check that it's been slightly more than half a period since the last // event so that we don't accidentally fall into double-rate vsyncs - if (t - listener.mLastEventTime < (3 * mPeriod / 5)) { + if (isCloseToPeriod(t - listener.mLastEventTime)) { t += mPeriod; ALOGV("[%s] Modifying t -> %" PRId64, mName, ns2us(t)); } @@ -393,7 +453,10 @@ DispSync::DispSync(const char* name) : mName(name), mRefreshSkipCount(0) { mThread = new DispSyncThread(name, mTraceDetailedInfo); } -DispSync::~DispSync() {} +DispSync::~DispSync() { + mThread->stop(); + mThread->requestExitAndWait(); +} void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { mIgnorePresentFences = !hasSyncFramework; @@ -412,7 +475,7 @@ void DispSync::init(bool hasSyncFramework, int64_t dispSyncPresentTimeOffset) { if (mTraceDetailedInfo && kEnableZeroPhaseTracer) { mZeroPhaseTracer = std::make_unique<ZeroPhaseTracer>(); - addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get()); + addEventListener("ZeroPhaseTracer", 0, mZeroPhaseTracer.get(), 0); } } @@ -423,8 +486,16 @@ void DispSync::reset() { 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; @@ -498,9 +569,10 @@ bool DispSync::addResyncSample(nsecs_t timestamp) { void DispSync::endResync() {} -status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback) { +status_t DispSync::addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) { Mutex::Autolock lock(mMutex); - return mThread->addEventListener(name, phase, callback); + return mThread->addEventListener(name, phase, callback, lastCallbackTime); } void DispSync::setRefreshSkipCount(int count) { @@ -510,9 +582,9 @@ void DispSync::setRefreshSkipCount(int count) { updateModelLocked(); } -status_t DispSync::removeEventListener(Callback* callback) { +status_t DispSync::removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) { Mutex::Autolock lock(mMutex); - return mThread->removeEventListener(callback); + return mThread->removeEventListener(callback, outLastCallbackTime); } status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { @@ -524,7 +596,6 @@ void DispSync::setPeriod(nsecs_t period) { Mutex::Autolock lock(mMutex); mPeriod = period; mPhase = 0; - mReferenceTime = 0; mThread->updateModel(mPeriod, mPhase, mReferenceTime); } @@ -580,11 +651,6 @@ void DispSync::updateModelLocked() { ALOGV("[%s] Adjusting mPhase -> %" PRId64, mName, ns2us(mPhase)); } - if (mTraceDetailedInfo) { - ATRACE_INT64("DispSync:Period", mPeriod); - ATRACE_INT64("DispSync:Phase", mPhase + mPeriod / 2); - } - // Artificially inflate the period if requested. mPeriod += mPeriod * mRefreshSkipCount; diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h index 4a90f10215..de2b8749c7 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 @@ class DispSync { 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 @@ public: virtual void setPeriod(nsecs_t period) = 0; virtual nsecs_t getPeriod() = 0; virtual void setRefreshSkipCount(int count) = 0; - virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) = 0; - virtual status_t removeEventListener(Callback* callback) = 0; + virtual status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) = 0; + virtual status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) = 0; virtual status_t changePhaseOffset(Callback* callback, nsecs_t phase) = 0; virtual nsecs_t computeNextRefresh(int periodOffset) const = 0; virtual void setIgnorePresentFences(bool ignore) = 0; 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 @@ public: // given phase offset from the hardware vsync events. The callback is // called from a separate thread and it should return reasonably quickly // (i.e. within a few hundred microseconds). - status_t addEventListener(const char* name, nsecs_t phase, Callback* callback) override; + // If the callback was previously registered, and the last clock time the + // callback was invoked was known to the caller (e.g. via removeEventListener), + // then the caller may pass that through to lastCallbackTime, so that + // callbacks do not accidentally double-fire if they are unregistered and + // reregistered in rapid succession. + status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) override; // removeEventListener removes an already-registered event callback. Once // this method returns that callback will no longer be called by the // DispSync object. - status_t removeEventListener(Callback* callback) override; + // outLastCallbackTime will contain the last time that the callback was invoked. + // If the caller wishes to reregister the same callback, they should pass the + // callback time back into lastCallbackTime (see addEventListener). + status_t removeEventListener(Callback* callback, nsecs_t* outLastCallbackTime) override; // changePhaseOffset changes the phase offset of an already-registered event callback. The // method will make sure that there is no skipping or double-firing on the listener per frame, @@ -204,7 +223,7 @@ private: // 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 @@ private: } // namespace impl } // namespace android - -#endif // ANDROID_DISPSYNC_H diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.cpp b/services/surfaceflinger/Scheduler/DispSyncSource.cpp index 697d6344fa..6e89648bdd 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.cpp +++ b/services/surfaceflinger/Scheduler/DispSyncSource.cpp @@ -40,13 +40,15 @@ void DispSyncSource::setVSyncEnabled(bool enable) { std::lock_guard lock(mVsyncMutex); if (enable) { status_t err = mDispSync->addEventListener(mName, mPhaseOffset, - static_cast<DispSync::Callback*>(this)); + static_cast<DispSync::Callback*>(this), + mLastCallbackTime); if (err != NO_ERROR) { ALOGE("error registering vsync callback: %s (%d)", strerror(-err), err); } // ATRACE_INT(mVsyncOnLabel.c_str(), 1); } else { - status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this)); + status_t err = mDispSync->removeEventListener(static_cast<DispSync::Callback*>(this), + &mLastCallbackTime); if (err != NO_ERROR) { ALOGE("error unregistering vsync callback: %s (%d)", strerror(-err), err); } @@ -85,7 +87,19 @@ void DispSyncSource::setPhaseOffset(nsecs_t phaseOffset) { } } +void DispSyncSource::pauseVsyncCallback(bool pause) { + std::lock_guard lock(mVsyncMutex); + mCallbackPaused = pause; +} + void DispSyncSource::onDispSyncEvent(nsecs_t when) { + { + std::lock_guard lock(mVsyncMutex); + if (mCallbackPaused) { + return; + } + } + VSyncSource::Callback* callback; { std::lock_guard lock(mCallbackMutex); diff --git a/services/surfaceflinger/Scheduler/DispSyncSource.h b/services/surfaceflinger/Scheduler/DispSyncSource.h index 0fd84a2321..2858678108 100644 --- a/services/surfaceflinger/Scheduler/DispSyncSource.h +++ b/services/surfaceflinger/Scheduler/DispSyncSource.h @@ -33,6 +33,7 @@ public: void setVSyncEnabled(bool enable) override; void setCallback(VSyncSource::Callback* callback) override; void setPhaseOffset(nsecs_t phaseOffset) override; + void pauseVsyncCallback(bool pause) override; private: // The following method is the implementation of the DispSync::Callback. @@ -44,6 +45,7 @@ private: const bool mTraceVsync; const std::string mVsyncOnLabel; const std::string mVsyncEventLabel; + nsecs_t mLastCallbackTime GUARDED_BY(mVsyncMutex) = 0; DispSync* mDispSync; @@ -53,6 +55,7 @@ private: std::mutex mVsyncMutex; nsecs_t mPhaseOffset GUARDED_BY(mVsyncMutex); bool mEnabled GUARDED_BY(mVsyncMutex) = false; + bool mCallbackPaused GUARDED_BY(mVsyncMutex) = false; }; } // namespace android
\ No newline at end of file diff --git a/services/surfaceflinger/Scheduler/EventThread.cpp b/services/surfaceflinger/Scheduler/EventThread.cpp index 5d9cfde51c..78bf7c5c49 100644 --- a/services/surfaceflinger/Scheduler/EventThread.cpp +++ b/services/surfaceflinger/Scheduler/EventThread.cpp @@ -97,6 +97,13 @@ DisplayEventReceiver::Event makeVSync(PhysicalDisplayId displayId, nsecs_t times return event; } +DisplayEventReceiver::Event makeConfigChanged(uint32_t displayId, int32_t configId) { + DisplayEventReceiver::Event event; + event.header = {DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, displayId, systemTime()}; + event.config.configId = configId; + return event; +} + } // namespace EventThreadConnection::EventThreadConnection(EventThread* eventThread, @@ -203,6 +210,12 @@ void EventThread::setPhaseOffset(nsecs_t phaseOffset) { mVSyncSource->setPhaseOffset(phaseOffset); } +void EventThread::pauseVsyncCallback(bool pause) { + std::lock_guard<std::mutex> lock(mMutex); + ATRACE_INT("vsyncPaused", pause); + mVSyncSource->pauseVsyncCallback(pause); +} + sp<EventThreadConnection> EventThread::createEventConnection( ResyncCallback resyncCallback, ResetIdleTimerCallback resetIdleTimerCallback) const { return new EventThreadConnection(const_cast<EventThread*>(this), std::move(resyncCallback), @@ -301,6 +314,13 @@ void EventThread::onHotplugReceived(PhysicalDisplayId displayId, bool connected) mCondition.notify_all(); } +void EventThread::onConfigChanged(PhysicalDisplayId displayId, int32_t configId) { + std::lock_guard<std::mutex> lock(mMutex); + + mPendingEvents.push_back(makeConfigChanged(displayId, configId)); + mCondition.notify_all(); +} + void EventThread::threadMain(std::unique_lock<std::mutex>& lock) { DisplayEventConsumers consumers; @@ -398,6 +418,7 @@ bool EventThread::shouldConsumeEvent(const DisplayEventReceiver::Event& event, const sp<EventThreadConnection>& connection) const { switch (event.header.type) { case DisplayEventReceiver::DISPLAY_EVENT_HOTPLUG: + case DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED: return true; case DisplayEventReceiver::DISPLAY_EVENT_VSYNC: diff --git a/services/surfaceflinger/Scheduler/EventThread.h b/services/surfaceflinger/Scheduler/EventThread.h index b275183233..d5e33490a5 100644 --- a/services/surfaceflinger/Scheduler/EventThread.h +++ b/services/surfaceflinger/Scheduler/EventThread.h @@ -66,6 +66,9 @@ public: virtual void setVSyncEnabled(bool enable) = 0; virtual void setCallback(Callback* callback) = 0; virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; + + // pause/resume vsync callback generation + virtual void pauseVsyncCallback(bool pause) = 0; }; class EventThreadConnection : public BnDisplayEventConnection { @@ -109,6 +112,9 @@ public: virtual void onHotplugReceived(PhysicalDisplayId displayId, bool connected) = 0; + // called when SF changes the active config and apps needs to be notified about the change + virtual void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) = 0; + virtual void dump(std::string& result) const = 0; virtual void setPhaseOffset(nsecs_t phaseOffset) = 0; @@ -119,6 +125,8 @@ public: // Requests the next vsync. If resetIdleTimer is set to true, it resets the idle timer. virtual void requestNextVsync(const sp<EventThreadConnection>& connection, bool resetIdleTimer) = 0; + + virtual void pauseVsyncCallback(bool pause) = 0; }; namespace impl { @@ -148,10 +156,14 @@ public: void onHotplugReceived(PhysicalDisplayId displayId, bool connected) override; + void onConfigChanged(PhysicalDisplayId displayId, int32_t configId) override; + void dump(std::string& result) const override; void setPhaseOffset(nsecs_t phaseOffset) override; + void pauseVsyncCallback(bool pause) override; + private: friend EventThreadTest; diff --git a/services/surfaceflinger/Scheduler/InjectVSyncSource.h b/services/surfaceflinger/Scheduler/InjectVSyncSource.h index a0e14478ab..90609afaa7 100644 --- a/services/surfaceflinger/Scheduler/InjectVSyncSource.h +++ b/services/surfaceflinger/Scheduler/InjectVSyncSource.h @@ -44,6 +44,7 @@ public: void setVSyncEnabled(bool) override {} void setPhaseOffset(nsecs_t) override {} + void pauseVsyncCallback(bool) {} private: std::mutex mCallbackMutex; diff --git a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp index ab1f460357..7e2b03d1fe 100644 --- a/services/surfaceflinger/Scheduler/PhaseOffsets.cpp +++ b/services/surfaceflinger/Scheduler/PhaseOffsets.cpp @@ -76,13 +76,13 @@ PhaseOffsets::PhaseOffsets() { mDefaultRefreshRateOffsets.late = {sfVsyncPhaseOffsetNs, vsyncPhaseOffsetNs}; mHighRefreshRateOffsets.early = {highFpsEarlySfOffsetNs != -1 ? highFpsEarlySfOffsetNs - : highFpsLateAppOffsetNs, + : highFpsLateSfOffsetNs, highFpsEarlyAppOffsetNs != -1 ? highFpsEarlyAppOffsetNs - : highFpsLateSfOffsetNs}; + : highFpsLateAppOffsetNs}; mHighRefreshRateOffsets.earlyGl = {highFpsEarlyGlSfOffsetNs != -1 ? highFpsEarlyGlSfOffsetNs - : highFpsLateAppOffsetNs, + : highFpsLateSfOffsetNs, highFpsEarlyGlAppOffsetNs != -1 ? highFpsEarlyGlAppOffsetNs - : highFpsLateSfOffsetNs}; + : highFpsLateAppOffsetNs}; mHighRefreshRateOffsets.late = {highFpsLateSfOffsetNs, highFpsLateAppOffsetNs}; } diff --git a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h index 026f7c7754..cbcc031767 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateConfigs.h +++ b/services/surfaceflinger/Scheduler/RefreshRateConfigs.h @@ -28,7 +28,7 @@ namespace android { namespace scheduler { /** - * This class is used to encapsulate configuration for refresh rates. It holds infomation + * This class is used to encapsulate configuration for refresh rates. It holds information * about available refresh rates on the device, and the mapping between the numbers and human * readable names. */ @@ -40,8 +40,6 @@ public: enum class RefreshRateType { POWER_SAVING, DEFAULT, PERFORMANCE }; struct RefreshRate { - // Type of the refresh rate. - RefreshRateType type; // This config ID corresponds to the position of the config in the vector that is stored // on the device. int configId; @@ -59,13 +57,16 @@ public: } ~RefreshRateConfigs() = default; - const std::vector<RefreshRate>& getRefreshRates() { return mRefreshRates; } + const std::unordered_map<RefreshRateType, RefreshRate>& getRefreshRates() { + return mRefreshRates; + } + const RefreshRate& getRefreshRate(RefreshRateType type) { return mRefreshRates[type]; } private: void init(const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs) { // This is the rate that HWC encapsulates right now when the device is in DOZE mode. - mRefreshRates.push_back( - RefreshRate{RefreshRateType::POWER_SAVING, SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}); + mRefreshRates.emplace(RefreshRateType::POWER_SAVING, + RefreshRate{SCREEN_OFF_CONFIG_ID, "ScreenOff", 0}); if (configs.size() < 1) { ALOGE("Device does not have valid configs. Config size is 0."); @@ -88,9 +89,10 @@ private: nsecs_t vsyncPeriod = configIdToVsyncPeriod[0].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; - mRefreshRates.push_back( - RefreshRate{RefreshRateType::DEFAULT, configIdToVsyncPeriod[0].first, - base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); + mRefreshRates.emplace(RefreshRateType::DEFAULT, + RefreshRate{configIdToVsyncPeriod[0].first, + base::StringPrintf("%2.ffps", fps), + static_cast<uint32_t>(fps)}); } if (configs.size() < 2) { @@ -102,13 +104,14 @@ private: vsyncPeriod = configIdToVsyncPeriod[1].second; if (vsyncPeriod != 0) { const float fps = 1e9 / vsyncPeriod; - mRefreshRates.push_back( - RefreshRate{RefreshRateType::PERFORMANCE, configIdToVsyncPeriod[1].first, - base::StringPrintf("%2.ffps", fps), static_cast<uint32_t>(fps)}); + mRefreshRates.emplace(RefreshRateType::PERFORMANCE, + RefreshRate{configIdToVsyncPeriod[1].first, + base::StringPrintf("%2.ffps", fps), + static_cast<uint32_t>(fps)}); } } - std::vector<RefreshRate> mRefreshRates; + std::unordered_map<RefreshRateType, RefreshRate> mRefreshRates; }; } // namespace scheduler diff --git a/services/surfaceflinger/Scheduler/RefreshRateStats.h b/services/surfaceflinger/Scheduler/RefreshRateStats.h index dcb2988e07..24910812c3 100644 --- a/services/surfaceflinger/Scheduler/RefreshRateStats.h +++ b/services/surfaceflinger/Scheduler/RefreshRateStats.h @@ -41,10 +41,9 @@ class RefreshRateStats { static constexpr int64_t MS_PER_DAY = 24 * MS_PER_HOUR; public: - explicit RefreshRateStats( - const std::vector<std::shared_ptr<const HWC2::Display::Config>>& configs, - const std::shared_ptr<TimeStats>& timeStats) - : mRefreshRateConfigs(std::make_unique<RefreshRateConfigs>(configs)), + explicit RefreshRateStats(const std::shared_ptr<RefreshRateConfigs>& refreshRateConfigs, + const std::shared_ptr<TimeStats>& timeStats) + : mRefreshRateConfigs(refreshRateConfigs), mTimeStats(timeStats), mPreviousRecordedTime(systemTime()) {} ~RefreshRateStats() = default; @@ -84,7 +83,7 @@ public: flushTime(); std::unordered_map<std::string, int64_t> totalTime; - for (auto config : mRefreshRateConfigs->getRefreshRates()) { + for (auto [type, config] : mRefreshRateConfigs->getRefreshRates()) { int64_t totalTimeForConfig = 0; if (mConfigModesTotalTime.find(config.configId) != mConfigModesTotalTime.end()) { totalTimeForConfig = mConfigModesTotalTime.at(config.configId); @@ -124,7 +123,7 @@ private: mPreviousRecordedTime = currentTime; mConfigModesTotalTime[mode] += timeElapsedMs; - for (const auto& config : mRefreshRateConfigs->getRefreshRates()) { + for (const auto& [type, config] : mRefreshRateConfigs->getRefreshRates()) { if (config.configId == mode) { mTimeStats->recordRefreshRate(config.fps, timeElapsed); } @@ -143,7 +142,7 @@ private: } // Keeps information about refresh rate configs that device has. - std::unique_ptr<RefreshRateConfigs> mRefreshRateConfigs; + std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs; // Aggregate refresh rate statistics for telemetry. std::shared_ptr<TimeStats> mTimeStats; diff --git a/services/surfaceflinger/Scheduler/Scheduler.cpp b/services/surfaceflinger/Scheduler/Scheduler.cpp index af439f7f46..0063c8a202 100644 --- a/services/surfaceflinger/Scheduler/Scheduler.cpp +++ b/services/surfaceflinger/Scheduler/Scheduler.cpp @@ -26,9 +26,9 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> -#include <android/hardware/configstore/1.2/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> @@ -68,9 +68,14 @@ Scheduler::Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function) 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 = @@ -153,6 +158,12 @@ void Scheduler::onScreenReleased(const sp<Scheduler::ConnectionHandle>& handle) mConnections[handle->id]->thread->onScreenReleased(); } +void Scheduler::onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId, + int32_t configId) { + RETURN_IF_INVALID(); + mConnections[handle->id]->thread->onConfigChanged(displayId, configId); +} + void Scheduler::dump(const sp<Scheduler::ConnectionHandle>& handle, std::string& result) const { RETURN_IF_INVALID(); mConnections.at(handle->id)->thread->dump(result); @@ -163,6 +174,12 @@ void Scheduler::setPhaseOffset(const sp<Scheduler::ConnectionHandle>& handle, ns mConnections[handle->id]->thread->setPhaseOffset(phaseOffset); } +void Scheduler::pauseVsyncCallback(const android::sp<android::Scheduler::ConnectionHandle>& handle, + bool pause) { + RETURN_IF_INVALID(); + mConnections[handle->id]->thread->pauseVsyncCallback(pause); +} + void Scheduler::getDisplayStatInfo(DisplayStatInfo* stats) { stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0); stats->vsyncPeriod = mPrimaryDispSync->getPeriod(); @@ -189,6 +206,49 @@ void Scheduler::disableHardwareVsync(bool makeUnavailable) { } } +void Scheduler::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) { + { + std::lock_guard<std::mutex> lock(mHWVsyncLock); + if (makeAvailable) { + mHWVsyncAvailable = makeAvailable; + } else if (!mHWVsyncAvailable) { + // Hardware vsync is not currently available, so abort the resync + // attempt for now + return; + } + } + + if (period <= 0) { + return; + } + + setVsyncPeriod(period); +} + +ResyncCallback Scheduler::makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) { + std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState; + return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() { + if (const auto vsync = ptr.lock()) { + vsync->resync(getVsyncPeriod); + } + }; +} + +void Scheduler::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) { + static constexpr nsecs_t kIgnoreDelay = ms2ns(500); + + const nsecs_t now = systemTime(); + const nsecs_t last = lastResyncTime.exchange(now); + + if (now - last > kIgnoreDelay) { + scheduler.resyncToHardwareVsync(false, getVsyncPeriod()); + } +} + +void Scheduler::setRefreshSkipCount(int count) { + mPrimaryDispSync->setRefreshSkipCount(count); +} + void Scheduler::setVsyncPeriod(const nsecs_t period) { std::lock_guard<std::mutex> lock(mHWVsyncLock); mPrimaryDispSync->reset(); @@ -229,16 +289,6 @@ void Scheduler::setIgnorePresentFences(bool ignore) { mPrimaryDispSync->setIgnorePresentFences(ignore); } -void Scheduler::makeHWSyncAvailable(bool makeAvailable) { - std::lock_guard<std::mutex> lock(mHWVsyncLock); - mHWVsyncAvailable = makeAvailable; -} - -bool Scheduler::getHWSyncAvailable() { - std::lock_guard<std::mutex> lock(mHWVsyncLock); - return mHWVsyncAvailable; -} - nsecs_t Scheduler::expectedPresentTime() { return mPrimaryDispSync->expectedPresentTime(); } @@ -247,30 +297,39 @@ void Scheduler::dumpPrimaryDispSync(std::string& result) const { 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() { - mLayerHistory.incrementCounter(); +void Scheduler::withPrimaryDispSync(std::function<void(DispSync&)> const& fn) { + fn(*mPrimaryDispSync); } -void Scheduler::setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback) { - std::lock_guard<std::mutex> lock(mCallbackLock); - mExpiredTimerCallback = expiredTimerCallback; +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::setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback) { +void Scheduler::setChangeRefreshRateCallback( + const ChangeRefreshRateCallback& changeRefreshRateCallback) { std::lock_guard<std::mutex> lock(mCallbackLock); - mResetTimerCallback = resetTimerCallback; + mChangeRefreshRateCallback = changeRefreshRateCallback; } void Scheduler::updateFrameSkipping(const int64_t skipCount) { @@ -282,82 +341,6 @@ void Scheduler::updateFrameSkipping(const int64_t skipCount) { } } -void Scheduler::determineLayerTimestampStats(const std::string layerName, - const nsecs_t framePresentTime) { - mLayerHistory.insert(layerName, framePresentTime); - std::vector<int64_t> differencesMs; - - // Traverse through the layer history, and determine the differences in present times. - nsecs_t newestPresentTime = framePresentTime; - std::string differencesText = ""; - 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(); @@ -365,19 +348,15 @@ void Scheduler::resetIdleTimer() { } void Scheduler::resetTimerCallback() { - std::lock_guard<std::mutex> lock(mCallbackLock); - if (mResetTimerCallback) { - mResetTimerCallback(); - 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 (mExpiredTimerCallback) { - mExpiredTimerCallback(); - 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() { @@ -386,4 +365,46 @@ std::string Scheduler::doDump() { 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 d628e404a8..73896d5763 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> @@ -28,6 +29,7 @@ #include "IdleTimer.h" #include "InjectVSyncSource.h" #include "LayerHistory.h" +#include "RefreshRateConfigs.h" #include "SchedulerUtils.h" namespace android { @@ -36,8 +38,19 @@ class EventControlThread; class Scheduler { public: - using ExpiredIdleTimerCallback = std::function<void()>; - using ResetIdleTimerCallback = std::function<void()>; + // Enum to keep track of whether we trigger event to notify choreographer of config changes. + enum class ConfigEvent { None, Changed }; + + // logical or operator with the semantics of at least one of the events is Changed + friend ConfigEvent operator|(const ConfigEvent& first, const ConfigEvent& second) { + if (first == ConfigEvent::Changed) return ConfigEvent::Changed; + if (second == ConfigEvent::Changed) return ConfigEvent::Changed; + return ConfigEvent::None; + } + + using RefreshRateType = scheduler::RefreshRateConfigs::RefreshRateType; + using ChangeRefreshRateCallback = std::function<void(RefreshRateType, ConfigEvent)>; + using GetVsyncPeriod = std::function<nsecs_t()>; // Enum to indicate whether to start the transaction early, or at vsync time. enum class TransactionStart { EARLY, NORMAL }; @@ -67,6 +80,16 @@ public: const std::unique_ptr<EventThread> thread; }; + // Stores per-display state about VSYNC. + struct VsyncState { + explicit VsyncState(Scheduler& scheduler) : scheduler(scheduler) {} + + void resync(const GetVsyncPeriod&); + + Scheduler& scheduler; + std::atomic<nsecs_t> lastResyncTime = 0; + }; + explicit Scheduler(impl::EventControlThread::SetVSyncEnabledFunction function); virtual ~Scheduler(); @@ -82,6 +105,9 @@ public: // 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. @@ -94,33 +120,41 @@ public: // Should be called before the screen is turned off. void onScreenReleased(const sp<ConnectionHandle>& handle); + // Should be called when display config changed + void onConfigChanged(const sp<ConnectionHandle>& handle, PhysicalDisplayId displayId, + int32_t configId); + // Should be called when dumpsys command is received. void dump(const sp<ConnectionHandle>& handle, std::string& result) const; // Offers ability to modify phase offset in the event thread. void setPhaseOffset(const sp<ConnectionHandle>& handle, nsecs_t phaseOffset); + // pause/resume vsync callback generation to avoid sending vsync callbacks during config switch + void pauseVsyncCallback(const sp<ConnectionHandle>& handle, bool pause); + void getDisplayStatInfo(DisplayStatInfo* stats); void enableHardwareVsync(); void disableHardwareVsync(bool makeUnavailable); - void setVsyncPeriod(const nsecs_t period); + void resyncToHardwareVsync(bool makeAvailable, nsecs_t period); + // Creates a callback for resyncing. + ResyncCallback makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod); + void setRefreshSkipCount(int count); void addResyncSample(const nsecs_t timestamp); void addPresentFence(const std::shared_ptr<FenceTime>& fenceTime); void setIgnorePresentFences(bool ignore); - void makeHWSyncAvailable(bool makeAvailable); - // returns HWSyncAvailable flag to SF would enable HW vsync based on this - bool getHWSyncAvailable(); 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(); - // Callback that gets invoked once the idle timer expires. - void setExpiredIdleTimerCallback(const ExpiredIdleTimerCallback& expiredTimerCallback); - // Callback that gets invoked once the idle timer is reset. - void setResetIdleTimerCallback(const ResetIdleTimerCallback& resetTimerCallback); + // 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(); @@ -133,23 +167,32 @@ protected: impl::EventThread::InterceptVSyncsCallback interceptCallback); 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. void resetTimerCallback(); // Function that is called when the timer expires. 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; @@ -167,6 +210,7 @@ private: std::mutex mHWVsyncLock; bool mPrimaryHWVsyncEnabled GUARDED_BY(mHWVsyncLock); bool mHWVsyncAvailable GUARDED_BY(mHWVsyncLock); + const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)}; std::unique_ptr<DispSync> mPrimaryDispSync; std::unique_ptr<EventControlThread> mEventControlThread; @@ -182,7 +226,13 @@ private: std::array<int64_t, scheduler::ARRAY_SIZE> mTimeDifferences{}; size_t mCounter = 0; - LayerHistory mLayerHistory; + // 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. @@ -190,8 +240,14 @@ private: std::unique_ptr<scheduler::IdleTimer> mIdleTimer; std::mutex mCallbackLock; - ExpiredIdleTimerCallback mExpiredTimerCallback GUARDED_BY(mCallbackLock); - ExpiredIdleTimerCallback mResetTimerCallback GUARDED_BY(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 191022dbbf..fb5414fd4b 100644 --- a/services/surfaceflinger/Scheduler/SchedulerUtils.cpp +++ b/services/surfaceflinger/Scheduler/SchedulerUtils.cpp @@ -34,23 +34,5 @@ int64_t calculate_median(std::vector<int64_t>* v) { 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 edd23de983..9e6e8c7af6 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 @@ auto calculate_mean(const T& v) { 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/Scheduler/VSyncModulator.h b/services/surfaceflinger/Scheduler/VSyncModulator.h index 0bf3ceb07b..dab2003d91 100644 --- a/services/surfaceflinger/Scheduler/VSyncModulator.h +++ b/services/surfaceflinger/Scheduler/VSyncModulator.h @@ -54,6 +54,15 @@ public: mEarlyOffsets = early; mEarlyGlOffsets = earlyGl; mLateOffsets = late; + + if (mSfConnectionHandle && late.sf != mOffsets.load().sf) { + mScheduler->setPhaseOffset(mSfConnectionHandle, late.sf); + } + + if (mAppConnectionHandle && late.app != mOffsets.load().app) { + mScheduler->setPhaseOffset(mAppConnectionHandle, late.app); + } + mOffsets = late; } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 08ba17a331..08a728a2c3 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -62,7 +62,6 @@ #include <ui/GraphicBufferAllocator.h> #include <ui/PixelFormat.h> #include <ui/UiConfig.h> -#include <utils/CallStack.h> #include <utils/StopWatch.h> #include <utils/String16.h> #include <utils/String8.h> @@ -110,7 +109,6 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> -#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> #include <configstore/Utils.h> @@ -159,6 +157,28 @@ bool isWideColorMode(const ColorMode colorMode) { return false; } +bool isHdrColorMode(const ColorMode colorMode) { + switch (colorMode) { + case ColorMode::BT2100_PQ: + case ColorMode::BT2100_HLG: + return true; + case ColorMode::DISPLAY_P3: + case ColorMode::ADOBE_RGB: + case ColorMode::DCI_P3: + case ColorMode::BT2020: + case ColorMode::DISPLAY_BT2020: + case ColorMode::NATIVE: + case ColorMode::STANDARD_BT601_625: + case ColorMode::STANDARD_BT601_625_UNADJUSTED: + case ColorMode::STANDARD_BT601_525: + case ColorMode::STANDARD_BT601_525_UNADJUSTED: + case ColorMode::STANDARD_BT709: + case ColorMode::SRGB: + return false; + } + return false; +} + ui::Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) { switch (rotation) { case ISurfaceComposer::eRotateNone: @@ -203,22 +223,6 @@ const String16 sAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER" const String16 sReadFramebuffer("android.permission.READ_FRAME_BUFFER"); const String16 sDump("android.permission.DUMP"); -constexpr float kSrgbRedX = 0.4123f; -constexpr float kSrgbRedY = 0.2126f; -constexpr float kSrgbRedZ = 0.0193f; -constexpr float kSrgbGreenX = 0.3576f; -constexpr float kSrgbGreenY = 0.7152f; -constexpr float kSrgbGreenZ = 0.1192f; -constexpr float kSrgbBlueX = 0.1805f; -constexpr float kSrgbBlueY = 0.0722f; -constexpr float kSrgbBlueZ = 0.9506f; -constexpr float kSrgbWhiteX = 0.9505f; -constexpr float kSrgbWhiteY = 1.0000f; -constexpr float kSrgbWhiteZ = 1.0891f; - -constexpr float kDefaultRefreshRate = 60.f; -constexpr float kPerformanceRefreshRate = 90.f; - // --------------------------------------------------------------------------- int64_t SurfaceFlinger::dispSyncPresentTimeOffset; bool SurfaceFlinger::useHwcForRgbToYuv; @@ -288,18 +292,20 @@ SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory, mDebugRegion(0), mDebugDisableHWC(0), mDebugDisableTransformHint(0), + mDebugEnableProtectedContent(false), mDebugInTransaction(0), mLastTransactionTime(0), mForceFullDamage(false), + mTracing(*this), mTimeStats(factory.createTimeStats()), - mPrimaryHWVsyncEnabled(false), - mHWVsyncAvailable(false), mRefreshStartTime(0), mHasPoweredOff(false), mNumLayers(0), mVrFlingerRequestsDisplay(false), mMainThreadId(std::this_thread::get_id()), - mCompositionEngine{getFactory().createCompositionEngine()} {} + mCompositionEngine{getFactory().createCompositionEngine()} { + mSetInputWindowsListener = new SetInputWindowsListener(this); +} SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory) : SurfaceFlinger(factory, SkipInitialization) { @@ -353,15 +359,7 @@ SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory) } ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation); - auto surfaceFlingerConfigsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService(); - if (surfaceFlingerConfigsServiceV1_2) { - surfaceFlingerConfigsServiceV1_2->getDisplayNativePrimaries( - [&](auto tmpPrimaries) { - memcpy(&mInternalDisplayPrimaries, &tmpPrimaries, sizeof(ui::DisplayPrimaries)); - }); - } else { - initDefaultDisplayNativePrimaries(); - } + mInternalDisplayPrimaries = sysprop::getDisplayNativePrimaries(); // debugging stuff... char value[PROPERTY_VALUE_MAX]; @@ -395,15 +393,17 @@ SurfaceFlinger::SurfaceFlinger(surfaceflinger::Factory& factory) 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_scheduler", value, "0"); - mUseScheduler = atoi(value); + mUseSmart90ForVideo = use_smart_90_for_video(false); + property_get("debug.sf.use_smart_90_for_video", value, "0"); - if (!mUseScheduler) { - mPrimaryDispSync = - getFactory().createDispSync("PrimaryDispSync", SurfaceFlinger::hasSyncFramework, - SurfaceFlinger::dispSyncPresentTimeOffset); + int int_value = atoi(value); + if (int_value) { + mUseSmart90ForVideo = true; } + property_get("debug.sf.luma_sampling", value, "1"); + mLumaSampling = atoi(value); + const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); mVsyncModulator.setPhaseOffsets(early, gl, late); @@ -584,9 +584,22 @@ void SurfaceFlinger::bootFinished() LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); - postMessageAsync(new LambdaMessage([this] { + postMessageAsync(new LambdaMessage([this]() NO_THREAD_SAFETY_ANALYSIS { readPersistentProperties(); mBootStage = BootStage::FINISHED; + + // set the refresh rate according to the policy + const auto displayId = getInternalDisplayIdLocked(); + LOG_ALWAYS_FATAL_IF(!displayId); + + const auto performanceRefreshRate = + mRefreshRateConfigs[*displayId]->getRefreshRate(RefreshRateType::PERFORMANCE); + + if (isConfigAllowed(*displayId, performanceRefreshRate.configId)) { + setRefreshRateTo(RefreshRateType::PERFORMANCE, Scheduler::ConfigEvent::None); + } else { + setRefreshRateTo(RefreshRateType::DEFAULT, Scheduler::ConfigEvent::None); + } })); } @@ -624,53 +637,28 @@ void SurfaceFlinger::init() { ALOGI("Phase offset NS: %" PRId64 "", mPhaseOffsets->getCurrentAppOffset()); Mutex::Autolock _l(mStateLock); - - auto resyncCallback = makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); - // start the EventThread - if (mUseScheduler) { - mScheduler = getFactory().createScheduler( - [this](bool enabled) { setPrimaryVsyncEnabled(enabled); }); - - // TODO(b/113612090): Currently we assume that if scheduler is turned on, then the refresh - // rate is 90. Once b/122905403 is completed, this should be updated accordingly. - mPhaseOffsets->setRefreshRateType( - scheduler::RefreshRateConfigs::RefreshRateType::PERFORMANCE); - - mAppConnectionHandle = - mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(), - resyncCallback, - impl::EventThread::InterceptVSyncsCallback()); - mSfConnectionHandle = - mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(), - resyncCallback, [this](nsecs_t timestamp) { - mInterceptor->saveVSyncEvent(timestamp); - }); - - mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); - mVsyncModulator.setSchedulerAndHandles(mScheduler.get(), mAppConnectionHandle.get(), - mSfConnectionHandle.get()); - } else { - mEventThreadSource = - std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), - mPhaseOffsets->getCurrentAppOffset(), true, "app"); - mEventThread = - std::make_unique<impl::EventThread>(mEventThreadSource.get(), - impl::EventThread::InterceptVSyncsCallback(), - "appEventThread"); - mSfEventThreadSource = - std::make_unique<DispSyncSource>(mPrimaryDispSync.get(), - mPhaseOffsets->getCurrentSfOffset(), true, "sf"); - - mSFEventThread = - std::make_unique<impl::EventThread>(mSfEventThreadSource.get(), - [this](nsecs_t timestamp) { - mInterceptor->saveVSyncEvent(timestamp); - }, - "sfEventThread"); - mEventQueue->setEventThread(mSFEventThread.get(), std::move(resyncCallback)); - mVsyncModulator.setEventThreads(mSFEventThread.get(), mEventThread.get()); - } + mScheduler = + getFactory().createScheduler([this](bool enabled) { setPrimaryVsyncEnabled(enabled); }); + auto resyncCallback = + mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); + + mAppConnectionHandle = + mScheduler->createConnection("app", mPhaseOffsets->getCurrentAppOffset(), + resyncCallback, + impl::EventThread::InterceptVSyncsCallback()); + mSfConnectionHandle = mScheduler->createConnection("sf", mPhaseOffsets->getCurrentSfOffset(), + resyncCallback, [this](nsecs_t timestamp) { + mInterceptor->saveVSyncEvent(timestamp); + }); + + mEventQueue->setEventConnection(mScheduler->getEventConnection(mSfConnectionHandle)); + 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; @@ -680,9 +668,10 @@ void SurfaceFlinger::init() { renderengine::RenderEngine::USE_HIGH_PRIORITY_CONTEXT : 0); // TODO(b/77156734): We need to stop casting and use HAL types when possible. + // Sending maxFrameBufferAcquiredBuffers as the cache size is tightly tuned to single-display. mCompositionEngine->setRenderEngine( renderengine::RenderEngine::create(static_cast<int32_t>(defaultCompositionPixelFormat), - renderEngineFeature)); + renderEngineFeature, maxFrameBufferAcquiredBuffers)); LOG_ALWAYS_FATAL_IF(mVrFlingerRequestsDisplay, "Starting with vr flinger active is not currently supported."); @@ -719,11 +708,6 @@ void SurfaceFlinger::init() { } } - if (!mUseScheduler) { - mEventControlThread = getFactory().createEventControlThread( - [this](bool enabled) { setPrimaryVsyncEnabled(enabled); }); - } - // initialize our drawing state mDrawingState = mCurrentState; @@ -742,21 +726,18 @@ void SurfaceFlinger::init() { ALOGE("Run StartPropertySetThread failed!"); } - if (mUseScheduler) { - mScheduler->setExpiredIdleTimerCallback([this] { - Mutex::Autolock lock(mStateLock); - setRefreshRateTo(RefreshRateType::DEFAULT); - }); - mScheduler->setResetIdleTimerCallback([this] { - Mutex::Autolock lock(mStateLock); - setRefreshRateTo(RefreshRateType::PERFORMANCE); - }); - - mRefreshRateStats = - std::make_unique<scheduler::RefreshRateStats>(getHwComposer().getConfigs( - *display->getId()), - mTimeStats); + if (mScheduler->isIdleTimerEnabled()) { + mScheduler->setChangeRefreshRateCallback( + [this](RefreshRateType type, Scheduler::ConfigEvent event) { + Mutex::Autolock lock(mStateLock); + setRefreshRateTo(type, event); + }); } + mRefreshRateConfigs[*display->getId()] = std::make_shared<scheduler::RefreshRateConfigs>( + getHwComposer().getConfigs(*display->getId())); + mRefreshRateStats = + std::make_unique<scheduler::RefreshRateStats>(mRefreshRateConfigs[*display->getId()], + mTimeStats); ALOGV("Done initializing"); } @@ -945,12 +926,7 @@ status_t SurfaceFlinger::getDisplayStats(const sp<IBinder>&, DisplayStatInfo* st return BAD_VALUE; } - if (mUseScheduler) { - mScheduler->getDisplayStatInfo(stats); - } else { - stats->vsyncTime = mPrimaryDispSync->computeNextRefresh(0); - stats->vsyncPeriod = mPrimaryDispSync->getPeriod(); - } + mScheduler->getDisplayStatInfo(stats); return NO_ERROR; } @@ -964,17 +940,10 @@ int SurfaceFlinger::getActiveConfig(const sp<IBinder>& displayToken) { return display->getActiveConfig(); } -void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) { +void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode, + Scheduler::ConfigEvent event) { ATRACE_CALL(); - Vector<DisplayInfo> configs; - // Lock is acquired by setRefreshRateTo. - getDisplayConfigsLocked(displayToken, &configs); - if (mode < 0 || mode >= static_cast<int>(configs.size())) { - ALOGE("Attempt to set active config %d for display with %zu configs", mode, configs.size()); - return; - } - // Lock is acquired by setRefreshRateTo. const auto display = getDisplayDeviceLocked(displayToken); if (!display) { @@ -993,94 +962,107 @@ void SurfaceFlinger::setDesiredActiveConfig(const sp<IBinder>& displayToken, int } // Don't check against the current mode yet. Worst case we set the desired - // config twice. - { - std::lock_guard<std::mutex> lock(mActiveConfigLock); - mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken}; + // config twice. However event generation config might have changed so we need to update it + // accordingly + std::lock_guard<std::mutex> lock(mActiveConfigLock); + const Scheduler::ConfigEvent desiredConfig = mDesiredActiveConfig.event | event; + mDesiredActiveConfig = ActiveConfigInfo{mode, displayToken, desiredConfig}; + + if (!mDesiredActiveConfigChanged) { + // This is the first time we set the desired + mScheduler->pauseVsyncCallback(mAppConnectionHandle, true); + + // This will trigger HWC refresh without resetting the idle timer. + repaintEverythingForHWC(); } - // This will trigger HWC refresh without resetting the idle timer. - repaintEverythingForHWC(); + mDesiredActiveConfigChanged = true; + ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); } status_t SurfaceFlinger::setActiveConfig(const sp<IBinder>& displayToken, int mode) { ATRACE_CALL(); - postMessageSync(new LambdaMessage( - [&]() NO_THREAD_SAFETY_ANALYSIS { setDesiredActiveConfig(displayToken, mode); })); - return NO_ERROR; -} -void SurfaceFlinger::setActiveConfigInHWC() { - ATRACE_CALL(); - - const auto display = getDisplayDevice(mUpcomingActiveConfig.displayToken); - if (!display) { - return; - } - const auto displayId = display->getId(); - LOG_ALWAYS_FATAL_IF(!displayId); + std::vector<int32_t> allowedConfig; + allowedConfig.push_back(mode); - ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId); - getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId); - mSetActiveConfigState = SetActiveConfigState::NOTIFIED_HWC; - ATRACE_INT("SetActiveConfigState", mSetActiveConfigState); + return setAllowedDisplayConfigs(displayToken, allowedConfig); } void SurfaceFlinger::setActiveConfigInternal() { ATRACE_CALL(); std::lock_guard<std::mutex> lock(mActiveConfigLock); - if (mUseScheduler) { - mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId); - } + mRefreshRateStats->setConfigMode(mUpcomingActiveConfig.configId); const auto display = getDisplayDeviceLocked(mUpcomingActiveConfig.displayToken); display->setActiveConfig(mUpcomingActiveConfig.configId); - mSetActiveConfigState = SetActiveConfigState::NONE; - ATRACE_INT("SetActiveConfigState", mSetActiveConfigState); - - resyncToHardwareVsync(true, getVsyncPeriod()); + 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, + mUpcomingActiveConfig.configId); + } } -bool SurfaceFlinger::updateSetActiveConfigStateMachine() NO_THREAD_SAFETY_ANALYSIS { +bool SurfaceFlinger::performSetActiveConfig() NO_THREAD_SAFETY_ANALYSIS { + ATRACE_CALL(); + if (mCheckPendingFence) { + if (mPreviousPresentFence != Fence::NO_FENCE && + (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled)) { + // fence has not signaled yet. wait for the next invalidate + repaintEverythingForHWC(); + return true; + } + + // We received the present fence from the HWC, so we assume it successfully updated + // the config, hence we update SF. + mCheckPendingFence = false; + setActiveConfigInternal(); + } + // Store the local variable to release the lock. ActiveConfigInfo desiredActiveConfig; { std::lock_guard<std::mutex> lock(mActiveConfigLock); + if (!mDesiredActiveConfigChanged) { + return false; + } desiredActiveConfig = mDesiredActiveConfig; } const auto display = getDisplayDevice(desiredActiveConfig.displayToken); - if (display) { - if (mSetActiveConfigState == SetActiveConfigState::NONE && - display->getActiveConfig() != desiredActiveConfig.configId) { - // Step 1) Desired active config was set, it is different than the - // config currently in use. Notify HWC. - mUpcomingActiveConfig = desiredActiveConfig; - setActiveConfigInHWC(); - } else if (mSetActiveConfigState == SetActiveConfigState::NOTIFIED_HWC) { - // Step 2) HWC was notified and we received refresh from it. - mSetActiveConfigState = SetActiveConfigState::REFRESH_RECEIVED; - ATRACE_INT("SetActiveConfigState", mSetActiveConfigState); - repaintEverythingForHWC(); - // We do not want to give another frame to HWC while we are transitioning. - return false; - } else if (mSetActiveConfigState == SetActiveConfigState::REFRESH_RECEIVED && - !(mPreviousPresentFence != Fence::NO_FENCE && - (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled))) { - // Step 3) We received the present fence from the HWC, so we assume it - // successfully updated the config, hence we update SF. - setActiveConfigInternal(); - // If the config changed again while we were transitioning, restart the - // process. - if (desiredActiveConfig != mUpcomingActiveConfig) { - mUpcomingActiveConfig = desiredActiveConfig; - setActiveConfigInHWC(); // sets the state to NOTIFY_HWC - } - } + if (!display || display->getActiveConfig() == desiredActiveConfig.configId) { + // display is not valid or we are already in the requested mode + // on both cases there is nothing left to do + std::lock_guard<std::mutex> lock(mActiveConfigLock); + mScheduler->pauseVsyncCallback(mAppConnectionHandle, false); + mDesiredActiveConfigChanged = false; + ATRACE_INT("DesiredActiveConfigChanged", mDesiredActiveConfigChanged); + return false; } - return true; + + // Desired active config was set, it is different than the config currently in use, however + // allowed configs might have change by the time we process the refresh. + // Make sure the desired config is still allowed + if (!isConfigAllowed(*display->getId(), desiredActiveConfig.configId)) { + std::lock_guard<std::mutex> lock(mActiveConfigLock); + mDesiredActiveConfig.configId = display->getActiveConfig(); + return false; + } + mUpcomingActiveConfig = desiredActiveConfig; + const auto displayId = display->getId(); + LOG_ALWAYS_FATAL_IF(!displayId); + + ATRACE_INT("ActiveConfigModeHWC", mUpcomingActiveConfig.configId); + getHwComposer().setActiveConfig(*displayId, mUpcomingActiveConfig.configId); + + // we need to submit an empty frame to HWC to start the process + mCheckPendingFence = true; + + return false; } status_t SurfaceFlinger::getDisplayColorModes(const sp<IBinder>& displayToken, @@ -1260,7 +1242,8 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { return; } - auto resyncCallback = makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); + auto resyncCallback = + mScheduler->makeResyncCallback(std::bind(&SurfaceFlinger::getVsyncPeriod, this)); // TODO(akrulec): Part of the Injector should be refactored, so that it // can be passed to Scheduler. @@ -1276,7 +1259,8 @@ status_t SurfaceFlinger::enableVSyncInjections(bool enable) { mEventQueue->setEventThread(mInjectorEventThread.get(), std::move(resyncCallback)); } else { ALOGV("VSync Injections disabled"); - mEventQueue->setEventThread(mSFEventThread.get(), std::move(resyncCallback)); + mEventQueue->setEventThread(mScheduler->getEventThread(mSfConnectionHandle), + std::move(resyncCallback)); } mInjectVSyncs = enable; @@ -1329,38 +1313,63 @@ status_t SurfaceFlinger::getCompositionPreference( return NO_ERROR; } -status_t SurfaceFlinger::addRegionSamplingListener( - const Rect& /*samplingArea*/, const sp<IBinder>& /*stopLayerHandle*/, - const sp<IRegionSamplingListener>& /*listener*/) { +status_t SurfaceFlinger::addRegionSamplingListener(const Rect& samplingArea, + const sp<IBinder>& stopLayerHandle, + const sp<IRegionSamplingListener>& listener) { + if (!listener || samplingArea == Rect::INVALID_RECT) { + return BAD_VALUE; + } + mRegionSamplingThread->addListener(samplingArea, stopLayerHandle, listener); + return NO_ERROR; +} + +status_t SurfaceFlinger::removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) { + if (!listener) { + return BAD_VALUE; + } + mRegionSamplingThread->removeListener(listener); return NO_ERROR; } -status_t SurfaceFlinger::removeRegionSamplingListener( - const sp<IRegionSamplingListener>& /*listener*/) { +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( ISurfaceComposer::VsyncSource vsyncSource) { - auto resyncCallback = makeResyncCallback([this] { + auto resyncCallback = mScheduler->makeResyncCallback([this] { Mutex::Autolock lock(mStateLock); return getVsyncPeriod(); }); - if (mUseScheduler) { - const auto& handle = vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle - : mAppConnectionHandle; + const auto& handle = + vsyncSource == eVsyncSourceSurfaceFlinger ? mSfConnectionHandle : mAppConnectionHandle; - return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback)); - } else { - if (vsyncSource == eVsyncSourceSurfaceFlinger) { - return mSFEventThread->createEventConnection(resyncCallback, ResetIdleTimerCallback()); - } else { - return mEventThread->createEventConnection(resyncCallback, ResetIdleTimerCallback()); - } - } + return mScheduler->createDisplayEventConnection(handle, std::move(resyncCallback)); } // ---------------------------------------------------------------------------- @@ -1412,79 +1421,6 @@ nsecs_t SurfaceFlinger::getVsyncPeriod() const { return config ? config->getVsyncPeriod() : 0; } -void SurfaceFlinger::enableHardwareVsync() { - assert(!mUseScheduler); - Mutex::Autolock _l(mHWVsyncLock); - if (!mPrimaryHWVsyncEnabled && mHWVsyncAvailable) { - mPrimaryDispSync->beginResync(); - mEventControlThread->setVsyncEnabled(true); - mPrimaryHWVsyncEnabled = true; - } -} - -void SurfaceFlinger::resyncToHardwareVsync(bool makeAvailable, nsecs_t period) { - Mutex::Autolock _l(mHWVsyncLock); - // TODO(b/113612090): This is silly, but necessary evil until we turn on the flag for good. - if (mUseScheduler) { - if (makeAvailable) { - mScheduler->makeHWSyncAvailable(true); - } else if (!mScheduler->getHWSyncAvailable()) { - // Hardware vsync is not currently available, so abort the resync - // attempt for now - return; - } - } else { - if (makeAvailable) { - mHWVsyncAvailable = true; - } else if (!mHWVsyncAvailable) { - // Hardware vsync is not currently available, so abort the resync - // attempt for now - return; - } - } - - if (period <= 0) { - return; - } - - if (mUseScheduler) { - mScheduler->setVsyncPeriod(period); - } else { - mPrimaryDispSync->reset(); - mPrimaryDispSync->setPeriod(period); - - if (!mPrimaryHWVsyncEnabled) { - mPrimaryDispSync->beginResync(); - mEventControlThread->setVsyncEnabled(true); - mPrimaryHWVsyncEnabled = true; - } - } -} - -void SurfaceFlinger::disableHardwareVsync(bool makeUnavailable) { - assert(!mUseScheduler); - Mutex::Autolock _l(mHWVsyncLock); - if (mPrimaryHWVsyncEnabled) { - mEventControlThread->setVsyncEnabled(false); - mPrimaryDispSync->endResync(); - mPrimaryHWVsyncEnabled = false; - } - if (makeUnavailable) { - mHWVsyncAvailable = false; - } -} - -void SurfaceFlinger::VsyncState::resync(const GetVsyncPeriod& getVsyncPeriod) { - static constexpr nsecs_t kIgnoreDelay = ms2ns(500); - - const nsecs_t now = systemTime(); - const nsecs_t last = lastResyncTime.exchange(now); - - if (now - last > kIgnoreDelay) { - flinger.resyncToHardwareVsync(false, getVsyncPeriod()); - } -} - void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, int64_t timestamp) { ATRACE_NAME("SF onVsync"); @@ -1504,23 +1440,7 @@ void SurfaceFlinger::onVsyncReceived(int32_t sequenceId, hwc2_display_t hwcDispl return; } - if (mUseScheduler) { - mScheduler->addResyncSample(timestamp); - } else { - bool needsHwVsync = false; - { // Scope for the lock - Mutex::Autolock _l(mHWVsyncLock); - if (mPrimaryHWVsyncEnabled) { - needsHwVsync = mPrimaryDispSync->addResyncSample(timestamp); - } - } - - if (needsHwVsync) { - enableHardwareVsync(); - } else { - disableHardwareVsync(false); - } - } + mScheduler->addResyncSample(timestamp); } void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { @@ -1528,52 +1448,41 @@ void SurfaceFlinger::getCompositorTiming(CompositorTiming* compositorTiming) { *compositorTiming = getBE().mCompositorTiming; } -void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate) { - mPhaseOffsets->setRefreshRateType(refreshRate); +bool SurfaceFlinger::isConfigAllowed(const DisplayId& displayId, int32_t config) { + std::lock_guard lock(mAllowedConfigsLock); - const auto [early, gl, late] = mPhaseOffsets->getCurrentOffsets(); - mVsyncModulator.setPhaseOffsets(early, gl, late); + // if allowed configs are not set yet for this display, every config is considered allowed + if (mAllowedConfigs.find(displayId) == mAllowedConfigs.end()) { + return true; + } + return mAllowedConfigs[displayId]->isConfigAllowed(config); +} + +void SurfaceFlinger::setRefreshRateTo(RefreshRateType refreshRate, Scheduler::ConfigEvent event) { if (mBootStage != BootStage::FINISHED) { return; } - - // TODO(b/113612090): There should be a message queue flush here. Because this esentially - // runs on a mainthread, we cannot call postMessageSync. This can be resolved in a better - // manner, once the setActiveConfig is synchronous, and is executed at a known time in a - // refresh cycle. + ATRACE_CALL(); // Don't do any updating if the current fps is the same as the new one. - const nsecs_t currentVsyncPeriod = getVsyncPeriod(); - if (currentVsyncPeriod == 0) { + const auto displayId = getInternalDisplayIdLocked(); + LOG_ALWAYS_FATAL_IF(!displayId); + const auto displayToken = getInternalDisplayTokenLocked(); + + auto desiredConfigId = mRefreshRateConfigs[*displayId]->getRefreshRate(refreshRate).configId; + const auto display = getDisplayDeviceLocked(displayToken); + if (desiredConfigId == display->getActiveConfig()) { return; } - // TODO(b/113612090): Consider having an enum value for correct refresh rates, rather than - // floating numbers. - const float currentFps = 1e9 / currentVsyncPeriod; - const float newFps = refreshRate == RefreshRateType::PERFORMANCE ? kPerformanceRefreshRate - : kDefaultRefreshRate; - if (std::abs(currentFps - newFps) <= 1) { + if (!isConfigAllowed(*displayId, desiredConfigId)) { + ALOGV("Skipping config %d as it is not part of allowed configs", desiredConfigId); return; } - const auto displayId = getInternalDisplayIdLocked(); - LOG_ALWAYS_FATAL_IF(!displayId); - - auto configs = getHwComposer().getConfigs(*displayId); - for (int i = 0; i < configs.size(); i++) { - const nsecs_t vsyncPeriod = configs.at(i)->getVsyncPeriod(); - if (vsyncPeriod == 0) { - continue; - } - const float fps = 1e9 / vsyncPeriod; - // TODO(b/113612090): There should be a better way at determining which config - // has the right refresh rate. - if (std::abs(fps - newFps) <= 1) { - setDesiredActiveConfig(getInternalDisplayTokenLocked(), i); - } - } + mPhaseOffsets->setRefreshRateType(refreshRate); + setDesiredActiveConfig(getInternalDisplayTokenLocked(), desiredConfigId, event); } void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hwc2_display_t hwcDisplayId, @@ -1621,11 +1530,7 @@ void SurfaceFlinger::setPrimaryVsyncEnabled(bool enabled) { // Note: it is assumed the caller holds |mStateLock| when this is called void SurfaceFlinger::resetDisplayState() { - if (mUseScheduler) { - mScheduler->disableHardwareVsync(true); - } else { - disableHardwareVsync(true); - } + mScheduler->disableHardwareVsync(true); // Clear the drawing state so that the logic inside of // handleTransactionLocked will fire. It will determine the delta between // mCurrentState and mDrawingState and re-apply all changes when we make the @@ -1635,6 +1540,7 @@ void SurfaceFlinger::resetDisplayState() { } void SurfaceFlinger::updateVrFlinger() { + ATRACE_CALL(); if (!mVrFlinger) return; bool vrFlingerRequestsDisplay = mVrFlingerRequestsDisplay; @@ -1654,7 +1560,18 @@ void SurfaceFlinger::updateVrFlinger() { sp<DisplayDevice> display = getDefaultDisplayDeviceLocked(); LOG_ALWAYS_FATAL_IF(!display); + const int currentDisplayPowerMode = display->getPowerMode(); + + // Clear out all the output layers from the composition engine for all + // displays before destroying the hardware composer interface. This ensures + // any HWC layers are destroyed through that interface before it becomes + // invalid. + for (const auto& [token, displayDevice] : mDisplays) { + displayDevice->getCompositionDisplay()->setOutputLayersOrderedByZ( + compositionengine::Output::OutputLayers()); + } + // This DisplayDevice will no longer be relevant once resetDisplayState() is // called below. Clear the reference now so we don't accidentally use it // later. @@ -1692,20 +1609,14 @@ void SurfaceFlinger::updateVrFlinger() { // The present fences returned from vr_hwc are not an accurate // representation of vsync times. - if (mUseScheduler) { - mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || - !hasSyncFramework); - } else { - mPrimaryDispSync->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || - !hasSyncFramework); - } + mScheduler->setIgnorePresentFences(getHwComposer().isUsingVrComposer() || !hasSyncFramework); // Use phase of 0 since phase is not known. // Use latency of 0, which will snap to the ideal latency. DisplayStatInfo stats{0 /* vsyncTime */, vsyncPeriod}; setCompositorTimingSnapped(stats, 0); - resyncToHardwareVsync(false, vsyncPeriod); + mScheduler->resyncToHardwareVsync(false, vsyncPeriod); mRepaintEverything = true; setTransactionFlags(eDisplayTransactionNeeded); @@ -1715,20 +1626,38 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { ATRACE_CALL(); switch (what) { case MessageQueue::INVALIDATE: { - if (!updateSetActiveConfigStateMachine()) { - break; - } - - if (mUseScheduler) { - // This call is made each time SF wakes up and creates a new frame. - mScheduler->incrementFrameCounter(); - } - bool frameMissed = !mHadClientComposition && mPreviousPresentFence != Fence::NO_FENCE && + bool frameMissed = mPreviousPresentFence != Fence::NO_FENCE && (mPreviousPresentFence->getStatus() == Fence::Status::Unsignaled); - mFrameMissedCount += frameMissed; + 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; + } + + // For now, only propagate backpressure when missing a hwc frame. + if (hwcFrameMissed) { if (mPropagateBackpressure) { signalLayerUpdate(); break; @@ -1763,6 +1692,7 @@ void SurfaceFlinger::onMessageReceived(int32_t what) { } bool SurfaceFlinger::handleMessageTransaction() { + ATRACE_CALL(); uint32_t transactionFlags = peekTransactionFlags(); // Apply any ready transactions in the queues if there are still transactions that have not been @@ -1799,36 +1729,24 @@ void SurfaceFlinger::handleMessageRefresh() { 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); - } - - // Setup RenderEngine sync fences if native sync is supported. - if (getRenderEngine().useNativeFenceSync()) { - if (mHadClientComposition) { - base::unique_fd flushFence(getRenderEngine().flush()); - ALOGE_IF(flushFence < 0, "Failed to flush RenderEngine!"); - getBE().flushFence = new Fence(std::move(flushFence)); - } else { - // Cleanup for hygiene. - getBE().flushFence = Fence::NO_FENCE; - } + mHadDeviceComposition = + mHadDeviceComposition || getHwComposer().hasDeviceComposition(displayId); } mVsyncModulator.onRefreshed(mHadClientComposition); - getBE().mEndOfFrameCompositionInfo = std::move(getBE().mCompositionInfo); - mLayersWithQueuedFrames.clear(); } @@ -1839,6 +1757,9 @@ bool SurfaceFlinger::handleMessageInvalidate() { if (mVisibleRegionsDirty) { computeLayerBounds(); + if (mTracingEnabled) { + mTracing.notify("visibleRegionsDirty"); + } } for (auto& layer : mLayersPendingRefresh) { @@ -1859,26 +1780,30 @@ void SurfaceFlinger::calculateWorkingSet() { mGeometryInvalid = false; for (const auto& [token, displayDevice] : mDisplays) { auto display = displayDevice->getCompositionDisplay(); - const auto displayId = display->getId(); - if (!displayId) { - continue; - } - const Vector<sp<Layer>>& currentLayers = displayDevice->getVisibleLayersSortedByZ(); - for (size_t i = 0; i < currentLayers.size(); i++) { - const auto& layer = currentLayers[i]; + uint32_t zOrder = 0; - if (!layer->hasHwcLayer(displayDevice)) { - if (!layer->createHwcLayer(&getHwComposer(), 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); } } } @@ -1895,6 +1820,13 @@ void SurfaceFlinger::calculateWorkingSet() { if (mDrawingState.colorMatrixChanged) { display->setColorTransform(mDrawingState.colorMatrix); } + Dataspace targetDataspace = Dataspace::UNKNOWN; + if (useColorManagement) { + ColorMode colorMode; + RenderIntent renderIntent; + pickColorMode(displayDevice, &colorMode, &targetDataspace, &renderIntent); + display->setColorMode(colorMode, targetDataspace, renderIntent); + } for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { if (layer->isHdrY410()) { layer->forceClientComposition(displayDevice); @@ -1926,15 +1858,9 @@ void SurfaceFlinger::calculateWorkingSet() { const auto& displayState = display->getState(); layer->setPerFrameData(displayDevice, displayState.transform, displayState.viewport, - displayDevice->getSupportedPerFrameMetadata()); - } - - if (useColorManagement) { - ColorMode colorMode; - Dataspace dataSpace; - RenderIntent renderIntent; - pickColorMode(displayDevice, &colorMode, &dataSpace, &renderIntent); - display->setColorMode(colorMode, dataSpace, renderIntent); + displayDevice->getSupportedPerFrameMetadata(), + isHdrColorMode(displayState.colorMode) ? Dataspace::UNKNOWN + : targetDataspace); } } @@ -1943,12 +1869,9 @@ void SurfaceFlinger::calculateWorkingSet() { for (const auto& [token, displayDevice] : mDisplays) { auto display = displayDevice->getCompositionDisplay(); for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { - const auto displayId = display->getId(); auto& layerState = layer->getCompositionLayer()->editState().frontEnd; layerState.compositionType = static_cast<Hwc2::IComposerClient::Composition>( layer->getCompositionType(displayDevice)); - layer->getBE().compositionInfo.hwc.displayId = *displayId; - getBE().mCompositionInfo[token].push_back(layer->getBE().compositionInfo); } } } @@ -1983,14 +1906,6 @@ void SurfaceFlinger::doDebugFlashRegions(const sp<DisplayDevice>& displayDevice, 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())) { @@ -2080,78 +1995,22 @@ void SurfaceFlinger::setCompositorTimingSnapped(const DisplayStatInfo& stats, getBE().mCompositorTiming.presentLatency = snappedCompositeToPresentLatency; } -// debug patch for b/119477596 - add stack guards to catch stack -// corruptions and disable clang optimizations. -// The code below is temporary and planned to be removed once stack -// corruptions are found. -#pragma clang optimize off -class StackGuard { -public: - StackGuard(const char* name, const char* func, int line) { - guarders.reserve(MIN_CAPACITY); - guarders.push_back({this, name, func, line}); - validate(); - } - ~StackGuard() { - for (auto i = guarders.end() - 1; i >= guarders.begin(); --i) { - if (i->guard == this) { - guarders.erase(i); - break; - } - } - } - - static void validate() { - for (const auto& guard : guarders) { - if (guard.guard->cookie != COOKIE_VALUE) { - ALOGE("%s:%d: Stack corruption detected at %s", guard.func, guard.line, guard.name); - CallStack stack(LOG_TAG); - abort(); - } - } - } - -private: - uint64_t cookie = COOKIE_VALUE; - static constexpr uint64_t COOKIE_VALUE = 0xc0febebedeadbeef; - static constexpr size_t MIN_CAPACITY = 16; - - struct GuarderElement { - StackGuard* guard; - const char* name; - const char* func; - int line; - }; - - static std::vector<GuarderElement> guarders; -}; -std::vector<StackGuard::GuarderElement> StackGuard::guarders; - -#define DEFINE_STACK_GUARD(__n) StackGuard __n##StackGuard(#__n, __FUNCTION__, __LINE__); - -#define ASSERT_ON_STACK_GUARD() StackGuard::validate(); void SurfaceFlinger::postComposition() { - DEFINE_STACK_GUARD(begin); ATRACE_CALL(); ALOGV("postComposition"); // Release any buffers which were replaced this frame nsecs_t dequeueReadyTime = systemTime(); - DEFINE_STACK_GUARD(dequeueReadyTime); for (auto& layer : mLayersWithQueuedFrames) { layer->releasePendingBuffer(dequeueReadyTime); } - ASSERT_ON_STACK_GUARD(); // |mStateLock| not needed as we are on the main thread const auto displayDevice = getDefaultDisplayDeviceLocked(); - DEFINE_STACK_GUARD(displayDevice); getBE().mGlCompositionDoneTimeline.updateSignalTimes(); std::shared_ptr<FenceTime> glCompositionDoneFenceTime; - DEFINE_STACK_GUARD(glCompositionDoneFenceTime); - if (displayDevice && getHwComposer().hasClientComposition(displayDevice->getId())) { glCompositionDoneFenceTime = std::make_shared<FenceTime>(displayDevice->getCompositionDisplay() @@ -2162,161 +2021,107 @@ void SurfaceFlinger::postComposition() glCompositionDoneFenceTime = FenceTime::NO_FENCE; } - ASSERT_ON_STACK_GUARD(); - getBE().mDisplayTimeline.updateSignalTimes(); mPreviousPresentFence = displayDevice ? getHwComposer().getPresentFence(*displayDevice->getId()) : Fence::NO_FENCE; auto presentFenceTime = std::make_shared<FenceTime>(mPreviousPresentFence); - DEFINE_STACK_GUARD(presentFenceTime); getBE().mDisplayTimeline.push(presentFenceTime); DisplayStatInfo stats; - DEFINE_STACK_GUARD(stats); - if (mUseScheduler) { - mScheduler->getDisplayStatInfo(&stats); - } else { - stats.vsyncTime = mPrimaryDispSync->computeNextRefresh(0); - stats.vsyncPeriod = mPrimaryDispSync->getPeriod(); - } - - ASSERT_ON_STACK_GUARD(); + mScheduler->getDisplayStatInfo(&stats); // We use the mRefreshStartTime which might be sampled a little later than // when we started doing work for this frame, but that should be okay // since updateCompositorTiming has snapping logic. updateCompositorTiming(stats, mRefreshStartTime, presentFenceTime); CompositorTiming compositorTiming; - DEFINE_STACK_GUARD(compositorTiming); - { std::lock_guard<std::mutex> lock(getBE().mCompositorTimingLock); - DEFINE_STACK_GUARD(lock); compositorTiming = getBE().mCompositorTiming; - - ASSERT_ON_STACK_GUARD(); } mDrawingState.traverseInZOrder([&](Layer* layer) { bool frameLatched = layer->onPostComposition(displayDevice->getId(), glCompositionDoneFenceTime, presentFenceTime, compositorTiming); - DEFINE_STACK_GUARD(frameLatched); if (frameLatched) { recordBufferingStats(layer->getName().string(), layer->getOccupancyHistory(false)); } - ASSERT_ON_STACK_GUARD(); }); if (presentFenceTime->isValid()) { - ASSERT_ON_STACK_GUARD(); - if (mUseScheduler) { - mScheduler->addPresentFence(presentFenceTime); - ASSERT_ON_STACK_GUARD(); - } else { - if (mPrimaryDispSync->addPresentFence(presentFenceTime)) { - enableHardwareVsync(); - } else { - disableHardwareVsync(false); - } - ASSERT_ON_STACK_GUARD(); - } + mScheduler->addPresentFence(presentFenceTime); } if (!hasSyncFramework) { if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) && displayDevice->isPoweredOn()) { - if (mUseScheduler) { - mScheduler->enableHardwareVsync(); - } else { - enableHardwareVsync(); - } + mScheduler->enableHardwareVsync(); } } - ASSERT_ON_STACK_GUARD(); - if (mAnimCompositionPending) { mAnimCompositionPending = false; if (presentFenceTime->isValid()) { mAnimFrameTracker.setActualPresentFence( std::move(presentFenceTime)); - - ASSERT_ON_STACK_GUARD(); } else if (displayDevice && getHwComposer().isConnected(*displayDevice->getId())) { // The HWC doesn't support present fences, so use the refresh // timestamp instead. const nsecs_t presentTime = getHwComposer().getRefreshTimestamp(*displayDevice->getId()); - DEFINE_STACK_GUARD(presentTime); - mAnimFrameTracker.setActualPresentTime(presentTime); - ASSERT_ON_STACK_GUARD(); } mAnimFrameTracker.advanceFrame(); } - ASSERT_ON_STACK_GUARD(); - mTimeStats->incrementTotalFrames(); if (mHadClientComposition) { mTimeStats->incrementClientCompositionFrames(); } - ASSERT_ON_STACK_GUARD(); - mTimeStats->setPresentFenceGlobal(presentFenceTime); - ASSERT_ON_STACK_GUARD(); - if (displayDevice && getHwComposer().isConnected(*displayDevice->getId()) && !displayDevice->isPoweredOn()) { return; } nsecs_t currentTime = systemTime(); - DEFINE_STACK_GUARD(currentTime); if (mHasPoweredOff) { mHasPoweredOff = false; } else { nsecs_t elapsedTime = currentTime - getBE().mLastSwapTime; - DEFINE_STACK_GUARD(elapsedTime); size_t numPeriods = static_cast<size_t>(elapsedTime / stats.vsyncPeriod); - DEFINE_STACK_GUARD(numPeriods); if (numPeriods < SurfaceFlingerBE::NUM_BUCKETS - 1) { getBE().mFrameBuckets[numPeriods] += elapsedTime; } else { getBE().mFrameBuckets[SurfaceFlingerBE::NUM_BUCKETS - 1] += elapsedTime; } getBE().mTotalTime += elapsedTime; - - ASSERT_ON_STACK_GUARD(); } getBE().mLastSwapTime = currentTime; - ASSERT_ON_STACK_GUARD(); { std::lock_guard lock(mTexturePoolMutex); - DEFINE_STACK_GUARD(lock); const size_t refillCount = mTexturePoolSize - mTexturePool.size(); - DEFINE_STACK_GUARD(refillCount); if (refillCount > 0) { const size_t offset = mTexturePool.size(); mTexturePool.resize(mTexturePoolSize); getRenderEngine().genTextures(refillCount, mTexturePool.data() + offset); ATRACE_INT("TexturePoolSize", mTexturePool.size()); } - ASSERT_ON_STACK_GUARD(); } mTransactionCompletedThread.addPresentFence(mPreviousPresentFence); mTransactionCompletedThread.sendCallbacks(); - ASSERT_ON_STACK_GUARD(); + if (mLumaSampling && mRegionSamplingThread) { + mRegionSamplingThread->notifyNewContent(); + } } -#pragma clang optimize on // b/119477596 void SurfaceFlinger::computeLayerBounds() { for (const auto& pair : mDisplays) { @@ -2337,17 +2142,6 @@ void SurfaceFlinger::rebuildLayerStacks() { ATRACE_CALL(); ALOGV("rebuildLayerStacks"); - // We need to clear these out now as these may be holding on to a - // HWC2::Layer reference at the same time as the LayerBE::HWCInfo structure - // also holds a reference. When the set of visible layers is recomputed, - // some layers may be destroyed if the only thing keeping them alive was - // that list of visible layers associated with each display. The layer - // destruction code asserts that the HWC2::Layer is properly destroyed, but - // that doesn't happen if SurfaceFlingerBE::mCompositionInfo keeps it alive. - for (const auto& [token, display] : mDisplays) { - getBE().mCompositionInfo[token].clear(); - } - // rebuild the visible layer list per screen if (CC_UNLIKELY(mVisibleRegionsDirty)) { ATRACE_NAME("rebuildLayerStacks VR Dirty"); @@ -2374,42 +2168,42 @@ void SurfaceFlinger::rebuildLayerStacks() { return; } - bool hwcLayerDestroyed = false; const auto displayId = displayDevice->getId(); sp<compositionengine::LayerFE> layerFE = compositionLayer->getLayerFE(); LOG_ALWAYS_FATAL_IF(layerFE.get() == nullptr); + bool needsOutputLayer = false; + if (display->belongsInOutput(layer->getLayerStack(), layer->getPrimaryDisplayOnly())) { Region drawRegion(tr.transform( layer->visibleNonTransparentRegion)); drawRegion.andSelf(bounds); if (!drawRegion.isEmpty()) { - layersSortedByZ.emplace_back( - display->getOrCreateOutputLayer(layer->getCompositionLayer(), - layerFE)); - - deprecated_layersSortedByZ.add(layer); - } else { - // Clear out the HWC layer if this layer was - // previously visible, but no longer is - hwcLayerDestroyed = displayId && layer->destroyHwcLayer(displayDevice); + needsOutputLayer = true; } - } else { - // WM changes display->layerStack upon sleep/awake. - // Here we make sure we delete the HWC layers even if - // WM changed their layer stack. - hwcLayerDestroyed = displayId && layer->destroyHwcLayer(displayDevice); } - // If a layer is not going to get a release fence because - // it is invisible, but it is also going to release its - // old buffer, add it to the list of layers needing - // fences. - if (hwcLayerDestroyed) { - auto found = std::find(mLayersWithQueuedFrames.cbegin(), - mLayersWithQueuedFrames.cend(), layer); - if (found != mLayersWithQueuedFrames.cend()) { + if (needsOutputLayer) { + layersSortedByZ.emplace_back( + display->getOrCreateOutputLayer(displayId, compositionLayer, + layerFE)); + deprecated_layersSortedByZ.add(layer); + + auto& outputLayerState = layersSortedByZ.back()->editState(); + outputLayerState.visibleRegion = + tr.transform(layer->visibleRegion.intersect(displayState.viewport)); + } else if (displayId) { + // For layers that are being removed from a HWC display, + // and that have queued frames, add them to a a list of + // released layers so we can properly set a fence. + bool hasExistingOutputLayer = + display->getOutputLayerForLayer(compositionLayer.get()) != nullptr; + bool hasQueuedFrames = std::find(mLayersWithQueuedFrames.cbegin(), + mLayersWithQueuedFrames.cend(), + layer) != mLayersWithQueuedFrames.cend(); + + if (hasExistingOutputLayer && hasQueuedFrames) { layersNeedingFences.add(layer); } } @@ -2621,16 +2415,19 @@ void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) { getHwComposer().presentAndGetReleaseFences(*displayId); } display->getRenderSurface()->onPresentDisplayCompleted(); - for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + for (auto& layer : display->getOutputLayersOrderedByZ()) { sp<Fence> releaseFence = Fence::NO_FENCE; + bool usedClientComposition = true; // The layer buffer from the previous frame (if any) is released // by HWC only when the release fence from this frame (if any) is // signaled. Always get the release fence from HWC first. - if (displayId && layer->hasHwcLayer(displayDevice)) { + if (layer->getState().hwc) { + const auto& hwcState = *layer->getState().hwc; releaseFence = - getHwComposer().getLayerReleaseFence(*displayId, - layer->getHwcLayer(displayDevice)); + getHwComposer().getLayerReleaseFence(*displayId, hwcState.hwcLayer.get()); + usedClientComposition = + hwcState.hwcCompositionType == Hwc2::IComposerClient::Composition::CLIENT; } // If the layer was client composited in the previous frame, we @@ -2638,14 +2435,13 @@ void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) { // Since we do not track that, always merge with the current // client target acquire fence when it is available, even though // this is suboptimal. - if (layer->getCompositionType(displayDevice) == - Hwc2::IComposerClient::Composition::CLIENT) { + if (usedClientComposition) { releaseFence = Fence::merge("LayerRelease", releaseFence, display->getRenderSurface()->getClientTargetAcquireFence()); } - layer->getBE().onLayerDisplayed(releaseFence); + layer->getLayerFE().onLayerDisplayed(releaseFence); } // We've got a list of layers needing fences, that are disjoint with @@ -2655,7 +2451,7 @@ void SurfaceFlinger::postFramebuffer(const sp<DisplayDevice>& displayDevice) { sp<Fence> presentFence = displayId ? getHwComposer().getPresentFence(*displayId) : Fence::NO_FENCE; for (auto& layer : displayDevice->getLayersNeedingFences()) { - layer->getBE().onLayerDisplayed(presentFence); + layer->getCompositionLayer()->getLayerFE()->onLayerDisplayed(presentFence); } } @@ -2734,13 +2530,8 @@ void SurfaceFlinger::processDisplayHotplugEventsLocked() { } void SurfaceFlinger::dispatchDisplayHotplugEvent(PhysicalDisplayId displayId, bool connected) { - if (mUseScheduler) { - mScheduler->hotplugReceived(mAppConnectionHandle, displayId, connected); - mScheduler->hotplugReceived(mSfConnectionHandle, displayId, connected); - } else { - mEventThread->onHotplugReceived(displayId, connected); - mSFEventThread->onHotplugReceived(displayId, connected); - } + mScheduler->hotplugReceived(mAppConnectionHandle, displayId, connected); + mScheduler->hotplugReceived(mSfConnectionHandle, displayId, connected); } sp<DisplayDevice> SurfaceFlinger::setupNewDisplayDeviceInternal( @@ -3105,6 +2896,10 @@ void SurfaceFlinger::updateInputFlinger() { if (mVisibleRegionsDirty || mInputInfoChanged) { mInputInfoChanged = false; updateInputWindowInfo(); + } else if (mInputWindowCommands.syncInputWindows) { + // If the caller requested to sync input windows, but there are no + // changes to input windows, notify immediately. + setInputWindowsFinished(); } executeInputWindowCommands(); @@ -3120,7 +2915,10 @@ void SurfaceFlinger::updateInputWindowInfo() { inputHandles.add(layer->fillInputInfo()); } }); - mInputFlinger->setInputWindows(inputHandles); + + mInputFlinger->setInputWindows(inputHandles, + mInputWindowCommands.syncInputWindows ? mSetInputWindowsListener + : nullptr); } void SurfaceFlinger::commitInputWindowCommands() { @@ -3157,7 +2955,7 @@ void SurfaceFlinger::updateCursorAsync() void SurfaceFlinger::latchAndReleaseBuffer(const sp<Layer>& layer) { if (layer->hasReadyFrame()) { bool ignored = false; - layer->latchBuffer(ignored, systemTime(), Fence::NO_FENCE); + layer->latchBuffer(ignored, systemTime()); } layer->releasePendingBuffer(systemTime()); } @@ -3170,12 +2968,8 @@ void SurfaceFlinger::commitTransaction() recordBufferingStats(l->getName().string(), l->getOccupancyHistory(true)); - // We need to release the HWC layers when the Layer is removed - // from the current state otherwise the HWC layer just continues - // showing at its last configured state until we eventually - // abandon the buffer queue. + // Ensure any buffers set to display on any children are released. if (l->isRemovedFromCurrentState()) { - l->destroyHwcLayersForAllDisplays(); latchAndReleaseBuffer(l); } } @@ -3186,18 +2980,39 @@ void SurfaceFlinger::commitTransaction() // 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(); @@ -3348,21 +3163,6 @@ void SurfaceFlinger::invalidateLayerStack(const sp<const Layer>& layer, const Re } } -void SurfaceFlinger::initDefaultDisplayNativePrimaries() { - mInternalDisplayPrimaries.red.X = kSrgbRedX; - mInternalDisplayPrimaries.red.Y = kSrgbRedY; - mInternalDisplayPrimaries.red.Z = kSrgbRedZ; - mInternalDisplayPrimaries.green.X = kSrgbGreenX; - mInternalDisplayPrimaries.green.Y = kSrgbGreenY; - mInternalDisplayPrimaries.green.Z = kSrgbGreenZ; - mInternalDisplayPrimaries.blue.X = kSrgbBlueX; - mInternalDisplayPrimaries.blue.Y = kSrgbBlueY; - mInternalDisplayPrimaries.blue.Z = kSrgbBlueZ; - mInternalDisplayPrimaries.white.X = kSrgbWhiteX; - mInternalDisplayPrimaries.white.Y = kSrgbWhiteY; - mInternalDisplayPrimaries.white.Z = kSrgbWhiteZ; -} - bool SurfaceFlinger::handlePageFlip() { ALOGV("handlePageFlip"); @@ -3386,11 +3186,7 @@ bool SurfaceFlinger::handlePageFlip() if (layer->hasReadyFrame()) { frameQueued = true; nsecs_t expectedPresentTime; - if (mUseScheduler) { - expectedPresentTime = mScheduler->expectedPresentTime(); - } else { - expectedPresentTime = mPrimaryDispSync->expectedPresentTime(); - } + expectedPresentTime = mScheduler->expectedPresentTime(); if (layer->shouldPresentNow(expectedPresentTime)) { mLayersWithQueuedFrames.push_back(layer); } else { @@ -3407,7 +3203,7 @@ bool SurfaceFlinger::handlePageFlip() Mutex::Autolock lock(mStateLock); for (auto& layer : mLayersWithQueuedFrames) { - if (layer->latchBuffer(visibleRegions, latchTime, getBE().flushFence)) { + if (layer->latchBuffer(visibleRegions, latchTime)) { mLayersPendingRefresh.push_back(layer); } layer->useSurfaceDamage(); @@ -3417,11 +3213,6 @@ bool SurfaceFlinger::handlePageFlip() } } - // Clear the renderengine fence here... - // downstream code assumes that a cleared fence == NO_FENCE, so reassign to - // clear instead of sp::clear. - getBE().flushFence = Fence::NO_FENCE; - mVisibleRegionsDirty |= visibleRegions; // If we will need to wake up at some time in the future to deal with a @@ -3474,6 +3265,9 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, auto display = displayDevice->getCompositionDisplay(); const auto& displayState = display->getState(); const auto displayId = display->getId(); + auto& renderEngine = getRenderEngine(); + const bool supportProtectedContent = + mDebugEnableProtectedContent && renderEngine.supportsProtectedContent(); const Region bounds(displayState.bounds); const DisplayRenderArea renderArea(displayDevice); @@ -3486,11 +3280,27 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, renderengine::DisplaySettings clientCompositionDisplay; std::vector<renderengine::LayerSettings> clientCompositionLayers; sp<GraphicBuffer> buf; + base::unique_fd fd; if (hasClientComposition) { ALOGV("hasClientComposition"); - buf = display->getRenderSurface()->dequeueBuffer(); + if (displayDevice->isPrimary() && supportProtectedContent) { + bool needsProtected = false; + for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) { + // If the layer is a protected layer, mark protected context is needed. + if (layer->isProtected()) { + needsProtected = true; + break; + } + } + if (needsProtected != renderEngine.isProtected() && + renderEngine.useProtectedContext(needsProtected)) { + display->getRenderSurface()->setProtected(needsProtected); + } + } + + buf = display->getRenderSurface()->dequeueBuffer(&fd); if (buf == nullptr) { ALOGW("Dequeuing buffer for display [%s] failed, bailing out of " @@ -3502,18 +3312,7 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, clientCompositionDisplay.physicalDisplay = displayState.scissor; clientCompositionDisplay.clip = displayState.scissor; const ui::Transform& displayTransform = displayState.transform; - mat4 m; - m[0][0] = displayTransform[0][0]; - m[0][1] = displayTransform[0][1]; - m[0][3] = displayTransform[0][2]; - m[1][0] = displayTransform[1][0]; - m[1][1] = displayTransform[1][1]; - m[1][3] = displayTransform[1][2]; - m[3][0] = displayTransform[2][0]; - m[3][1] = displayTransform[2][1]; - m[3][3] = displayTransform[2][2]; - - clientCompositionDisplay.globalTransform = m; + clientCompositionDisplay.globalTransform = displayTransform.asMatrix4(); const auto* profile = display->getDisplayColorProfile(); Dataspace outputDataspace = Dataspace::UNKNOWN; @@ -3564,8 +3363,9 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, // guaranteed the FB is already cleared renderengine::LayerSettings layerSettings; Region dummyRegion; - bool prepared = layer->prepareClientLayer(renderArea, clip, dummyRegion, - layerSettings); + bool prepared = + layer->prepareClientLayer(renderArea, clip, dummyRegion, + supportProtectedContent, layerSettings); if (prepared) { layerSettings.source.buffer.buffer = nullptr; @@ -3580,7 +3380,8 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, case Hwc2::IComposerClient::Composition::CLIENT: { renderengine::LayerSettings layerSettings; bool prepared = - layer->prepareClientLayer(renderArea, clip, clearRegion, layerSettings); + layer->prepareClientLayer(renderArea, clip, clearRegion, + supportProtectedContent, layerSettings); if (prepared) { clientCompositionLayers.push_back(layerSettings); } @@ -3598,6 +3399,16 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, // Perform some cleanup steps if we used client composition. if (hasClientComposition) { clientCompositionDisplay.clearRegion = clearRegion; + + // We boost GPU frequency here because there will be color spaces conversion + // and it's expensive. We boost the GPU frequency so that GPU composition can + // finish in time. We must reset GPU frequency afterwards, because high frequency + // consumes extra battery. + const bool expensiveRenderingExpected = + clientCompositionDisplay.outputDataspace == Dataspace::DISPLAY_P3; + if (expensiveRenderingExpected && displayId) { + mPowerAdvisor.setExpensiveRenderingExpected(*displayId, true); + } if (!debugRegion.isEmpty()) { Region::const_iterator it = debugRegion.begin(); Region::const_iterator end = debugRegion.end(); @@ -3611,8 +3422,11 @@ bool SurfaceFlinger::doComposeSurfaces(const sp<DisplayDevice>& displayDevice, clientCompositionLayers.push_back(layerSettings); } } - getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, - buf->getNativeBuffer(), readyFence); + renderEngine.drawLayers(clientCompositionDisplay, clientCompositionLayers, + buf->getNativeBuffer(), std::move(fd), readyFence); + if (expensiveRenderingExpected && displayId) { + mPowerAdvisor.setExpensiveRenderingExpected(*displayId, false); + } } return true; } @@ -3694,12 +3508,13 @@ bool SurfaceFlinger::flushTransactionQueues() { auto& [applyToken, transactionQueue] = *it; while (!transactionQueue.empty()) { - const auto& [states, displays, flags, desiredPresentTime, privileged] = + const auto& [states, displays, flags, desiredPresentTime, postTime, privileged] = transactionQueue.front(); if (!transactionIsReadyToBeApplied(desiredPresentTime, states)) { break; } - applyTransactionState(states, displays, flags, mPendingInputWindowCommands, privileged); + applyTransactionState(states, displays, flags, mPendingInputWindowCommands, + desiredPresentTime, postTime, privileged); transactionQueue.pop(); } @@ -3732,12 +3547,7 @@ bool SurfaceFlinger::containsAnyInvalidClientState(const Vector<ComposerState>& bool SurfaceFlinger::transactionIsReadyToBeApplied(int64_t desiredPresentTime, const Vector<ComposerState>& states) { - nsecs_t expectedPresentTime; - if (mUseScheduler) { - expectedPresentTime = mScheduler->expectedPresentTime(); - } else { - expectedPresentTime = mPrimaryDispSync->expectedPresentTime(); - } + nsecs_t expectedPresentTime = mScheduler->expectedPresentTime(); // Do not present if the desiredPresentTime has not passed unless it is more than one second // in the future. We ignore timestamps more than 1 second in the future for stability reasons. if (desiredPresentTime >= 0 && desiredPresentTime >= expectedPresentTime && @@ -3764,6 +3574,8 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, int64_t desiredPresentTime) { ATRACE_CALL(); + const int64_t postTime = systemTime(); + bool privileged = callingThreadHasUnscopedSurfaceFlingerAccess(); Mutex::Autolock _l(mStateLock); @@ -3776,17 +3588,19 @@ void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& states, if (mTransactionQueues.find(applyToken) != mTransactionQueues.end() || !transactionIsReadyToBeApplied(desiredPresentTime, states)) { mTransactionQueues[applyToken].emplace(states, displays, flags, desiredPresentTime, - privileged); + postTime, privileged); setTransactionFlags(eTransactionNeeded); return; } - applyTransactionState(states, displays, flags, inputWindowCommands, privileged); + applyTransactionState(states, displays, flags, inputWindowCommands, desiredPresentTime, + postTime, privileged); } void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, + const int64_t desiredPresentTime, const int64_t postTime, bool privileged) { uint32_t transactionFlags = 0; @@ -3812,7 +3626,7 @@ void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, uint32_t clientStateFlags = 0; for (const ComposerState& state : states) { - clientStateFlags |= setClientStateLocked(state, privileged); + clientStateFlags |= setClientStateLocked(state, desiredPresentTime, postTime, privileged); } // If the state doesn't require a traversal and there are callbacks, send them now if (!(clientStateFlags & eTraversalNeeded)) { @@ -3849,13 +3663,16 @@ void SurfaceFlinger::applyTransactionState(const Vector<ComposerState>& states, if (flags & eAnimation) { mAnimTransactionPending = true; } - while (mTransactionPending) { + + mPendingSyncInputWindows = mPendingInputWindowCommands.syncInputWindows; + while (mTransactionPending || mPendingSyncInputWindows) { status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5)); if (CC_UNLIKELY(err != NO_ERROR)) { // just in case something goes wrong in SF, return to the // called after a few seconds. ALOGW_IF(err == TIMED_OUT, "setTransactionState timed out!"); mTransactionPending = false; + mPendingSyncInputWindows = false; break; } } @@ -3922,7 +3739,8 @@ bool SurfaceFlinger::callingThreadHasUnscopedSurfaceFlingerAccess() { } uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState, - bool privileged) { + int64_t desiredPresentTime, int64_t postTime, + bool privileged) { const layer_state_t& s = composerState.state; sp<Client> client(static_cast<Client*>(composerState.client.get())); @@ -4132,7 +3950,7 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState if (layer->setSidebandStream(s.sidebandStream)) flags |= eTraversalNeeded; } if (what & layer_state_t::eInputInfoChanged) { - if (callingThreadHasUnscopedSurfaceFlingerAccess()) { + if (privileged) { layer->setInputInfo(s.inputInfo); flags |= eTraversalNeeded; } else { @@ -4142,6 +3960,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState if (what & layer_state_t::eMetadataChanged) { if (layer->setMetadata(s.metadata)) flags |= eTraversalNeeded; } + if (what & layer_state_t::eColorSpaceAgnosticChanged) { + if (layer->setColorSpaceAgnostic(s.colorSpaceAgnostic)) { + flags |= eTraversalNeeded; + } + } std::vector<sp<CallbackHandle>> callbackHandles; if ((what & layer_state_t::eListenerCallbacksChanged) && (!s.listenerCallbacks.empty())) { mTransactionCompletedThread.run(); @@ -4159,7 +3982,11 @@ uint32_t SurfaceFlinger::setClientStateLocked(const ComposerState& composerState sp<GraphicBuffer> buffer = BufferStateLayerCache::getInstance().get(s.cachedBuffer.token, s.cachedBuffer.bufferId); - if (layer->setBuffer(buffer)) flags |= eTraversalNeeded; + if (layer->setBuffer(buffer)) { + flags |= eTraversalNeeded; + layer->setPostTime(postTime); + layer->setDesiredPresentTime(desiredPresentTime); + } } if (layer->setTransactionCompletedListeners(callbackHandles)) flags |= eTraversalNeeded; // Do not put anything that updates layer state or modifies flags after @@ -4173,6 +4000,10 @@ uint32_t SurfaceFlinger::addInputWindowCommands(const InputWindowCommands& input flags |= eTraversalNeeded; } + if (inputWindowCommands.syncInputWindows) { + flags |= eTraversalNeeded; + } + mPendingInputWindowCommands.merge(inputWindowCommands); return flags; } @@ -4193,14 +4024,27 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie String8 uniqueName = getUniqueLayerName(name); + bool primaryDisplayOnly = false; + + // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java + // TODO b/64227542 + if (metadata.has(METADATA_WINDOW_TYPE)) { + int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0); + if (windowType == 441731) { + metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL + primaryDisplayOnly = true; + } + } + switch (flags & ISurfaceComposerClient::eFXSurfaceMask) { case ISurfaceComposerClient::eFXSurfaceBufferQueue: - result = createBufferQueueLayer(client, uniqueName, w, h, flags, format, handle, gbp, - &layer); + result = createBufferQueueLayer(client, uniqueName, w, h, flags, std::move(metadata), + format, handle, gbp, &layer); break; case ISurfaceComposerClient::eFXSurfaceBufferState: - result = createBufferStateLayer(client, uniqueName, w, h, flags, handle, &layer); + result = createBufferStateLayer(client, uniqueName, w, h, flags, std::move(metadata), + handle, &layer); break; case ISurfaceComposerClient::eFXSurfaceColor: // check if buffer size is set for color layer. @@ -4210,9 +4054,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return BAD_VALUE; } - result = createColorLayer(client, - uniqueName, w, h, flags, - handle, &layer); + result = createColorLayer(client, uniqueName, w, h, flags, std::move(metadata), handle, + &layer); break; case ISurfaceComposerClient::eFXSurfaceContainer: // check if buffer size is set for container layer. @@ -4221,9 +4064,8 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie int(w), int(h)); return BAD_VALUE; } - result = createContainerLayer(client, - uniqueName, w, h, flags, - handle, &layer); + result = createContainerLayer(client, uniqueName, w, h, flags, std::move(metadata), + handle, &layer); break; default: result = BAD_VALUE; @@ -4234,18 +4076,10 @@ status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& clie return result; } - // window type is WINDOW_TYPE_DONT_SCREENSHOT from SurfaceControl.java - // TODO b/64227542 - if (metadata.has(METADATA_WINDOW_TYPE)) { - int32_t windowType = metadata.getInt32(METADATA_WINDOW_TYPE, 0); - if (windowType == 441731) { - metadata.setInt32(METADATA_WINDOW_TYPE, 2024); // TYPE_NAVIGATION_BAR_PANEL - layer->setPrimaryDisplayOnly(); - } + if (primaryDisplayOnly) { + layer->setPrimaryDisplayOnly(); } - layer->setMetadata(std::move(metadata)); - bool addToCurrentState = callingThreadHasUnscopedSurfaceFlingerAccess(); result = addClientLayer(client, *handle, *gbp, layer, *parent, addToCurrentState); @@ -4288,7 +4122,8 @@ String8 SurfaceFlinger::getUniqueLayerName(const String8& name) status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, - PixelFormat& format, sp<IBinder>* handle, + LayerMetadata metadata, PixelFormat& format, + sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer) { // initialize the surfaces @@ -4302,8 +4137,8 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const break; } - sp<BufferQueueLayer> layer = - getFactory().createBufferQueueLayer(LayerCreationArgs(this, client, name, w, h, flags)); + sp<BufferQueueLayer> layer = getFactory().createBufferQueueLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); status_t err = layer->setDefaultBufferProperties(w, h, format); if (err == NO_ERROR) { *handle = layer->getHandle(); @@ -4317,30 +4152,31 @@ status_t SurfaceFlinger::createBufferQueueLayer(const sp<Client>& client, const status_t SurfaceFlinger::createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) { - sp<BufferStateLayer> layer = - getFactory().createBufferStateLayer(LayerCreationArgs(this, client, name, w, h, flags)); + LayerMetadata metadata, sp<IBinder>* handle, + sp<Layer>* outLayer) { + sp<BufferStateLayer> layer = getFactory().createBufferStateLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = layer->getHandle(); *outLayer = layer; return NO_ERROR; } -status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) -{ - *outLayer = getFactory().createColorLayer(LayerCreationArgs(this, client, name, w, h, flags)); +status_t SurfaceFlinger::createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* handle, sp<Layer>* outLayer) { + *outLayer = getFactory().createColorLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = (*outLayer)->getHandle(); return NO_ERROR; } -status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, - const String8& name, uint32_t w, uint32_t h, uint32_t flags, - sp<IBinder>* handle, sp<Layer>* outLayer) -{ - *outLayer = - getFactory().createContainerLayer(LayerCreationArgs(this, client, name, w, h, flags)); +status_t SurfaceFlinger::createContainerLayer(const sp<Client>& client, const String8& name, + uint32_t w, uint32_t h, uint32_t flags, + LayerMetadata metadata, sp<IBinder>* handle, + sp<Layer>* outLayer) { + *outLayer = getFactory().createContainerLayer( + LayerCreationArgs(this, client, name, w, h, flags, std::move(metadata))); *handle = (*outLayer)->getHandle(); return NO_ERROR; } @@ -4434,12 +4270,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int // Turn on the display getHwComposer().setPowerMode(*displayId, mode); if (display->isPrimary() && mode != HWC_POWER_MODE_DOZE_SUSPEND) { - if (mUseScheduler) { - mScheduler->onScreenAcquired(mAppConnectionHandle); - } else { - mEventThread->onScreenAcquired(); - } - resyncToHardwareVsync(true, getVsyncPeriod()); + mScheduler->onScreenAcquired(mAppConnectionHandle); + mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); } mVisibleRegionsDirty = true; @@ -4459,16 +4291,8 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int } if (display->isPrimary() && currentMode != HWC_POWER_MODE_DOZE_SUSPEND) { - if (mUseScheduler) { - mScheduler->disableHardwareVsync(true); - } else { - disableHardwareVsync(true); // also cancels any in-progress resync - } - if (mUseScheduler) { - mScheduler->onScreenReleased(mAppConnectionHandle); - } else { - mEventThread->onScreenReleased(); - } + mScheduler->disableHardwareVsync(true); + mScheduler->onScreenReleased(mAppConnectionHandle); } getHwComposer().setPowerMode(*displayId, mode); @@ -4479,26 +4303,14 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int // Update display while dozing getHwComposer().setPowerMode(*displayId, mode); if (display->isPrimary() && currentMode == HWC_POWER_MODE_DOZE_SUSPEND) { - if (mUseScheduler) { - mScheduler->onScreenAcquired(mAppConnectionHandle); - } else { - mEventThread->onScreenAcquired(); - } - resyncToHardwareVsync(true, getVsyncPeriod()); + mScheduler->onScreenAcquired(mAppConnectionHandle); + mScheduler->resyncToHardwareVsync(true, getVsyncPeriod()); } } else if (mode == HWC_POWER_MODE_DOZE_SUSPEND) { // Leave display going to doze if (display->isPrimary()) { - if (mUseScheduler) { - mScheduler->disableHardwareVsync(true); - } else { - disableHardwareVsync(true); // also cancels any in-progress resync - } - if (mUseScheduler) { - mScheduler->onScreenReleased(mAppConnectionHandle); - } else { - mEventThread->onScreenReleased(); - } + mScheduler->disableHardwareVsync(true); + mScheduler->onScreenReleased(mAppConnectionHandle); } getHwComposer().setPowerMode(*displayId, mode); } else { @@ -4508,7 +4320,7 @@ void SurfaceFlinger::setPowerModeInternal(const sp<DisplayDevice>& display, int if (display->isPrimary()) { mTimeStats->setPowerMode(mode); - if (mUseScheduler && mRefreshRateStats) { + if (mRefreshRateStats) { // Update refresh rate stats. mRefreshRateStats->setPowerMode(mode); } @@ -4565,11 +4377,7 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, {"--disable-layer-stats"s, dumper([this](std::string&) { mLayerStats.disable(); })}, {"--display-id"s, dumper(&SurfaceFlinger::dumpDisplayIdentificationData)}, {"--dispsync"s, dumper([this](std::string& s) { - if (mUseScheduler) { mScheduler->dumpPrimaryDispSync(s); - } else { - mPrimaryDispSync->dump(s); - } })}, {"--dump-layer-stats"s, dumper([this](std::string& s) { mLayerStats.dump(s); })}, {"--enable-layer-stats"s, dumper([this](std::string&) { mLayerStats.enable(); })}, @@ -4604,6 +4412,14 @@ status_t SurfaceFlinger::doDump(int fd, const DumpArgs& args, 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()); }); @@ -4669,13 +4485,10 @@ void SurfaceFlinger::dumpVSync(std::string& result) const { " present offset: %9" PRId64 " ns\t VSYNC period: %9" PRId64 " ns\n\n", dispSyncPresentTimeOffset, getVsyncPeriod()); - StringAppendF(&result, "Scheduler: %s\n\n", mUseScheduler ? "enabled" : "disabled"); - - if (mUseScheduler) { - mScheduler->dump(mAppConnectionHandle, result); - } else { - mEventThread->dump(result); - } + StringAppendF(&result, "Scheduler enabled."); + StringAppendF(&result, "+ Smart 90 for video detection: %s\n\n", + mUseSmart90ForVideo ? "on" : "off"); + mScheduler->dump(mAppConnectionHandle, result); } void SurfaceFlinger::dumpStaticScreenStats(std::string& result) const { @@ -4907,7 +4720,9 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co dumpStaticScreenStats(result); result.append("\n"); - StringAppendF(&result, "Missed frame count: %u\n\n", mFrameMissedCount.load()); + StringAppendF(&result, "Total missed frame count: %u\n", mFrameMissedCount.load()); + StringAppendF(&result, "HWC missed frame count: %u\n", mHwcFrameMissedCount.load()); + StringAppendF(&result, "GPU missed frame count: %u\n\n", mGpuFrameMissedCount.load()); dumpBufferingStats(result); @@ -4927,6 +4742,14 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co result.append("\n"); } + { + StringAppendF(&result, "Composition layers\n"); + mDrawingState.traverseInZOrder([&](Layer* layer) { + auto compositionLayer = layer->getCompositionLayer(); + if (compositionLayer) compositionLayer->dump(result); + }); + } + /* * Dump Display state */ @@ -5024,11 +4847,10 @@ void SurfaceFlinger::dumpAllLocked(const DumpArgs& args, std::string& result) co /** * Scheduler dump state. */ - if (mUseScheduler) { - result.append("\nScheduler state:\n"); - result.append(mScheduler->doDump() + "\n"); - result.append(mRefreshRateStats->doDump() + "\n"); - } + 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"); } const Vector<sp<Layer>>& SurfaceFlinger::getLayerSortedByZForHwcDisplay(DisplayId displayId) { @@ -5079,10 +4901,11 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case CREATE_DISPLAY: case DESTROY_DISPLAY: case ENABLE_VSYNC_INJECTIONS: - case GET_ACTIVE_COLOR_MODE: case GET_ANIMATION_FRAME_STATS: case GET_HDR_CAPABILITIES: case SET_ACTIVE_CONFIG: + case SET_ALLOWED_DISPLAY_CONFIGS: + case GET_ALLOWED_DISPLAY_CONFIGS: case SET_ACTIVE_COLOR_MODE: case INJECT_VSYNC: case SET_POWER_MODE: @@ -5113,6 +4936,7 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { // request necessary permissions. However, they do not expose any secret // information, so it is OK to pass them. case AUTHENTICATE_SURFACE: + case GET_ACTIVE_COLOR_MODE: case GET_ACTIVE_CONFIG: case GET_PHYSICAL_DISPLAY_IDS: case GET_PHYSICAL_DISPLAY_TOKEN: @@ -5128,7 +4952,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { case GET_COLOR_MANAGEMENT: case GET_COMPOSITION_PREFERENCE: case GET_PROTECTED_CONTENT_SUPPORT: - case IS_WIDE_COLOR_DISPLAY: { + case IS_WIDE_COLOR_DISPLAY: + case GET_DISPLAY_BRIGHTNESS_SUPPORT: + case SET_DISPLAY_BRIGHTNESS: { return OK; } case CAPTURE_LAYERS: @@ -5162,9 +4988,9 @@ status_t SurfaceFlinger::CheckTransactCodeCredentials(uint32_t code) { code == IBinder::SYSPROPS_TRANSACTION) { return OK; } - // Numbers from 1000 to 1031 are currently use for backdoors. The code + // Numbers from 1000 to 1032 are currently use for backdoors. The code // in onTransact verifies that the user is root, and has access to use SF. - if (code >= 1000 && code <= 1031) { + if (code >= 1000 && code <= 1032) { ALOGV("Accessing SurfaceFlinger through backdoor code: %u", code); return OK; } @@ -5304,7 +5130,7 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r case 1016: { n = data.readInt32(); // TODO(b/113612090): Evaluate if this can be removed. - mPrimaryDispSync->setRefreshSkipCount(n); + mScheduler->setRefreshSkipCount(n); return NO_ERROR; } case 1017: { @@ -5314,20 +5140,12 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } case 1018: { // Modify Choreographer's phase offset n = data.readInt32(); - if (mUseScheduler) { - mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n)); - } else { - mEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); - } + mScheduler->setPhaseOffset(mAppConnectionHandle, static_cast<nsecs_t>(n)); return NO_ERROR; } case 1019: { // Modify SurfaceFlinger's phase offset n = data.readInt32(); - if (mUseScheduler) { - mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n)); - } else { - mSFEventThread->setPhaseOffset(static_cast<nsecs_t>(n)); - } + mScheduler->setPhaseOffset(mSfConnectionHandle, static_cast<nsecs_t>(n)); return NO_ERROR; } case 1020: { // Layer updates interceptor @@ -5368,13 +5186,24 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r 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; } @@ -5413,6 +5242,20 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r 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); @@ -5450,6 +5293,11 @@ status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* r } return NO_ERROR; } + case 1032: { + n = data.readInt32(); + mDebugEnableProtectedContent = n; + return NO_ERROR; + } } } return err; @@ -5483,7 +5331,8 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, bool useIdentityTransform, - ISurfaceComposer::Rotation rotation) { + ISurfaceComposer::Rotation rotation, + bool captureSecureLayers) { ATRACE_CALL(); if (!displayToken) return BAD_VALUE; @@ -5506,7 +5355,7 @@ status_t SurfaceFlinger::captureScreen(const sp<IBinder>& displayToken, } DisplayRenderArea renderArea(display, sourceCrop, reqWidth, reqHeight, reqDataspace, - renderAreaRotation); + renderAreaRotation, captureSecureLayers); auto traverseLayers = std::bind(&SurfaceFlinger::traverseLayersInDisplay, this, display, std::placeholders::_1); @@ -5580,7 +5429,8 @@ status_t SurfaceFlinger::captureLayers(const sp<IBinder>& layerHandleBinder, Rect bounds = getBounds(); screenshotParentLayer = mFlinger->getFactory().createContainerLayer( LayerCreationArgs(mFlinger, nullptr, String8("Screenshot Parent"), - bounds.getWidth(), bounds.getHeight(), 0)); + bounds.getWidth(), bounds.getHeight(), 0, + LayerMetadata())); ReparentForDrawing reparent(mLayer, screenshotParentLayer, sourceCrop); drawLayers(); @@ -5668,6 +5518,13 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, static_cast<android_pixel_format>(reqPixelFormat), 1, usage, "screenshot"); + return captureScreenCommon(renderArea, traverseLayers, *outBuffer, useIdentityTransform); +} + +status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, + TraverseLayersFunction traverseLayers, + const sp<GraphicBuffer>& buffer, + bool useIdentityTransform) { // This mutex protects syncFd and captureResult for communication of the return values from the // main thread back to this Binder thread std::mutex captureMutex; @@ -5695,7 +5552,7 @@ status_t SurfaceFlinger::captureScreenCommon(RenderArea& renderArea, { Mutex::Autolock _l(mStateLock); renderArea.render([&] { - result = captureScreenImplLocked(renderArea, traverseLayers, (*outBuffer).get(), + result = captureScreenImplLocked(renderArea, traverseLayers, buffer.get(), useIdentityTransform, forSystem, &fd); }); } @@ -5748,18 +5605,7 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, // buffer bounds. clientCompositionDisplay.physicalDisplay = Rect(reqWidth, reqHeight); ui::Transform transform = renderArea.getTransform(); - mat4 m; - m[0][0] = transform[0][0]; - m[0][1] = transform[0][1]; - m[0][3] = transform[0][2]; - m[1][0] = transform[1][0]; - m[1][1] = transform[1][1]; - m[1][3] = transform[1][2]; - m[3][0] = transform[2][0]; - m[3][1] = transform[2][1]; - m[3][3] = transform[2][2]; - - clientCompositionDisplay.globalTransform = m; + clientCompositionDisplay.globalTransform = transform.asMatrix4(); mat4 rotMatrix; // Displacement for repositioning the clipping rectangle after rotating it // with the rotation hint. @@ -5823,16 +5669,20 @@ void SurfaceFlinger::renderScreenImplLocked(const RenderArea& renderArea, traverseLayers([&](Layer* layer) { renderengine::LayerSettings layerSettings; bool prepared = layer->prepareClientLayer(renderArea, useIdentityTransform, clearRegion, - layerSettings); + false, layerSettings); if (prepared) { clientCompositionLayers.push_back(layerSettings); } }); clientCompositionDisplay.clearRegion = clearRegion; + // Use an empty fence for the buffer fence, since we just created the buffer so + // there is no need for synchronization with the GPU. + base::unique_fd bufferFence; base::unique_fd drawFence; + getRenderEngine().useProtectedContext(false); getRenderEngine().drawLayers(clientCompositionDisplay, clientCompositionLayers, buffer, - &drawFence); + std::move(bufferFence), &drawFence); *outSyncFd = drawFence.release(); } @@ -5862,6 +5712,13 @@ status_t SurfaceFlinger::captureScreenImplLocked(const RenderArea& renderArea, return NO_ERROR; } +void SurfaceFlinger::setInputWindowsFinished() { + Mutex::Autolock _l(mStateLock); + + mPendingSyncInputWindows = false; + mTransactionCV.broadcast(); +} + // --------------------------------------------------------------------------- void SurfaceFlinger::State::traverseInZOrder(const LayerVector::Visitor& visitor) const { @@ -5893,6 +5750,119 @@ void SurfaceFlinger::traverseLayersInDisplay(const sp<const DisplayDevice>& disp } } +void SurfaceFlinger::setAllowedDisplayConfigsInternal( + const android::sp<android::IBinder>& displayToken, + std::unique_ptr<const AllowedDisplayConfigs>&& allowedConfigs) { + const auto displayId = getPhysicalDisplayIdLocked(displayToken); + if (!displayId) { + ALOGE("setAllowedDisplayConfigsInternal: getPhysicalDisplayId failed"); + return; + } + + ALOGV("Updating allowed configs"); + { + std::lock_guard lock(mAllowedConfigsLock); + mAllowedConfigs[*displayId] = std::move(allowedConfigs); + } + + // make sure that the current config is still allowed + int currentConfigIndex = getHwComposer().getActiveConfigIndex(*displayId); + if (!isConfigAllowed(*displayId, currentConfigIndex)) { + for (const auto& [type, config] : mRefreshRateConfigs[*displayId]->getRefreshRates()) { + if (isConfigAllowed(*displayId, config.configId)) { + // TODO: we switch to the first allowed config. In the future + // we may want to enhance this logic to pick a similar config + // to the current one + ALOGV("Old config is not allowed - switching to config %d", config.configId); + setDesiredActiveConfig(displayToken, config.configId, + Scheduler::ConfigEvent::Changed); + break; + } + } + } + + // 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, + const std::vector<int32_t>& allowedConfigs) { + ATRACE_CALL(); + + if (!displayToken) { + ALOGE("setAllowedDisplayConfigs: displayToken is null"); + return BAD_VALUE; + } + + if (!allowedConfigs.size()) { + ALOGE("setAllowedDisplayConfigs: empty config set provided"); + return BAD_VALUE; + } + + { + ConditionalLock lock(mStateLock, std::this_thread::get_id() != mMainThreadId); + const auto displayId = getPhysicalDisplayIdLocked(displayToken); + if (!displayId) { + ALOGE("setAllowedDisplayConfigs: display not found"); + return NAME_NOT_FOUND; + } + } + + auto allowedDisplayConfigsBuilder = AllowedDisplayConfigs::Builder(); + for (int config : allowedConfigs) { + ALOGV("setAllowedDisplayConfigs: Adding config to the allowed configs = %d", config); + allowedDisplayConfigsBuilder.addConfig(config); + } + auto allowedDisplayConfigs = allowedDisplayConfigsBuilder.build(); + postMessageSync(new LambdaMessage([&]() NO_THREAD_SAFETY_ANALYSIS { + setAllowedDisplayConfigsInternal(displayToken, std::move(allowedDisplayConfigs)); + })); + return NO_ERROR; +} + +status_t SurfaceFlinger::getAllowedDisplayConfigs(const android::sp<android::IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) { + ATRACE_CALL(); + + if (!displayToken) { + ALOGE("getAllowedDisplayConfigs: displayToken is null"); + return BAD_VALUE; + } + + if (!outAllowedConfigs) { + ALOGE("getAllowedDisplayConfigs: outAllowedConfigs is null"); + return BAD_VALUE; + } + + ConditionalLock stateLock(mStateLock, std::this_thread::get_id() != mMainThreadId); + const auto displayId = getPhysicalDisplayIdLocked(displayToken); + if (!displayId) { + ALOGE("getAllowedDisplayConfigs: display not found"); + return NAME_NOT_FOUND; + } + + std::lock_guard allowedConfigLock(mAllowedConfigsLock); + auto allowedConfigIterator = mAllowedConfigs.find(displayId.value()); + if (allowedConfigIterator != mAllowedConfigs.end()) { + allowedConfigIterator->second->getAllowedConfigs(outAllowedConfigs); + } + + return NO_ERROR; +} + +// ---------------------------------------------------------------------------- + +void SetInputWindowsListener::onSetInputWindowsFinished() { + mFlinger->setInputWindowsFinished(); +} + }; // namespace android diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h index 18f0ced17a..0d39cb58dd 100644 --- a/services/surfaceflinger/SurfaceFlinger.h +++ b/services/surfaceflinger/SurfaceFlinger.h @@ -33,6 +33,7 @@ #include <gui/LayerState.h> #include <gui/OccupancyTracker.h> #include <hardware/hwcomposer_defs.h> +#include <input/ISetInputWindowsListener.h> #include <layerproto/LayerProtoHeader.h> #include <math/mat4.h> #include <serviceutils/PriorityDumper.h> @@ -46,16 +47,18 @@ #include <utils/Trace.h> #include <utils/threads.h> +#include "AllowedDisplayConfigs.h" #include "Barrier.h" #include "BufferStateLayerCache.h" #include "DisplayDevice.h" #include "DisplayHardware/HWC2.h" #include "DisplayHardware/HWComposer.h" +#include "DisplayHardware/PowerAdvisor.h" #include "Effects/Daltonizer.h" #include "FrameTracker.h" -#include "LayerBE.h" #include "LayerStats.h" #include "LayerVector.h" +#include "RegionSamplingThread.h" #include "Scheduler/DispSync.h" #include "Scheduler/EventThread.h" #include "Scheduler/MessageQueue.h" @@ -103,7 +106,6 @@ class Surface; class SurfaceFlingerBE; class TimeStats; class VSyncSource; -struct CompositionInfo; namespace compositionengine { class DisplaySurface; @@ -169,9 +171,6 @@ public: nsecs_t mTotalTime; std::atomic<nsecs_t> mLastSwapTime; - // Synchronization fence from a GL composition. - sp<Fence> flushFence = Fence::NO_FENCE; - // Double- vs. triple-buffering stats struct BufferingStats { BufferingStats() @@ -199,11 +198,16 @@ public: // use to differentiate callbacks from different hardware composer // instances. Each hardware composer instance gets a different sequence id. int32_t mComposerSequenceId; - - std::map<wp<IBinder>, std::vector<CompositionInfo>> mCompositionInfo; - std::map<wp<IBinder>, std::vector<CompositionInfo>> mEndOfFrameCompositionInfo; }; +class SetInputWindowsListener : public BnSetInputWindowsListener { +public: + SetInputWindowsListener(const sp<SurfaceFlinger>& flinger) : mFlinger(flinger) {} + void onSetInputWindowsFinished() override; + +private: + const sp<SurfaceFlinger> mFlinger; +}; class SurfaceFlinger : public BnSurfaceComposer, public PriorityDumper, @@ -351,6 +355,8 @@ public: return mTransactionCompletedThread; } + void setInputWindowsFinished(); + private: friend class Client; friend class DisplayEventConnection; @@ -360,6 +366,8 @@ private: friend class BufferQueueLayer; friend class BufferStateLayer; friend class MonitoredProducer; + friend class RegionSamplingThread; + friend class SurfaceTracing; // For unit tests friend class TestableSurfaceFlinger; @@ -369,6 +377,7 @@ private: 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(); @@ -432,7 +441,8 @@ private: status_t captureScreen(const sp<IBinder>& displayToken, sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight, - bool useIdentityTransform, ISurfaceComposer::Rotation rotation) override; + bool useIdentityTransform, ISurfaceComposer::Rotation rotation, + bool captureSecureLayers) override; status_t captureLayers(const sp<IBinder>& parentHandle, sp<GraphicBuffer>* outBuffer, const ui::Dataspace reqDataspace, const ui::PixelFormat reqPixelFormat, const Rect& sourceCrop, float frameScale, bool childrenOnly) override; @@ -480,6 +490,14 @@ private: status_t addRegionSamplingListener(const Rect& samplingArea, const sp<IBinder>& stopLayerHandle, const sp<IRegionSamplingListener>& listener) override; status_t removeRegionSamplingListener(const sp<IRegionSamplingListener>& listener) override; + status_t setAllowedDisplayConfigs(const sp<IBinder>& displayToken, + const std::vector<int32_t>& allowedConfigs) override; + status_t getAllowedDisplayConfigs(const sp<IBinder>& displayToken, + std::vector<int32_t>* outAllowedConfigs) override; + status_t getDisplayBrightnessSupport(const sp<IBinder>& displayToken, + bool* outSupport) const override; + status_t setDisplayBrightness(const sp<IBinder>& displayToken, float brightness) const override; + /* ------------------------------------------------------------------------ * DeathRecipient interface */ @@ -512,20 +530,24 @@ private: // called on the main thread in response to initializeDisplays() void onInitializeDisplays() REQUIRES(mStateLock); // Sets the desired active config bit. It obtains the lock, and sets mDesiredActiveConfig. - void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode) REQUIRES(mStateLock); - // Calls setActiveConfig in HWC. - void setActiveConfigInHWC(); + void setDesiredActiveConfig(const sp<IBinder>& displayToken, int mode, + Scheduler::ConfigEvent event) REQUIRES(mStateLock); // Once HWC has returned the present fence, this sets the active config and a new refresh // rate in SF. It also triggers HWC vsync. void setActiveConfigInternal() REQUIRES(mStateLock); // Active config is updated on INVALIDATE call in a state machine-like manner. When the - // desired config was set, HWC needs to update the pannel on the next refresh, and when + // desired config was set, HWC needs to update the panel on the next refresh, and when // we receive the fence back, we know that the process was complete. It returns whether - // the invalidate process should continue. - bool updateSetActiveConfigStateMachine(); + // we need to wait for the next invalidate + bool performSetActiveConfig(); // called on the main thread in response to setPowerMode() void setPowerModeInternal(const sp<DisplayDevice>& display, int mode) REQUIRES(mStateLock); + // called on the main thread in response to setAllowedDisplayConfigs() + void setAllowedDisplayConfigsInternal( + const sp<IBinder>& displayToken, + std::unique_ptr<const AllowedDisplayConfigs>&& allowedConfigs) REQUIRES(mStateLock); + // Returns whether the transaction actually modified any state bool handleMessageTransaction(); @@ -555,6 +577,7 @@ private: void applyTransactionState(const Vector<ComposerState>& state, const Vector<DisplayState>& displays, uint32_t flags, const InputWindowCommands& inputWindowCommands, + const int64_t desiredPresentTime, const int64_t postTime, bool privileged) REQUIRES(mStateLock); bool flushTransactionQueues(); uint32_t getTransactionFlags(uint32_t flags); @@ -563,12 +586,13 @@ private: 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); @@ -580,21 +604,21 @@ private: sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* parent); status_t createBufferQueueLayer(const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags, PixelFormat& format, - sp<IBinder>* outHandle, sp<IGraphicBufferProducer>* outGbp, - sp<Layer>* outLayer); + uint32_t h, uint32_t flags, LayerMetadata metadata, + PixelFormat& format, sp<IBinder>* outHandle, + sp<IGraphicBufferProducer>* outGbp, sp<Layer>* outLayer); status_t createBufferStateLayer(const sp<Client>& client, const String8& name, uint32_t w, - uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* outHandle, sp<Layer>* outLayer); - status_t createColorLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + status_t createColorLayer(const sp<Client>& client, const String8& name, uint32_t w, uint32_t h, + uint32_t flags, LayerMetadata metadata, sp<IBinder>* outHandle, + sp<Layer>* outLayer); - status_t createContainerLayer(const sp<Client>& client, const String8& name, - uint32_t w, uint32_t h, uint32_t flags, sp<IBinder>* outHandle, - sp<Layer>* outLayer); + status_t createContainerLayer(const sp<Client>& client, const String8& name, uint32_t w, + uint32_t h, uint32_t flags, LayerMetadata metadata, + sp<IBinder>* outHandle, sp<Layer>* outLayer); String8 getUniqueLayerName(const String8& name); @@ -627,6 +651,8 @@ private: status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, sp<GraphicBuffer>* outBuffer, const ui::PixelFormat reqPixelFormat, bool useIdentityTransform); + status_t captureScreenCommon(RenderArea& renderArea, TraverseLayersFunction traverseLayers, + const sp<GraphicBuffer>& buffer, bool useIdentityTransform); status_t captureScreenImplLocked(const RenderArea& renderArea, TraverseLayersFunction traverseLayers, ANativeWindowBuffer* buffer, bool useIdentityTransform, @@ -689,10 +715,6 @@ private: // region of all screens presenting this layer stack. void invalidateLayerStack(const sp<const Layer>& layer, const Region& dirty); - // Initialize structures containing information about the internal - // display's native color coordinates using default data - void initDefaultDisplayNativePrimaries(); - /* ------------------------------------------------------------------------ * H/W composer */ @@ -758,7 +780,6 @@ private: 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); @@ -789,36 +810,13 @@ private: * VSync */ nsecs_t getVsyncPeriod() const REQUIRES(mStateLock); - void enableHardwareVsync(); - void resyncToHardwareVsync(bool makeAvailable, nsecs_t period); - void disableHardwareVsync(bool makeUnavailable); // Sets the refresh rate by switching active configs, if they are available for // the desired refresh rate. - void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType) REQUIRES(mStateLock); + void setRefreshRateTo(scheduler::RefreshRateConfigs::RefreshRateType, + Scheduler::ConfigEvent event) REQUIRES(mStateLock); - using GetVsyncPeriod = std::function<nsecs_t()>; - - // Stores per-display state about VSYNC. - struct VsyncState { - explicit VsyncState(SurfaceFlinger& flinger) : flinger(flinger) {} - - void resync(const GetVsyncPeriod&); - - SurfaceFlinger& flinger; - std::atomic<nsecs_t> lastResyncTime = 0; - }; - - const std::shared_ptr<VsyncState> mPrimaryVsyncState{std::make_shared<VsyncState>(*this)}; - - auto makeResyncCallback(GetVsyncPeriod&& getVsyncPeriod) { - std::weak_ptr<VsyncState> ptr = mPrimaryVsyncState; - return [ptr, getVsyncPeriod = std::move(getVsyncPeriod)]() { - if (const auto vsync = ptr.lock()) { - vsync->resync(getVsyncPeriod); - } - }; - } + bool isConfigAllowed(const DisplayId& displayId, int32_t config); /* * Display identification @@ -898,6 +896,7 @@ private: 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 { @@ -906,9 +905,7 @@ private: 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); @@ -939,6 +936,9 @@ private: 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; @@ -957,11 +957,7 @@ private: // constant members (no synchronization needed for access) nsecs_t mBootTime; bool mGpuToCpuSupported; - std::unique_ptr<EventThread> mEventThread; - std::unique_ptr<EventThread> mSFEventThread; std::unique_ptr<EventThread> mInjectorEventThread; - std::unique_ptr<VSyncSource> mEventThreadSource; - std::unique_ptr<VSyncSource> mSfEventThreadSource; std::unique_ptr<InjectVSyncSource> mVSyncInjector; std::unique_ptr<EventControlThread> mEventControlThread; @@ -982,7 +978,12 @@ private: // 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, @@ -1007,6 +1008,7 @@ private: int mDebugRegion; int mDebugDisableHWC; int mDebugDisableTransformHint; + bool mDebugEnableProtectedContent; volatile nsecs_t mDebugInSwapBuffers; volatile nsecs_t mDebugInTransaction; nsecs_t mLastTransactionTime; @@ -1015,10 +1017,14 @@ private: 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; @@ -1028,16 +1034,11 @@ private: // these are thread safe mutable std::unique_ptr<MessageQueue> mEventQueue{mFactory.createMessageQueue()}; FrameTracker mAnimFrameTracker; - std::unique_ptr<DispSync> mPrimaryDispSync; // protected by mDestroyedLayerLock; mutable Mutex mDestroyedLayerLock; Vector<Layer const *> mDestroyedLayers; - // protected by mHWVsyncLock - Mutex mHWVsyncLock; - bool mPrimaryHWVsyncEnabled; - bool mHWVsyncAvailable; nsecs_t mRefreshStartTime; std::atomic<bool> mRefreshPending{false}; @@ -1057,18 +1058,19 @@ private: struct TransactionState { TransactionState(const Vector<ComposerState>& composerStates, const Vector<DisplayState>& displayStates, uint32_t transactionFlags, - int64_t desiredPresentTime, - bool privileged) + int64_t desiredPresentTime, int64_t postTime, bool privileged) : states(composerStates), displays(displayStates), flags(transactionFlags), - time(desiredPresentTime), + desiredPresentTime(desiredPresentTime), + postTime(postTime), privileged(privileged) {} Vector<ComposerState> states; Vector<DisplayState> displays; uint32_t flags; - int64_t time; + const int64_t desiredPresentTime; + const int64_t postTime; bool privileged; }; std::unordered_map<sp<IBinder>, std::queue<TransactionState>, IBinderHash> mTransactionQueues; @@ -1113,24 +1115,23 @@ private: /* ------------------------------------------------------------------------ * Scheduler */ - bool mUseScheduler = false; + bool mUseSmart90ForVideo = false; std::unique_ptr<Scheduler> mScheduler; sp<Scheduler::ConnectionHandle> mAppConnectionHandle; sp<Scheduler::ConnectionHandle> mSfConnectionHandle; std::unique_ptr<scheduler::RefreshRateStats> mRefreshRateStats; - // The following structs are used for configuring active config state at a desired time, - // which is once per vsync at invalidate time. - enum SetActiveConfigState { - NONE, // not in progress - NOTIFIED_HWC, // call to HWC has been made - REFRESH_RECEIVED // onRefresh was received from HWC - }; - std::atomic<SetActiveConfigState> mSetActiveConfigState = SetActiveConfigState::NONE; + std::unordered_map<DisplayId, std::shared_ptr<scheduler::RefreshRateConfigs>> + mRefreshRateConfigs; + + std::mutex mAllowedConfigsLock; + std::unordered_map<DisplayId, std::unique_ptr<const AllowedDisplayConfigs>> mAllowedConfigs + GUARDED_BY(mAllowedConfigsLock); struct ActiveConfigInfo { int configId; sp<IBinder> displayToken; + Scheduler::ConfigEvent event; bool operator!=(const ActiveConfigInfo& other) const { if (configId != other.configId) { @@ -1147,14 +1148,24 @@ private: // This bit can be set at any point in time when the system wants the new config. ActiveConfigInfo mDesiredActiveConfig GUARDED_BY(mActiveConfigLock); + // below flags are set by main thread only + bool mDesiredActiveConfigChanged GUARDED_BY(mActiveConfigLock) = false; + bool mCheckPendingFence = false; + /* ------------------------------------------------------------------------ */ + bool mLumaSampling = true; + sp<RegionSamplingThread> mRegionSamplingThread; + sp<IInputFlinger> mInputFlinger; InputWindowCommands mPendingInputWindowCommands GUARDED_BY(mStateLock); // Should only be accessed by the main thread. InputWindowCommands mInputWindowCommands; - ui::DisplayPrimaries mInternalDisplayPrimaries; + + sp<SetInputWindowsListener> mSetInputWindowsListener; + bool mPendingSyncInputWindows GUARDED_BY(mStateLock); + Hwc2::impl::PowerAdvisor mPowerAdvisor; }; }; // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.cpp b/services/surfaceflinger/SurfaceFlingerProperties.cpp index b654ba7669..e130511e94 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.cpp +++ b/services/surfaceflinger/SurfaceFlingerProperties.cpp @@ -4,9 +4,9 @@ #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> #include <android/hardware/configstore/1.1/types.h> -#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h> #include <configstore/Utils.h> +#include <cstdlib> #include <tuple> #include "SurfaceFlingerProperties.h" @@ -15,8 +15,9 @@ namespace android { namespace sysprop { using namespace android::hardware::configstore; using namespace android::hardware::configstore::V1_0; -using ::android::hardware::graphics::common::V1_2::Dataspace; -using ::android::hardware::graphics::common::V1_2::PixelFormat; +using android::hardware::graphics::common::V1_2::Dataspace; +using android::hardware::graphics::common::V1_2::PixelFormat; +using android::ui::DisplayPrimaries; int64_t vsync_event_phase_offset_ns(int64_t defaultValue) { auto temp = SurfaceFlingerProperties::vsync_event_phase_offset_ns(); @@ -165,33 +166,15 @@ bool use_color_management(bool defaultValue) { auto tmpuseColorManagement = SurfaceFlingerProperties::use_color_management(); auto tmpHasHDRDisplay = SurfaceFlingerProperties::has_HDR_display(); auto tmpHasWideColorDisplay = SurfaceFlingerProperties::has_wide_color_display(); - if (tmpuseColorManagement.has_value() && tmpHasHDRDisplay.has_value() && - tmpHasWideColorDisplay.has_value()) { - return *tmpuseColorManagement || *tmpHasHDRDisplay || *tmpHasWideColorDisplay; - } - auto surfaceFlingerConfigsServiceV1_2 = ISurfaceFlingerConfigs::getService(); - if (surfaceFlingerConfigsServiceV1_2) { - return getBool<V1_2::ISurfaceFlingerConfigs, - &V1_2::ISurfaceFlingerConfigs::useColorManagement>(defaultValue); - } - return defaultValue; -} -auto getCompositionPreference(sp<V1_2::ISurfaceFlingerConfigs> configsServiceV1_2) { - Dataspace defaultCompositionDataspace = Dataspace::V0_SRGB; - PixelFormat defaultCompositionPixelFormat = PixelFormat::RGBA_8888; - Dataspace wideColorGamutCompositionDataspace = Dataspace::V0_SRGB; - PixelFormat wideColorGamutCompositionPixelFormat = PixelFormat::RGBA_8888; - configsServiceV1_2->getCompositionPreference( - [&](auto tmpDefaultDataspace, auto tmpDefaultPixelFormat, - auto tmpWideColorGamutDataspace, auto tmpWideColorGamutPixelFormat) { - defaultCompositionDataspace = tmpDefaultDataspace; - defaultCompositionPixelFormat = tmpDefaultPixelFormat; - wideColorGamutCompositionDataspace = tmpWideColorGamutDataspace; - wideColorGamutCompositionPixelFormat = tmpWideColorGamutPixelFormat; - }); - return std::tuple(defaultCompositionDataspace, defaultCompositionPixelFormat, - wideColorGamutCompositionDataspace, wideColorGamutCompositionPixelFormat); + auto tmpuseColorManagementVal = tmpuseColorManagement.has_value() ? *tmpuseColorManagement : + defaultValue; + auto tmpHasHDRDisplayVal = tmpHasHDRDisplay.has_value() ? *tmpHasHDRDisplay : + defaultValue; + auto tmpHasWideColorDisplayVal = tmpHasWideColorDisplay.has_value() ? *tmpHasWideColorDisplay : + defaultValue; + + return tmpuseColorManagementVal || tmpHasHDRDisplayVal || tmpHasWideColorDisplayVal; } int64_t default_composition_dataspace(Dataspace defaultValue) { @@ -199,10 +182,6 @@ int64_t default_composition_dataspace(Dataspace defaultValue) { if (temp.has_value()) { return *temp; } - auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService(); - if (configsServiceV1_2) { - return static_cast<int64_t>(get<0>(getCompositionPreference(configsServiceV1_2))); - } return static_cast<int64_t>(defaultValue); } @@ -211,10 +190,6 @@ int32_t default_composition_pixel_format(PixelFormat defaultValue) { if (temp.has_value()) { return *temp; } - auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService(); - if (configsServiceV1_2) { - return static_cast<int32_t>(get<1>(getCompositionPreference(configsServiceV1_2))); - } return static_cast<int32_t>(defaultValue); } @@ -223,10 +198,6 @@ int64_t wcg_composition_dataspace(Dataspace defaultValue) { if (temp.has_value()) { return *temp; } - auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService(); - if (configsServiceV1_2) { - return static_cast<int64_t>(get<2>(getCompositionPreference(configsServiceV1_2))); - } return static_cast<int64_t>(defaultValue); } @@ -235,12 +206,66 @@ int32_t wcg_composition_pixel_format(PixelFormat defaultValue) { if (temp.has_value()) { return *temp; } - auto configsServiceV1_2 = V1_2::ISurfaceFlingerConfigs::getService(); - if (configsServiceV1_2) { - return static_cast<int32_t>(get<3>(getCompositionPreference(configsServiceV1_2))); - } 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; +constexpr float kSrgbRedY = 0.2126f; +constexpr float kSrgbRedZ = 0.0193f; +constexpr float kSrgbGreenX = 0.3576f; +constexpr float kSrgbGreenY = 0.7152f; +constexpr float kSrgbGreenZ = 0.1192f; +constexpr float kSrgbBlueX = 0.1805f; +constexpr float kSrgbBlueY = 0.0722f; +constexpr float kSrgbBlueZ = 0.9506f; +constexpr float kSrgbWhiteX = 0.9505f; +constexpr float kSrgbWhiteY = 1.0000f; +constexpr float kSrgbWhiteZ = 1.0891f; + +DisplayPrimaries getDisplayNativePrimaries() { + auto mDisplay_primary_red = SurfaceFlingerProperties::display_primary_red(); + auto mDisplay_primary_green = SurfaceFlingerProperties::display_primary_green(); + auto mDisplay_primary_blue = SurfaceFlingerProperties::display_primary_blue(); + auto mDisplay_primary_white = SurfaceFlingerProperties::display_primary_white(); + // To avoid null point exception. + mDisplay_primary_red.resize(DISPLAY_PRIMARY_SIZE); + mDisplay_primary_green.resize(DISPLAY_PRIMARY_SIZE); + mDisplay_primary_blue.resize(DISPLAY_PRIMARY_SIZE); + mDisplay_primary_white.resize(DISPLAY_PRIMARY_SIZE); + DisplayPrimaries primaries = + {{static_cast<float>(mDisplay_primary_red[0].value_or(kSrgbRedX)), + static_cast<float>(mDisplay_primary_red[1].value_or(kSrgbRedY)), + static_cast<float>(mDisplay_primary_red[2].value_or(kSrgbRedZ))}, + {static_cast<float>(mDisplay_primary_green[0].value_or(kSrgbGreenX)), + static_cast<float>(mDisplay_primary_green[1].value_or(kSrgbGreenY)), + static_cast<float>(mDisplay_primary_green[2].value_or(kSrgbGreenZ))}, + {static_cast<float>(mDisplay_primary_blue[0].value_or(kSrgbBlueX)), + static_cast<float>(mDisplay_primary_blue[1].value_or(kSrgbBlueY)), + static_cast<float>(mDisplay_primary_blue[2].value_or(kSrgbBlueZ))}, + {static_cast<float>(mDisplay_primary_white[0].value_or(kSrgbWhiteX)), + static_cast<float>(mDisplay_primary_white[1].value_or(kSrgbWhiteY)), + static_cast<float>(mDisplay_primary_white[2].value_or(kSrgbWhiteZ))}}; + + return primaries; +} + } // namespace sysprop } // namespace android diff --git a/services/surfaceflinger/SurfaceFlingerProperties.h b/services/surfaceflinger/SurfaceFlingerProperties.h index 9b26883dae..6f90117458 100644 --- a/services/surfaceflinger/SurfaceFlingerProperties.h +++ b/services/surfaceflinger/SurfaceFlingerProperties.h @@ -3,8 +3,9 @@ #define SURFACEFLINGERPROPERTIES_H_ #include <android/hardware/configstore/1.1/ISurfaceFlingerConfigs.h> -#include <android/hardware/configstore/1.2/ISurfaceFlingerConfigs.h> +#include <android/hardware/graphics/common/1.2/types.h> #include <sysprop/SurfaceFlingerProperties.sysprop.h> +#include <ui/ConfigStoreTypes.h> #include <cstdint> #include <optional> @@ -53,6 +54,12 @@ int64_t wcg_composition_dataspace( 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 #endif // SURFACEFLINGERPROPERTIES_H_ diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp index b7e9a915fa..db78f1db2e 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 = 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::LayersTraceBuffer::flush(LayersTraceFileProto* fileProto) { } } -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 @@ status_t SurfaceTracing::writeProtoFileLocked() { 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 fd919af999..94844803e9 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 @@ private: 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 @@ private: std::queue<LayersTraceProto> mStorage; }; - status_t writeProtoFileLocked(); + void mainLoop(); + void traceLayers(const char* where); + LayersTraceProto traceLayersLocked(const char* where); + void writeProtoFileLocked() REQUIRES(mTraceLock); + + const SurfaceFlinger& mFlinger; + + const char* mWhere = ""; + status_t mLastErr = NO_ERROR; + std::thread mThread; + std::condition_variable mConditionalVariable; + mutable std::mutex mTraceLock; - bool mEnabled = false; - mutable std::mutex mTraceMutex; - LayersTraceBuffer mBuffer; + 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/layerproto/LayerProtoParser.cpp b/services/surfaceflinger/layerproto/LayerProtoParser.cpp index 5c72fea375..7288232bf6 100644 --- a/services/surfaceflinger/layerproto/LayerProtoParser.cpp +++ b/services/surfaceflinger/layerproto/LayerProtoParser.cpp @@ -132,7 +132,6 @@ LayerProtoParser::Layer LayerProtoParser::generateLayer(const LayerProto& layerP LayerProtoParser::Region LayerProtoParser::generateRegion(const RegionProto& regionProto) { LayerProtoParser::Region region; - region.id = regionProto.id(); for (int i = 0; i < regionProto.rect_size(); i++) { const RectProto& rectProto = regionProto.rect(i); region.rects.push_back(generateRect(rectProto)); @@ -199,13 +198,13 @@ void LayerProtoParser::updateChildrenAndRelative(const LayerProto& layerProto, } } - if (layerProto.has_parent()) { + if (layerProto.parent() != -1) { if (layerMap.count(layerProto.parent()) > 0) { currLayer->parent = layerMap[layerProto.parent()]; } } - if (layerProto.has_z_order_relative_of()) { + if (layerProto.z_order_relative_of() != -1) { if (layerMap.count(layerProto.z_order_relative_of()) > 0) { currLayer->zOrderRelativeOf = layerMap[layerProto.z_order_relative_of()]; } diff --git a/services/surfaceflinger/layerproto/layers.proto b/services/surfaceflinger/layerproto/layers.proto index 72cbfac436..fd4695ecba 100644 --- a/services/surfaceflinger/layerproto/layers.proto +++ b/services/surfaceflinger/layerproto/layers.proto @@ -1,156 +1,157 @@ // Definitions for SurfaceFlinger layers. -syntax = "proto2"; +syntax = "proto3"; option optimize_for = LITE_RUNTIME; package android.surfaceflinger; // Contains a list of all layers. message LayersProto { repeated LayerProto layers = 1; - optional SizeProto resolution = 2; - optional string color_mode = 3; - optional string color_transform = 4; - optional int32 global_transform = 5; + SizeProto resolution = 2; + string color_mode = 3; + string color_transform = 4; + int32 global_transform = 5; } // Information about each layer. message LayerProto { // unique id per layer. - optional int32 id = 1; + int32 id = 1; // unique name per layer. - optional string name = 2; + string name = 2; // list of children this layer may have. May be empty. repeated int32 children = 3; // list of layers that are z order relative to this layer. repeated int32 relatives = 4; // The type of layer, ex Color, Layer - optional string type = 5; - optional RegionProto transparent_region = 6; - optional RegionProto visible_region = 7; - optional RegionProto damage_region = 8; - optional uint32 layer_stack = 9; + string type = 5; + RegionProto transparent_region = 6; + RegionProto visible_region = 7; + RegionProto damage_region = 8; + uint32 layer_stack = 9; // The layer's z order. Can be z order in layer stack, relative to parent, // or relative to another layer specified in zOrderRelative. - optional int32 z = 10; + int32 z = 10; // The layer's position on the display. - optional PositionProto position = 11; + PositionProto position = 11; // The layer's requested position. - optional PositionProto requested_position = 12; + PositionProto requested_position = 12; // The layer's size. - optional SizeProto size = 13; + SizeProto size = 13; // The layer's crop in it's own bounds. - optional RectProto crop = 14; + RectProto crop = 14; // The layer's crop in it's parent's bounds. - optional RectProto final_crop = 15 [deprecated=true]; - optional bool is_opaque = 16; - optional bool invalidate = 17; - optional string dataspace = 18; - optional string pixel_format = 19; + RectProto final_crop = 15 [deprecated=true]; + bool is_opaque = 16; + bool invalidate = 17; + string dataspace = 18; + string pixel_format = 19; // The layer's actual color. - optional ColorProto color = 20; + ColorProto color = 20; // The layer's requested color. - optional ColorProto requested_color = 21; + ColorProto requested_color = 21; // Can be any combination of // hidden = 0x01 // opaque = 0x02, // secure = 0x80, - optional uint32 flags = 22; + uint32 flags = 22; // The layer's actual transform - optional TransformProto transform = 23; + TransformProto transform = 23; // The layer's requested transform. - optional TransformProto requested_transform = 24; + TransformProto requested_transform = 24; // The parent layer. This value can be null if there is no parent. - optional int32 parent = 25 [default = -1]; + int32 parent = 25; // The layer that this layer has a z order relative to. This value can be null. - optional int32 z_order_relative_of = 26 [default = -1]; + int32 z_order_relative_of = 26; // This value can be null if there's nothing to draw. - optional ActiveBufferProto active_buffer = 27; + ActiveBufferProto active_buffer = 27; // The number of frames available. - optional int32 queued_frames = 28; - optional bool refresh_pending = 29; + int32 queued_frames = 28; + bool refresh_pending = 29; // The layer's composer backend destination frame - optional RectProto hwc_frame = 30; + RectProto hwc_frame = 30; // The layer's composer backend source crop - optional FloatRectProto hwc_crop = 31; + FloatRectProto hwc_crop = 31; // The layer's composer backend transform - optional int32 hwc_transform = 32; - optional int32 window_type = 33 [deprecated=true]; - optional int32 app_id = 34 [deprecated=true]; + int32 hwc_transform = 32; + int32 window_type = 33 [deprecated=true]; + int32 app_id = 34 [deprecated=true]; // The layer's composition type - optional int32 hwc_composition_type = 35; + int32 hwc_composition_type = 35; // If it's a buffer layer, indicate if the content is protected - optional bool is_protected = 36; + bool is_protected = 36; // Current frame number being rendered. - optional uint64 curr_frame = 37; + uint64 curr_frame = 37; // A list of barriers that the layer is waiting to update state. repeated BarrierLayerProto barrier_layer = 38; // If active_buffer is not null, record its transform. - optional TransformProto buffer_transform = 39; - optional int32 effective_scaling_mode = 40; + TransformProto buffer_transform = 39; + int32 effective_scaling_mode = 40; // Layer's corner radius. - optional float corner_radius = 41; + float corner_radius = 41; // Metadata map. May be empty. map<int32, bytes> metadata = 42; - optional TransformProto effective_transform = 43; - optional FloatRectProto source_bounds = 44; - optional FloatRectProto bounds = 45; - optional FloatRectProto screen_bounds = 46; + TransformProto effective_transform = 43; + FloatRectProto source_bounds = 44; + FloatRectProto bounds = 45; + FloatRectProto screen_bounds = 46; } message PositionProto { - optional float x = 1; - optional float y = 2; + float x = 1; + float y = 2; } message SizeProto { - optional int32 w = 1; - optional int32 h = 2; + int32 w = 1; + int32 h = 2; } message TransformProto { - optional float dsdx = 1; - optional float dtdx = 2; - optional float dsdy = 3; - optional float dtdy = 4; + float dsdx = 1; + float dtdx = 2; + float dsdy = 3; + float dtdy = 4; + int32 type = 5; } message RegionProto { - optional uint64 id = 1; + reserved 1; // Previously: uint64 id repeated RectProto rect = 2; } message RectProto { - optional int32 left = 1; - optional int32 top = 2; - optional int32 right = 3; - optional int32 bottom = 4; + int32 left = 1; + int32 top = 2; + int32 right = 3; + int32 bottom = 4; } message FloatRectProto { - optional float left = 1; - optional float top = 2; - optional float right = 3; - optional float bottom = 4; + float left = 1; + float top = 2; + float right = 3; + float bottom = 4; } message ActiveBufferProto { - optional uint32 width = 1; - optional uint32 height = 2; - optional uint32 stride = 3; - optional int32 format = 4; + uint32 width = 1; + uint32 height = 2; + uint32 stride = 3; + int32 format = 4; } message ColorProto { - optional float r = 1; - optional float g = 2; - optional float b = 3; - optional float a = 4; + float r = 1; + float g = 2; + float b = 3; + float a = 4; } message BarrierLayerProto { // layer id the barrier is waiting on. - optional int32 id = 1; + int32 id = 1; // frame number the barrier is waiting on. - optional uint64 frame_number = 2; + uint64 frame_number = 2; } diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index cc7b280330..fe6dc931f7 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -250,3 +250,60 @@ prop { access: Readonly prop_name: "ro.surface_flinger.wcg_composition_pixel_format" } + +# Return the native panel primary data. The data includes red, green, +# blue and white. The primary format is CIE 1931 XYZ color space. +# If unspecified, the primaries is sRGB gamut by default. + +prop { + api_name: "display_primary_red" + type: DoubleList + scope: Internal + access: Readonly + prop_name: "ro.surface_flinger.display_primary_red" +} + +prop { + api_name: "display_primary_green" + type: DoubleList + scope: Internal + access: Readonly + prop_name: "ro.surface_flinger.display_primary_green" +} + +prop { + api_name: "display_primary_blue" + type: DoubleList + scope: Internal + access: Readonly + prop_name: "ro.surface_flinger.display_primary_blue" +} + +prop { + api_name: "display_primary_white" + type: DoubleList + scope: Internal + 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 61d09daed2..b667a74db0 100644 --- a/services/surfaceflinger/tests/Credentials_test.cpp +++ b/services/surfaceflinger/tests/Credentials_test.cpp @@ -12,7 +12,6 @@ #include <private/android_filesystem_config.h> #include <private/gui/ComposerService.h> - #include <ui/DisplayInfo.h> #include <utils/String8.h> @@ -356,4 +355,11 @@ TEST_F(CredentialsTest, IsWideColorDisplayWithPrivileges) { ASSERT_NO_FATAL_FAILURE(checkWithPrivileges<status_t>(condition, NO_ERROR, NO_ERROR)); } +TEST_F(CredentialsTest, GetActiveColorModeBasicCorrectness) { + const auto display = SurfaceComposerClient::getInternalDisplayToken(); + ASSERT_FALSE(display == nullptr); + ColorMode colorMode = SurfaceComposerClient::getActiveColorMode(display); + ASSERT_NE(static_cast<ColorMode>(BAD_VALUE), colorMode); +} + } // namespace android diff --git a/services/surfaceflinger/tests/SurfaceFlinger_test.filter b/services/surfaceflinger/tests/SurfaceFlinger_test.filter index 91999ae7f0..be862c9d16 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 7b21dbca01..d62afa5ec2 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -32,6 +32,8 @@ #include <gui/LayerState.h> #include <gui/Surface.h> #include <gui/SurfaceComposerClient.h> +#include <hardware/hwcomposer_defs.h> +#include <private/android_filesystem_config.h> #include <private/gui/ComposerService.h> #include <ui/ColorSpace.h> @@ -41,6 +43,8 @@ #include <math.h> #include <math/vec3.h> +#include <sys/types.h> +#include <unistd.h> #include "BufferGenerator.h" @@ -195,12 +199,15 @@ static void fillSurfaceRGBA8(const sp<SurfaceControl>& sc, uint8_t r, uint8_t g, 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); } @@ -479,6 +486,12 @@ protected: 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); @@ -1201,6 +1214,56 @@ TEST_P(LayerTypeTransactionTest, SetFlagsSecure) { composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); } +/** RAII Wrapper around get/seteuid */ +class UIDFaker { + uid_t oldId; +public: + UIDFaker(uid_t uid) { + oldId = geteuid(); + seteuid(uid); + } + ~UIDFaker() { + seteuid(oldId); + } +}; + +TEST_F(LayerTransactionTest, SetFlagsSecureEUidSystem) { + sp<SurfaceControl> layer; + ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", 32, 32)); + ASSERT_NO_FATAL_FAILURE(fillBufferQueueLayerColor(layer, Color::RED, 32, 32)); + + sp<ISurfaceComposer> composer = ComposerService::getComposerService(); + sp<GraphicBuffer> outBuffer; + Transaction() + .setFlags(layer, layer_state_t::eLayerSecure, layer_state_t::eLayerSecure) + .apply(true); + ASSERT_EQ(PERMISSION_DENIED, + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); + + UIDFaker f(AID_SYSTEM); + + // By default the system can capture screenshots with secure layers but they + // will be blacked out + ASSERT_EQ(NO_ERROR, + composer->captureScreen(mDisplay, &outBuffer, Rect(), 0, 0, false)); + + { + SCOPED_TRACE("as system"); + auto shot = screenshot(); + shot->expectColor(Rect(0, 0, 32, 32), Color::BLACK); + } + + // Here we pass captureSecureLayers = true and since we are AID_SYSTEM we should be able + // to receive them...we are expected to take care with the results. + ASSERT_EQ(NO_ERROR, + composer->captureScreen(mDisplay, &outBuffer, + ui::Dataspace::V0_SRGB, ui::PixelFormat::RGBA_8888, + Rect(), 0, 0, false, + ISurfaceComposer::eRotateNone, true)); + ScreenCapture sc(outBuffer); + sc.expectColor(Rect(0, 0, 32, 32), Color::RED); +} + TEST_P(LayerRenderTypeTransactionTest, SetTransparentRegionHintBasic_BufferQueue) { const Rect top(0, 0, 32, 16); const Rect bottom(0, 16, 32, 32); @@ -1960,8 +2023,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropBasic_BufferState) { Transaction().setCrop(layer, crop).apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferQueue) { @@ -1991,13 +2054,13 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropEmpty_BufferState) { { SCOPED_TRACE("empty rect"); Transaction().setCrop(layer, Rect(8, 8, 8, 8)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); } { SCOPED_TRACE("negative rect"); Transaction().setCrop(layer, Rect(8, 8, 0, 0)).apply(); - getScreenCapture()->expectColor(Rect(0, 0, 32, 32), Color::RED); + getScreenCapture()->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); } } @@ -2014,37 +2077,36 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferQueue) { TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) { sp<SurfaceControl> layer; - ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", mDisplayWidth, mDisplayHeight / 2, - ISurfaceComposerClient::eFXSurfaceBufferState)); + ASSERT_NO_FATAL_FAILURE( + layer = createLayer("test", 32, 64, ISurfaceComposerClient::eFXSurfaceBufferState)); sp<GraphicBuffer> buffer = - new GraphicBuffer(mDisplayWidth, mDisplayHeight / 2, PIXEL_FORMAT_RGBA_8888, 1, + new GraphicBuffer(32, 64, PIXEL_FORMAT_RGBA_8888, 1, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | BufferUsage::COMPOSER_OVERLAY, "test"); - fillGraphicBufferColor(buffer, Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE); - fillGraphicBufferColor(buffer, Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2), - Color::RED); + fillGraphicBufferColor(buffer, Rect(0, 0, 32, 16), Color::BLUE); + fillGraphicBufferColor(buffer, Rect(0, 16, 32, 64), Color::RED); + + Transaction().setFrame(layer, Rect(0, 0, 64, 64)).apply(); Transaction().setBuffer(layer, buffer).apply(); // Partially out of bounds in the negative (upper left) direction - Transaction().setCrop(layer, Rect(-128, -128, mDisplayWidth, mDisplayHeight / 4)).apply(); + Transaction().setCrop(layer, Rect(-128, -128, 32, 16)).apply(); { SCOPED_TRACE("out of bounds, negative (upper left) direction"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLUE); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK); + shot->expectColor(Rect(0, 0, 64, 64), Color::BLUE); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); } // Partially out of bounds in the positive (lower right) direction - Transaction() - .setCrop(layer, Rect(0, mDisplayHeight / 4, mDisplayWidth + 1, mDisplayHeight)) - .apply(); + Transaction().setCrop(layer, Rect(0, 16, 128, 128)).apply(); { SCOPED_TRACE("out of bounds, positive (lower right) direction"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::RED); - shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight / 2), Color::BLACK); + shot->expectColor(Rect(0, 0, 64, 64), Color::RED); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); } // Fully out of buffer space bounds @@ -2052,9 +2114,9 @@ TEST_P(LayerRenderTypeTransactionTest, SetCropOutOfBounds_BufferState) { { SCOPED_TRACE("Fully out of bounds"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight / 4), Color::BLUE); - shot->expectColor(Rect(0, mDisplayHeight / 4, mDisplayWidth, mDisplayHeight / 2), - Color::RED); + shot->expectColor(Rect(0, 0, 64, 16), Color::BLUE); + shot->expectColor(Rect(0, 16, 64, 64), Color::RED); + shot->expectBorder(Rect(0, 0, 64, 64), Color::BLACK); } } @@ -2231,8 +2293,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultParentless_BufferState) { // A parentless layer will default to a frame with the same size as the buffer auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 10, 10), Color::RED); - shot->expectBorder(Rect(0, 0, 10, 10), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetFrameDefaultBSParent_BufferState) { @@ -2315,8 +2377,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferBasic_BufferState) { ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { @@ -2329,8 +2391,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { { SCOPED_TRACE("set buffer 1"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::BLUE, 32, 32)); @@ -2338,8 +2400,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { { SCOPED_TRACE("set buffer 2"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::BLUE); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLUE); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } ASSERT_NO_FATAL_FAILURE(fillBufferStateLayerColor(layer, Color::RED, 32, 32)); @@ -2347,8 +2409,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferMultipleBuffers_BufferState) { { SCOPED_TRACE("set buffer 3"); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } } @@ -2430,8 +2492,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_BufferState) { Color color = colors[idx % colors.size()]; auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), color); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } idx++; } @@ -2466,8 +2528,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_LeastRecentlyUsed_Buffer Color color = colors[idx % colors.size()]; auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), color); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } idx++; } @@ -2502,8 +2564,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetBufferCaching_DestroyedBuffer_BufferSt Color color = colors[idx % colors.size()]; auto shot = screenshot(); - shot->expectColor(Rect(0, 0, 32, 32), color); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), color); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } if (idx == 0) { buffers[0].clear(); @@ -2601,8 +2663,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetFenceBasic_BufferState) { std::this_thread::sleep_for(200ms); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { @@ -2625,8 +2687,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetFenceNull_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) { @@ -2647,8 +2709,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetDataspaceBasic_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) { @@ -2671,8 +2733,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetHdrMetadataBasic_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) { @@ -2695,8 +2757,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetSurfaceDamageRegionBasic_BufferState) .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) { @@ -2717,8 +2779,8 @@ TEST_P(LayerRenderTypeTransactionTest, SetApiBasic_BufferState) { .apply(); auto shot = getScreenCapture(); - shot->expectColor(Rect(0, 0, 32, 32), Color::RED); - shot->expectBorder(Rect(0, 0, 32, 32), Color::BLACK); + shot->expectColor(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::RED); + shot->expectBorder(Rect(0, 0, mDisplayWidth, mDisplayHeight), Color::BLACK); } TEST_F(LayerTransactionTest, SetSidebandStreamNull_BufferState) { @@ -4033,11 +4095,6 @@ protected: 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; @@ -4250,7 +4307,7 @@ class ChildLayerTest : public LayerUpdateTest { protected: void SetUp() override { LayerUpdateTest::SetUp(); - mChild = createSurface(mClient, "Child surface", 10, 10, PIXEL_FORMAT_RGBA_8888, 0, + mChild = createSurface(mClient, "Child surface", 10, 15, PIXEL_FORMAT_RGBA_8888, 0, mFGSurfaceControl.get()); fillSurfaceRGBA8(mChild, 200, 200, 200); @@ -4356,6 +4413,32 @@ TEST_F(ChildLayerTest, ChildLayerScaling) { } } +// A child with a scale transform should be cropped by its parent bounds. +TEST_F(ChildLayerTest, ChildLayerScalingCroppedByParent) { + asTransaction([&](Transaction& t) { + t.setPosition(mFGSurfaceControl, 0, 0); + t.setPosition(mChild, 0, 0); + }); + + // Find the boundary between the parent and child. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 9); + mCapture->expectFGColor(10, 10); + } + + asTransaction([&](Transaction& t) { t.setMatrix(mChild, 10.0, 0, 0, 10.0); }); + + // The child should fill its parent bounds and be cropped by it. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(63, 63); + mCapture->expectBGColor(64, 64); + } +} + TEST_F(ChildLayerTest, ChildLayerAlpha) { fillSurfaceRGBA8(mBGSurfaceControl, 0, 0, 254); fillSurfaceRGBA8(mFGSurfaceControl, 254, 0, 0); @@ -4592,8 +4675,8 @@ TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); - // But it's only 10x10. - mCapture->expectFGColor(10, 10); + // But it's only 10x15. + mCapture->expectFGColor(10, 15); } asTransaction([&](Transaction& t) { @@ -4607,9 +4690,9 @@ TEST_F(ChildLayerTest, ChildrenInheritNonTransformScalingFromParent) { // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); mCapture->expectChildColor(10, 10); - mCapture->expectChildColor(19, 19); - // And now it should be scaled all the way to 20x20 - mCapture->expectFGColor(20, 20); + mCapture->expectChildColor(19, 29); + // And now it should be scaled all the way to 20x30 + mCapture->expectFGColor(20, 30); } } @@ -4625,8 +4708,9 @@ TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { mCapture = screenshot(); // We've positioned the child in the top left. mCapture->expectChildColor(0, 0); - // But it's only 10x10. - mCapture->expectFGColor(10, 10); + mCapture->expectChildColor(9, 14); + // But it's only 10x15. + mCapture->expectFGColor(10, 15); } // We set things up as in b/37673612 so that there is a mismatch between the buffer size and // the WM specified state size. @@ -4647,6 +4731,115 @@ TEST_F(ChildLayerTest, ChildrenWithParentBufferTransform) { } } +// A child with a buffer transform from its parents should be cropped by its parent bounds. +TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferTransform) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + t.setSize(mChild, 100, 100); + }); + fillSurfaceRGBA8(mChild, 200, 200, 200); + + { + mCapture = screenshot(); + + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(63, 63); + mCapture->expectBGColor(64, 64); + } + + asTransaction([&](Transaction& t) { t.setSize(mFGSurfaceControl, 128, 64); }); + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + // Apply a 90 transform on the buffer. + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 64, 128); + fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + waitForPostedBuffers(); + + // The child should be cropped by the new parent bounds. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(99, 63); + mCapture->expectFGColor(100, 63); + mCapture->expectBGColor(128, 64); + } +} + +// A child with a scale transform from its parents should be cropped by its parent bounds. +TEST_F(ChildLayerTest, ChildCroppedByParentWithBufferScale) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + t.setSize(mChild, 200, 200); + }); + fillSurfaceRGBA8(mChild, 200, 200, 200); + + { + mCapture = screenshot(); + + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(63, 63); + mCapture->expectBGColor(64, 64); + } + + asTransaction([&](Transaction& t) { + t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + // Set a scaling by 2. + t.setSize(mFGSurfaceControl, 128, 128); + }); + + // Child should inherit its parents scale but should be cropped by its parent bounds. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(127, 127); + mCapture->expectBGColor(128, 128); + } +} + +// Regression test for b/127368943 +// Child should ignore the buffer transform but apply parent scale transform. +TEST_F(ChildLayerTest, ChildrenWithParentBufferTransformAndScale) { + asTransaction([&](Transaction& t) { + t.show(mChild); + t.setPosition(mChild, 0, 0); + t.setPosition(mFGSurfaceControl, 0, 0); + }); + + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(9, 14); + mCapture->expectFGColor(10, 15); + } + + // Change the size of the foreground to 128 * 64 so we can test rotation as well. + asTransaction([&](Transaction& t) { + t.setOverrideScalingMode(mFGSurfaceControl, NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW); + t.setSize(mFGSurfaceControl, 128, 64); + }); + sp<Surface> s = mFGSurfaceControl->getSurface(); + auto anw = static_cast<ANativeWindow*>(s.get()); + // Apply a 90 transform on the buffer and submit a buffer half the expected size so that we + // have an effective scale of 2.0 applied to the buffer along with a rotation transform. + native_window_set_buffers_transform(anw, NATIVE_WINDOW_TRANSFORM_ROT_90); + native_window_set_buffers_dimensions(anw, 32, 64); + fillSurfaceRGBA8(mFGSurfaceControl, 195, 63, 63); + waitForPostedBuffers(); + + // The child should ignore the buffer transform but apply the 2.0 scale from parent. + { + mCapture = screenshot(); + mCapture->expectChildColor(0, 0); + mCapture->expectChildColor(19, 29); + mCapture->expectFGColor(20, 30); + } +} + TEST_F(ChildLayerTest, Bug36858924) { // Destroy the child layer mChild.clear(); @@ -4800,6 +4993,7 @@ TEST_F(ChildLayerTest, ChildLayerRelativeLayer) { mCapture->checkPixel(10, 10, 255, 255, 255); } } + class BoundlessLayerTest : public LayerUpdateTest { protected: std::unique_ptr<ScreenCapture> mCapture; @@ -5126,23 +5320,35 @@ public: SurfaceComposerClient::Transaction().show(mChild).apply(true); } - void verify() { + void verify(std::function<void()> verifyStartingState) { + // Verify starting state before a screenshot is taken. + verifyStartingState(); + + // Verify child layer does not inherit any of the properties of its + // parent when its screenshot is captured. auto fgHandle = mFGSurfaceControl->getHandle(); ScreenCapture::captureChildLayers(&mCapture, fgHandle); mCapture->checkPixel(10, 10, 0, 0, 0); mCapture->expectChildColor(0, 0); + + // Verify all assumptions are still true after the screenshot is taken. + verifyStartingState(); } std::unique_ptr<ScreenCapture> mCapture; sp<SurfaceControl> mChild; }; +// Regression test b/76099859 TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentVisibility) { SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true); // Even though the parent is hidden we should still capture the child. - verify(); + + // Before and after reparenting, verify child is properly hidden + // when rendering full-screen. + verify([&] { screenshot()->expectBGColor(64, 64); }); } TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) { @@ -5151,25 +5357,19 @@ TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresParentCrop) { .apply(true); // Even though the parent is cropped out we should still capture the child. - verify(); + + // Before and after reparenting, verify child is cropped by parent. + verify([&] { screenshot()->expectBGColor(65, 65); }); } +// Regression test b/124372894 TEST_F(ScreenCaptureChildOnlyTest, CaptureLayerIgnoresTransform) { - - SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2); + SurfaceComposerClient::Transaction().setMatrix(mFGSurfaceControl, 2, 0, 0, 2).apply(true); // We should not inherit the parent scaling. - verify(); -} - -TEST_F(ScreenCaptureChildOnlyTest, RegressionTest76099859) { - SurfaceComposerClient::Transaction().hide(mFGSurfaceControl).apply(true); - - // Even though the parent is hidden we should still capture the child. - verify(); - // Verify everything was properly hidden when rendering the full-screen. - screenshot()->expectBGColor(0,0); + // Before and after reparenting, verify child is properly scaled. + verify([&] { screenshot()->expectChildColor(80, 80); }); } @@ -5362,4 +5562,162 @@ TEST_F(DereferenceSurfaceControlTest, LayerInTransaction) { } } +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 0000000000..42742543ac --- /dev/null +++ b/services/surfaceflinger/tests/unittests/AllowedDisplayConfigsTest.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" +#define LOG_NDEBUG 0 + +#include <memory> +#include <vector> + +#include <gtest/gtest.h> + +#include <log/log.h> + +#include "AllowedDisplayConfigs.h" + +namespace android { +namespace { + +class AllowedDisplayConfigsTest : public testing::Test { +protected: + AllowedDisplayConfigsTest(); + ~AllowedDisplayConfigsTest() override; + + void buildAllowedConfigs(); + + const std::vector<int32_t> expectedConfigs = {0, 2, 7, 129}; + constexpr static int32_t notAllowedConfig = 5; + std::unique_ptr<const AllowedDisplayConfigs> mAllowedConfigs; +}; + +AllowedDisplayConfigsTest::AllowedDisplayConfigsTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +AllowedDisplayConfigsTest::~AllowedDisplayConfigsTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +void AllowedDisplayConfigsTest::buildAllowedConfigs() { + AllowedDisplayConfigs::Builder builder; + for (int config : expectedConfigs) { + builder.addConfig(config); + } + mAllowedConfigs = builder.build(); +} + +/* ------------------------------------------------------------------------ + * Test cases + */ + +TEST_F(AllowedDisplayConfigsTest, checkConfigs) { + buildAllowedConfigs(); + + // Check that all expected configs are allowed + for (int config : expectedConfigs) { + EXPECT_TRUE(mAllowedConfigs->isConfigAllowed(config)); + } + + // Check that all the allowed configs are expected + std::vector<int32_t> allowedConfigVector; + mAllowedConfigs->getAllowedConfigs(&allowedConfigVector); + EXPECT_EQ(allowedConfigVector, expectedConfigs); + + // Check that notAllowedConfig is indeed not allowed + EXPECT_TRUE(std::find(expectedConfigs.begin(), expectedConfigs.end(), notAllowedConfig) == + expectedConfigs.end()); + EXPECT_FALSE(mAllowedConfigs->isConfigAllowed(notAllowedConfig)); +} + +TEST_F(AllowedDisplayConfigsTest, getAllowedConfigsNullptr) { + buildAllowedConfigs(); + + // No other expectations rather than no crash + mAllowedConfigs->getAllowedConfigs(nullptr); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/Android.bp b/services/surfaceflinger/tests/unittests/Android.bp index b1d45f39b0..25ce4ac605 100644 --- a/services/surfaceflinger/tests/unittests/Android.bp +++ b/services/surfaceflinger/tests/unittests/Android.bp @@ -35,7 +35,9 @@ cc_test { srcs: [ ":libsurfaceflinger_sources", "libsurfaceflinger_unittest_main.cpp", + "AllowedDisplayConfigsTest.cpp", "CompositionTest.cpp", + "DispSyncSourceTest.cpp", "DisplayIdentificationTest.cpp", "DisplayTransactionTest.cpp", "EventControlThreadTest.cpp", diff --git a/services/surfaceflinger/tests/unittests/CompositionTest.cpp b/services/surfaceflinger/tests/unittests/CompositionTest.cpp index 3addd61e55..ea2818d532 100644 --- a/services/surfaceflinger/tests/unittests/CompositionTest.cpp +++ b/services/surfaceflinger/tests/unittests/CompositionTest.cpp @@ -22,6 +22,7 @@ #include <gmock/gmock.h> #include <gtest/gtest.h> #include <gui/IProducerListener.h> +#include <gui/LayerMetadata.h> #include <log/log.h> #include <renderengine/mock/Framebuffer.h> #include <renderengine/mock/Image.h> @@ -33,6 +34,7 @@ #include "ColorLayer.h" #include "Layer.h" +#include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/MockDispSync.h" @@ -90,11 +92,9 @@ public: ::testing::UnitTest::GetInstance()->current_test_info(); ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); - mFlinger.mutableEventControlThread().reset(mEventControlThread); - mFlinger.mutableEventThread().reset(mEventThread); mFlinger.mutableEventQueue().reset(mMessageQueue); + setupScheduler(); - mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync); EXPECT_CALL(*mPrimaryDispSync, computeNextRefresh(0)).WillRepeatedly(Return(0)); EXPECT_CALL(*mPrimaryDispSync, getPeriod()) .WillRepeatedly(Return(FakeHwcDisplayInjector::DEFAULT_REFRESH_RATE)); @@ -124,6 +124,18 @@ public: Mock::VerifyAndClear(mComposer); } + void setupScheduler() { + mScheduler = new TestableScheduler(); + mScheduler->mutableEventControlThread().reset(mEventControlThread); + mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); + EXPECT_CALL(*mEventThread.get(), registerDisplayEventConnection(_)); + sp<Scheduler::ConnectionHandle> connectionHandle = + mScheduler->addConnection(std::move(mEventThread)); + mFlinger.mutableSfConnectionHandle() = std::move(connectionHandle); + + mFlinger.mutableScheduler().reset(mScheduler); + } + void setupForceGeometryDirty() { // TODO: This requires the visible region and other related // state to be set, and is problematic for BufferLayers since they are @@ -145,6 +157,7 @@ public: std::unordered_set<HWC2::Capability> mDefaultCapabilities = {HWC2::Capability::SidebandStream}; + TestableScheduler* mScheduler; TestableSurfaceFlinger mFlinger; sp<DisplayDevice> mDisplay; sp<DisplayDevice> mExternalDisplay; @@ -155,7 +168,7 @@ public: sp<GraphicBuffer> mBuffer = new GraphicBuffer(); ANativeWindowBuffer* mNativeWindowBuffer = mBuffer->getNativeBuffer(); - mock::EventThread* mEventThread = new mock::EventThread(); + std::unique_ptr<mock::EventThread> mEventThread = std::make_unique<mock::EventThread>(); mock::EventControlThread* mEventControlThread = new mock::EventControlThread(); Hwc2::mock::Composer* mComposer = nullptr; @@ -300,7 +313,7 @@ struct BaseDisplayVariant { .WillRepeatedly( [](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& /*layerSettings*/, - ANativeWindowBuffer*, base::unique_fd*) -> status_t { + ANativeWindowBuffer*, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -339,7 +352,7 @@ struct BaseDisplayVariant { .WillRepeatedly( [](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& /*layerSettings*/, - ANativeWindowBuffer*, base::unique_fd*) -> status_t { + ANativeWindowBuffer*, base::unique_fd&&, base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -482,7 +495,7 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, useNativeFenceSync()).WillRepeatedly(Return(true)); bool ignoredRecomputeVisibleRegions; - layer->latchBuffer(ignoredRecomputeVisibleRegions, 0, Fence::NO_FENCE); + layer->latchBuffer(ignoredRecomputeVisibleRegions, 0); Mock::VerifyAndClear(test->mRenderEngine); } @@ -568,7 +581,8 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, - ANativeWindowBuffer*, base::unique_fd*) -> status_t { + ANativeWindowBuffer*, base::unique_fd&&, + base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -579,8 +593,6 @@ struct BaseLayerProperties { renderengine::LayerSettings layer = layerSettings.back(); EXPECT_THAT(layer.source.buffer.buffer, Not(IsNull())); EXPECT_THAT(layer.source.buffer.fence, Not(IsNull())); - EXPECT_EQ(renderengine::Buffer::CachingHint::NO_CACHE, - layer.source.buffer.cacheHint); EXPECT_EQ(DEFAULT_TEXTURE_ID, layer.source.buffer.textureName); EXPECT_EQ(false, layer.source.buffer.isY410BT2020); EXPECT_EQ(true, layer.source.buffer.usePremultipliedAlpha); @@ -612,7 +624,8 @@ struct BaseLayerProperties { EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, - ANativeWindowBuffer*, base::unique_fd*) -> status_t { + ANativeWindowBuffer*, base::unique_fd&&, + base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -681,7 +694,8 @@ struct SecureLayerProperties : public BaseLayerProperties<SecureLayerProperties> EXPECT_CALL(*test->mRenderEngine, drawLayers) .WillOnce([](const renderengine::DisplaySettings& displaySettings, const std::vector<renderengine::LayerSettings>& layerSettings, - ANativeWindowBuffer*, base::unique_fd*) -> status_t { + ANativeWindowBuffer*, base::unique_fd&&, + base::unique_fd*) -> status_t { EXPECT_EQ(DEFAULT_DISPLAY_MAX_LUMINANCE, displaySettings.maxLuminance); EXPECT_EQ(Rect(DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT), displaySettings.physicalDisplay); @@ -754,16 +768,16 @@ struct BaseLayerVariant { } static void injectLayer(CompositionTest* test, sp<Layer> layer) { - std::vector<std::unique_ptr<compositionengine::OutputLayer>> outputLayers; - outputLayers.emplace_back( - test->mDisplay->getCompositionDisplay() - ->getOrCreateOutputLayer(layer->getCompositionLayer(), layer)); - test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(std::move(outputLayers)); - EXPECT_CALL(*test->mComposer, createLayer(HWC_DISPLAY, _)) .WillOnce(DoAll(SetArgPointee<1>(HWC_LAYER), Return(Error::NONE))); - layer->createHwcLayer(&test->mFlinger.getHwComposer(), test->mDisplay); + std::vector<std::unique_ptr<compositionengine::OutputLayer>> outputLayers; + outputLayers.emplace_back(test->mDisplay->getCompositionDisplay() + ->getOrCreateOutputLayer(DEFAULT_DISPLAY_ID, + layer->getCompositionLayer(), + layer)); + + test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ(std::move(outputLayers)); Mock::VerifyAndClear(test->mComposer); @@ -779,10 +793,6 @@ struct BaseLayerVariant { test->mDisplay->getCompositionDisplay()->setOutputLayersOrderedByZ( std::vector<std::unique_ptr<compositionengine::OutputLayer>>()); - - for (auto layer : test->mFlinger.mutableDrawingState().layersSortedByZ) { - layer->destroyHwcLayer(test->mDisplay); - } test->mFlinger.mutableDrawingState().layersSortedByZ.clear(); } }; @@ -797,7 +807,7 @@ struct ColorLayerVariant : public BaseLayerVariant<LayerProperties> { return new ColorLayer(LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), String8("test-layer"), LayerProperties::WIDTH, LayerProperties::HEIGHT, - LayerProperties::LAYER_FLAGS)); + LayerProperties::LAYER_FLAGS, LayerMetadata())); }); auto& layerDrawingState = test->mFlinger.mutableLayerDrawingState(layer); @@ -838,7 +848,7 @@ struct BufferLayerVariant : public BaseLayerVariant<LayerProperties> { LayerCreationArgs(test->mFlinger.mFlinger.get(), sp<Client>(), String8("test-layer"), LayerProperties::WIDTH, LayerProperties::HEIGHT, - LayerProperties::LAYER_FLAGS)); + LayerProperties::LAYER_FLAGS, LayerMetadata())); }); LayerProperties::setupLayerState(test, layer); diff --git a/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp new file mode 100644 index 0000000000..92bdebd400 --- /dev/null +++ b/services/surfaceflinger/tests/unittests/DispSyncSourceTest.cpp @@ -0,0 +1,164 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#undef LOG_TAG +#define LOG_TAG "LibSurfaceFlingerUnittests" +#define LOG_NDEBUG 0 + +#include <inttypes.h> + +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <log/log.h> + +#include "AsyncCallRecorder.h" +#include "Scheduler/DispSyncSource.h" +#include "mock/MockDispSync.h" + +namespace android { +namespace { + +using namespace std::chrono_literals; +using testing::Return; + +class DispSyncSourceTest : public testing::Test, private VSyncSource::Callback { +protected: + DispSyncSourceTest(); + ~DispSyncSourceTest() override; + + void createDispSync(); + void createDispSyncSource(); + + void onVSyncEvent(nsecs_t when) override; + + std::unique_ptr<mock::DispSync> mDispSync; + std::unique_ptr<DispSyncSource> mDispSyncSource; + + AsyncCallRecorder<void (*)(nsecs_t)> mVSyncEventCallRecorder; + + static constexpr std::chrono::nanoseconds mPhaseOffset = 6ms; + static constexpr int mIterations = 100; +}; + +DispSyncSourceTest::DispSyncSourceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Setting up for %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +DispSyncSourceTest::~DispSyncSourceTest() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); +} + +void DispSyncSourceTest::onVSyncEvent(nsecs_t when) { + ALOGD("onVSyncEvent: %" PRId64, when); + + mVSyncEventCallRecorder.recordCall(when); +} + +void DispSyncSourceTest::createDispSync() { + mDispSync = std::make_unique<mock::DispSync>(); +} + +void DispSyncSourceTest::createDispSyncSource() { + createDispSync(); + mDispSyncSource = std::make_unique<DispSyncSource>(mDispSync.get(), mPhaseOffset.count(), true, + "DispSyncSourceTest"); + mDispSyncSource->setCallback(this); +} + +/* ------------------------------------------------------------------------ + * Test cases + */ + +TEST_F(DispSyncSourceTest, createDispSync) { + createDispSync(); + EXPECT_TRUE(mDispSync); +} + +TEST_F(DispSyncSourceTest, createDispSyncSource) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); +} + +TEST_F(DispSyncSourceTest, noCallbackAfterInit) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + // DispSyncSource starts with Vsync disabled + mDispSync->triggerCallback(); + EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); +} + +TEST_F(DispSyncSourceTest, waitForCallbacks) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + mDispSyncSource->setVSyncEnabled(true); + EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); + + for (int i = 0; i < mIterations; i++) { + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + } +} + +TEST_F(DispSyncSourceTest, waitForCallbacksWithPhaseChange) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + mDispSyncSource->setVSyncEnabled(true); + EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); + + for (int i = 0; i < mIterations; i++) { + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + } + + EXPECT_CALL(*mDispSync, getPeriod()).Times(1).WillOnce(Return(16666666)); + mDispSyncSource->setPhaseOffset((mPhaseOffset / 2).count()); + + EXPECT_EQ(mDispSync->getCallbackPhase(), (mPhaseOffset / 2).count()); + + for (int i = 0; i < mIterations; i++) { + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + } +} + +TEST_F(DispSyncSourceTest, pauseCallbacks) { + createDispSyncSource(); + EXPECT_TRUE(mDispSyncSource); + + mDispSyncSource->setVSyncEnabled(true); + EXPECT_EQ(mDispSync->getCallbackPhase(), mPhaseOffset.count()); + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForCall().has_value()); + + mDispSyncSource->pauseVsyncCallback(true); + mDispSync->triggerCallback(); + EXPECT_FALSE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); + + mDispSyncSource->pauseVsyncCallback(false); + mDispSync->triggerCallback(); + EXPECT_TRUE(mVSyncEventCallRecorder.waitForUnexpectedCall().has_value()); +} + +} // namespace +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp index 6659d4ac8e..9bf29a212f 100644 --- a/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp +++ b/services/surfaceflinger/tests/unittests/DisplayTransactionTest.cpp @@ -29,6 +29,7 @@ #include <ui/DebugUtils.h> #include "DisplayIdentificationTest.h" +#include "TestableScheduler.h" #include "TestableSurfaceFlinger.h" #include "mock/DisplayHardware/MockComposer.h" #include "mock/MockDispSync.h" @@ -94,6 +95,8 @@ public: DisplayTransactionTest(); ~DisplayTransactionTest() override; + void setupScheduler(); + // -------------------------------------------------------------------- // Mock/Fake injection @@ -116,6 +119,7 @@ public: // -------------------------------------------------------------------- // Test instances + TestableScheduler* mScheduler; TestableSurfaceFlinger mFlinger; mock::EventThread* mEventThread = new mock::EventThread(); mock::EventThread* mSFEventThread = new mock::EventThread(); @@ -160,13 +164,10 @@ DisplayTransactionTest::DisplayTransactionTest() { return nullptr; }); - mFlinger.mutableEventControlThread().reset(mEventControlThread); - mFlinger.mutableEventThread().reset(mEventThread); - mFlinger.mutableSFEventThread().reset(mSFEventThread); + setupScheduler(); mFlinger.mutableEventQueue().reset(mMessageQueue); mFlinger.setupRenderEngine(std::unique_ptr<renderengine::RenderEngine>(mRenderEngine)); mFlinger.mutableInterceptor().reset(mSurfaceInterceptor); - mFlinger.mutablePrimaryDispSync().reset(mPrimaryDispSync); injectMockComposer(0); } @@ -177,6 +178,22 @@ DisplayTransactionTest::~DisplayTransactionTest() { ALOGD("**** Tearing down after %s.%s\n", test_info->test_case_name(), test_info->name()); } +void DisplayTransactionTest::setupScheduler() { + mScheduler = new TestableScheduler(); + mScheduler->mutableEventControlThread().reset(mEventControlThread); + mScheduler->mutablePrimaryDispSync().reset(mPrimaryDispSync); + EXPECT_CALL(*mEventThread, registerDisplayEventConnection(_)); + EXPECT_CALL(*mSFEventThread, registerDisplayEventConnection(_)); + + sp<Scheduler::ConnectionHandle> sfConnectionHandle = + mScheduler->addConnection(std::unique_ptr<EventThread>(mSFEventThread)); + mFlinger.mutableSfConnectionHandle() = std::move(sfConnectionHandle); + sp<Scheduler::ConnectionHandle> appConnectionHandle = + mScheduler->addConnection(std::unique_ptr<EventThread>(mEventThread)); + mFlinger.mutableAppConnectionHandle() = std::move(appConnectionHandle); + mFlinger.mutableScheduler().reset(mScheduler); +} + void DisplayTransactionTest::injectMockComposer(int virtualDisplayCount) { mComposer = new Hwc2::mock::Composer(); EXPECT_CALL(*mComposer, getCapabilities()) @@ -1106,8 +1123,8 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { // Preconditions // vsync is enabled and available - mFlinger.mutablePrimaryHWVsyncEnabled() = true; - mFlinger.mutableHWVsyncAvailable() = true; + mScheduler->mutablePrimaryHWVsyncEnabled() = true; + mScheduler->mutableHWVsyncAvailable() = true; // A display exists auto existing = Case::Display::makeFakeExistingDisplayInjector(this); @@ -1131,8 +1148,8 @@ TEST_F(DisplayTransactionTest, resetDisplayStateClearsState) { // Postconditions // vsyncs should be off and not available. - EXPECT_FALSE(mFlinger.mutablePrimaryHWVsyncEnabled()); - EXPECT_FALSE(mFlinger.mutableHWVsyncAvailable()); + EXPECT_FALSE(mScheduler->mutablePrimaryHWVsyncEnabled()); + EXPECT_FALSE(mScheduler->mutableHWVsyncAvailable()); // The display should have been removed from the display map. EXPECT_FALSE(hasDisplayDevice(existing.token())); @@ -1305,32 +1322,6 @@ TEST_F(GetDisplayNativePrimaries, nullDisplayToken) { EXPECT_EQ(BAD_VALUE, mFlinger.getDisplayNativePrimaries(nullptr, primaries)); } -TEST_F(GetDisplayNativePrimaries, internalDisplayWithDefaultPrimariesData) { - auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this); - injector.inject(); - auto internalDisplayToken = injector.token(); - // A nullptr would trigger a different execution path than what's being tested here - EXPECT_NE(nullptr, internalDisplayToken.get()); - - mFlinger.initDefaultDisplayNativePrimaries(); - - ui::DisplayPrimaries primaries; - // Expecting sRGB primaries - EXPECT_EQ(NO_ERROR, mFlinger.getDisplayNativePrimaries(internalDisplayToken, primaries)); - EXPECT_EQ(primaries.red.X, 0.4123f); - EXPECT_EQ(primaries.red.Y, 0.2126f); - EXPECT_EQ(primaries.red.Z, 0.0193f); - EXPECT_EQ(primaries.green.X, 0.3576f); - EXPECT_EQ(primaries.green.Y, 0.7152f); - EXPECT_EQ(primaries.green.Z, 0.1192f); - EXPECT_EQ(primaries.blue.X, 0.1805f); - EXPECT_EQ(primaries.blue.Y, 0.0722f); - EXPECT_EQ(primaries.blue.Z, 0.9506f); - EXPECT_EQ(primaries.white.X, 0.9505f); - EXPECT_EQ(primaries.white.Y, 1.0000f); - EXPECT_EQ(primaries.white.Z, 1.0891f); -} - TEST_F(GetDisplayNativePrimaries, internalDisplayWithPrimariesData) { auto injector = SimplePrimaryDisplayCase::Display::makeFakeExistingDisplayInjector(this); injector.inject(); @@ -1557,7 +1548,6 @@ void HandleTransactionLockedTest::setupCommonCallExpectationsForConnectProcessin Case::PerFrameMetadataSupport::setupComposerCallExpectations(this); EXPECT_CALL(*mSurfaceInterceptor, saveDisplayCreation(_)).Times(1); - expectHotplugReceived<Case, true>(mEventThread); expectHotplugReceived<Case, true>(mSFEventThread); } @@ -1936,6 +1926,10 @@ TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAdded) { EXPECT_CALL(*mComposer, destroyVirtualDisplay(Case::Display::HWC_DISPLAY_ID)) .WillOnce(Return(Error::NONE)); EXPECT_CALL(*mConsumer, consumerDisconnect()).WillOnce(Return(NO_ERROR)); + + // Cleanup + mFlinger.mutableCurrentState().displays.removeItem(displayToken); + mFlinger.mutableDrawingState().displays.removeItem(displayToken); } TEST_F(HandleTransactionLockedTest, processesVirtualDisplayAddedWithNoSurface) { @@ -3008,7 +3002,7 @@ struct DisplayPowerCase { } static void setInitialPrimaryHWVsyncEnabled(DisplayTransactionTest* test, bool enabled) { - test->mFlinger.mutablePrimaryHWVsyncEnabled() = enabled; + test->mScheduler->mutablePrimaryHWVsyncEnabled() = enabled; } static void setupRepaintEverythingCallExpectations(DisplayTransactionTest* test) { diff --git a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp index 3a7cfba805..406ec81ce4 100644 --- a/services/surfaceflinger/tests/unittests/EventThreadTest.cpp +++ b/services/surfaceflinger/tests/unittests/EventThreadTest.cpp @@ -44,6 +44,7 @@ public: MOCK_METHOD1(setVSyncEnabled, void(bool)); MOCK_METHOD1(setCallback, void(VSyncSource::Callback*)); MOCK_METHOD1(setPhaseOffset, void(nsecs_t)); + MOCK_METHOD1(pauseVsyncCallback, void(bool)); }; } // namespace @@ -71,6 +72,7 @@ protected: void expectVSyncSetEnabledCallReceived(bool expectedState); void expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhaseOffset); + void expectVSyncPauseVsyncCallbackCallReceived(bool expectedPause); VSyncSource::Callback* expectVSyncSetCallbackCallReceived(); void expectInterceptCallReceived(nsecs_t expectedTimestamp); void expectVsyncEventReceivedByConnection(const char* name, @@ -79,10 +81,13 @@ protected: void expectVsyncEventReceivedByConnection(nsecs_t expectedTimestamp, unsigned expectedCount); void expectHotplugEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, bool expectedConnected); + void expectConfigChangedEventReceivedByConnection(PhysicalDisplayId expectedDisplayId, + int32_t expectedConfigId); AsyncCallRecorder<void (*)(bool)> mVSyncSetEnabledCallRecorder; AsyncCallRecorder<void (*)(VSyncSource::Callback*)> mVSyncSetCallbackCallRecorder; AsyncCallRecorder<void (*)(nsecs_t)> mVSyncSetPhaseOffsetCallRecorder; + AsyncCallRecorder<void (*)(bool)> mVSyncPauseVsyncCallbackCallRecorder; AsyncCallRecorder<void (*)()> mResyncCallRecorder; AsyncCallRecorder<void (*)()> mResetIdleTimerCallRecorder; AsyncCallRecorder<void (*)(nsecs_t)> mInterceptVSyncCallRecorder; @@ -108,6 +113,9 @@ EventThreadTest::EventThreadTest() { EXPECT_CALL(mVSyncSource, setPhaseOffset(_)) .WillRepeatedly(Invoke(mVSyncSetPhaseOffsetCallRecorder.getInvocable())); + EXPECT_CALL(mVSyncSource, pauseVsyncCallback(_)) + .WillRepeatedly(Invoke(mVSyncPauseVsyncCallbackCallRecorder.getInvocable())); + createThread(); mConnection = createConnection(mConnectionEventCallRecorder); @@ -157,6 +165,12 @@ void EventThreadTest::expectVSyncSetPhaseOffsetCallReceived(nsecs_t expectedPhas EXPECT_EQ(expectedPhaseOffset, std::get<0>(args.value())); } +void EventThreadTest::expectVSyncPauseVsyncCallbackCallReceived(bool expectedPause) { + auto args = mVSyncPauseVsyncCallbackCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + EXPECT_EQ(expectedPause, std::get<0>(args.value())); +} + VSyncSource::Callback* EventThreadTest::expectVSyncSetCallbackCallReceived() { auto callbackSet = mVSyncSetCallbackCallRecorder.waitForCall(); return callbackSet.has_value() ? std::get<0>(callbackSet.value()) : nullptr; @@ -200,6 +214,16 @@ void EventThreadTest::expectHotplugEventReceivedByConnection(PhysicalDisplayId e EXPECT_EQ(expectedConnected, event.hotplug.connected); } +void EventThreadTest::expectConfigChangedEventReceivedByConnection( + PhysicalDisplayId expectedDisplayId, int32_t expectedConfigId) { + auto args = mConnectionEventCallRecorder.waitForCall(); + ASSERT_TRUE(args.has_value()); + const auto& event = std::get<0>(args.value()); + EXPECT_EQ(DisplayEventReceiver::DISPLAY_EVENT_CONFIG_CHANGED, event.header.type); + EXPECT_EQ(expectedDisplayId, event.header.displayId); + EXPECT_EQ(expectedConfigId, event.config.configId); +} + namespace { /* ------------------------------------------------------------------------ @@ -406,6 +430,16 @@ TEST_F(EventThreadTest, setPhaseOffsetForwardsToVSyncSource) { expectVSyncSetPhaseOffsetCallReceived(321); } +TEST_F(EventThreadTest, pauseVsyncCallbackForwardsToVSyncSource) { + mThread->pauseVsyncCallback(true); + expectVSyncPauseVsyncCallbackCallReceived(true); +} + +TEST_F(EventThreadTest, resumeVsyncCallbackForwardsToVSyncSource) { + mThread->pauseVsyncCallback(false); + expectVSyncPauseVsyncCallbackCallReceived(false); +} + TEST_F(EventThreadTest, postHotplugInternalDisconnect) { mThread->onHotplugReceived(INTERNAL_DISPLAY_ID, false); expectHotplugEventReceivedByConnection(INTERNAL_DISPLAY_ID, false); @@ -426,5 +460,15 @@ TEST_F(EventThreadTest, postHotplugExternalConnect) { expectHotplugEventReceivedByConnection(EXTERNAL_DISPLAY_ID, true); } +TEST_F(EventThreadTest, postConfigChangedPrimary) { + mThread->onConfigChanged(INTERNAL_DISPLAY_ID, 7); + expectConfigChangedEventReceivedByConnection(INTERNAL_DISPLAY_ID, 7); +} + +TEST_F(EventThreadTest, postConfigChangedExternal) { + mThread->onConfigChanged(EXTERNAL_DISPLAY_ID, 5); + expectConfigChangedEventReceivedByConnection(EXTERNAL_DISPLAY_ID, 5); +} + } // namespace } // namespace android diff --git a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp index 5e82225dfa..ea39bf561b 100644 --- a/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp +++ b/services/surfaceflinger/tests/unittests/IdleTimerTest.cpp @@ -140,7 +140,7 @@ TEST_F(IdleTimerTest, startNotCalledTest) { mExpiredTimerCallback.getInvocable()); // The start hasn't happened, so the callback does not happen. EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value()); - EXPECT_FALSE(mResetTimerCallback.waitForCall(1us).has_value()); + EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value()); mIdleTimer->stop(); // Final quick check that no more callback were observed. EXPECT_FALSE(mExpiredTimerCallback.waitForCall(0ms).has_value()); @@ -159,7 +159,7 @@ TEST_F(IdleTimerTest, idleTimerIdlesTest) { EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value()); // Once reset, it should generate another mIdleTimer->reset(); - EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value()); + EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value()); mIdleTimer->stop(); // Final quick check that no more callback were observed. @@ -171,7 +171,7 @@ TEST_F(IdleTimerTest, timeoutCallbackExecutionTest) { mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); - EXPECT_TRUE(mResetTimerCallback.waitForCall(1us).has_value()); + EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value()); mIdleTimer->stop(); } @@ -180,21 +180,21 @@ TEST_F(IdleTimerTest, noCallbacksAfterStopAndResetTest) { mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); - EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value()); + EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); EXPECT_TRUE(mExpiredTimerCallback.waitForCall(waitTimeForExpected3msCallback).has_value()); mIdleTimer->stop(); clearPendingCallbacks(); mIdleTimer->reset(); EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value()); - EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value()); + EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value()); } TEST_F(IdleTimerTest, noCallbacksAfterStopTest) { mIdleTimer = std::make_unique<scheduler::IdleTimer>(3ms, mResetTimerCallback.getInvocable(), mExpiredTimerCallback.getInvocable()); mIdleTimer->start(); - EXPECT_TRUE(mResetTimerCallback.waitForCall(1ms).has_value()); + EXPECT_TRUE(mResetTimerCallback.waitForCall().has_value()); mIdleTimer->stop(); clearPendingCallbacks(); @@ -202,7 +202,7 @@ TEST_F(IdleTimerTest, noCallbacksAfterStopTest) { // No more idle events should be observed EXPECT_FALSE(mExpiredTimerCallback.waitForCall(waitTimeForUnexpected3msCallback).has_value()); - EXPECT_FALSE(mResetTimerCallback.waitForCall(1ms).has_value()); + EXPECT_FALSE(mResetTimerCallback.waitForCall().has_value()); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp index 92c9f92bf1..75a061bf77 100644 --- a/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp +++ b/services/surfaceflinger/tests/unittests/LayerMetadataTest.cpp @@ -62,24 +62,47 @@ TEST_F(LayerMetadataTest, testLayerMetadata) { metadata.mMap[2] = std::vector<uint8_t>{'a', 'b'}; ASSERT_EQ(0, metadata.getInt32(2, 0)); + Parcel p; + metadata.writeToParcel(&p); + LayerMetadata reconstructed; + reconstructed.setInt32(3, 1); // to make sure it gets replaced + p.setDataPosition(0); + reconstructed.readFromParcel(&p); + ASSERT_EQ(metadata.mMap, reconstructed.mMap); +} + +TEST_F(LayerMetadataTest, merge) { + LayerMetadata metadata; + metadata.setInt32(4, 2); + metadata.mMap[2] = std::vector<uint8_t>{'a', 'b'}; + LayerMetadata second; std::vector<uint8_t> someData{'c', 'd', '\0'}; second.mMap[2] = someData; second.setInt32(6, 5); - metadata.merge(second); + second.mMap[4].clear(); // will not delete if eraseEmpty is false + bool changed = metadata.merge(second); + ASSERT_TRUE(changed); ASSERT_EQ(3, metadata.mMap.size()); ASSERT_EQ(someData, second.mMap[2]); ASSERT_EQ(5, metadata.getInt32(6, 0)); - ASSERT_EQ(2, metadata.getInt32(4, 0)); + ASSERT_TRUE(metadata.mMap.at(4).empty()); - Parcel p; - metadata.writeToParcel(&p); - LayerMetadata reconstructed; - reconstructed.setInt32(3, 1); // to make sure it gets replaced - p.setDataPosition(0); - reconstructed.readFromParcel(&p); - ASSERT_EQ(metadata.mMap, reconstructed.mMap); + LayerMetadata withErase; + withErase.mMap[6].clear(); + changed = metadata.merge(withErase, true /* eraseEmpty */); + ASSERT_TRUE(changed); + ASSERT_EQ(2, metadata.mMap.size()); + ASSERT_EQ(someData, second.mMap[2]); + ASSERT_EQ(true, metadata.has(4)); + + // test for change detection + LayerMetadata third; + third.mMap[2] = someData; + third.mMap[5].clear(); + changed = metadata.merge(third); + ASSERT_FALSE(changed); } } // namespace diff --git a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp index 3d887ea623..4342dc983d 100644 --- a/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/RefreshRateStatsTest.cpp @@ -45,6 +45,7 @@ protected: std::unique_ptr<RefreshRateStats> mRefreshRateStats; std::shared_ptr<android::mock::TimeStats> mTimeStats; + std::shared_ptr<RefreshRateConfigs> mRefreshRateConfigs; }; RefreshRateStatsTest::RefreshRateStatsTest() { @@ -61,7 +62,8 @@ RefreshRateStatsTest::~RefreshRateStatsTest() { void RefreshRateStatsTest::init(std::vector<std::shared_ptr<const HWC2::Display::Config>> configs) { mTimeStats = std::make_shared<android::mock::TimeStats>(); - mRefreshRateStats = std::make_unique<RefreshRateStats>(configs, mTimeStats); + mRefreshRateConfigs = std::make_shared<RefreshRateConfigs>(configs); + mRefreshRateStats = std::make_unique<RefreshRateStats>(mRefreshRateConfigs, mTimeStats); } namespace { diff --git a/services/surfaceflinger/tests/unittests/TestableScheduler.h b/services/surfaceflinger/tests/unittests/TestableScheduler.h new file mode 100644 index 0000000000..dcbf973d2c --- /dev/null +++ b/services/surfaceflinger/tests/unittests/TestableScheduler.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <gmock/gmock.h> + +#include "Scheduler/EventThread.h" +#include "Scheduler/Scheduler.h" + +namespace android { + +class TestableScheduler : public Scheduler { +public: + TestableScheduler() : Scheduler([](bool) {}) {} + + // Creates EventThreadConnection with the given eventThread. Creates Scheduler::Connection + // and adds it to the list of connectins. Returns the ConnectionHandle for the + // Scheduler::Connection. This allows plugging in mock::EventThread. + sp<Scheduler::ConnectionHandle> addConnection(std::unique_ptr<EventThread> eventThread) { + sp<EventThreadConnection> eventThreadConnection = + new EventThreadConnection(eventThread.get(), ResyncCallback(), + ResetIdleTimerCallback()); + const int64_t id = sNextId++; + mConnections.emplace(id, + std::make_unique<Scheduler::Connection>(new ConnectionHandle(id), + eventThreadConnection, + std::move(eventThread))); + return mConnections[id]->handle; + } + + /* ------------------------------------------------------------------------ + * Read-write access to private data to set up preconditions and assert + * post-conditions. + */ + auto& mutablePrimaryHWVsyncEnabled() { return mPrimaryHWVsyncEnabled; } + auto& mutableEventControlThread() { return mEventControlThread; } + auto& mutablePrimaryDispSync() { return mPrimaryDispSync; } + auto& mutableHWVsyncAvailable() { return mHWVsyncAvailable; } + + ~TestableScheduler() { + // All these pointer and container clears help ensure that GMock does + // not report a leaked object, since the Scheduler instance may + // still be referenced by something despite our best efforts to destroy + // it after each test is done. + mutableEventControlThread().reset(); + mutablePrimaryDispSync().reset(); + mConnections.clear(); + }; +}; + +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h index 959126ef08..46fd964047 100644 --- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h +++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h @@ -257,7 +257,10 @@ public: 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 { @@ -289,17 +292,12 @@ public: return mFlinger->SurfaceFlinger::getDisplayNativePrimaries(displayToken, primaries); } - void initDefaultDisplayNativePrimaries() { - mFlinger->SurfaceFlinger::initDefaultDisplayNativePrimaries(); - } - /* ------------------------------------------------------------------------ * Read-only access to private data to assert post-conditions. */ const auto& getAnimFrameTracker() const { return mFlinger->mAnimFrameTracker; } const auto& getHasPoweredOff() const { return mFlinger->mHasPoweredOff; } - const auto& getHWVsyncAvailable() const { return mFlinger->mHWVsyncAvailable; } const auto& getVisibleRegionsDirty() const { return mFlinger->mVisibleRegionsDirty; } auto& getHwComposer() const { return static_cast<impl::HWComposer&>(mFlinger->getHwComposer()); @@ -320,27 +318,25 @@ public: auto& mutableDisplayColorSetting() { return mFlinger->mDisplayColorSetting; } auto& mutableDisplays() { return mFlinger->mDisplays; } auto& mutableDrawingState() { return mFlinger->mDrawingState; } - auto& mutableEventControlThread() { return mFlinger->mEventControlThread; } auto& mutableEventQueue() { return mFlinger->mEventQueue; } - auto& mutableEventThread() { return mFlinger->mEventThread; } - auto& mutableSFEventThread() { return mFlinger->mSFEventThread; } auto& mutableGeometryInvalid() { return mFlinger->mGeometryInvalid; } - auto& mutableHWVsyncAvailable() { return mFlinger->mHWVsyncAvailable; } auto& mutableInterceptor() { return mFlinger->mInterceptor; } auto& mutableMainThreadId() { return mFlinger->mMainThreadId; } auto& mutablePendingHotplugEvents() { return mFlinger->mPendingHotplugEvents; } auto& mutablePhysicalDisplayTokens() { return mFlinger->mPhysicalDisplayTokens; } - auto& mutablePrimaryDispSync() { return mFlinger->mPrimaryDispSync; } - auto& mutablePrimaryHWVsyncEnabled() { return mFlinger->mPrimaryHWVsyncEnabled; } auto& mutableTexturePool() { return mFlinger->mTexturePool; } auto& mutableTransactionFlags() { return mFlinger->mTransactionFlags; } auto& mutableUseHwcVirtualDisplays() { return mFlinger->mUseHwcVirtualDisplays; } + auto& mutablePowerAdvisor() { return mFlinger->mPowerAdvisor; } auto& mutableComposerSequenceId() { return mFlinger->getBE().mComposerSequenceId; } auto& mutableHwcDisplayData() { return getHwComposer().mDisplayData; } auto& mutableHwcPhysicalDisplayIdMap() { return getHwComposer().mPhysicalDisplayIdMap; } auto& mutableInternalHwcDisplayId() { return getHwComposer().mInternalHwcDisplayId; } auto& mutableExternalHwcDisplayId() { return getHwComposer().mExternalHwcDisplayId; } + auto& mutableScheduler() { return mFlinger->mScheduler; } + auto& mutableAppConnectionHandle() { return mFlinger->mAppConnectionHandle; } + auto& mutableSfConnectionHandle() { return mFlinger->mSfConnectionHandle; } ~TestableSurfaceFlinger() { // All these pointer and container clears help ensure that GMock does @@ -348,12 +344,11 @@ public: // still be referenced by something despite our best efforts to destroy // it after each test is done. mutableDisplays().clear(); - mutableEventControlThread().reset(); + mutableCurrentState().displays.clear(); + mutableDrawingState().displays.clear(); mutableEventQueue().reset(); - mutableEventThread().reset(); - mutableSFEventThread().reset(); mutableInterceptor().reset(); - mutablePrimaryDispSync().reset(); + mutableScheduler().reset(); mFlinger->mCompositionEngine->setHwComposer(std::unique_ptr<HWComposer>()); mFlinger->mCompositionEngine->setRenderEngine( std::unique_ptr<renderengine::RenderEngine>()); @@ -363,18 +358,11 @@ public: * Wrapper classes for Read-write access to private data to set up * preconditions and assert post-conditions. */ - class FakePowerAdvisor : public Hwc2::PowerAdvisor { - public: - FakePowerAdvisor() = default; - ~FakePowerAdvisor() override = default; - void setExpensiveRenderingExpected(hwc2_display_t, bool) override {} - }; - struct HWC2Display : public HWC2::impl::Display { - HWC2Display(Hwc2::Composer& composer, Hwc2::PowerAdvisor& advisor, + HWC2Display(Hwc2::Composer& composer, const std::unordered_set<HWC2::Capability>& capabilities, hwc2_display_t id, HWC2::DisplayType type) - : HWC2::impl::Display(composer, advisor, capabilities, id, type) {} + : HWC2::impl::Display(composer, capabilities, id, type) {} ~HWC2Display() { // Prevents a call to disable vsyncs. mType = HWC2::DisplayType::Invalid; @@ -438,14 +426,7 @@ public: return *this; } - auto& setPowerAdvisor(Hwc2::PowerAdvisor* powerAdvisor) { - mPowerAdvisor = powerAdvisor; - return *this; - } - void inject(TestableSurfaceFlinger* flinger, Hwc2::Composer* composer) { - static FakePowerAdvisor defaultPowerAdvisor; - if (mPowerAdvisor == nullptr) mPowerAdvisor = &defaultPowerAdvisor; static const std::unordered_set<HWC2::Capability> defaultCapabilities; if (mCapabilities == nullptr) mCapabilities = &defaultCapabilities; @@ -453,8 +434,8 @@ public: // not refer to an instance owned by FakeHwcDisplayInjector. This // class has temporary lifetime, while the constructed HWC2::Display // is much longer lived. - auto display = std::make_unique<HWC2Display>(*composer, *mPowerAdvisor, *mCapabilities, - mHwcDisplayId, mHwcDisplayType); + auto display = std::make_unique<HWC2Display>(*composer, *mCapabilities, mHwcDisplayId, + mHwcDisplayType); auto config = HWC2::Display::Config::Builder(*display, mActiveConfig); config.setWidth(mWidth); @@ -489,7 +470,6 @@ public: int32_t mDpiY = DEFAULT_DPI; int32_t mActiveConfig = DEFAULT_ACTIVE_CONFIG; const std::unordered_set<HWC2::Capability>* mCapabilities = nullptr; - Hwc2::PowerAdvisor* mPowerAdvisor = nullptr; }; class FakeDisplayDeviceInjector { diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h index e6f1a0652d..bb9202054f 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockComposer.h @@ -121,6 +121,8 @@ public: MOCK_METHOD2(getDisplayCapabilities, Error(Display, std::vector<DisplayCapability>*)); MOCK_METHOD3(setLayerPerFrameMetadataBlobs, Error(Display, Layer, const std::vector<IComposerClient::PerFrameMetadataBlob>&)); + MOCK_METHOD2(setDisplayBrightness, Error(Display, float)); + MOCK_METHOD2(getDisplayBrightnessSupport, Error(Display, bool*)); }; } // namespace mock diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h index d7e20c4fc8..6dc28bcb73 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockDisplay.h @@ -79,8 +79,9 @@ public: MOCK_METHOD2(validate, Error(uint32_t*, uint32_t*)); MOCK_METHOD4(presentOrValidate, Error(uint32_t*, uint32_t*, android::sp<android::Fence>*, uint32_t*)); + MOCK_CONST_METHOD1(setDisplayBrightness, Error(float)); }; } // namespace mock } // namespace Hwc2 -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h index dc6d83b0fd..7c65f95cb7 100644 --- a/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h +++ b/services/surfaceflinger/tests/unittests/mock/DisplayHardware/MockPowerAdvisor.h @@ -29,7 +29,7 @@ public: PowerAdvisor(); ~PowerAdvisor() override; - MOCK_METHOD2(setExpensiveRenderingExpected, void(hwc2_display_t displayId, bool expected)); + MOCK_METHOD2(setExpensiveRenderingExpected, void(DisplayId displayId, bool expected)); }; } // namespace mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp index 2f7e5ead0f..f6c4f62022 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.cpp @@ -15,6 +15,7 @@ */ #include "mock/MockDispSync.h" +#include <thread> namespace android { namespace mock { @@ -23,5 +24,39 @@ namespace mock { DispSync::DispSync() = default; DispSync::~DispSync() = default; +status_t DispSync::addEventListener(const char* /*name*/, nsecs_t phase, Callback* callback, + nsecs_t /*lastCallbackTime*/) { + if (mCallback.callback != nullptr) { + return BAD_VALUE; + } + + mCallback = {callback, phase}; + return NO_ERROR; +} +status_t DispSync::removeEventListener(Callback* callback, nsecs_t* /*outLastCallback*/) { + if (mCallback.callback != callback) { + return BAD_VALUE; + } + + mCallback = {nullptr, 0}; + return NO_ERROR; +} + +status_t DispSync::changePhaseOffset(Callback* callback, nsecs_t phase) { + if (mCallback.callback != callback) { + return BAD_VALUE; + } + + mCallback.phase = phase; + return NO_ERROR; +} + +void DispSync::triggerCallback() { + if (mCallback.callback == nullptr) return; + + mCallback.callback->onDispSyncEvent( + std::chrono::steady_clock::now().time_since_epoch().count()); +} + } // namespace mock } // namespace android diff --git a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h index 9213ae5fdf..afcda5bc47 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockDispSync.h +++ b/services/surfaceflinger/tests/unittests/mock/MockDispSync.h @@ -36,14 +36,27 @@ public: MOCK_METHOD1(setPeriod, void(nsecs_t)); MOCK_METHOD0(getPeriod, nsecs_t()); MOCK_METHOD1(setRefreshSkipCount, void(int)); - MOCK_METHOD3(addEventListener, status_t(const char*, nsecs_t, Callback*)); - MOCK_METHOD1(removeEventListener, status_t(Callback*)); - MOCK_METHOD2(changePhaseOffset, status_t(Callback*, nsecs_t)); MOCK_CONST_METHOD1(computeNextRefresh, nsecs_t(int)); MOCK_METHOD1(setIgnorePresentFences, void(bool)); MOCK_METHOD0(expectedPresentTime, nsecs_t()); MOCK_CONST_METHOD1(dump, void(std::string&)); + + status_t addEventListener(const char* name, nsecs_t phase, Callback* callback, + nsecs_t lastCallbackTime) override; + status_t removeEventListener(Callback* callback, nsecs_t* outLastCallback) override; + status_t changePhaseOffset(Callback* callback, nsecs_t phase) override; + + nsecs_t getCallbackPhase() { return mCallback.phase; } + + void triggerCallback(); + +private: + struct CallbackType { + Callback* callback = nullptr; + nsecs_t phase; + }; + CallbackType mCallback; }; } // namespace mock diff --git a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h index 589237d1e9..cb4a300611 100644 --- a/services/surfaceflinger/tests/unittests/mock/MockEventThread.h +++ b/services/surfaceflinger/tests/unittests/mock/MockEventThread.h @@ -33,12 +33,14 @@ public: MOCK_METHOD0(onScreenReleased, void()); MOCK_METHOD0(onScreenAcquired, void()); MOCK_METHOD2(onHotplugReceived, void(PhysicalDisplayId, bool)); + MOCK_METHOD2(onConfigChanged, void(PhysicalDisplayId, int32_t)); MOCK_CONST_METHOD1(dump, void(std::string&)); MOCK_METHOD1(setPhaseOffset, void(nsecs_t phaseOffset)); MOCK_METHOD1(registerDisplayEventConnection, status_t(const sp<android::EventThreadConnection> &)); MOCK_METHOD2(setVsyncRate, void(uint32_t, const sp<android::EventThreadConnection> &)); MOCK_METHOD2(requestNextVsync, void(const sp<android::EventThreadConnection> &, bool)); + MOCK_METHOD1(pauseVsyncCallback, void(bool)); }; } // namespace mock diff --git a/services/vr/bufferhubd/producer_channel.cpp b/services/vr/bufferhubd/producer_channel.cpp index b49d89470f..a7fd912294 100644 --- a/services/vr/bufferhubd/producer_channel.cpp +++ b/services/vr/bufferhubd/producer_channel.cpp @@ -392,8 +392,8 @@ Status<RemoteChannelHandle> ProducerChannel::OnNewConsumer(Message& message) { Status<void> ProducerChannel::OnProducerPost(Message&, LocalFence acquire_fence) { ATRACE_NAME("ProducerChannel::OnProducerPost"); - ALOGD("ProducerChannel::OnProducerPost: buffer_id=%d, state=0x%x", - buffer_id(), buffer_state_->load(std::memory_order_acquire)); + ALOGD_IF(TRACE, "%s: buffer_id=%d, state=0x%x", __FUNCTION__, buffer_id(), + buffer_state_->load(std::memory_order_acquire)); epoll_event event; event.events = 0; @@ -437,7 +437,7 @@ Status<void> ProducerChannel::OnProducerPost(Message&, Status<LocalFence> ProducerChannel::OnProducerGain(Message& /*message*/) { ATRACE_NAME("ProducerChannel::OnGain"); - ALOGW("ProducerChannel::OnGain: buffer_id=%d", buffer_id()); + ALOGD_IF(TRACE, "%s: buffer_id=%d", __FUNCTION__, buffer_id()); ClearAvailable(); post_fence_.close(); diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 3a8e34eff2..b3259de6ea 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -33,6 +33,7 @@ #include <configstore/Utils.h> #include <cutils/properties.h> #include <graphicsenv/GraphicsEnv.h> +#include <utils/Timers.h> #include <utils/Trace.h> #include <utils/Vector.h> @@ -210,6 +211,8 @@ int LoadBuiltinDriver(const hwvulkan_module_t** module) { auto ns = android_get_exported_namespace("sphal"); if (!ns) return -ENOENT; + android::GraphicsEnv::getInstance().setDriverToLoad( + android::GraphicsEnv::Driver::VULKAN); return LoadDriver(ns, module); } @@ -219,12 +222,16 @@ int LoadUpdatedDriver(const hwvulkan_module_t** module) { auto ns = android::GraphicsEnv::getInstance().getDriverNamespace(); if (!ns) return -ENOENT; + android::GraphicsEnv::getInstance().setDriverToLoad( + android::GraphicsEnv::Driver::VULKAN_UPDATED); return LoadDriver(ns, module); } bool Hal::Open() { ATRACE_CALL(); + const nsecs_t openTime = systemTime(); + ALOG_ASSERT(!hal_.dev_, "OpenHAL called more than once"); // Use a stub device unless we successfully open a real HAL device. @@ -250,11 +257,12 @@ bool Hal::Open() { } } if (result != 0) { + android::GraphicsEnv::getInstance().setDriverLoaded( + android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime); ALOGV("unable to load Vulkan HAL, using stub HAL (result=%d)", result); return true; } - android::GraphicsEnv::getInstance().sendGpuStats(); hwvulkan_device_t* device; ATRACE_BEGIN("hwvulkan module open"); @@ -263,6 +271,8 @@ bool Hal::Open() { reinterpret_cast<hw_device_t**>(&device)); ATRACE_END(); if (result != 0) { + android::GraphicsEnv::getInstance().setDriverLoaded( + android::GraphicsEnv::Api::API_VK, false, systemTime() - openTime); // Any device with a Vulkan HAL should be able to open the device. ALOGE("failed to open Vulkan HAL device: %s (%d)", strerror(-result), result); @@ -273,6 +283,9 @@ bool Hal::Open() { hal_.InitDebugReportIndex(); + android::GraphicsEnv::getInstance().setDriverLoaded( + android::GraphicsEnv::Api::API_VK, true, systemTime() - openTime); + return true; } diff --git a/vulkan/libvulkan/swapchain.cpp b/vulkan/libvulkan/swapchain.cpp index 32e19f7ba3..73fc7b2467 100644 --- a/vulkan/libvulkan/swapchain.cpp +++ b/vulkan/libvulkan/swapchain.cpp @@ -688,6 +688,8 @@ VkResult GetPhysicalDeviceSurfaceFormatsKHR(VkPhysicalDevice pdev, {VK_FORMAT_R8G8B8A8_UNORM, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, {VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, {VK_FORMAT_R5G6B5_UNORM_PACK16, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_A2B10G10R10_UNORM_PACK32, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, + {VK_FORMAT_R16G16B16A16_SFLOAT, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}, }; const uint32_t kNumFormats = sizeof(kFormats) / sizeof(kFormats[0]); uint32_t total_num_formats = kNumFormats; |